Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52d4705a91 |
22
.github/dependabot.yml
vendored
Normal file
22
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Docs: <https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/customizing-dependency-updates>
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: gomod
|
||||||
|
directory: /
|
||||||
|
schedule: {interval: weekly}
|
||||||
|
reviewers: [42wim]
|
||||||
|
assignees: [42wim]
|
||||||
|
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: /
|
||||||
|
schedule: {interval: weekly}
|
||||||
|
reviewers: [42wim]
|
||||||
|
assignees: [42wim]
|
||||||
|
|
||||||
|
- package-ecosystem: docker
|
||||||
|
directory: /
|
||||||
|
schedule: {interval: weekly}
|
||||||
|
reviewers: [42wim]
|
||||||
|
assignees: [42wim]
|
||||||
8
.github/workflows/development.yml
vendored
8
.github/workflows/development.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
test-build-upload:
|
test-build-upload:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.18.x]
|
go-version: [1.17.x]
|
||||||
platform: [ubuntu-latest]
|
platform: [ubuntu-latest]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
@@ -39,19 +39,19 @@ jobs:
|
|||||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/win/matterbridge-$VERSION-windows-amd64.exe
|
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/win/matterbridge-$VERSION-windows-amd64.exe
|
||||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/mac/matterbridge-$VERSION-darwin-amd64
|
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/mac/matterbridge-$VERSION-darwin-amd64
|
||||||
- name: Upload linux 64-bit
|
- name: Upload linux 64-bit
|
||||||
if: startsWith(matrix.go-version,'1.18')
|
if: startsWith(matrix.go-version,'1.17')
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: matterbridge-linux-64bit
|
name: matterbridge-linux-64bit
|
||||||
path: output/lin
|
path: output/lin
|
||||||
- name: Upload windows 64-bit
|
- name: Upload windows 64-bit
|
||||||
if: startsWith(matrix.go-version,'1.18')
|
if: startsWith(matrix.go-version,'1.17')
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: matterbridge-windows-64bit
|
name: matterbridge-windows-64bit
|
||||||
path: output/win
|
path: output/win
|
||||||
- name: Upload darwin 64-bit
|
- name: Upload darwin 64-bit
|
||||||
if: startsWith(matrix.go-version,'1.18')
|
if: startsWith(matrix.go-version,'1.17')
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: matterbridge-darwin-64bit
|
name: matterbridge-darwin-64bit
|
||||||
|
|||||||
@@ -204,14 +204,6 @@ linters:
|
|||||||
- tagliatelle
|
- tagliatelle
|
||||||
- errname
|
- errname
|
||||||
- typecheck
|
- typecheck
|
||||||
- grouper
|
|
||||||
- decorder
|
|
||||||
- maintidx
|
|
||||||
- exhaustruct
|
|
||||||
- asasalint
|
|
||||||
- execinquery
|
|
||||||
- nosnakecase
|
|
||||||
- exhaustive
|
|
||||||
# rules to deal with reported isues
|
# rules to deal with reported isues
|
||||||
issues:
|
issues:
|
||||||
# List of regexps of issue texts to exclude, empty list by default.
|
# List of regexps of issue texts to exclude, empty list by default.
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
FROM alpine AS builder
|
|
||||||
|
|
||||||
COPY . /go/src/matterbridge
|
|
||||||
RUN apk --no-cache add go git \
|
|
||||||
&& cd /go/src/matterbridge \
|
|
||||||
&& CGO_ENABLED=0 go build -tags whatsappmulti -mod vendor -ldflags "-X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge
|
|
||||||
|
|
||||||
FROM alpine
|
|
||||||
RUN apk --no-cache add ca-certificates mailcap
|
|
||||||
COPY --from=builder /bin/matterbridge /bin/matterbridge
|
|
||||||
RUN mkdir /etc/matterbridge \
|
|
||||||
&& touch /etc/matterbridge/matterbridge.toml \
|
|
||||||
&& ln -sf /matterbridge.toml /etc/matterbridge/matterbridge.toml
|
|
||||||
ENTRYPOINT ["/bin/matterbridge", "-conf", "/etc/matterbridge/matterbridge.toml"]
|
|
||||||
79
README.md
79
README.md
@@ -58,22 +58,21 @@ And more...
|
|||||||
- [Binaries](#binaries)
|
- [Binaries](#binaries)
|
||||||
- [Packages](#packages)
|
- [Packages](#packages)
|
||||||
- [Building](#building)
|
- [Building](#building)
|
||||||
- [Building with whatsapp (beta) multidevice support](#building-with-whatsapp-beta-multidevice-support)
|
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
- [Basic configuration](#basic-configuration)
|
- [Basic configuration](#basic-configuration)
|
||||||
- [Settings](#settings)
|
- [Settings](#settings)
|
||||||
- [Advanced configuration](#advanced-configuration)
|
- [Advanced configuration](#advanced-configuration)
|
||||||
- [Examples](#examples)
|
- [Examples](#examples)
|
||||||
- [Bridge mattermost (off-topic) - irc (#testing)](#bridge-mattermost-off-topic---irc-testing)
|
- [Bridge mattermost (off-topic) - irc (#testing)](#bridge-mattermost-off-topic---irc-testing)
|
||||||
- [Bridge slack (#general) - discord (general)](#bridge-slack-general---discord-general)
|
- [Bridge slack (#general) - discord (general)](#bridge-slack-general---discord-general)
|
||||||
- [Running](#running)
|
- [Running](#running)
|
||||||
- [Docker](#docker)
|
- [Docker](#docker)
|
||||||
- [Systemd](#systemd)
|
- [Systemd](#systemd)
|
||||||
- [Changelog](#changelog)
|
- [Changelog](#changelog)
|
||||||
- [FAQ](#faq)
|
- [FAQ](#faq)
|
||||||
- [Related projects](#related-projects)
|
- [Related projects](#related-projects)
|
||||||
- [Articles / Tutorials](#articles--tutorials)
|
- [Articles / Tutorials](#articles--tutorials)
|
||||||
- [Thanks](#thanks)
|
- [Thanks](#thanks)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -90,7 +89,6 @@ And more...
|
|||||||
|
|
||||||
- [Discord](https://discordapp.com)
|
- [Discord](https://discordapp.com)
|
||||||
- [Gitter](https://gitter.im)
|
- [Gitter](https://gitter.im)
|
||||||
- [Harmony](https://harmonyapp.io)
|
|
||||||
- [IRC](http://www.mirc.com/servers.html)
|
- [IRC](http://www.mirc.com/servers.html)
|
||||||
- [Keybase](https://keybase.io)
|
- [Keybase](https://keybase.io)
|
||||||
- [Matrix](https://matrix.org)
|
- [Matrix](https://matrix.org)
|
||||||
@@ -107,8 +105,6 @@ And more...
|
|||||||
- [Twitch](https://twitch.tv)
|
- [Twitch](https://twitch.tv)
|
||||||
- [VK](https://vk.com/)
|
- [VK](https://vk.com/)
|
||||||
- [WhatsApp](https://www.whatsapp.com/)
|
- [WhatsApp](https://www.whatsapp.com/)
|
||||||
- Whatsapp legacy is natively supported
|
|
||||||
- Whatsapp multidevice beta is natively supported but you need to build yourself, see [here](#building-with-whatsapp-beta-multidevice-support)
|
|
||||||
- [XMPP](https://xmpp.org)
|
- [XMPP](https://xmpp.org)
|
||||||
- [Zulip](https://zulipchat.com)
|
- [Zulip](https://zulipchat.com)
|
||||||
|
|
||||||
@@ -124,8 +120,6 @@ And more...
|
|||||||
- [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
|
- [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
|
||||||
- [MatterAMXX](https://github.com/GabeIggy/MatterAMXX)
|
- [MatterAMXX](https://github.com/GabeIggy/MatterAMXX)
|
||||||
- [Vintage Story](https://github.com/NikkyAI/vs-matterbridge)
|
- [Vintage Story](https://github.com/NikkyAI/vs-matterbridge)
|
||||||
- [Ultima Online Emulator](https://github.com/kuoushi/ServUO-Matterbridge)
|
|
||||||
- [Teamspeak](https://github.com/Archeb/ts-matterbridge)
|
|
||||||
|
|
||||||
### API
|
### API
|
||||||
|
|
||||||
@@ -144,9 +138,6 @@ Used by the projects below. Feel free to make a PR to add your project to this l
|
|||||||
- [matterbabble](https://github.com/DeclanHoare/matterbabble) (Discourse support)
|
- [matterbabble](https://github.com/DeclanHoare/matterbabble) (Discourse support)
|
||||||
- [MatterAMXX](https://forums.alliedmods.net/showthread.php?t=319430) (Counter-Strike, half-life and more via AMXX mod)
|
- [MatterAMXX](https://forums.alliedmods.net/showthread.php?t=319430) (Counter-Strike, half-life and more via AMXX mod)
|
||||||
- [Vintage Story](https://github.com/NikkyAI/vs-matterbridge)
|
- [Vintage Story](https://github.com/NikkyAI/vs-matterbridge)
|
||||||
- [ServUO-matterbridge](https://github.com/kuoushi/ServUO-Matterbridge) (A matterbridge connector for ServUO servers)
|
|
||||||
- [ts-matterbridge](https://github.com/Archeb/ts-matterbridge) (Integrate teamspeak chat with matterbridge)
|
|
||||||
- [beerchat](https://github.com/mt-mods/beerchat) (Matterbridge link for minetest)
|
|
||||||
|
|
||||||
## Chat with us
|
## Chat with us
|
||||||
|
|
||||||
@@ -173,10 +164,10 @@ See <https://github.com/42wim/matterbridge/wiki>
|
|||||||
|
|
||||||
### Binaries
|
### Binaries
|
||||||
|
|
||||||
- Latest stable release [v1.26.0](https://github.com/42wim/matterbridge/releases/latest)
|
- Latest stable release [v1.23.2](https://github.com/42wim/matterbridge/releases/latest)
|
||||||
- Development releases (follows master) can be downloaded [here](https://github.com/42wim/matterbridge/actions) selecting the latest green build and then artifacts.
|
- Development releases (follows master) can be downloaded [here](https://github.com/42wim/matterbridge/actions) selecting the latest green build and then artifacts.
|
||||||
|
|
||||||
To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest). On \*nix platforms you may need to make the binary executable - you can do this by running `chmod a+x` on the binary (example: `chmod a+x matterbridge-1.24.1-linux-64bit`). After downloading (and making the binary executable, if necessary), follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
|
To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest). On \*nix platforms you may need to make the binary executable - you can do this by running `chmod a+x` on the binary (example: `chmod a+x matterbridge-1.20.0-linux-64bit`). After downloading (and making the binary executable, if necessary), follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
|
||||||
|
|
||||||
### Packages
|
### Packages
|
||||||
|
|
||||||
@@ -189,12 +180,7 @@ To install or upgrade just download the latest [binary](https://github.com/42wim
|
|||||||
Most people just want to use binaries, you can find those [here](https://github.com/42wim/matterbridge/releases/latest)
|
Most people just want to use binaries, you can find those [here](https://github.com/42wim/matterbridge/releases/latest)
|
||||||
|
|
||||||
If you really want to build from source, follow these instructions:
|
If you really want to build from source, follow these instructions:
|
||||||
Go 1.18+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed.
|
Go 1.17+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed.
|
||||||
|
|
||||||
Building the binary with **all** the bridges enabled needs about 3GB RAM to compile.
|
|
||||||
You can reduce this memory requirement to 0,5GB RAM by adding the `nomsteams` tag if you don't need/use the Microsoft Teams bridge.
|
|
||||||
|
|
||||||
Matterbridge can be build without gcc/c-compiler: If you're running on windows first run `set CGO_ENABLED=0` on other platforms you prepend `CGO_ENABLED=0` to the `go build` command. (eg `CGO_ENABLED=0 go install github.com/42wim/matterbridge`)
|
|
||||||
|
|
||||||
To install the latest stable run:
|
To install the latest stable run:
|
||||||
|
|
||||||
@@ -208,38 +194,6 @@ To install the latest dev run:
|
|||||||
go install github.com/42wim/matterbridge@master
|
go install github.com/42wim/matterbridge@master
|
||||||
```
|
```
|
||||||
|
|
||||||
To install the latest stable run without msteams or zulip bridge:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go install -tags nomsteams,nozulip github.com/42wim/matterbridge
|
|
||||||
```
|
|
||||||
|
|
||||||
You should now have matterbridge binary in the ~/go/bin directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ ls ~/go/bin/
|
|
||||||
matterbridge
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building with whatsapp (beta) multidevice support
|
|
||||||
|
|
||||||
Because the library we use for Whatsapp multidevice support includes a GPL3 library we can not provide you binaries.
|
|
||||||
(as this would require the Matterbridge to change it license to GPL)
|
|
||||||
|
|
||||||
Matterbridge can be build without gcc/c-compiler: If you're running on windows first run `set CGO_ENABLED=0` on other platforms you prepend `CGO_ENABLED=0` to the `go build` command. (eg `CGO_ENABLED=0 go install github.com/42wim/matterbridge`)
|
|
||||||
|
|
||||||
So this means you have to build it yourself using the instructions below:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go install -tags whatsappmulti github.com/42wim/matterbridge@master
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're low on memory and don't need msteams:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go install -tags nomsteams,whatsappmulti github.com/42wim/matterbridge@master
|
|
||||||
```
|
|
||||||
|
|
||||||
You should now have matterbridge binary in the ~/go/bin directory:
|
You should now have matterbridge binary in the ~/go/bin directory:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -369,8 +323,6 @@ See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ)
|
|||||||
- [nextcloud talk](https://github.com/nextcloud/talk_matterbridge) (Integrates matterbridge in Nextcloud Talk)
|
- [nextcloud talk](https://github.com/nextcloud/talk_matterbridge) (Integrates matterbridge in Nextcloud Talk)
|
||||||
- [mattercraft](https://github.com/raws/mattercraft) (Minecraft bridge)
|
- [mattercraft](https://github.com/raws/mattercraft) (Minecraft bridge)
|
||||||
- [vs-matterbridge](https://github.com/NikkyAI/vs-matterbridge) (Vintage Story bridge)
|
- [vs-matterbridge](https://github.com/NikkyAI/vs-matterbridge) (Vintage Story bridge)
|
||||||
- [ServUO-matterbridge](https://github.com/kuoushi/ServUO-Matterbridge) (A matterbridge connector for ServUO servers)
|
|
||||||
- [ts-matterbridge](https://github.com/Archeb/ts-matterbridge) (Integrate teamspeak chat with matterbridge)
|
|
||||||
|
|
||||||
## Articles / Tutorials
|
## Articles / Tutorials
|
||||||
|
|
||||||
@@ -404,7 +356,6 @@ Matterbridge wouldn't exist without these libraries:
|
|||||||
- gops - <https://github.com/google/gops>
|
- gops - <https://github.com/google/gops>
|
||||||
- gozulipbot - <https://github.com/ifo/gozulipbot>
|
- gozulipbot - <https://github.com/ifo/gozulipbot>
|
||||||
- gumble - <https://github.com/layeh/gumble>
|
- gumble - <https://github.com/layeh/gumble>
|
||||||
- harmony - <https://github.com/harmony-development/shibshib>
|
|
||||||
- irc - <https://github.com/lrstanley/girc>
|
- irc - <https://github.com/lrstanley/girc>
|
||||||
- keybase - <https://github.com/keybase/go-keybase-chat-bot>
|
- keybase - <https://github.com/keybase/go-keybase-chat-bot>
|
||||||
- matrix - <https://github.com/matrix-org/gomatrix>
|
- matrix - <https://github.com/matrix-org/gomatrix>
|
||||||
@@ -412,7 +363,6 @@ Matterbridge wouldn't exist without these libraries:
|
|||||||
- msgraph.go - <https://github.com/yaegashi/msgraph.go>
|
- msgraph.go - <https://github.com/yaegashi/msgraph.go>
|
||||||
- mumble - <https://github.com/layeh/gumble>
|
- mumble - <https://github.com/layeh/gumble>
|
||||||
- nctalk - <https://github.com/gary-kim/go-nc-talk>
|
- nctalk - <https://github.com/gary-kim/go-nc-talk>
|
||||||
- rocketchat - <https://github.com/RocketChat/Rocket.Chat.Go.SDK>
|
|
||||||
- slack - <https://github.com/nlopes/slack>
|
- slack - <https://github.com/nlopes/slack>
|
||||||
- sshchat - <https://github.com/shazow/ssh-chat>
|
- sshchat - <https://github.com/shazow/ssh-chat>
|
||||||
- steam - <https://github.com/Philipp15b/go-steam>
|
- steam - <https://github.com/Philipp15b/go-steam>
|
||||||
@@ -420,7 +370,6 @@ Matterbridge wouldn't exist without these libraries:
|
|||||||
- tengo - <https://github.com/d5/tengo>
|
- tengo - <https://github.com/d5/tengo>
|
||||||
- vk - <https://github.com/SevereCloud/vksdk>
|
- vk - <https://github.com/SevereCloud/vksdk>
|
||||||
- whatsapp - <https://github.com/Rhymen/go-whatsapp>
|
- whatsapp - <https://github.com/Rhymen/go-whatsapp>
|
||||||
- whatsapp - <https://github.com/tulir/whatsmeow>
|
|
||||||
- xmpp - <https://github.com/mattn/go-xmpp>
|
- xmpp - <https://github.com/mattn/go-xmpp>
|
||||||
- zulip - <https://github.com/ifo/gozulipbot>
|
- zulip - <https://github.com/ifo/gozulipbot>
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
"github.com/42wim/matterbridge/bridge/discord/transmitter"
|
"github.com/42wim/matterbridge/bridge/discord/transmitter"
|
||||||
"github.com/42wim/matterbridge/bridge/helper"
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
"github.com/bwmarrin/discordgo"
|
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
"github.com/matterbridge/discordgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -83,12 +83,12 @@ func (b *Bdiscord) Connect() error {
|
|||||||
b.Log.Info("Connection succeeded")
|
b.Log.Info("Connection succeeded")
|
||||||
b.c.AddHandler(b.messageCreate)
|
b.c.AddHandler(b.messageCreate)
|
||||||
b.c.AddHandler(b.messageTyping)
|
b.c.AddHandler(b.messageTyping)
|
||||||
|
b.c.AddHandler(b.memberUpdate)
|
||||||
b.c.AddHandler(b.messageUpdate)
|
b.c.AddHandler(b.messageUpdate)
|
||||||
b.c.AddHandler(b.messageDelete)
|
b.c.AddHandler(b.messageDelete)
|
||||||
b.c.AddHandler(b.messageDeleteBulk)
|
b.c.AddHandler(b.messageDeleteBulk)
|
||||||
b.c.AddHandler(b.memberAdd)
|
b.c.AddHandler(b.memberAdd)
|
||||||
b.c.AddHandler(b.memberRemove)
|
b.c.AddHandler(b.memberRemove)
|
||||||
b.c.AddHandler(b.memberUpdate)
|
|
||||||
if b.GetInt("debuglevel") == 1 {
|
if b.GetInt("debuglevel") == 1 {
|
||||||
b.c.AddHandler(b.messageEvent)
|
b.c.AddHandler(b.messageEvent)
|
||||||
}
|
}
|
||||||
@@ -272,6 +272,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
|
|||||||
// Handle prefix hint for unthreaded messages.
|
// Handle prefix hint for unthreaded messages.
|
||||||
if msg.ParentNotFound() {
|
if msg.ParentNotFound() {
|
||||||
msg.ParentID = ""
|
msg.ParentID = ""
|
||||||
|
msg.Text = fmt.Sprintf("[thread]: %s", msg.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use webhook to send the message
|
// Use webhook to send the message
|
||||||
|
|||||||
@@ -2,15 +2,11 @@ package bdiscord
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
"github.com/bwmarrin/discordgo"
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/matterbridge/discordgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) { //nolint:unparam
|
func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) { //nolint:unparam
|
||||||
if m.GuildID != b.guildID {
|
|
||||||
b.Log.Debugf("Ignoring messageDelete because it originates from a different guild")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rmsg := config.Message{Account: b.Account, ID: m.ID, Event: config.EventMsgDelete, Text: config.EventMsgDelete}
|
rmsg := config.Message{Account: b.Account, ID: m.ID, Event: config.EventMsgDelete, Text: config.EventMsgDelete}
|
||||||
rmsg.Channel = b.getChannelName(m.ChannelID)
|
rmsg.Channel = b.getChannelName(m.ChannelID)
|
||||||
|
|
||||||
@@ -21,10 +17,6 @@ func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelet
|
|||||||
|
|
||||||
// TODO(qaisjp): if other bridges support bulk deletions, it could be fanned out centrally
|
// TODO(qaisjp): if other bridges support bulk deletions, it could be fanned out centrally
|
||||||
func (b *Bdiscord) messageDeleteBulk(s *discordgo.Session, m *discordgo.MessageDeleteBulk) { //nolint:unparam
|
func (b *Bdiscord) messageDeleteBulk(s *discordgo.Session, m *discordgo.MessageDeleteBulk) { //nolint:unparam
|
||||||
if m.GuildID != b.guildID {
|
|
||||||
b.Log.Debugf("Ignoring messageDeleteBulk because it originates from a different guild")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, msgID := range m.Messages {
|
for _, msgID := range m.Messages {
|
||||||
rmsg := config.Message{
|
rmsg := config.Message{
|
||||||
Account: b.Account,
|
Account: b.Account,
|
||||||
@@ -45,10 +37,6 @@ func (b *Bdiscord) messageEvent(s *discordgo.Session, m *discordgo.Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bdiscord) messageTyping(s *discordgo.Session, m *discordgo.TypingStart) {
|
func (b *Bdiscord) messageTyping(s *discordgo.Session, m *discordgo.TypingStart) {
|
||||||
if m.GuildID != b.guildID {
|
|
||||||
b.Log.Debugf("Ignoring messageTyping because it originates from a different guild")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !b.GetBool("ShowUserTyping") {
|
if !b.GetBool("ShowUserTyping") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -64,15 +52,11 @@ func (b *Bdiscord) messageTyping(s *discordgo.Session, m *discordgo.TypingStart)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) { //nolint:unparam
|
func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) { //nolint:unparam
|
||||||
if m.GuildID != b.guildID {
|
|
||||||
b.Log.Debugf("Ignoring messageUpdate because it originates from a different guild")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if b.GetBool("EditDisable") {
|
if b.GetBool("EditDisable") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// only when message is actually edited
|
// only when message is actually edited
|
||||||
if m.Message.EditedTimestamp != nil {
|
if m.Message.EditedTimestamp != "" {
|
||||||
b.Log.Debugf("Sending edit message")
|
b.Log.Debugf("Sending edit message")
|
||||||
m.Content += b.GetString("EditSuffix")
|
m.Content += b.GetString("EditSuffix")
|
||||||
msg := &discordgo.MessageCreate{
|
msg := &discordgo.MessageCreate{
|
||||||
@@ -83,10 +67,6 @@ func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { //nolint:unparam
|
func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { //nolint:unparam
|
||||||
if m.GuildID != b.guildID {
|
|
||||||
b.Log.Debugf("Ignoring messageCreate because it originates from a different guild")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// not relay our own messages
|
// not relay our own messages
|
||||||
@@ -164,10 +144,6 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bdiscord) memberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) {
|
func (b *Bdiscord) memberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) {
|
||||||
if m.GuildID != b.guildID {
|
|
||||||
b.Log.Debugf("Ignoring memberUpdate because it originates from a different guild")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if m.Member == nil {
|
if m.Member == nil {
|
||||||
b.Log.Warnf("Received member update with no member information: %#v", m)
|
b.Log.Warnf("Received member update with no member information: %#v", m)
|
||||||
}
|
}
|
||||||
@@ -195,13 +171,6 @@ func (b *Bdiscord) memberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUp
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bdiscord) memberAdd(s *discordgo.Session, m *discordgo.GuildMemberAdd) {
|
func (b *Bdiscord) memberAdd(s *discordgo.Session, m *discordgo.GuildMemberAdd) {
|
||||||
if m.GuildID != b.guildID {
|
|
||||||
b.Log.Debugf("Ignoring memberAdd because it originates from a different guild")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if b.GetBool("nosendjoinpart") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if m.Member == nil {
|
if m.Member == nil {
|
||||||
b.Log.Warnf("Received member update with no member information: %#v", m)
|
b.Log.Warnf("Received member update with no member information: %#v", m)
|
||||||
return
|
return
|
||||||
@@ -223,13 +192,6 @@ func (b *Bdiscord) memberAdd(s *discordgo.Session, m *discordgo.GuildMemberAdd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bdiscord) memberRemove(s *discordgo.Session, m *discordgo.GuildMemberRemove) {
|
func (b *Bdiscord) memberRemove(s *discordgo.Session, m *discordgo.GuildMemberRemove) {
|
||||||
if m.GuildID != b.guildID {
|
|
||||||
b.Log.Debugf("Ignoring memberRemove because it originates from a different guild")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if b.GetBool("nosendjoinpart") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if m.Member == nil {
|
if m.Member == nil {
|
||||||
b.Log.Warnf("Received member update with no member information: %#v", m)
|
b.Log.Warnf("Received member update with no member information: %#v", m)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package bdiscord
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/matterbridge/discordgo"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/matterbridge/discordgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *Bdiscord) getAllowedMentions() *discordgo.MessageAllowedMentions {
|
func (b *Bdiscord) getAllowedMentions() *discordgo.MessageAllowedMentions {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/matterbridge/discordgo"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package transmitter
|
package transmitter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/matterbridge/discordgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// isDiscordPermissionError returns false for nil, and true if a Discord RESTError with code discordgo.ErrorCodeMissionPermissions
|
// isDiscordPermissionError returns false for nil, and true if a Discord RESTError with code discordgo.ErrorCodeMissionPermissions
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
"github.com/42wim/matterbridge/bridge/helper"
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/matterbridge/discordgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// shouldMessageUseWebhooks checks if have a channel specific webhook, if we're not using auto webhooks
|
// shouldMessageUseWebhooks checks if have a channel specific webhook, if we're not using auto webhooks
|
||||||
@@ -89,7 +89,7 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg
|
|||||||
&discordgo.WebhookParams{
|
&discordgo.WebhookParams{
|
||||||
Username: msg.Username,
|
Username: msg.Username,
|
||||||
AvatarURL: msg.Avatar,
|
AvatarURL: msg.Avatar,
|
||||||
Files: []*discordgo.File{&file},
|
File: &file,
|
||||||
Content: content,
|
Content: content,
|
||||||
AllowedMentions: b.getAllowedMentions(),
|
AllowedMentions: b.getAllowedMentions(),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -80,18 +80,8 @@ func DownloadFileAuthRocket(url, token, userID string) (*[]byte, error) {
|
|||||||
// word boundaries when splitting but this is hard to solve without potentially
|
// word boundaries when splitting but this is hard to solve without potentially
|
||||||
// breaking formatting and other stylistic effects.
|
// breaking formatting and other stylistic effects.
|
||||||
func GetSubLines(message string, maxLineLength int, clippingMessage string) []string {
|
func GetSubLines(message string, maxLineLength int, clippingMessage string) []string {
|
||||||
if clippingMessage == "" {
|
|
||||||
clippingMessage = " <clipped message>"
|
|
||||||
}
|
|
||||||
|
|
||||||
var lines []string
|
var lines []string
|
||||||
for _, line := range strings.Split(strings.TrimSpace(message), "\n") {
|
for _, line := range strings.Split(strings.TrimSpace(message), "\n") {
|
||||||
if line == "" {
|
|
||||||
// Prevent sending empty messages, so we'll skip this line
|
|
||||||
// if it has no content.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if maxLineLength == 0 || len([]byte(line)) <= maxLineLength {
|
if maxLineLength == 0 || len([]byte(line)) <= maxLineLength {
|
||||||
lines = append(lines, line)
|
lines = append(lines, line)
|
||||||
continue
|
continue
|
||||||
@@ -104,8 +94,8 @@ func GetSubLines(message string, maxLineLength int, clippingMessage string) []st
|
|||||||
var splitStart int
|
var splitStart int
|
||||||
var startOfPreviousRune int
|
var startOfPreviousRune int
|
||||||
for i := range line {
|
for i := range line {
|
||||||
if i-splitStart > maxLineLength-len([]byte(clippingMessage)) {
|
if i-splitStart > maxLineLength {
|
||||||
lines = append(lines, line[splitStart:startOfPreviousRune]+clippingMessage)
|
lines = append(lines, line[splitStart:startOfPreviousRune])
|
||||||
splitStart = startOfPreviousRune
|
splitStart = startOfPreviousRune
|
||||||
}
|
}
|
||||||
startOfPreviousRune = i
|
startOfPreviousRune = i
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build cgolottie
|
//go:build cgo
|
||||||
|
// +build cgo
|
||||||
|
|
||||||
package helper
|
package helper
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !cgolottie
|
// +build !cgo
|
||||||
|
|
||||||
package helper
|
package helper
|
||||||
|
|
||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
package birc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/text/encoding"
|
|
||||||
"golang.org/x/text/encoding/japanese"
|
|
||||||
"golang.org/x/text/encoding/korean"
|
|
||||||
"golang.org/x/text/encoding/simplifiedchinese"
|
|
||||||
"golang.org/x/text/encoding/traditionalchinese"
|
|
||||||
"golang.org/x/text/encoding/unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
var encoders = map[string]encoding.Encoding{
|
|
||||||
"utf-8": unicode.UTF8,
|
|
||||||
"iso-2022-jp": japanese.ISO2022JP,
|
|
||||||
"big5": traditionalchinese.Big5,
|
|
||||||
"gbk": simplifiedchinese.GBK,
|
|
||||||
"euc-kr": korean.EUCKR,
|
|
||||||
"gb2312": simplifiedchinese.HZGB2312,
|
|
||||||
"shift-jis": japanese.ShiftJIS,
|
|
||||||
"euc-jp": japanese.EUCJP,
|
|
||||||
"gb18030": simplifiedchinese.GB18030,
|
|
||||||
}
|
|
||||||
|
|
||||||
func toUTF8(from string, input string) string {
|
|
||||||
enc, ok := encoders[from]
|
|
||||||
if !ok {
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _ := enc.NewDecoder().String(input)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
"github.com/42wim/matterbridge/bridge/helper"
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
"github.com/lrstanley/girc"
|
"github.com/lrstanley/girc"
|
||||||
|
"github.com/missdeer/golib/ic"
|
||||||
"github.com/paulrosania/go-charset/charset"
|
"github.com/paulrosania/go-charset/charset"
|
||||||
"github.com/saintfish/chardet"
|
"github.com/saintfish/chardet"
|
||||||
|
|
||||||
@@ -23,12 +24,12 @@ func (b *Birc) handleCharset(msg *config.Message) error {
|
|||||||
if b.GetString("Charset") != "" {
|
if b.GetString("Charset") != "" {
|
||||||
switch b.GetString("Charset") {
|
switch b.GetString("Charset") {
|
||||||
case "gbk", "gb18030", "gb2312", "big5", "euc-kr", "euc-jp", "shift-jis", "iso-2022-jp":
|
case "gbk", "gb18030", "gb2312", "big5", "euc-kr", "euc-jp", "shift-jis", "iso-2022-jp":
|
||||||
msg.Text = toUTF8(b.GetString("Charset"), msg.Text)
|
msg.Text = ic.ConvertString("utf-8", b.GetString("Charset"), msg.Text)
|
||||||
default:
|
default:
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
w, err := charset.NewWriter(b.GetString("Charset"), buf)
|
w, err := charset.NewWriter(b.GetString("Charset"), buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Log.Errorf("charset to utf-8 conversion failed: %s", err)
|
b.Log.Errorf("charset from utf-8 conversion failed: %s", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprint(w, msg.Text)
|
fmt.Fprint(w, msg.Text)
|
||||||
@@ -226,7 +227,7 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
|
|||||||
}
|
}
|
||||||
switch mycharset {
|
switch mycharset {
|
||||||
case "gbk", "gb18030", "gb2312", "big5", "euc-kr", "euc-jp", "shift-jis", "iso-2022-jp":
|
case "gbk", "gb18030", "gb2312", "big5", "euc-kr", "euc-jp", "shift-jis", "iso-2022-jp":
|
||||||
rmsg.Text = toUTF8(b.GetString("Charset"), rmsg.Text)
|
rmsg.Text = ic.ConvertString("utf-8", b.GetString("Charset"), rmsg.Text)
|
||||||
default:
|
default:
|
||||||
r, err := charset.NewReader(mycharset, strings.NewReader(rmsg.Text))
|
r, err := charset.NewReader(mycharset, strings.NewReader(rmsg.Text))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -243,7 +244,6 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
|
|||||||
|
|
||||||
func (b *Birc) handleRunCommands() {
|
func (b *Birc) handleRunCommands() {
|
||||||
for _, cmd := range b.GetStringSlice("RunCommands") {
|
for _, cmd := range b.GetStringSlice("RunCommands") {
|
||||||
cmd = strings.ReplaceAll(cmd, "{BOTNICK}", b.Nick)
|
|
||||||
if err := b.i.Cmd.SendRaw(cmd); err != nil {
|
if err := b.i.Cmd.SendRaw(cmd); err != nil {
|
||||||
b.Log.Errorf("RunCommands %s failed: %s", cmd, err)
|
b.Log.Errorf("RunCommands %s failed: %s", cmd, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
|
|
||||||
type Birc struct {
|
type Birc struct {
|
||||||
i *girc.Client
|
i *girc.Client
|
||||||
Nick string
|
Nick, MessageClipped string
|
||||||
names map[string][]string
|
names map[string][]string
|
||||||
connected chan error
|
connected chan error
|
||||||
Local chan config.Message // local queue for flood control
|
Local chan config.Message // local queue for flood control
|
||||||
@@ -172,10 +172,11 @@ func (b *Birc) Send(msg config.Message) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if b.GetBool("MessageSplit") {
|
if b.GetBool("MessageSplit") {
|
||||||
msgLines = helper.GetSubLines(msg.Text, b.MessageLength, b.GetString("MessageClipped"))
|
msgLines = helper.GetSubLines(msg.Text, b.MessageLength, "")
|
||||||
} else {
|
} else {
|
||||||
msgLines = helper.GetSubLines(msg.Text, 0, b.GetString("MessageClipped"))
|
msgLines = []string{helper.GetSubLines(msg.Text, b.MessageLength, "")[0] + b.getMessageClipped()}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range msgLines {
|
for i := range msgLines {
|
||||||
if len(b.Local) >= b.MessageQueue {
|
if len(b.Local) >= b.MessageQueue {
|
||||||
b.Log.Debugf("flooding, dropping message (queue at %d)", len(b.Local))
|
b.Log.Debugf("flooding, dropping message (queue at %d)", len(b.Local))
|
||||||
@@ -362,10 +363,8 @@ func (b *Birc) skipPrivMsg(event girc.Event) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// don't forward message from ourself
|
// don't forward message from ourself
|
||||||
if event.Source != nil {
|
if event.Source.Name == b.Nick {
|
||||||
if event.Source.Name == b.Nick {
|
return true
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// don't forward messages we sent via RELAYMSG
|
// don't forward messages we sent via RELAYMSG
|
||||||
if relayedNick, ok := event.Tags.Get("draft/relaymsg"); ok && relayedNick == b.Nick {
|
if relayedNick, ok := event.Tags.Get("draft/relaymsg"); ok && relayedNick == b.Nick {
|
||||||
@@ -413,3 +412,11 @@ func (b *Birc) getTLSConfig() (*tls.Config, error) {
|
|||||||
|
|
||||||
return tlsConfig, nil
|
return tlsConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Birc) getMessageClipped() string {
|
||||||
|
if b.GetString("MessageClipped") == "" {
|
||||||
|
return " <clipped message>"
|
||||||
|
}
|
||||||
|
|
||||||
|
return " " + b.GetString("MessageClipped")
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
matrix "github.com/matterbridge/gomatrix"
|
matrix "github.com/matrix-org/gomatrix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newMatrixUsername(username string) *matrixUsername {
|
func newMatrixUsername(username string) *matrixUsername {
|
||||||
@@ -51,7 +51,7 @@ func interface2Struct(in interface{}, out interface{}) error {
|
|||||||
return json.Unmarshal(jsonObj, out)
|
return json.Unmarshal(jsonObj, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDisplayName retrieves the displayName for mxid, querying the homeserver if the mxid is not in the cache.
|
// getDisplayName retrieves the displayName for mxid, querying the homserver if the mxid is not in the cache.
|
||||||
func (b *Bmatrix) getDisplayName(mxid string) string {
|
func (b *Bmatrix) getDisplayName(mxid string) string {
|
||||||
if b.GetBool("UseUserName") {
|
if b.GetBool("UseUserName") {
|
||||||
return mxid[1:]
|
return mxid[1:]
|
||||||
|
|||||||
@@ -12,7 +12,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/bridge/helper"
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
matrix "github.com/matterbridge/gomatrix"
|
matrix "github.com/matrix-org/gomatrix"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -148,37 +148,12 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
|
|||||||
|
|
||||||
username := newMatrixUsername(msg.Username)
|
username := newMatrixUsername(msg.Username)
|
||||||
|
|
||||||
body := username.plain + msg.Text
|
|
||||||
formattedBody := username.formatted + helper.ParseMarkdown(msg.Text)
|
|
||||||
|
|
||||||
if b.GetBool("SpoofUsername") {
|
|
||||||
// https://spec.matrix.org/v1.3/client-server-api/#mroommember
|
|
||||||
type stateMember struct {
|
|
||||||
AvatarURL string `json:"avatar_url,omitempty"`
|
|
||||||
DisplayName string `json:"displayname"`
|
|
||||||
Membership string `json:"membership"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: reset username afterwards with DisplayName: null ?
|
|
||||||
m := stateMember{
|
|
||||||
AvatarURL: "",
|
|
||||||
DisplayName: username.plain,
|
|
||||||
Membership: "join",
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := b.mc.SendStateEvent(channel, "m.room.member", b.UserID, m)
|
|
||||||
if err == nil {
|
|
||||||
body = msg.Text
|
|
||||||
formattedBody = helper.ParseMarkdown(msg.Text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a action /me of the message
|
// Make a action /me of the message
|
||||||
if msg.Event == config.EventUserAction {
|
if msg.Event == config.EventUserAction {
|
||||||
m := matrix.TextMessage{
|
m := matrix.TextMessage{
|
||||||
MsgType: "m.emote",
|
MsgType: "m.emote",
|
||||||
Body: body,
|
Body: username.plain + msg.Text,
|
||||||
FormattedBody: formattedBody,
|
FormattedBody: username.formatted + helper.ParseMarkdown(msg.Text),
|
||||||
Format: "org.matrix.custom.html",
|
Format: "org.matrix.custom.html",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,10 +224,10 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
|
|||||||
if msg.ID != "" {
|
if msg.ID != "" {
|
||||||
rmsg := EditedMessage{
|
rmsg := EditedMessage{
|
||||||
TextMessage: matrix.TextMessage{
|
TextMessage: matrix.TextMessage{
|
||||||
Body: body,
|
Body: username.plain + msg.Text,
|
||||||
MsgType: "m.text",
|
MsgType: "m.text",
|
||||||
Format: "org.matrix.custom.html",
|
Format: "org.matrix.custom.html",
|
||||||
FormattedBody: formattedBody,
|
FormattedBody: username.formatted + helper.ParseMarkdown(msg.Text),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,8 +266,8 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
|
|||||||
if msg.Event == config.EventJoinLeave {
|
if msg.Event == config.EventJoinLeave {
|
||||||
m := matrix.TextMessage{
|
m := matrix.TextMessage{
|
||||||
MsgType: "m.notice",
|
MsgType: "m.notice",
|
||||||
Body: body,
|
Body: username.plain + msg.Text,
|
||||||
FormattedBody: formattedBody,
|
FormattedBody: username.formatted + msg.Text,
|
||||||
Format: "org.matrix.custom.html",
|
Format: "org.matrix.custom.html",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,8 +297,8 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
|
|||||||
m := ReplyMessage{
|
m := ReplyMessage{
|
||||||
TextMessage: matrix.TextMessage{
|
TextMessage: matrix.TextMessage{
|
||||||
MsgType: "m.text",
|
MsgType: "m.text",
|
||||||
Body: body,
|
Body: username.plain + msg.Text,
|
||||||
FormattedBody: formattedBody,
|
FormattedBody: username.formatted + helper.ParseMarkdown(msg.Text),
|
||||||
Format: "org.matrix.custom.html",
|
Format: "org.matrix.custom.html",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -363,7 +338,7 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
err = b.retry(func() error {
|
err = b.retry(func() error {
|
||||||
resp, err = b.mc.SendText(channel, body)
|
resp, err = b.mc.SendText(channel, username.plain+msg.Text)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
@@ -381,7 +356,8 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
err = b.retry(func() error {
|
err = b.retry(func() error {
|
||||||
resp, err = b.mc.SendFormattedText(channel, body, formattedBody)
|
resp, err = b.mc.SendFormattedText(channel, username.plain+msg.Text,
|
||||||
|
username.formatted+helper.ParseMarkdown(msg.Text))
|
||||||
|
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
@@ -452,15 +428,12 @@ func (b *Bmatrix) handleReply(ev *matrix.Event, rmsg config.Message) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body := rmsg.Text
|
body := rmsg.Text
|
||||||
|
for strings.HasPrefix(body, "> ") {
|
||||||
if !b.GetBool("keepquotedreply") {
|
lineIdx := strings.IndexRune(body, '\n')
|
||||||
for strings.HasPrefix(body, "> ") {
|
if lineIdx == -1 {
|
||||||
lineIdx := strings.IndexRune(body, '\n')
|
body = ""
|
||||||
if lineIdx == -1 {
|
} else {
|
||||||
body = ""
|
body = body[(lineIdx + 1):]
|
||||||
} else {
|
|
||||||
body = body[(lineIdx + 1):]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ package bmattermost
|
|||||||
import (
|
import (
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
"github.com/42wim/matterbridge/bridge/helper"
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
"github.com/matterbridge/matterclient"
|
"github.com/42wim/matterbridge/matterclient"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
matterclient6 "github.com/matterbridge/matterclient"
|
||||||
|
"github.com/mattermost/mattermost-server/v5/model"
|
||||||
|
model6 "github.com/mattermost/mattermost-server/v6/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// handleDownloadAvatar downloads the avatar of userid from channel
|
// handleDownloadAvatar downloads the avatar of userid from channel
|
||||||
@@ -24,11 +26,20 @@ func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
|
|||||||
var (
|
var (
|
||||||
data []byte
|
data []byte
|
||||||
err error
|
err error
|
||||||
|
resp *model.Response
|
||||||
)
|
)
|
||||||
data, _, err = b.mc.Client.GetProfileImage(userid, "")
|
if b.mc6 != nil {
|
||||||
if err != nil {
|
data, _, err = b.mc6.Client.GetProfileImage(userid, "")
|
||||||
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, err)
|
if err != nil {
|
||||||
return
|
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data, resp = b.mc.Client.GetProfileImage(userid, "")
|
||||||
|
if resp.Error != nil {
|
||||||
|
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, resp.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = helper.HandleDownloadSize(b.Log, &rmsg, userid+".png", int64(len(data)), b.General)
|
err = helper.HandleDownloadSize(b.Log, &rmsg, userid+".png", int64(len(data)), b.General)
|
||||||
@@ -41,10 +52,33 @@ func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:wrapcheck
|
// handleDownloadFile handles file download
|
||||||
func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error {
|
func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error {
|
||||||
url, _, _ := b.mc.Client.GetFileLink(id)
|
if b.mc6 != nil {
|
||||||
finfo, _, err := b.mc.Client.GetFileInfo(id)
|
return b.handleDownloadFile6(rmsg, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
url, _ := b.mc.Client.GetFileLink(id)
|
||||||
|
finfo, resp := b.mc.Client.GetFileInfo(id)
|
||||||
|
if resp.Error != nil {
|
||||||
|
return resp.Error
|
||||||
|
}
|
||||||
|
err := helper.HandleDownloadSize(b.Log, rmsg, finfo.Name, finfo.Size, b.General)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data, resp := b.mc.Client.DownloadFile(id, true)
|
||||||
|
if resp.Error != nil {
|
||||||
|
return resp.Error
|
||||||
|
}
|
||||||
|
helper.HandleDownloadData(b.Log, rmsg, finfo.Name, rmsg.Text, url, &data, b.General)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:wrapcheck
|
||||||
|
func (b *Bmattermost) handleDownloadFile6(rmsg *config.Message, id string) error {
|
||||||
|
url, _, _ := b.mc6.Client.GetFileLink(id)
|
||||||
|
finfo, _, err := b.mc6.Client.GetFileInfo(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -52,7 +86,7 @@ func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data, _, err := b.mc.Client.DownloadFile(id, true)
|
data, _, err := b.mc6.Client.DownloadFile(id, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -91,10 +125,15 @@ func (b *Bmattermost) handleMatter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:cyclop
|
|
||||||
func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
|
func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
|
||||||
|
if b.mc6 != nil {
|
||||||
|
b.Log.Debug("starting matterclient6")
|
||||||
|
b.handleMatterClient6(messages)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for message := range b.mc.MessageChan {
|
for message := range b.mc.MessageChan {
|
||||||
b.Log.Debugf("%#v %#v", message.Raw.GetData(), message.Raw.EventType())
|
b.Log.Debugf("%#v", message.Raw.Data)
|
||||||
|
|
||||||
if b.skipMessage(message) {
|
if b.skipMessage(message) {
|
||||||
b.Log.Debugf("Skipped message: %#v", message)
|
b.Log.Debugf("Skipped message: %#v", message)
|
||||||
@@ -127,11 +166,11 @@ func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
|
|||||||
b.handleProps(rmsg, message)
|
b.handleProps(rmsg, message)
|
||||||
|
|
||||||
// create a text for bridges that don't support native editing
|
// create a text for bridges that don't support native editing
|
||||||
if message.Raw.EventType() == model.WebsocketEventPostEdited && !b.GetBool("EditDisable") {
|
if message.Raw.Event == model.WEBSOCKET_EVENT_POST_EDITED && !b.GetBool("EditDisable") {
|
||||||
rmsg.Text = message.Text + b.GetString("EditSuffix")
|
rmsg.Text = message.Text + b.GetString("EditSuffix")
|
||||||
}
|
}
|
||||||
|
|
||||||
if message.Raw.EventType() == model.WebsocketEventPostDeleted {
|
if message.Raw.Event == model.WEBSOCKET_EVENT_POST_DELETED {
|
||||||
rmsg.Event = config.EventMsgDelete
|
rmsg.Event = config.EventMsgDelete
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +192,68 @@ func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:cyclop
|
||||||
|
func (b *Bmattermost) handleMatterClient6(messages chan *config.Message) {
|
||||||
|
for message := range b.mc6.MessageChan {
|
||||||
|
b.Log.Debugf("%#v %#v", message.Raw.GetData(), message.Raw.EventType())
|
||||||
|
|
||||||
|
if b.skipMessage6(message) {
|
||||||
|
b.Log.Debugf("Skipped message: %#v", message)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
channelName := b.getChannelName(message.Post.ChannelId)
|
||||||
|
if channelName == "" {
|
||||||
|
channelName = message.Channel
|
||||||
|
}
|
||||||
|
|
||||||
|
// only download avatars if we have a place to upload them (configured mediaserver)
|
||||||
|
if b.General.MediaServerUpload != "" || b.General.MediaDownloadPath != "" {
|
||||||
|
b.handleDownloadAvatar(message.UserID, channelName)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Log.Debugf("== Receiving event %#v", message)
|
||||||
|
|
||||||
|
rmsg := &config.Message{
|
||||||
|
Username: message.Username,
|
||||||
|
UserID: message.UserID,
|
||||||
|
Channel: channelName,
|
||||||
|
Text: message.Text,
|
||||||
|
ID: message.Post.Id,
|
||||||
|
ParentID: message.Post.RootId, // ParentID is obsolete with mattermost
|
||||||
|
Extra: make(map[string][]interface{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle mattermost post properties (override username and attachments)
|
||||||
|
b.handleProps6(rmsg, message)
|
||||||
|
|
||||||
|
// create a text for bridges that don't support native editing
|
||||||
|
if message.Raw.EventType() == model6.WebsocketEventPostEdited && !b.GetBool("EditDisable") {
|
||||||
|
rmsg.Text = message.Text + b.GetString("EditSuffix")
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.Raw.EventType() == model6.WebsocketEventPostDeleted {
|
||||||
|
rmsg.Event = config.EventMsgDelete
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range message.Post.FileIds {
|
||||||
|
err := b.handleDownloadFile(rmsg, id)
|
||||||
|
if err != nil {
|
||||||
|
b.Log.Errorf("download failed: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use nickname instead of username if defined
|
||||||
|
if !b.GetBool("useusername") {
|
||||||
|
if nick := b.mc6.GetNickName(rmsg.UserID); nick != "" {
|
||||||
|
rmsg.Username = nick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
messages <- rmsg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Bmattermost) handleMatterHook(messages chan *config.Message) {
|
func (b *Bmattermost) handleMatterHook(messages chan *config.Message) {
|
||||||
for {
|
for {
|
||||||
message := b.mh.Receive()
|
message := b.mh.Receive()
|
||||||
@@ -167,7 +268,12 @@ func (b *Bmattermost) handleMatterHook(messages chan *config.Message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleUploadFile handles native upload of files
|
||||||
func (b *Bmattermost) handleUploadFile(msg *config.Message) (string, error) {
|
func (b *Bmattermost) handleUploadFile(msg *config.Message) (string, error) {
|
||||||
|
if b.mc6 != nil {
|
||||||
|
return b.handleUploadFile6(msg)
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var res, id string
|
var res, id string
|
||||||
channelID := b.getChannelID(msg.Channel)
|
channelID := b.getChannelID(msg.Channel)
|
||||||
@@ -186,8 +292,53 @@ func (b *Bmattermost) handleUploadFile(msg *config.Message) (string, error) {
|
|||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:forcetypeassert
|
// nolint:forcetypeassert,wrapcheck
|
||||||
|
func (b *Bmattermost) handleUploadFile6(msg *config.Message) (string, error) {
|
||||||
|
var err error
|
||||||
|
var res, id string
|
||||||
|
channelID := b.getChannelID(msg.Channel)
|
||||||
|
for _, f := range msg.Extra["file"] {
|
||||||
|
fi := f.(config.FileInfo)
|
||||||
|
id, err = b.mc6.UploadFile(*fi.Data, channelID, fi.Name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
msg.Text = fi.Comment
|
||||||
|
if b.GetBool("PrefixMessagesWithNick") {
|
||||||
|
msg.Text = msg.Username + msg.Text
|
||||||
|
}
|
||||||
|
res, err = b.mc6.PostMessageWithFiles(channelID, msg.Text, msg.ParentID, []string{id})
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Bmattermost) handleProps(rmsg *config.Message, message *matterclient.Message) {
|
func (b *Bmattermost) handleProps(rmsg *config.Message, message *matterclient.Message) {
|
||||||
|
props := message.Post.Props
|
||||||
|
if props == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok := props["override_username"].(string); ok {
|
||||||
|
rmsg.Username = props["override_username"].(string)
|
||||||
|
}
|
||||||
|
if _, ok := props["attachments"].([]interface{}); ok {
|
||||||
|
rmsg.Extra["attachments"] = props["attachments"].([]interface{})
|
||||||
|
if rmsg.Text == "" {
|
||||||
|
for _, attachment := range rmsg.Extra["attachments"] {
|
||||||
|
attach := attachment.(map[string]interface{})
|
||||||
|
if attach["text"].(string) != "" {
|
||||||
|
rmsg.Text += attach["text"].(string)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if attach["fallback"].(string) != "" {
|
||||||
|
rmsg.Text += attach["fallback"].(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:forcetypeassert
|
||||||
|
func (b *Bmattermost) handleProps6(rmsg *config.Message, message *matterclient6.Message) {
|
||||||
props := message.Post.Props
|
props := message.Post.Props
|
||||||
if props == nil {
|
if props == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import (
|
|||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
"github.com/42wim/matterbridge/bridge/helper"
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
|
"github.com/42wim/matterbridge/matterclient"
|
||||||
"github.com/42wim/matterbridge/matterhook"
|
"github.com/42wim/matterbridge/matterhook"
|
||||||
"github.com/matterbridge/matterclient"
|
matterclient6 "github.com/matterbridge/matterclient"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v5/model"
|
||||||
|
model6 "github.com/mattermost/mattermost-server/v6/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *Bmattermost) doConnectWebhookBind() error {
|
func (b *Bmattermost) doConnectWebhookBind() error {
|
||||||
@@ -22,15 +24,33 @@ func (b *Bmattermost) doConnectWebhookBind() error {
|
|||||||
})
|
})
|
||||||
case b.GetString("Token") != "":
|
case b.GetString("Token") != "":
|
||||||
b.Log.Info("Connecting using token (sending)")
|
b.Log.Info("Connecting using token (sending)")
|
||||||
err := b.apiLogin()
|
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||||
if err != nil {
|
|
||||||
return err
|
if b.v6 {
|
||||||
|
err := b.apiLogin6()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := b.apiLogin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case b.GetString("Login") != "":
|
case b.GetString("Login") != "":
|
||||||
b.Log.Info("Connecting using login/password (sending)")
|
b.Log.Info("Connecting using login/password (sending)")
|
||||||
err := b.apiLogin()
|
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||||
if err != nil {
|
|
||||||
return err
|
if b.v6 {
|
||||||
|
err := b.apiLogin6()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := b.apiLogin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
b.Log.Info("Connecting using webhookbindaddress (receiving)")
|
b.Log.Info("Connecting using webhookbindaddress (receiving)")
|
||||||
@@ -52,28 +72,45 @@ func (b *Bmattermost) doConnectWebhookURL() error {
|
|||||||
})
|
})
|
||||||
if b.GetString("Token") != "" {
|
if b.GetString("Token") != "" {
|
||||||
b.Log.Info("Connecting using token (receiving)")
|
b.Log.Info("Connecting using token (receiving)")
|
||||||
err := b.apiLogin()
|
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||||
if err != nil {
|
|
||||||
return err
|
if b.v6 {
|
||||||
|
err := b.apiLogin6()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := b.apiLogin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if b.GetString("Login") != "" {
|
} else if b.GetString("Login") != "" {
|
||||||
b.Log.Info("Connecting using login/password (receiving)")
|
b.Log.Info("Connecting using login/password (receiving)")
|
||||||
err := b.apiLogin()
|
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||||
if err != nil {
|
|
||||||
return err
|
if b.v6 {
|
||||||
|
err := b.apiLogin6()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := b.apiLogin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:wrapcheck
|
|
||||||
func (b *Bmattermost) apiLogin() error {
|
func (b *Bmattermost) apiLogin() error {
|
||||||
password := b.GetString("Password")
|
password := b.GetString("Password")
|
||||||
if b.GetString("Token") != "" {
|
if b.GetString("Token") != "" {
|
||||||
password = "token=" + b.GetString("Token")
|
password = "token=" + b.GetString("Token")
|
||||||
}
|
}
|
||||||
|
|
||||||
b.mc = matterclient.New(b.GetString("Login"), password, b.GetString("Team"), b.GetString("Server"), "")
|
b.mc = matterclient.New(b.GetString("Login"), password, b.GetString("Team"), b.GetString("Server"))
|
||||||
if b.GetBool("debug") {
|
if b.GetBool("debug") {
|
||||||
b.mc.SetLogLevel("debug")
|
b.mc.SetLogLevel("debug")
|
||||||
}
|
}
|
||||||
@@ -81,13 +118,39 @@ func (b *Bmattermost) apiLogin() error {
|
|||||||
b.mc.SkipVersionCheck = b.GetBool("SkipVersionCheck")
|
b.mc.SkipVersionCheck = b.GetBool("SkipVersionCheck")
|
||||||
b.mc.NoTLS = b.GetBool("NoTLS")
|
b.mc.NoTLS = b.GetBool("NoTLS")
|
||||||
b.Log.Infof("Connecting %s (team: %s) on %s", b.GetString("Login"), b.GetString("Team"), b.GetString("Server"))
|
b.Log.Infof("Connecting %s (team: %s) on %s", b.GetString("Login"), b.GetString("Team"), b.GetString("Server"))
|
||||||
|
err := b.mc.Login()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.Log.Info("Connection succeeded")
|
||||||
|
b.TeamID = b.mc.GetTeamId()
|
||||||
|
go b.mc.WsReceiver()
|
||||||
|
go b.mc.StatusLoop()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := b.mc.Login(); err != nil {
|
// nolint:wrapcheck
|
||||||
|
func (b *Bmattermost) apiLogin6() error {
|
||||||
|
password := b.GetString("Password")
|
||||||
|
if b.GetString("Token") != "" {
|
||||||
|
password = "token=" + b.GetString("Token")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.mc6 = matterclient6.New(b.GetString("Login"), password, b.GetString("Team"), b.GetString("Server"), "")
|
||||||
|
if b.GetBool("debug") {
|
||||||
|
b.mc6.SetLogLevel("debug")
|
||||||
|
}
|
||||||
|
b.mc6.SkipTLSVerify = b.GetBool("SkipTLSVerify")
|
||||||
|
b.mc6.SkipVersionCheck = b.GetBool("SkipVersionCheck")
|
||||||
|
b.mc6.NoTLS = b.GetBool("NoTLS")
|
||||||
|
b.Log.Infof("Connecting %s (team: %s) on %s", b.GetString("Login"), b.GetString("Team"), b.GetString("Server"))
|
||||||
|
|
||||||
|
if err := b.mc6.Login(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.Log.Info("Connection succeeded")
|
b.Log.Info("Connection succeeded")
|
||||||
b.TeamID = b.mc.GetTeamID()
|
b.TeamID = b.mc6.GetTeamID()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +183,6 @@ func (b *Bmattermost) sendWebhook(msg config.Message) (string, error) {
|
|||||||
if b.GetBool("PrefixMessagesWithNick") {
|
if b.GetBool("PrefixMessagesWithNick") {
|
||||||
msg.Text = msg.Username + msg.Text
|
msg.Text = msg.Username + msg.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg.Extra != nil {
|
if msg.Extra != nil {
|
||||||
// this sends a message only if we received a config.EVENT_FILE_FAILURE_SIZE
|
// this sends a message only if we received a config.EVENT_FILE_FAILURE_SIZE
|
||||||
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
|
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
|
||||||
@@ -144,7 +206,7 @@ func (b *Bmattermost) sendWebhook(msg config.Message) (string, error) {
|
|||||||
for _, f := range msg.Extra["file"] {
|
for _, f := range msg.Extra["file"] {
|
||||||
fi := f.(config.FileInfo)
|
fi := f.(config.FileInfo)
|
||||||
if fi.URL != "" {
|
if fi.URL != "" {
|
||||||
msg.Text += " " + fi.URL
|
msg.Text += fi.URL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,7 +233,6 @@ func (b *Bmattermost) sendWebhook(msg config.Message) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// skipMessages returns true if this message should not be handled
|
// skipMessages returns true if this message should not be handled
|
||||||
//nolint:gocyclo,cyclop
|
|
||||||
func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
|
func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
|
||||||
// Handle join/leave
|
// Handle join/leave
|
||||||
if message.Type == "system_join_leave" ||
|
if message.Type == "system_join_leave" ||
|
||||||
@@ -198,7 +259,76 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle edited messages
|
// Handle edited messages
|
||||||
if (message.Raw.EventType() == model.WebsocketEventPostEdited) && b.GetBool("EditDisable") {
|
if (message.Raw.Event == model.WEBSOCKET_EVENT_POST_EDITED) && b.GetBool("EditDisable") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore non-post messages
|
||||||
|
if message.Post == nil {
|
||||||
|
b.Log.Debugf("ignoring nil message.Post: %#v", message)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore messages sent from matterbridge
|
||||||
|
if message.Post.Props != nil {
|
||||||
|
if _, ok := message.Post.Props["matterbridge_"+b.uuid].(bool); ok {
|
||||||
|
b.Log.Debugf("sent by matterbridge, ignoring")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore messages sent from a user logged in as the bot
|
||||||
|
if b.mc.User.Username == message.Username {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the message has reactions don't repost it (for now, until we can correlate reaction with message)
|
||||||
|
if message.Post.HasReactions {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore messages from other teams than ours
|
||||||
|
if message.Raw.Data["team_id"].(string) != b.TeamID {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// only handle posted, edited or deleted events
|
||||||
|
if !(message.Raw.Event == "posted" || message.Raw.Event == model.WEBSOCKET_EVENT_POST_EDITED ||
|
||||||
|
message.Raw.Event == model.WEBSOCKET_EVENT_POST_DELETED) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// skipMessages returns true if this message should not be handled
|
||||||
|
// nolint:gocyclo,cyclop
|
||||||
|
func (b *Bmattermost) skipMessage6(message *matterclient6.Message) bool {
|
||||||
|
// Handle join/leave
|
||||||
|
if message.Type == "system_join_leave" ||
|
||||||
|
message.Type == "system_join_channel" ||
|
||||||
|
message.Type == "system_leave_channel" {
|
||||||
|
if b.GetBool("nosendjoinpart") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
channelName := b.getChannelName(message.Post.ChannelId)
|
||||||
|
if channelName == "" {
|
||||||
|
channelName = message.Channel
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Log.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
|
||||||
|
b.Remote <- config.Message{
|
||||||
|
Username: "system",
|
||||||
|
Text: message.Text,
|
||||||
|
Channel: channelName,
|
||||||
|
Account: b.Account,
|
||||||
|
Event: config.EventJoinLeave,
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle edited messages
|
||||||
|
if (message.Raw.EventType() == model6.WebsocketEventPostEdited) && b.GetBool("EditDisable") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +347,7 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ignore messages sent from a user logged in as the bot
|
// Ignore messages sent from a user logged in as the bot
|
||||||
if b.mc.User.Username == message.Username {
|
if b.mc6.User.Username == message.Username {
|
||||||
b.Log.Debug("message from same user as bot, ignoring")
|
b.Log.Debug("message from same user as bot, ignoring")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -234,8 +364,8 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only handle posted, edited or deleted events
|
// only handle posted, edited or deleted events
|
||||||
if !(message.Raw.EventType() == "posted" || message.Raw.EventType() == model.WebsocketEventPostEdited ||
|
if !(message.Raw.EventType() == "posted" || message.Raw.EventType() == model6.WebsocketEventPostEdited ||
|
||||||
message.Raw.EventType() == model.WebsocketEventPostDeleted) {
|
message.Raw.EventType() == model6.WebsocketEventPostDeleted) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -265,7 +395,11 @@ func (b *Bmattermost) getChannelID(name string) string {
|
|||||||
return idcheck[1]
|
return idcheck[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.mc.GetChannelID(name, b.TeamID)
|
if b.mc6 != nil {
|
||||||
|
return b.mc6.GetChannelID(name, b.TeamID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.mc.GetChannelId(name, b.TeamID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bmattermost) getChannelName(id string) string {
|
func (b *Bmattermost) getChannelName(id string) string {
|
||||||
|
|||||||
@@ -9,14 +9,16 @@ 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/bridge/helper"
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
|
"github.com/42wim/matterbridge/matterclient"
|
||||||
"github.com/42wim/matterbridge/matterhook"
|
"github.com/42wim/matterbridge/matterhook"
|
||||||
"github.com/matterbridge/matterclient"
|
matterclient6 "github.com/matterbridge/matterclient"
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bmattermost struct {
|
type Bmattermost struct {
|
||||||
mh *matterhook.Client
|
mh *matterhook.Client
|
||||||
mc *matterclient.Client
|
mc *matterclient.MMClient
|
||||||
|
mc6 *matterclient6.Client
|
||||||
v6 bool
|
v6 bool
|
||||||
uuid string
|
uuid string
|
||||||
TeamID string
|
TeamID string
|
||||||
@@ -50,7 +52,7 @@ func (b *Bmattermost) Connect() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(b.getVersion(), "6.") || strings.HasPrefix(b.getVersion(), "7.") {
|
if strings.HasPrefix(b.getVersion(), "6.") {
|
||||||
if !b.v6 {
|
if !b.v6 {
|
||||||
b.v6 = true
|
b.v6 = true
|
||||||
}
|
}
|
||||||
@@ -72,17 +74,34 @@ func (b *Bmattermost) Connect() error {
|
|||||||
return nil
|
return nil
|
||||||
case b.GetString("Token") != "":
|
case b.GetString("Token") != "":
|
||||||
b.Log.Info("Connecting using token (sending and receiving)")
|
b.Log.Info("Connecting using token (sending and receiving)")
|
||||||
err := b.apiLogin()
|
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||||
if err != nil {
|
|
||||||
return err
|
if b.v6 {
|
||||||
|
err := b.apiLogin6()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := b.apiLogin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
go b.handleMatter()
|
go b.handleMatter()
|
||||||
case b.GetString("Login") != "":
|
case b.GetString("Login") != "":
|
||||||
b.Log.Info("Connecting using login/password (sending and receiving)")
|
b.Log.Info("Connecting using login/password (sending and receiving)")
|
||||||
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||||
err := b.apiLogin()
|
|
||||||
if err != nil {
|
if b.v6 {
|
||||||
return err
|
err := b.apiLogin6()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := b.apiLogin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
go b.handleMatter()
|
go b.handleMatter()
|
||||||
}
|
}
|
||||||
@@ -113,6 +132,10 @@ func (b *Bmattermost) JoinChannel(channel config.ChannelInfo) error {
|
|||||||
return fmt.Errorf("Could not find channel ID for channel %s", channel.Name)
|
return fmt.Errorf("Could not find channel ID for channel %s", channel.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.mc6 != nil {
|
||||||
|
return b.mc6.JoinChannel(id) // nolint:wrapcheck
|
||||||
|
}
|
||||||
|
|
||||||
return b.mc.JoinChannel(id)
|
return b.mc.JoinChannel(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +168,9 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
|||||||
if msg.ID == "" {
|
if msg.ID == "" {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
if b.mc6 != nil {
|
||||||
|
return msg.ID, b.mc6.DeleteMessage(msg.ID) // nolint:wrapcheck
|
||||||
|
}
|
||||||
|
|
||||||
return msg.ID, b.mc.DeleteMessage(msg.ID)
|
return msg.ID, b.mc.DeleteMessage(msg.ID)
|
||||||
}
|
}
|
||||||
@@ -157,20 +183,36 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
|||||||
|
|
||||||
// we only can reply to the root of the thread, not to a specific ID (like discord for example does)
|
// we only can reply to the root of the thread, not to a specific ID (like discord for example does)
|
||||||
if msg.ParentID != "" {
|
if msg.ParentID != "" {
|
||||||
post, _, err := b.mc.Client.GetPost(msg.ParentID, "")
|
if b.mc6 != nil {
|
||||||
if err != nil {
|
post, _, err := b.mc6.Client.GetPost(msg.ParentID, "")
|
||||||
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, err)
|
if err != nil {
|
||||||
}
|
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, err)
|
||||||
if post.RootId != "" {
|
}
|
||||||
msg.ParentID = post.RootId
|
if post.RootId != "" {
|
||||||
|
msg.ParentID = post.RootId
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
post, res := b.mc.Client.GetPost(msg.ParentID, "")
|
||||||
|
if res.Error != nil {
|
||||||
|
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, res.Error.DetailedError)
|
||||||
|
}
|
||||||
|
if post.RootId != "" {
|
||||||
|
msg.ParentID = post.RootId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
if _, err := b.mc.PostMessage(b.getChannelID(rmsg.Channel), rmsg.Username+rmsg.Text, msg.ParentID); err != nil {
|
if b.mc6 != nil {
|
||||||
b.Log.Errorf("PostMessage failed: %s", err)
|
if _, err := b.mc6.PostMessage(b.getChannelID(rmsg.Channel), rmsg.Username+rmsg.Text, msg.ParentID); err != nil {
|
||||||
|
b.Log.Errorf("PostMessage failed: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := b.mc.PostMessage(b.getChannelID(rmsg.Channel), rmsg.Username+rmsg.Text, msg.ParentID); err != nil {
|
||||||
|
b.Log.Errorf("PostMessage failed: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(msg.Extra["file"]) > 0 {
|
if len(msg.Extra["file"]) > 0 {
|
||||||
@@ -185,9 +227,17 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
|||||||
|
|
||||||
// Edit message if we have an ID
|
// Edit message if we have an ID
|
||||||
if msg.ID != "" {
|
if msg.ID != "" {
|
||||||
|
if b.mc6 != nil {
|
||||||
|
return b.mc6.EditMessage(msg.ID, msg.Text) // nolint:wrapcheck
|
||||||
|
}
|
||||||
|
|
||||||
return b.mc.EditMessage(msg.ID, msg.Text)
|
return b.mc.EditMessage(msg.ID, msg.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post normal message
|
// Post normal message
|
||||||
|
if b.mc6 != nil {
|
||||||
|
return b.mc6.PostMessage(b.getChannelID(msg.Channel), msg.Text, msg.ParentID) // nolint:wrapcheck
|
||||||
|
}
|
||||||
|
|
||||||
return b.mc.PostMessage(b.getChannelID(msg.Channel), msg.Text, msg.ParentID)
|
return b.mc.PostMessage(b.getChannelID(msg.Channel), msg.Text, msg.ParentID)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
package bmumble
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"layeh.com/gumble/gumble"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a dummy implementation of a Gumble audio codec which claims
|
|
||||||
// to implement Opus, but does not actually do anything. This serves
|
|
||||||
// as a workaround until https://github.com/layeh/gumble/pull/61 is
|
|
||||||
// merged.
|
|
||||||
// See https://github.com/42wim/matterbridge/issues/1750 for details.
|
|
||||||
|
|
||||||
const (
|
|
||||||
audioCodecIDOpus = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
func registerNullCodecAsOpus() {
|
|
||||||
codec := &NullCodec{
|
|
||||||
encoder: &NullAudioEncoder{},
|
|
||||||
decoder: &NullAudioDecoder{},
|
|
||||||
}
|
|
||||||
gumble.RegisterAudioCodec(audioCodecIDOpus, codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
type NullCodec struct {
|
|
||||||
encoder *NullAudioEncoder
|
|
||||||
decoder *NullAudioDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *NullCodec) ID() int {
|
|
||||||
return audioCodecIDOpus
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *NullCodec) NewEncoder() gumble.AudioEncoder {
|
|
||||||
e := &NullAudioEncoder{}
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *NullCodec) NewDecoder() gumble.AudioDecoder {
|
|
||||||
d := &NullAudioDecoder{}
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
type NullAudioEncoder struct{}
|
|
||||||
|
|
||||||
func (e *NullAudioEncoder) ID() int {
|
|
||||||
return audioCodecIDOpus
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *NullAudioEncoder) Encode(pcm []int16, mframeSize, maxDataBytes int) ([]byte, error) {
|
|
||||||
return nil, fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *NullAudioEncoder) Reset() {
|
|
||||||
}
|
|
||||||
|
|
||||||
type NullAudioDecoder struct{}
|
|
||||||
|
|
||||||
func (d *NullAudioDecoder) ID() int {
|
|
||||||
return audioCodecIDOpus
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *NullAudioDecoder) Decode(data []byte, frameSize int) ([]int16, error) {
|
|
||||||
return nil, fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *NullAudioDecoder) Reset() {
|
|
||||||
}
|
|
||||||
@@ -78,75 +78,19 @@ func (b *Bmumble) handleConnect(event *gumble.ConnectEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bmumble) handleJoinLeave(event *gumble.UserChangeEvent) {
|
func (b *Bmumble) handleUserChange(event *gumble.UserChangeEvent) {
|
||||||
// Ignore events happening before setup is done
|
// Only care about changes to self
|
||||||
if b.Channel == nil {
|
if event.User != event.Client.Self {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if b.GetBool("nosendjoinpart") {
|
// Someone attempted to move the user out of the configured channel; attempt to join back
|
||||||
return
|
if b.Channel != nil {
|
||||||
}
|
|
||||||
b.Log.Debugf("Received gumble user change event: %+v", event)
|
|
||||||
|
|
||||||
text := ""
|
|
||||||
switch {
|
|
||||||
case event.Type&gumble.UserChangeKicked > 0:
|
|
||||||
text = " was kicked"
|
|
||||||
case event.Type&gumble.UserChangeBanned > 0:
|
|
||||||
text = " was banned"
|
|
||||||
case event.Type&gumble.UserChangeDisconnected > 0:
|
|
||||||
if event.User.Channel != nil && event.User.Channel.ID == *b.Channel {
|
|
||||||
text = " left"
|
|
||||||
}
|
|
||||||
case event.Type&gumble.UserChangeConnected > 0:
|
|
||||||
if event.User.Channel != nil && event.User.Channel.ID == *b.Channel {
|
|
||||||
text = " joined"
|
|
||||||
}
|
|
||||||
case event.Type&gumble.UserChangeChannel > 0:
|
|
||||||
// Treat Mumble channel changes the same as connects/disconnects; as far as matterbridge is concerned, they are identical
|
|
||||||
if event.User.Channel != nil && event.User.Channel.ID == *b.Channel {
|
|
||||||
text = " joined"
|
|
||||||
} else {
|
|
||||||
text = " left"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if text != "" {
|
|
||||||
b.Remote <- config.Message{
|
|
||||||
Username: "system",
|
|
||||||
Text: event.User.Name + text,
|
|
||||||
Channel: strconv.FormatUint(uint64(*b.Channel), 10),
|
|
||||||
Account: b.Account,
|
|
||||||
Event: config.EventJoinLeave,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bmumble) handleUserModified(event *gumble.UserChangeEvent) {
|
|
||||||
// Ignore events happening before setup is done
|
|
||||||
if b.Channel == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.Type&gumble.UserChangeChannel > 0 {
|
|
||||||
// Someone attempted to move the user out of the configured channel; attempt to join back
|
|
||||||
if err := b.doJoin(event.Client, *b.Channel); err != nil {
|
if err := b.doJoin(event.Client, *b.Channel); err != nil {
|
||||||
b.Log.Error(err)
|
b.Log.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bmumble) handleUserChange(event *gumble.UserChangeEvent) {
|
|
||||||
// The UserChangeEvent is used for both the gumble client itself as well as other clients
|
|
||||||
if event.User != event.Client.Self {
|
|
||||||
// other users
|
|
||||||
b.handleJoinLeave(event)
|
|
||||||
} else {
|
|
||||||
// gumble user
|
|
||||||
b.handleUserModified(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bmumble) handleDisconnect(event *gumble.DisconnectEvent) {
|
func (b *Bmumble) handleDisconnect(event *gumble.DisconnectEvent) {
|
||||||
b.connected <- *event
|
b.connected <- *event
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ func (b *Bmumble) JoinChannel(channel config.ChannelInfo) error {
|
|||||||
func (b *Bmumble) Send(msg config.Message) (string, error) {
|
func (b *Bmumble) Send(msg config.Message) (string, error) {
|
||||||
// Only process text messages
|
// Only process text messages
|
||||||
b.Log.Debugf("=> Received local message %#v", msg)
|
b.Log.Debugf("=> Received local message %#v", msg)
|
||||||
if msg.Event != "" && msg.Event != config.EventUserAction && msg.Event != config.EventJoinLeave {
|
if msg.Event != "" && msg.Event != config.EventUserAction {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +185,6 @@ func (b *Bmumble) doConnect() error {
|
|||||||
gumbleConfig.Password = password
|
gumbleConfig.Password = password
|
||||||
}
|
}
|
||||||
|
|
||||||
registerNullCodecAsOpus()
|
|
||||||
client, err := gumble.DialWithDialer(new(net.Dialer), b.GetString("Server"), gumbleConfig, &b.tlsConfig)
|
client, err := gumble.DialWithDialer(new(net.Dialer), b.GetString("Server"), gumbleConfig, &b.tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) {
|
|||||||
case *slack.FileDeletedEvent:
|
case *slack.FileDeletedEvent:
|
||||||
rmsg, err := b.handleFileDeletedEvent(ev)
|
rmsg, err := b.handleFileDeletedEvent(ev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Log.Printf("%#v", err)
|
b.Log.Errorf("%#v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
messages <- rmsg
|
messages <- rmsg
|
||||||
@@ -282,13 +282,6 @@ func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message)
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMessageTitle(attach *slack.Attachment) string {
|
|
||||||
if attach.TitleLink != "" {
|
|
||||||
return fmt.Sprintf("[%s](%s)\n", attach.Title, attach.TitleLink)
|
|
||||||
}
|
|
||||||
return attach.Title
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message) {
|
func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message) {
|
||||||
// File comments are set by the system (because there is no username given).
|
// File comments are set by the system (because there is no username given).
|
||||||
if ev.SubType == sFileComment {
|
if ev.SubType == sFileComment {
|
||||||
@@ -297,15 +290,12 @@ func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message)
|
|||||||
|
|
||||||
// See if we have some text in the attachments.
|
// See if we have some text in the attachments.
|
||||||
if rmsg.Text == "" {
|
if rmsg.Text == "" {
|
||||||
for i, attach := range ev.Attachments {
|
for _, attach := range ev.Attachments {
|
||||||
if attach.Text != "" {
|
if attach.Text != "" {
|
||||||
if attach.Title != "" {
|
if attach.Title != "" {
|
||||||
rmsg.Text = getMessageTitle(&ev.Attachments[i])
|
rmsg.Text = attach.Title + "\n"
|
||||||
}
|
}
|
||||||
rmsg.Text += attach.Text
|
rmsg.Text += attach.Text
|
||||||
if attach.Footer != "" {
|
|
||||||
rmsg.Text += "\n\n" + attach.Footer
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
rmsg.Text = attach.Fallback
|
rmsg.Text = attach.Fallback
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,9 +87,6 @@ func (b *Bslack) populateMessageWithUserInfo(ev *slack.MessageEvent, rmsg *confi
|
|||||||
if user.Profile.DisplayName != "" {
|
if user.Profile.DisplayName != "" {
|
||||||
rmsg.Username = user.Profile.DisplayName
|
rmsg.Username = user.Profile.DisplayName
|
||||||
}
|
}
|
||||||
if b.GetBool("UseFullName") && user.Profile.RealName != "" {
|
|
||||||
rmsg.Username = user.Profile.RealName
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +124,7 @@ var (
|
|||||||
mentionRE = regexp.MustCompile(`<@([a-zA-Z0-9]+)>`)
|
mentionRE = regexp.MustCompile(`<@([a-zA-Z0-9]+)>`)
|
||||||
channelRE = regexp.MustCompile(`<#[a-zA-Z0-9]+\|(.+?)>`)
|
channelRE = regexp.MustCompile(`<#[a-zA-Z0-9]+\|(.+?)>`)
|
||||||
variableRE = regexp.MustCompile(`<!((?:subteam\^)?[a-zA-Z0-9]+)(?:\|@?(.+?))?>`)
|
variableRE = regexp.MustCompile(`<!((?:subteam\^)?[a-zA-Z0-9]+)(?:\|@?(.+?))?>`)
|
||||||
urlRE = regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`)
|
urlRE = regexp.MustCompile(`<(.*?)(\|.*?)?>`)
|
||||||
codeFenceRE = regexp.MustCompile(`(?m)^` + "```" + `\w+$`)
|
codeFenceRE = regexp.MustCompile(`(?m)^` + "```" + `\w+$`)
|
||||||
topicOrPurposeRE = regexp.MustCompile(`(?s)(@.+) (cleared|set)(?: the)? channel (topic|purpose)(?:: (.*))?`)
|
topicOrPurposeRE = regexp.MustCompile(`(?s)(@.+) (cleared|set)(?: the)? channel (topic|purpose)(?:: (.*))?`)
|
||||||
)
|
)
|
||||||
@@ -181,7 +178,14 @@ func (b *Bslack) replaceVariable(text string) string {
|
|||||||
|
|
||||||
// @see https://api.slack.com/docs/message-formatting#linking_to_urls
|
// @see https://api.slack.com/docs/message-formatting#linking_to_urls
|
||||||
func (b *Bslack) replaceURL(text string) string {
|
func (b *Bslack) replaceURL(text string) string {
|
||||||
return urlRE.ReplaceAllString(text, "[${2}](${1})")
|
for _, r := range urlRE.FindAllStringSubmatch(text, -1) {
|
||||||
|
if len(strings.TrimSpace(r[2])) == 1 { // A display text separator was found, but the text was blank
|
||||||
|
text = strings.Replace(text, r[0], "", 1)
|
||||||
|
} else {
|
||||||
|
text = strings.Replace(text, r[0], r[1], 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bslack) replaceb0rkedMarkDown(text string) string {
|
func (b *Bslack) replaceb0rkedMarkDown(text string) string {
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ func (b *Bslack) sendRTM(msg config.Message) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Upload a file if it exists.
|
// Upload a file if it exists.
|
||||||
if len(msg.Extra) > 0 {
|
if msg.Extra != nil {
|
||||||
extraMsgs := helper.HandleExtra(&msg, b.General)
|
extraMsgs := helper.HandleExtra(&msg, b.General)
|
||||||
for i := range extraMsgs {
|
for i := range extraMsgs {
|
||||||
rmsg := &extraMsgs[i]
|
rmsg := &extraMsgs[i]
|
||||||
@@ -332,7 +332,7 @@ func (b *Bslack) sendRTM(msg config.Message) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Upload files if necessary (from Slack, Telegram or Mattermost).
|
// Upload files if necessary (from Slack, Telegram or Mattermost).
|
||||||
return b.uploadFile(&msg, channelInfo.ID)
|
b.uploadFile(&msg, channelInfo.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post message.
|
// Post message.
|
||||||
@@ -443,8 +443,7 @@ func (b *Bslack) postMessage(msg *config.Message, channelInfo *slack.Channel) (s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// uploadFile handles native upload of files
|
// uploadFile handles native upload of files
|
||||||
func (b *Bslack) uploadFile(msg *config.Message, channelID string) (string, error) {
|
func (b *Bslack) uploadFile(msg *config.Message, channelID string) {
|
||||||
var messageID string
|
|
||||||
for _, f := range msg.Extra["file"] {
|
for _, f := range msg.Extra["file"] {
|
||||||
fi, ok := f.(config.FileInfo)
|
fi, ok := f.(config.FileInfo)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -472,22 +471,13 @@ func (b *Bslack) uploadFile(msg *config.Message, channelID string) (string, erro
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Log.Errorf("uploadfile %#v", err)
|
b.Log.Errorf("uploadfile %#v", err)
|
||||||
return "", err
|
return
|
||||||
}
|
}
|
||||||
if res.ID != "" {
|
if res.ID != "" {
|
||||||
b.Log.Debugf("Adding file ID %s to cache with timestamp %s", res.ID, ts.String())
|
b.Log.Debugf("Adding file ID %s to cache with timestamp %s", res.ID, ts.String())
|
||||||
b.cache.Add("file"+res.ID, ts)
|
b.cache.Add("file"+res.ID, ts)
|
||||||
|
|
||||||
// search for message id by uploaded file in private/public channels, get thread timestamp from uploaded file
|
|
||||||
if v, ok := res.Shares.Private[channelID]; ok && len(v) > 0 {
|
|
||||||
messageID = v[0].Ts
|
|
||||||
}
|
|
||||||
if v, ok := res.Shares.Public[channelID]; ok && len(v) > 0 {
|
|
||||||
messageID = v[0].Ts
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return messageID, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bslack) prepareMessageOptions(msg *config.Message) []slack.MsgOption {
|
func (b *Bslack) prepareMessageOptions(msg *config.Message) []slack.MsgOption {
|
||||||
|
|||||||
@@ -57,11 +57,6 @@ func (b *Btelegram) handleForwarded(rmsg *config.Message, message *tgbotapi.Mess
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if message.ForwardFromChat != nil && message.ForwardFrom == nil {
|
|
||||||
rmsg.Text = "Forwarded from " + message.ForwardFromChat.Title + ": " + rmsg.Text
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if message.ForwardFrom == nil {
|
if message.ForwardFrom == nil {
|
||||||
rmsg.Text = "Forwarded from " + unknownUser + ": " + rmsg.Text
|
rmsg.Text = "Forwarded from " + unknownUser + ": " + rmsg.Text
|
||||||
return
|
return
|
||||||
@@ -71,9 +66,6 @@ func (b *Btelegram) handleForwarded(rmsg *config.Message, message *tgbotapi.Mess
|
|||||||
if b.GetBool("UseFirstName") {
|
if b.GetBool("UseFirstName") {
|
||||||
usernameForward = message.ForwardFrom.FirstName
|
usernameForward = message.ForwardFrom.FirstName
|
||||||
}
|
}
|
||||||
if b.GetBool("UseFullName") {
|
|
||||||
usernameForward = message.ForwardFrom.FirstName + " " + message.ForwardFrom.LastName
|
|
||||||
}
|
|
||||||
|
|
||||||
if usernameForward == "" {
|
if usernameForward == "" {
|
||||||
usernameForward = message.ForwardFrom.UserName
|
usernameForward = message.ForwardFrom.UserName
|
||||||
@@ -97,9 +89,6 @@ func (b *Btelegram) handleQuoting(rmsg *config.Message, message *tgbotapi.Messag
|
|||||||
if b.GetBool("UseFirstName") {
|
if b.GetBool("UseFirstName") {
|
||||||
usernameReply = message.ReplyToMessage.From.FirstName
|
usernameReply = message.ReplyToMessage.From.FirstName
|
||||||
}
|
}
|
||||||
if b.GetBool("UseFullName") {
|
|
||||||
usernameReply = message.ReplyToMessage.From.FirstName + " " + message.ReplyToMessage.From.LastName
|
|
||||||
}
|
|
||||||
if usernameReply == "" {
|
if usernameReply == "" {
|
||||||
usernameReply = message.ReplyToMessage.From.UserName
|
usernameReply = message.ReplyToMessage.From.UserName
|
||||||
if usernameReply == "" {
|
if usernameReply == "" {
|
||||||
@@ -111,11 +100,7 @@ func (b *Btelegram) handleQuoting(rmsg *config.Message, message *tgbotapi.Messag
|
|||||||
usernameReply = unknownUser
|
usernameReply = unknownUser
|
||||||
}
|
}
|
||||||
if !b.GetBool("QuoteDisable") {
|
if !b.GetBool("QuoteDisable") {
|
||||||
quote := message.ReplyToMessage.Text
|
rmsg.Text = b.handleQuote(rmsg.Text, usernameReply, message.ReplyToMessage.Text)
|
||||||
if quote == "" {
|
|
||||||
quote = message.ReplyToMessage.Caption
|
|
||||||
}
|
|
||||||
rmsg.Text = b.handleQuote(rmsg.Text, usernameReply, quote)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,9 +112,6 @@ func (b *Btelegram) handleUsername(rmsg *config.Message, message *tgbotapi.Messa
|
|||||||
if b.GetBool("UseFirstName") {
|
if b.GetBool("UseFirstName") {
|
||||||
rmsg.Username = message.From.FirstName
|
rmsg.Username = message.From.FirstName
|
||||||
}
|
}
|
||||||
if b.GetBool("UseFullName") {
|
|
||||||
rmsg.Username = message.From.FirstName + " " + message.From.LastName
|
|
||||||
}
|
|
||||||
if rmsg.Username == "" {
|
if rmsg.Username == "" {
|
||||||
rmsg.Username = message.From.UserName
|
rmsg.Username = message.From.UserName
|
||||||
if rmsg.Username == "" {
|
if rmsg.Username == "" {
|
||||||
@@ -147,9 +129,6 @@ func (b *Btelegram) handleUsername(rmsg *config.Message, message *tgbotapi.Messa
|
|||||||
if b.GetBool("UseFirstName") {
|
if b.GetBool("UseFirstName") {
|
||||||
rmsg.Username = message.SenderChat.FirstName
|
rmsg.Username = message.SenderChat.FirstName
|
||||||
}
|
}
|
||||||
if b.GetBool("UseFullName") {
|
|
||||||
rmsg.Username = message.SenderChat.FirstName + " " + message.SenderChat.LastName
|
|
||||||
}
|
|
||||||
|
|
||||||
if rmsg.Username == "" || rmsg.Username == "Channel_Bot" {
|
if rmsg.Username == "" || rmsg.Username == "Channel_Bot" {
|
||||||
rmsg.Username = message.SenderChat.UserName
|
rmsg.Username = message.SenderChat.UserName
|
||||||
@@ -176,7 +155,7 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
|
|||||||
|
|
||||||
if update.Message == nil && update.ChannelPost == nil &&
|
if update.Message == nil && update.ChannelPost == nil &&
|
||||||
update.EditedMessage == nil && update.EditedChannelPost == nil {
|
update.EditedMessage == nil && update.EditedChannelPost == nil {
|
||||||
b.Log.Info("Received event without messages, skipping.")
|
b.Log.Error("Getting nil messages, this shouldn't happen.")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,14 +182,6 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
|
|||||||
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)
|
||||||
|
|
||||||
// preserve threading from telegram reply
|
|
||||||
if message.ReplyToMessage != nil {
|
|
||||||
rmsg.ParentID = strconv.Itoa(message.ReplyToMessage.MessageID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle entities (adding URLs)
|
|
||||||
b.handleEntities(&rmsg, message)
|
|
||||||
|
|
||||||
// handle username
|
// handle username
|
||||||
b.handleUsername(&rmsg, message)
|
b.handleUsername(&rmsg, message)
|
||||||
|
|
||||||
@@ -226,9 +197,11 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
|
|||||||
// quote the previous message
|
// quote the previous message
|
||||||
b.handleQuoting(&rmsg, message)
|
b.handleQuoting(&rmsg, message)
|
||||||
|
|
||||||
|
// handle entities (adding URLs)
|
||||||
|
b.handleEntities(&rmsg, message)
|
||||||
|
|
||||||
if rmsg.Text != "" || len(rmsg.Extra) > 0 {
|
if rmsg.Text != "" || len(rmsg.Extra) > 0 {
|
||||||
// Comment the next line out due to avoid removing empty lines in Telegram
|
rmsg.Text = helper.RemoveEmptyNewLines(rmsg.Text)
|
||||||
// rmsg.Text = helper.RemoveEmptyNewLines(rmsg.Text)
|
|
||||||
// channels don't have (always?) user information. see #410
|
// channels don't have (always?) user information. see #410
|
||||||
if message.From != nil {
|
if message.From != nil {
|
||||||
rmsg.Avatar = helper.GetAvatar(b.avatarMap, strconv.FormatInt(message.From.ID, 10), b.General)
|
rmsg.Avatar = helper.GetAvatar(b.avatarMap, strconv.FormatInt(message.From.ID, 10), b.General)
|
||||||
@@ -385,7 +358,7 @@ func (b *Btelegram) getDownloadInfo(id string, suffix string, urlpart bool) (str
|
|||||||
urlPart := strings.Split(url, "/")
|
urlPart := strings.Split(url, "/")
|
||||||
name = urlPart[len(urlPart)-1]
|
name = urlPart[len(urlPart)-1]
|
||||||
}
|
}
|
||||||
if suffix != "" && !strings.HasSuffix(name, suffix) && !strings.HasSuffix(name, ".webm") {
|
if suffix != "" && !strings.HasSuffix(name, suffix) {
|
||||||
name += suffix
|
name += suffix
|
||||||
}
|
}
|
||||||
text := " " + url
|
text := " " + url
|
||||||
@@ -404,7 +377,7 @@ func (b *Btelegram) handleDelete(msg *config.Message, chatid int64) (string, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg := tgbotapi.NewDeleteMessage(chatid, msgid)
|
cfg := tgbotapi.NewDeleteMessage(chatid, msgid)
|
||||||
_, err = b.c.Request(cfg)
|
_, err = b.c.Send(cfg)
|
||||||
|
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -443,57 +416,42 @@ func (b *Btelegram) handleEdit(msg *config.Message, chatid int64) (string, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handleUploadFile handles native upload of files
|
// handleUploadFile handles native upload of files
|
||||||
func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64, parentID int) (string, error) {
|
func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64) string {
|
||||||
var media []interface{}
|
var c tgbotapi.Chattable
|
||||||
for _, f := range msg.Extra["file"] {
|
for _, f := range msg.Extra["file"] {
|
||||||
fi := f.(config.FileInfo)
|
fi := f.(config.FileInfo)
|
||||||
file := tgbotapi.FileBytes{
|
file := tgbotapi.FileBytes{
|
||||||
Name: fi.Name,
|
Name: fi.Name,
|
||||||
Bytes: *fi.Data,
|
Bytes: *fi.Data,
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.GetString("MessageFormat") == HTMLFormat {
|
|
||||||
fi.Comment = makeHTML(html.EscapeString(fi.Comment))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch filepath.Ext(fi.Name) {
|
switch filepath.Ext(fi.Name) {
|
||||||
case ".jpg", ".jpe", ".png":
|
case ".jpg", ".jpe", ".png":
|
||||||
pc := tgbotapi.NewInputMediaPhoto(file)
|
pc := tgbotapi.NewPhoto(chatid, file)
|
||||||
if fi.Comment != "" {
|
pc.Caption, pc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
||||||
pc.Caption, pc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
c = pc
|
||||||
}
|
|
||||||
media = append(media, pc)
|
|
||||||
case ".mp4", ".m4v":
|
case ".mp4", ".m4v":
|
||||||
vc := tgbotapi.NewInputMediaVideo(file)
|
vc := tgbotapi.NewVideo(chatid, file)
|
||||||
if fi.Comment != "" {
|
vc.Caption, vc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
||||||
vc.Caption, vc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
c = vc
|
||||||
}
|
|
||||||
media = append(media, vc)
|
|
||||||
case ".mp3", ".oga":
|
case ".mp3", ".oga":
|
||||||
ac := tgbotapi.NewInputMediaAudio(file)
|
ac := tgbotapi.NewAudio(chatid, file)
|
||||||
if fi.Comment != "" {
|
ac.Caption, ac.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
||||||
ac.Caption, ac.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
c = ac
|
||||||
}
|
|
||||||
media = append(media, ac)
|
|
||||||
case ".ogg":
|
case ".ogg":
|
||||||
voc := tgbotapi.NewVoice(chatid, file)
|
voc := tgbotapi.NewVoice(chatid, file)
|
||||||
voc.Caption, voc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
voc.Caption, voc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
||||||
voc.ReplyToMessageID = parentID
|
c = voc
|
||||||
res, err := b.c.Send(voc)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return strconv.Itoa(res.MessageID), nil
|
|
||||||
default:
|
default:
|
||||||
dc := tgbotapi.NewInputMediaDocument(file)
|
dc := tgbotapi.NewDocument(chatid, file)
|
||||||
if fi.Comment != "" {
|
dc.Caption, dc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
||||||
dc.Caption, dc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
c = dc
|
||||||
}
|
}
|
||||||
media = append(media, dc)
|
_, err := b.c.Send(c)
|
||||||
|
if err != nil {
|
||||||
|
b.Log.Errorf("file upload failed: %#v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ""
|
||||||
return b.sendMediaFiles(msg, chatid, parentID, media)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Btelegram) handleQuote(message, quoteNick, quoteMessage string) string {
|
func (b *Btelegram) handleQuote(message, quoteNick, quoteMessage string) string {
|
||||||
@@ -522,59 +480,34 @@ func (b *Btelegram) handleEntities(rmsg *config.Message, message *tgbotapi.Messa
|
|||||||
}
|
}
|
||||||
|
|
||||||
indexMovedBy := 0
|
indexMovedBy := 0
|
||||||
prevLinkOffset := -1
|
|
||||||
|
|
||||||
|
// for now only do URL replacements
|
||||||
for _, e := range message.Entities {
|
for _, e := range message.Entities {
|
||||||
|
|
||||||
asRunes := utf16.Encode([]rune(rmsg.Text))
|
|
||||||
|
|
||||||
if e.Type == "text_link" {
|
if e.Type == "text_link" {
|
||||||
offset := e.Offset + indexMovedBy
|
|
||||||
url, err := e.ParseURL()
|
url, err := e.ParseURL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Log.Errorf("entity text_link url parse failed: %s", err)
|
b.Log.Errorf("entity text_link url parse failed: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
utfEncodedString := utf16.Encode([]rune(rmsg.Text))
|
utfEncodedString := utf16.Encode([]rune(rmsg.Text))
|
||||||
if offset+e.Length > len(utfEncodedString) {
|
if e.Offset+e.Length > len(utfEncodedString) {
|
||||||
b.Log.Errorf("entity length is too long %d > %d", offset+e.Length, len(utfEncodedString))
|
b.Log.Errorf("entity length is too long %d > %d", e.Offset+e.Length, len(utfEncodedString))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rmsg.Text = string(utf16.Decode(asRunes[:offset+e.Length])) + " (" + url.String() + ")" + string(utf16.Decode(asRunes[offset+e.Length:]))
|
link := utf16.Decode(utfEncodedString[e.Offset : e.Offset+e.Length])
|
||||||
indexMovedBy += len(url.String()) + 3
|
rmsg.Text = strings.Replace(rmsg.Text, string(link), url.String(), 1)
|
||||||
prevLinkOffset = e.Offset
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.Offset == prevLinkOffset {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Type == "code" {
|
if e.Type == "code" {
|
||||||
offset := e.Offset + indexMovedBy
|
offset := e.Offset + indexMovedBy
|
||||||
rmsg.Text = string(utf16.Decode(asRunes[:offset])) + "`" + string(utf16.Decode(asRunes[offset:offset+e.Length])) + "`" + string(utf16.Decode(asRunes[offset+e.Length:]))
|
rmsg.Text = rmsg.Text[:offset] + "`" + rmsg.Text[offset:offset+e.Length] + "`" + rmsg.Text[offset+e.Length:]
|
||||||
indexMovedBy += 2
|
indexMovedBy += 2
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Type == "pre" {
|
if e.Type == "pre" {
|
||||||
offset := e.Offset + indexMovedBy
|
offset := e.Offset + indexMovedBy
|
||||||
rmsg.Text = string(utf16.Decode(asRunes[:offset])) + "```\n" + string(utf16.Decode(asRunes[offset:offset+e.Length])) + "```\n" + string(utf16.Decode(asRunes[offset+e.Length:]))
|
rmsg.Text = rmsg.Text[:offset] + "```\n" + rmsg.Text[offset:offset+e.Length] + "\n```" + rmsg.Text[offset+e.Length:]
|
||||||
indexMovedBy += 8
|
indexMovedBy += 8
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Type == "bold" {
|
|
||||||
offset := e.Offset + indexMovedBy
|
|
||||||
rmsg.Text = string(utf16.Decode(asRunes[:offset])) + "*" + string(utf16.Decode(asRunes[offset:offset+e.Length])) + "*" + string(utf16.Decode(asRunes[offset+e.Length:]))
|
|
||||||
indexMovedBy += 2
|
|
||||||
}
|
|
||||||
if e.Type == "italic" {
|
|
||||||
offset := e.Offset + indexMovedBy
|
|
||||||
rmsg.Text = string(utf16.Decode(asRunes[:offset])) + "_" + string(utf16.Decode(asRunes[offset:offset+e.Length])) + "_" + string(utf16.Decode(asRunes[offset+e.Length:]))
|
|
||||||
indexMovedBy += 2
|
|
||||||
}
|
|
||||||
if e.Type == "strike" {
|
|
||||||
offset := e.Offset + indexMovedBy
|
|
||||||
rmsg.Text = string(utf16.Decode(asRunes[:offset])) + "~" + string(utf16.Decode(asRunes[offset:offset+e.Length])) + "~" + string(utf16.Decode(asRunes[offset+e.Length:]))
|
|
||||||
indexMovedBy += 2
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package btelegram
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"html"
|
||||||
|
|
||||||
"github.com/russross/blackfriday"
|
"github.com/russross/blackfriday"
|
||||||
)
|
)
|
||||||
@@ -23,16 +24,10 @@ func (options *customHTML) Paragraph(out *bytes.Buffer, text func() bool) {
|
|||||||
func (options *customHTML) BlockCode(out *bytes.Buffer, text []byte, lang string) {
|
func (options *customHTML) BlockCode(out *bytes.Buffer, text []byte, lang string) {
|
||||||
out.WriteString("<pre>")
|
out.WriteString("<pre>")
|
||||||
|
|
||||||
out.WriteString(string(text))
|
out.WriteString(html.EscapeString(string(text)))
|
||||||
out.WriteString("</pre>\n")
|
out.WriteString("</pre>\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (options *customHTML) CodeSpan(out *bytes.Buffer, text []byte) {
|
|
||||||
out.WriteString("<code>")
|
|
||||||
out.WriteString(string(text))
|
|
||||||
out.WriteString("</code>")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (options *customHTML) Header(out *bytes.Buffer, text func() bool, level int, id string) {
|
func (options *customHTML) Header(out *bytes.Buffer, text func() bool, level int, id string) {
|
||||||
options.Paragraph(out, text)
|
options.Paragraph(out, text)
|
||||||
}
|
}
|
||||||
@@ -47,10 +42,6 @@ func (options *customHTML) BlockQuote(out *bytes.Buffer, text []byte) {
|
|||||||
out.WriteByte('\n')
|
out.WriteByte('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
func (options *customHTML) LineBreak(out *bytes.Buffer) {
|
|
||||||
out.WriteByte('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (options *customHTML) List(out *bytes.Buffer, text func() bool, flags int) {
|
func (options *customHTML) List(out *bytes.Buffer, text func() bool, flags int) {
|
||||||
options.Paragraph(out, text)
|
options.Paragraph(out, text)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package btelegram
|
package btelegram
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"html"
|
"html"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -101,7 +100,7 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if b.GetString("MessageFormat") == HTMLFormat {
|
if b.GetString("MessageFormat") == HTMLFormat {
|
||||||
msg.Text = makeHTML(html.EscapeString(msg.Text))
|
msg.Text = makeHTML(msg.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete message
|
// Delete message
|
||||||
@@ -109,27 +108,16 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
|
|||||||
return b.handleDelete(&msg, chatid)
|
return b.handleDelete(&msg, chatid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle prefix hint for unthreaded messages.
|
|
||||||
if msg.ParentNotFound() {
|
|
||||||
msg.ParentID = ""
|
|
||||||
msg.Text = fmt.Sprintf("[reply]: %s", msg.Text)
|
|
||||||
}
|
|
||||||
|
|
||||||
var parentID int
|
|
||||||
if msg.ParentID != "" {
|
|
||||||
parentID, _ = b.intParentID(msg.ParentID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
||||||
if _, msgErr := b.sendMessage(chatid, rmsg.Username, rmsg.Text, parentID); msgErr != nil {
|
if _, msgErr := b.sendMessage(chatid, rmsg.Username, rmsg.Text); msgErr != nil {
|
||||||
b.Log.Errorf("sendMessage failed: %s", msgErr)
|
b.Log.Errorf("sendMessage failed: %s", msgErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check if we have files to upload (from slack, telegram or mattermost)
|
// check if we have files to upload (from slack, telegram or mattermost)
|
||||||
if len(msg.Extra["file"]) > 0 {
|
if len(msg.Extra["file"]) > 0 {
|
||||||
return b.handleUploadFile(&msg, chatid, parentID)
|
b.handleUploadFile(&msg, chatid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +131,7 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
|
|||||||
// Ignore empty text field needs for prevent double messages from whatsapp to telegram
|
// Ignore empty text field needs for prevent double messages from whatsapp to telegram
|
||||||
// when sending media with text caption
|
// when sending media with text caption
|
||||||
if msg.Text != "" {
|
if msg.Text != "" {
|
||||||
return b.sendMessage(chatid, msg.Username, msg.Text, parentID)
|
return b.sendMessage(chatid, msg.Username, msg.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", nil
|
return "", nil
|
||||||
@@ -157,10 +145,10 @@ func (b *Btelegram) getFileDirectURL(id string) string {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Btelegram) sendMessage(chatid int64, username, text string, parentID int) (string, error) {
|
func (b *Btelegram) sendMessage(chatid int64, username, text string) (string, error) {
|
||||||
m := tgbotapi.NewMessage(chatid, "")
|
m := tgbotapi.NewMessage(chatid, "")
|
||||||
m.Text, m.ParseMode = TGGetParseMode(b, username, text)
|
m.Text, m.ParseMode = TGGetParseMode(b, username, text)
|
||||||
m.ReplyToMessageID = parentID
|
|
||||||
m.DisableWebPagePreview = b.GetBool("DisableWebPagePreview")
|
m.DisableWebPagePreview = b.GetBool("DisableWebPagePreview")
|
||||||
|
|
||||||
res, err := b.c.Send(m)
|
res, err := b.c.Send(m)
|
||||||
@@ -170,29 +158,6 @@ func (b *Btelegram) sendMessage(chatid int64, username, text string, parentID in
|
|||||||
return strconv.Itoa(res.MessageID), nil
|
return strconv.Itoa(res.MessageID), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendMediaFiles native upload media files via media group
|
|
||||||
func (b *Btelegram) sendMediaFiles(msg *config.Message, chatid int64, parentID int, media []interface{}) (string, error) {
|
|
||||||
if len(media) == 0 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
mg := tgbotapi.MediaGroupConfig{ChatID: chatid, ChannelUsername: msg.Username, Media: media, ReplyToMessageID: parentID}
|
|
||||||
messages, err := b.c.SendMediaGroup(mg)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// return first message id
|
|
||||||
return strconv.Itoa(messages[0].MessageID), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// intParentID return integer parent id for telegram message
|
|
||||||
func (b *Btelegram) intParentID(parentID string) (int, error) {
|
|
||||||
pid, err := strconv.Atoi(parentID)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return pid, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Btelegram) cacheAvatar(msg *config.Message) (string, error) {
|
func (b *Btelegram) cacheAvatar(msg *config.Message) (string, error) {
|
||||||
fi := msg.Extra["file"][0].(config.FileInfo)
|
fi := msg.Extra["file"][0].(config.FileInfo)
|
||||||
/* if we have a sha we have successfully uploaded the file to the media server,
|
/* if we have a sha we have successfully uploaded the file to the media server,
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func (b *Bvk) Connect() error {
|
|||||||
go func() {
|
go func() {
|
||||||
err := b.lp.Run()
|
err := b.lp.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Log.WithError(err).Fatal("Enable longpoll in group management")
|
b.Log.Fatal("Enable longpoll in group management")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ func (b *Bvk) uploadFiles(extra map[string][]interface{}, peerID int) (string, s
|
|||||||
}
|
}
|
||||||
a, err := b.uploadFile(fi, peerID)
|
a, err := b.uploadFile(fi, peerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Log.WithError(err).Error("File upload error ", fi.Name)
|
b.Log.Error("File upload error ", fi.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
attachments = append(attachments, a)
|
attachments = append(attachments, a)
|
||||||
@@ -237,8 +237,7 @@ func (b *Bvk) uploadFile(file config.FileInfo, peerID int) (string, error) {
|
|||||||
|
|
||||||
photoRE := regexp.MustCompile(".(jpg|jpe|png)$")
|
photoRE := regexp.MustCompile(".(jpg|jpe|png)$")
|
||||||
if photoRE.MatchString(file.Name) {
|
if photoRE.MatchString(file.Name) {
|
||||||
// BUG(VK): for community chat peerID=0
|
p, err := b.c.UploadMessagesPhoto(peerID, r)
|
||||||
p, err := b.c.UploadMessagesPhoto(0, r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// nolint:goconst
|
|
||||||
package bwhatsapp
|
package bwhatsapp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -135,7 +134,6 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HandleImageMessage sent from WhatsApp, relay it to the brige
|
// HandleImageMessage sent from WhatsApp, relay it to the brige
|
||||||
// nolint:funlen
|
|
||||||
func (b *Bwhatsapp) HandleImageMessage(message whatsapp.ImageMessage) {
|
func (b *Bwhatsapp) HandleImageMessage(message whatsapp.ImageMessage) {
|
||||||
if message.Info.FromMe || message.Info.Timestamp < b.startedAt {
|
if message.Info.FromMe || message.Info.Timestamp < b.startedAt {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -111,7 +111,8 @@ func (b *Bwhatsapp) getSenderName(senderJid string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// try to reload this contact
|
// try to reload this contact
|
||||||
if _, err := b.conn.Contacts(); err != nil {
|
_, err := b.conn.Contacts()
|
||||||
|
if err != nil {
|
||||||
b.Log.Errorf("error on update of contacts: %v", err)
|
b.Log.Errorf("error on update of contacts: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,11 +40,6 @@ type Bwhatsapp struct {
|
|||||||
func New(cfg *bridge.Config) bridge.Bridger {
|
func New(cfg *bridge.Config) bridge.Bridger {
|
||||||
number := cfg.GetString(cfgNumber)
|
number := cfg.GetString(cfgNumber)
|
||||||
|
|
||||||
cfg.Log.Warn("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
|
||||||
cfg.Log.Warn("This bridge is deprecated and not supported anymore. Use the new multidevice whatsapp bridge")
|
|
||||||
cfg.Log.Warn("See https://github.com/42wim/matterbridge#building-with-whatsapp-beta-multidevice-support for more info")
|
|
||||||
cfg.Log.Warn("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
|
||||||
|
|
||||||
if number == "" {
|
if number == "" {
|
||||||
cfg.Log.Fatalf("Missing configuration for WhatsApp bridge: Number")
|
cfg.Log.Fatalf("Missing configuration for WhatsApp bridge: Number")
|
||||||
}
|
}
|
||||||
@@ -298,11 +293,7 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) {
|
|||||||
if msg.ID != "" {
|
if msg.ID != "" {
|
||||||
b.Log.Debugf("updating message with id %s", msg.ID)
|
b.Log.Debugf("updating message with id %s", msg.ID)
|
||||||
|
|
||||||
if b.GetString("editsuffix") != "" {
|
msg.Text += " (edited)"
|
||||||
msg.Text += b.GetString("EditSuffix")
|
|
||||||
} else {
|
|
||||||
msg.Text += " (edited)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Upload a file
|
// Handle Upload a file
|
||||||
|
|||||||
@@ -1,338 +0,0 @@
|
|||||||
// +build whatsappmulti
|
|
||||||
|
|
||||||
package bwhatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"mime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
|
||||||
"github.com/42wim/matterbridge/bridge/helper"
|
|
||||||
|
|
||||||
"go.mau.fi/whatsmeow/binary/proto"
|
|
||||||
"go.mau.fi/whatsmeow/types"
|
|
||||||
"go.mau.fi/whatsmeow/types/events"
|
|
||||||
)
|
|
||||||
|
|
||||||
// nolint:gocritic
|
|
||||||
func (b *Bwhatsapp) eventHandler(evt interface{}) {
|
|
||||||
switch e := evt.(type) {
|
|
||||||
case *events.Message:
|
|
||||||
b.handleMessage(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bwhatsapp) handleMessage(message *events.Message) {
|
|
||||||
msg := message.Message
|
|
||||||
switch {
|
|
||||||
case msg == nil, message.Info.IsFromMe, message.Info.Timestamp.Before(b.startedAt):
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
b.Log.Infof("Receiving message %#v", msg)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case msg.Conversation != nil || msg.ExtendedTextMessage != nil:
|
|
||||||
b.handleTextMessage(message.Info, msg)
|
|
||||||
case msg.VideoMessage != nil:
|
|
||||||
b.handleVideoMessage(message)
|
|
||||||
case msg.AudioMessage != nil:
|
|
||||||
b.handleAudioMessage(message)
|
|
||||||
case msg.DocumentMessage != nil:
|
|
||||||
b.handleDocumentMessage(message)
|
|
||||||
case msg.ImageMessage != nil:
|
|
||||||
b.handleImageMessage(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:funlen
|
|
||||||
func (b *Bwhatsapp) handleTextMessage(messageInfo types.MessageInfo, msg *proto.Message) {
|
|
||||||
senderJID := messageInfo.Sender
|
|
||||||
channel := messageInfo.Chat
|
|
||||||
|
|
||||||
senderName := b.getSenderName(messageInfo)
|
|
||||||
|
|
||||||
if msg.GetExtendedTextMessage() == nil && msg.GetConversation() == "" {
|
|
||||||
b.Log.Debugf("message without text content? %#v", msg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var text string
|
|
||||||
|
|
||||||
// nolint:nestif
|
|
||||||
if msg.GetExtendedTextMessage() == nil {
|
|
||||||
text = msg.GetConversation()
|
|
||||||
} else {
|
|
||||||
text = msg.GetExtendedTextMessage().GetText()
|
|
||||||
ci := msg.GetExtendedTextMessage().GetContextInfo()
|
|
||||||
|
|
||||||
if senderJID == (types.JID{}) && ci.Participant != nil {
|
|
||||||
senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ci.MentionedJid != nil {
|
|
||||||
// handle user mentions
|
|
||||||
for _, mentionedJID := range ci.MentionedJid {
|
|
||||||
numberAndSuffix := strings.SplitN(mentionedJID, "@", 2)
|
|
||||||
|
|
||||||
// mentions comes as telephone numbers and we don't want to expose it to other bridges
|
|
||||||
// replace it with something more meaninful to others
|
|
||||||
mention := b.getSenderNotify(types.NewJID(numberAndSuffix[0], types.DefaultUserServer))
|
|
||||||
|
|
||||||
text = strings.Replace(text, "@"+numberAndSuffix[0], "@"+mention, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rmsg := config.Message{
|
|
||||||
UserID: senderJID.String(),
|
|
||||||
Username: senderName,
|
|
||||||
Text: text,
|
|
||||||
Channel: channel.String(),
|
|
||||||
Account: b.Account,
|
|
||||||
Protocol: b.Protocol,
|
|
||||||
Extra: make(map[string][]interface{}),
|
|
||||||
// ParentID: TODO, // TODO handle thread replies // map from Info.QuotedMessageID string
|
|
||||||
ID: messageInfo.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
|
|
||||||
rmsg.Avatar = avatarURL
|
|
||||||
}
|
|
||||||
|
|
||||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
|
|
||||||
b.Log.Debugf("<= Message is %#v", rmsg)
|
|
||||||
|
|
||||||
b.Remote <- rmsg
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleImageMessage sent from WhatsApp, relay it to the brige
|
|
||||||
func (b *Bwhatsapp) handleImageMessage(msg *events.Message) {
|
|
||||||
imsg := msg.Message.GetImageMessage()
|
|
||||||
|
|
||||||
senderJID := msg.Info.Sender
|
|
||||||
senderName := b.getSenderName(msg.Info)
|
|
||||||
ci := imsg.GetContextInfo()
|
|
||||||
|
|
||||||
if senderJID == (types.JID{}) && ci.Participant != nil {
|
|
||||||
senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
|
|
||||||
}
|
|
||||||
|
|
||||||
rmsg := config.Message{
|
|
||||||
UserID: senderJID.String(),
|
|
||||||
Username: senderName,
|
|
||||||
Channel: msg.Info.Chat.String(),
|
|
||||||
Account: b.Account,
|
|
||||||
Protocol: b.Protocol,
|
|
||||||
Extra: make(map[string][]interface{}),
|
|
||||||
ID: msg.Info.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
|
|
||||||
rmsg.Avatar = avatarURL
|
|
||||||
}
|
|
||||||
|
|
||||||
fileExt, err := mime.ExtensionsByType(imsg.GetMimetype())
|
|
||||||
if err != nil {
|
|
||||||
b.Log.Errorf("Mimetype detection error: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// rename .jfif to .jpg https://github.com/42wim/matterbridge/issues/1292
|
|
||||||
if fileExt[0] == ".jfif" {
|
|
||||||
fileExt[0] = ".jpg"
|
|
||||||
}
|
|
||||||
|
|
||||||
// rename .jpe to .jpg https://github.com/42wim/matterbridge/issues/1463
|
|
||||||
if fileExt[0] == ".jpe" {
|
|
||||||
fileExt[0] = ".jpg"
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := fmt.Sprintf("%v%v", msg.Info.ID, fileExt[0])
|
|
||||||
|
|
||||||
b.Log.Debugf("Trying to download %s with type %s", filename, imsg.GetMimetype())
|
|
||||||
|
|
||||||
data, err := b.wc.Download(imsg)
|
|
||||||
if err != nil {
|
|
||||||
b.Log.Errorf("Download image failed: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move file to bridge storage
|
|
||||||
helper.HandleDownloadData(b.Log, &rmsg, filename, imsg.GetCaption(), "", &data, b.General)
|
|
||||||
|
|
||||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
|
|
||||||
b.Log.Debugf("<= Message is %#v", rmsg)
|
|
||||||
|
|
||||||
b.Remote <- rmsg
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleVideoMessage downloads video messages
|
|
||||||
func (b *Bwhatsapp) handleVideoMessage(msg *events.Message) {
|
|
||||||
imsg := msg.Message.GetVideoMessage()
|
|
||||||
|
|
||||||
senderJID := msg.Info.Sender
|
|
||||||
senderName := b.getSenderName(msg.Info)
|
|
||||||
ci := imsg.GetContextInfo()
|
|
||||||
|
|
||||||
if senderJID == (types.JID{}) && ci.Participant != nil {
|
|
||||||
senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
|
|
||||||
}
|
|
||||||
|
|
||||||
rmsg := config.Message{
|
|
||||||
UserID: senderJID.String(),
|
|
||||||
Username: senderName,
|
|
||||||
Channel: msg.Info.Chat.String(),
|
|
||||||
Account: b.Account,
|
|
||||||
Protocol: b.Protocol,
|
|
||||||
Extra: make(map[string][]interface{}),
|
|
||||||
ID: msg.Info.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
|
|
||||||
rmsg.Avatar = avatarURL
|
|
||||||
}
|
|
||||||
|
|
||||||
fileExt, err := mime.ExtensionsByType(imsg.GetMimetype())
|
|
||||||
if err != nil {
|
|
||||||
b.Log.Errorf("Mimetype detection error: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fileExt) == 0 {
|
|
||||||
fileExt = append(fileExt, ".mp4")
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := fmt.Sprintf("%v%v", msg.Info.ID, fileExt[0])
|
|
||||||
|
|
||||||
b.Log.Debugf("Trying to download %s with size %#v and type %s", filename, imsg.GetFileLength(), imsg.GetMimetype())
|
|
||||||
|
|
||||||
data, err := b.wc.Download(imsg)
|
|
||||||
if err != nil {
|
|
||||||
b.Log.Errorf("Download video failed: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move file to bridge storage
|
|
||||||
helper.HandleDownloadData(b.Log, &rmsg, filename, imsg.GetCaption(), "", &data, b.General)
|
|
||||||
|
|
||||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
|
|
||||||
b.Log.Debugf("<= Message is %#v", rmsg)
|
|
||||||
|
|
||||||
b.Remote <- rmsg
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleAudioMessage downloads audio messages
|
|
||||||
func (b *Bwhatsapp) handleAudioMessage(msg *events.Message) {
|
|
||||||
imsg := msg.Message.GetAudioMessage()
|
|
||||||
|
|
||||||
senderJID := msg.Info.Sender
|
|
||||||
senderName := b.getSenderName(msg.Info)
|
|
||||||
ci := imsg.GetContextInfo()
|
|
||||||
|
|
||||||
if senderJID == (types.JID{}) && ci.Participant != nil {
|
|
||||||
senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
|
|
||||||
}
|
|
||||||
|
|
||||||
rmsg := config.Message{
|
|
||||||
UserID: senderJID.String(),
|
|
||||||
Username: senderName,
|
|
||||||
Channel: msg.Info.Chat.String(),
|
|
||||||
Account: b.Account,
|
|
||||||
Protocol: b.Protocol,
|
|
||||||
Extra: make(map[string][]interface{}),
|
|
||||||
ID: msg.Info.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
|
|
||||||
rmsg.Avatar = avatarURL
|
|
||||||
}
|
|
||||||
|
|
||||||
fileExt, err := mime.ExtensionsByType(imsg.GetMimetype())
|
|
||||||
if err != nil {
|
|
||||||
b.Log.Errorf("Mimetype detection error: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fileExt) == 0 {
|
|
||||||
fileExt = append(fileExt, ".ogg")
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := fmt.Sprintf("%v%v", msg.Info.ID, fileExt[0])
|
|
||||||
|
|
||||||
b.Log.Debugf("Trying to download %s with size %#v and type %s", filename, imsg.GetFileLength(), imsg.GetMimetype())
|
|
||||||
|
|
||||||
data, err := b.wc.Download(imsg)
|
|
||||||
if err != nil {
|
|
||||||
b.Log.Errorf("Download video failed: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move file to bridge storage
|
|
||||||
helper.HandleDownloadData(b.Log, &rmsg, filename, "audio message", "", &data, b.General)
|
|
||||||
|
|
||||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
|
|
||||||
b.Log.Debugf("<= Message is %#v", rmsg)
|
|
||||||
|
|
||||||
b.Remote <- rmsg
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleDocumentMessage downloads documents
|
|
||||||
func (b *Bwhatsapp) handleDocumentMessage(msg *events.Message) {
|
|
||||||
imsg := msg.Message.GetDocumentMessage()
|
|
||||||
|
|
||||||
senderJID := msg.Info.Sender
|
|
||||||
senderName := b.getSenderName(msg.Info)
|
|
||||||
ci := imsg.GetContextInfo()
|
|
||||||
|
|
||||||
if senderJID == (types.JID{}) && ci.Participant != nil {
|
|
||||||
senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
|
|
||||||
}
|
|
||||||
|
|
||||||
rmsg := config.Message{
|
|
||||||
UserID: senderJID.String(),
|
|
||||||
Username: senderName,
|
|
||||||
Channel: msg.Info.Chat.String(),
|
|
||||||
Account: b.Account,
|
|
||||||
Protocol: b.Protocol,
|
|
||||||
Extra: make(map[string][]interface{}),
|
|
||||||
ID: msg.Info.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
|
|
||||||
rmsg.Avatar = avatarURL
|
|
||||||
}
|
|
||||||
|
|
||||||
fileExt, err := mime.ExtensionsByType(imsg.GetMimetype())
|
|
||||||
if err != nil {
|
|
||||||
b.Log.Errorf("Mimetype detection error: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := fmt.Sprintf("%v", imsg.GetFileName())
|
|
||||||
|
|
||||||
b.Log.Debugf("Trying to download %s with extension %s and type %s", filename, fileExt, imsg.GetMimetype())
|
|
||||||
|
|
||||||
data, err := b.wc.Download(imsg)
|
|
||||||
if err != nil {
|
|
||||||
b.Log.Errorf("Download document message failed: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move file to bridge storage
|
|
||||||
helper.HandleDownloadData(b.Log, &rmsg, filename, imsg.GetCaption(), "", &data, b.General)
|
|
||||||
|
|
||||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
|
|
||||||
b.Log.Debugf("<= Message is %#v", rmsg)
|
|
||||||
|
|
||||||
b.Remote <- rmsg
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
//go:build whatsappmulti
|
|
||||||
// +build whatsappmulti
|
|
||||||
|
|
||||||
package bwhatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"go.mau.fi/whatsmeow"
|
|
||||||
"go.mau.fi/whatsmeow/store"
|
|
||||||
"go.mau.fi/whatsmeow/store/sqlstore"
|
|
||||||
"go.mau.fi/whatsmeow/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ProfilePicInfo struct {
|
|
||||||
URL string `json:"eurl"`
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
Status int16 `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bwhatsapp) reloadContacts() {
|
|
||||||
if _, err := b.wc.Store.Contacts.GetAllContacts(); err != nil {
|
|
||||||
b.Log.Errorf("error on update of contacts: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
allcontacts, err := b.wc.Store.Contacts.GetAllContacts()
|
|
||||||
if err != nil {
|
|
||||||
b.Log.Errorf("error on update of contacts: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(allcontacts) > 0 {
|
|
||||||
b.contacts = allcontacts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bwhatsapp) getSenderName(info types.MessageInfo) string {
|
|
||||||
// Parse AD JID
|
|
||||||
var senderJid types.JID
|
|
||||||
senderJid.User, senderJid.Server = info.Sender.User, info.Sender.Server
|
|
||||||
|
|
||||||
sender, exists := b.contacts[senderJid]
|
|
||||||
|
|
||||||
if !exists || (sender.FullName == "" && sender.FirstName == "") {
|
|
||||||
b.reloadContacts() // Contacts may need to be reloaded
|
|
||||||
sender, exists = b.contacts[senderJid]
|
|
||||||
}
|
|
||||||
|
|
||||||
if exists && sender.FullName != "" {
|
|
||||||
return sender.FullName
|
|
||||||
}
|
|
||||||
|
|
||||||
if info.PushName != "" {
|
|
||||||
return info.PushName
|
|
||||||
}
|
|
||||||
|
|
||||||
if exists && sender.FirstName != "" {
|
|
||||||
return sender.FirstName
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Someone"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bwhatsapp) getSenderNotify(senderJid types.JID) string {
|
|
||||||
sender, exists := b.contacts[senderJid]
|
|
||||||
|
|
||||||
if !exists || (sender.FullName == "" && sender.PushName == "" && sender.FirstName == "") {
|
|
||||||
b.reloadContacts() // Contacts may need to be reloaded
|
|
||||||
sender, exists = b.contacts[senderJid]
|
|
||||||
}
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
return "someone"
|
|
||||||
}
|
|
||||||
|
|
||||||
if exists && sender.FullName != "" {
|
|
||||||
return sender.FullName
|
|
||||||
}
|
|
||||||
|
|
||||||
if exists && sender.PushName != "" {
|
|
||||||
return sender.PushName
|
|
||||||
}
|
|
||||||
|
|
||||||
if exists && sender.FirstName != "" {
|
|
||||||
return sender.FirstName
|
|
||||||
}
|
|
||||||
|
|
||||||
return "someone"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bwhatsapp) GetProfilePicThumb(jid string) (*types.ProfilePictureInfo, error) {
|
|
||||||
pjid, _ := types.ParseJID(jid)
|
|
||||||
|
|
||||||
info, err := b.wc.GetProfilePictureInfo(pjid, &whatsmeow.GetProfilePictureParams{
|
|
||||||
Preview: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get avatar: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return info, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isGroupJid(identifier string) bool {
|
|
||||||
return strings.HasSuffix(identifier, "@g.us") ||
|
|
||||||
strings.HasSuffix(identifier, "@temp") ||
|
|
||||||
strings.HasSuffix(identifier, "@broadcast")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bwhatsapp) getDevice() (*store.Device, error) {
|
|
||||||
device := &store.Device{}
|
|
||||||
|
|
||||||
storeContainer, err := sqlstore.New("sqlite", "file:"+b.Config.GetString("sessionfile")+".db?_foreign_keys=on&_pragma=busy_timeout=10000", nil)
|
|
||||||
if err != nil {
|
|
||||||
return device, fmt.Errorf("failed to connect to database: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
device, err = storeContainer.GetFirstDevice()
|
|
||||||
if err != nil {
|
|
||||||
return device, fmt.Errorf("failed to get device: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return device, nil
|
|
||||||
}
|
|
||||||
@@ -1,413 +0,0 @@
|
|||||||
//go:build whatsappmulti
|
|
||||||
// +build whatsappmulti
|
|
||||||
|
|
||||||
package bwhatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"mime"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge"
|
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
|
||||||
"github.com/mdp/qrterminal"
|
|
||||||
|
|
||||||
"go.mau.fi/whatsmeow"
|
|
||||||
"go.mau.fi/whatsmeow/binary/proto"
|
|
||||||
"go.mau.fi/whatsmeow/types"
|
|
||||||
waLog "go.mau.fi/whatsmeow/util/log"
|
|
||||||
|
|
||||||
goproto "google.golang.org/protobuf/proto"
|
|
||||||
|
|
||||||
_ "modernc.org/sqlite" // needed for sqlite
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Account config parameters
|
|
||||||
cfgNumber = "Number"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Bwhatsapp Bridge structure keeping all the information needed for relying
|
|
||||||
type Bwhatsapp struct {
|
|
||||||
*bridge.Config
|
|
||||||
|
|
||||||
startedAt time.Time
|
|
||||||
wc *whatsmeow.Client
|
|
||||||
contacts map[types.JID]types.ContactInfo
|
|
||||||
users map[string]types.ContactInfo
|
|
||||||
userAvatars map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// New Create a new WhatsApp bridge. This will be called for each [whatsapp.<server>] entry you have in the config file
|
|
||||||
func New(cfg *bridge.Config) bridge.Bridger {
|
|
||||||
number := cfg.GetString(cfgNumber)
|
|
||||||
|
|
||||||
if number == "" {
|
|
||||||
cfg.Log.Fatalf("Missing configuration for WhatsApp bridge: Number")
|
|
||||||
}
|
|
||||||
|
|
||||||
b := &Bwhatsapp{
|
|
||||||
Config: cfg,
|
|
||||||
|
|
||||||
users: make(map[string]types.ContactInfo),
|
|
||||||
userAvatars: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to WhatsApp. Required implementation of the Bridger interface
|
|
||||||
func (b *Bwhatsapp) Connect() error {
|
|
||||||
device, err := b.getDevice()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
number := b.GetString(cfgNumber)
|
|
||||||
if number == "" {
|
|
||||||
return errors.New("whatsapp's telephone number need to be configured")
|
|
||||||
}
|
|
||||||
|
|
||||||
b.Log.Debugln("Connecting to WhatsApp..")
|
|
||||||
|
|
||||||
b.wc = whatsmeow.NewClient(device, waLog.Stdout("Client", "INFO", true))
|
|
||||||
b.wc.AddEventHandler(b.eventHandler)
|
|
||||||
|
|
||||||
firstlogin := false
|
|
||||||
var qrChan <-chan whatsmeow.QRChannelItem
|
|
||||||
if b.wc.Store.ID == nil {
|
|
||||||
firstlogin = true
|
|
||||||
qrChan, err = b.wc.GetQRChannel(context.Background())
|
|
||||||
if err != nil && !errors.Is(err, whatsmeow.ErrQRStoreContainsID) {
|
|
||||||
return errors.New("failed to to get QR channel:" + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = b.wc.Connect()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("failed to connect to WhatsApp: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.wc.Store.ID == nil {
|
|
||||||
for evt := range qrChan {
|
|
||||||
if evt.Event == "code" {
|
|
||||||
qrterminal.GenerateHalfBlock(evt.Code, qrterminal.L, os.Stdout)
|
|
||||||
} else {
|
|
||||||
b.Log.Infof("QR channel result: %s", evt.Event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// disconnect and reconnect on our first login/pairing
|
|
||||||
// for some reason the GetJoinedGroups in JoinChannel doesn't work on first login
|
|
||||||
if firstlogin {
|
|
||||||
b.wc.Disconnect()
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
err = b.wc.Connect()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("failed to connect to WhatsApp: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.Log.Infoln("WhatsApp connection successful")
|
|
||||||
|
|
||||||
b.contacts, err = b.wc.Store.Contacts.GetAllContacts()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("failed to get contacts: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
b.startedAt = time.Now()
|
|
||||||
|
|
||||||
// map all the users
|
|
||||||
for id, contact := range b.contacts {
|
|
||||||
if !isGroupJid(id.String()) && id.String() != "status@broadcast" {
|
|
||||||
// it is user
|
|
||||||
b.users[id.String()] = contact
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get user avatar asynchronously
|
|
||||||
b.Log.Info("Getting user avatars..")
|
|
||||||
|
|
||||||
for jid := range b.users {
|
|
||||||
info, err := b.GetProfilePicThumb(jid)
|
|
||||||
if err != nil {
|
|
||||||
b.Log.Warnf("Could not get profile photo of %s: %v", jid, err)
|
|
||||||
} else {
|
|
||||||
b.Lock()
|
|
||||||
if info != nil {
|
|
||||||
b.userAvatars[jid] = info.URL
|
|
||||||
}
|
|
||||||
b.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.Log.Info("Finished getting avatars..")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disconnect is called while reconnecting to the bridge
|
|
||||||
// Required implementation of the Bridger interface
|
|
||||||
func (b *Bwhatsapp) Disconnect() error {
|
|
||||||
b.wc.Disconnect()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoinChannel Join a WhatsApp group specified in gateway config as channel='number-id@g.us' or channel='Channel name'
|
|
||||||
// Required implementation of the Bridger interface
|
|
||||||
// https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16
|
|
||||||
func (b *Bwhatsapp) JoinChannel(channel config.ChannelInfo) error {
|
|
||||||
byJid := isGroupJid(channel.Name)
|
|
||||||
|
|
||||||
groups, err := b.wc.GetJoinedGroups()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify if we are member of the given group
|
|
||||||
if byJid {
|
|
||||||
gJID, err := types.ParseJID(channel.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, group := range groups {
|
|
||||||
if group.JID == gJID {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foundGroups := []string{}
|
|
||||||
|
|
||||||
for _, group := range groups {
|
|
||||||
if group.Name == channel.Name {
|
|
||||||
foundGroups = append(foundGroups, group.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch len(foundGroups) {
|
|
||||||
case 0:
|
|
||||||
// didn't match any group - print out possibilites
|
|
||||||
for _, group := range groups {
|
|
||||||
b.Log.Infof("%s %s", group.JID, group.Name)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("please specify group's JID from the list above instead of the name '%s'", channel.Name)
|
|
||||||
case 1:
|
|
||||||
return fmt.Errorf("group name might change. Please configure gateway with channel=\"%v\" instead of channel=\"%v\"", foundGroups[0], channel.Name)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("there is more than one group with name '%s'. Please specify one of JIDs as channel name: %v", channel.Name, foundGroups)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post a document message from the bridge to WhatsApp
|
|
||||||
func (b *Bwhatsapp) PostDocumentMessage(msg config.Message, filetype string) (string, error) {
|
|
||||||
groupJID, _ := types.ParseJID(msg.Channel)
|
|
||||||
|
|
||||||
fi := msg.Extra["file"][0].(config.FileInfo)
|
|
||||||
|
|
||||||
caption := msg.Username + fi.Comment
|
|
||||||
|
|
||||||
resp, err := b.wc.Upload(context.Background(), *fi.Data, whatsmeow.MediaDocument)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post document message
|
|
||||||
var message proto.Message
|
|
||||||
|
|
||||||
message.DocumentMessage = &proto.DocumentMessage{
|
|
||||||
Title: &fi.Name,
|
|
||||||
FileName: &fi.Name,
|
|
||||||
Mimetype: &filetype,
|
|
||||||
Caption: &caption,
|
|
||||||
MediaKey: resp.MediaKey,
|
|
||||||
FileEncSha256: resp.FileEncSHA256,
|
|
||||||
FileSha256: resp.FileSHA256,
|
|
||||||
FileLength: goproto.Uint64(resp.FileLength),
|
|
||||||
Url: &resp.URL,
|
|
||||||
}
|
|
||||||
|
|
||||||
b.Log.Debugf("=> Sending %#v as a document", msg)
|
|
||||||
|
|
||||||
ID := whatsmeow.GenerateMessageID()
|
|
||||||
_, err = b.wc.SendMessage(context.TODO(), groupJID, &message, whatsmeow.SendRequestExtra{ID: ID})
|
|
||||||
|
|
||||||
return ID, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post an image message from the bridge to WhatsApp
|
|
||||||
// Handle, for sure image/jpeg, image/png and image/gif MIME types
|
|
||||||
func (b *Bwhatsapp) PostImageMessage(msg config.Message, filetype string) (string, error) {
|
|
||||||
groupJID, _ := types.ParseJID(msg.Channel)
|
|
||||||
|
|
||||||
fi := msg.Extra["file"][0].(config.FileInfo)
|
|
||||||
|
|
||||||
caption := msg.Username + fi.Comment
|
|
||||||
|
|
||||||
resp, err := b.wc.Upload(context.Background(), *fi.Data, whatsmeow.MediaImage)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var message proto.Message
|
|
||||||
|
|
||||||
message.ImageMessage = &proto.ImageMessage{
|
|
||||||
Mimetype: &filetype,
|
|
||||||
Caption: &caption,
|
|
||||||
MediaKey: resp.MediaKey,
|
|
||||||
FileEncSha256: resp.FileEncSHA256,
|
|
||||||
FileSha256: resp.FileSHA256,
|
|
||||||
FileLength: goproto.Uint64(resp.FileLength),
|
|
||||||
Url: &resp.URL,
|
|
||||||
}
|
|
||||||
|
|
||||||
b.Log.Debugf("=> Sending %#v as an image", msg)
|
|
||||||
|
|
||||||
ID := whatsmeow.GenerateMessageID()
|
|
||||||
_, err = b.wc.SendMessage(context.TODO(), groupJID, &message, whatsmeow.SendRequestExtra{ID: ID})
|
|
||||||
|
|
||||||
return ID, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post a video message from the bridge to WhatsApp
|
|
||||||
func (b *Bwhatsapp) PostVideoMessage(msg config.Message, filetype string) (string, error) {
|
|
||||||
groupJID, _ := types.ParseJID(msg.Channel)
|
|
||||||
|
|
||||||
fi := msg.Extra["file"][0].(config.FileInfo)
|
|
||||||
|
|
||||||
caption := msg.Username + fi.Comment
|
|
||||||
|
|
||||||
resp, err := b.wc.Upload(context.Background(), *fi.Data, whatsmeow.MediaVideo)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var message proto.Message
|
|
||||||
|
|
||||||
message.VideoMessage = &proto.VideoMessage{
|
|
||||||
Mimetype: &filetype,
|
|
||||||
Caption: &caption,
|
|
||||||
MediaKey: resp.MediaKey,
|
|
||||||
FileEncSha256: resp.FileEncSHA256,
|
|
||||||
FileSha256: resp.FileSHA256,
|
|
||||||
FileLength: goproto.Uint64(resp.FileLength),
|
|
||||||
Url: &resp.URL,
|
|
||||||
}
|
|
||||||
|
|
||||||
b.Log.Debugf("=> Sending %#v as a video", msg)
|
|
||||||
|
|
||||||
ID := whatsmeow.GenerateMessageID()
|
|
||||||
_, err = b.wc.SendMessage(context.TODO(), groupJID, &message, whatsmeow.SendRequestExtra{ID: ID})
|
|
||||||
|
|
||||||
return ID, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post audio inline
|
|
||||||
func (b *Bwhatsapp) PostAudioMessage(msg config.Message, filetype string) (string, error) {
|
|
||||||
groupJID, _ := types.ParseJID(msg.Channel)
|
|
||||||
|
|
||||||
fi := msg.Extra["file"][0].(config.FileInfo)
|
|
||||||
|
|
||||||
resp, err := b.wc.Upload(context.Background(), *fi.Data, whatsmeow.MediaAudio)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var message proto.Message
|
|
||||||
|
|
||||||
message.AudioMessage = &proto.AudioMessage{
|
|
||||||
Mimetype: &filetype,
|
|
||||||
MediaKey: resp.MediaKey,
|
|
||||||
FileEncSha256: resp.FileEncSHA256,
|
|
||||||
FileSha256: resp.FileSHA256,
|
|
||||||
FileLength: goproto.Uint64(resp.FileLength),
|
|
||||||
Url: &resp.URL,
|
|
||||||
}
|
|
||||||
|
|
||||||
b.Log.Debugf("=> Sending %#v as audio", msg)
|
|
||||||
|
|
||||||
ID := whatsmeow.GenerateMessageID()
|
|
||||||
_, err = b.wc.SendMessage(context.TODO(), groupJID, &message, whatsmeow.SendRequestExtra{ID: ID})
|
|
||||||
|
|
||||||
var captionMessage proto.Message
|
|
||||||
caption := msg.Username + fi.Comment + "\u2B06" // the char on the end is upwards arrow emoji
|
|
||||||
captionMessage.Conversation = &caption
|
|
||||||
|
|
||||||
captionID := whatsmeow.GenerateMessageID()
|
|
||||||
_, err = b.wc.SendMessage(context.TODO(), groupJID, &captionMessage, whatsmeow.SendRequestExtra{ID: captionID})
|
|
||||||
|
|
||||||
return ID, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a message from the bridge to WhatsApp
|
|
||||||
func (b *Bwhatsapp) Send(msg config.Message) (string, error) {
|
|
||||||
groupJID, _ := types.ParseJID(msg.Channel)
|
|
||||||
|
|
||||||
b.Log.Debugf("=> Receiving %#v", msg)
|
|
||||||
|
|
||||||
// Delete message
|
|
||||||
if msg.Event == config.EventMsgDelete {
|
|
||||||
if msg.ID == "" {
|
|
||||||
// No message ID in case action is executed on a message sent before the bridge was started
|
|
||||||
// and then the bridge cache doesn't have this message ID mapped
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := b.wc.RevokeMessage(groupJID, msg.ID)
|
|
||||||
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Edit message
|
|
||||||
if msg.ID != "" {
|
|
||||||
b.Log.Debugf("updating message with id %s", msg.ID)
|
|
||||||
|
|
||||||
if b.GetString("editsuffix") != "" {
|
|
||||||
msg.Text += b.GetString("EditSuffix")
|
|
||||||
} else {
|
|
||||||
msg.Text += " (edited)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Upload a file
|
|
||||||
if msg.Extra["file"] != nil {
|
|
||||||
fi := msg.Extra["file"][0].(config.FileInfo)
|
|
||||||
filetype := mime.TypeByExtension(filepath.Ext(fi.Name))
|
|
||||||
|
|
||||||
b.Log.Debugf("Extra file is %#v", filetype)
|
|
||||||
|
|
||||||
// TODO: add different types
|
|
||||||
// TODO: add webp conversion
|
|
||||||
switch filetype {
|
|
||||||
case "image/jpeg", "image/png", "image/gif":
|
|
||||||
return b.PostImageMessage(msg, filetype)
|
|
||||||
case "video/mp4", "video/3gpp": // TODO: Check if codecs are supported by WA
|
|
||||||
return b.PostVideoMessage(msg, filetype)
|
|
||||||
case "audio/ogg":
|
|
||||||
return b.PostAudioMessage(msg, "audio/ogg; codecs=opus") // TODO: Detect if it is actually OPUS
|
|
||||||
case "audio/aac", "audio/mp4", "audio/amr", "audio/mpeg":
|
|
||||||
return b.PostAudioMessage(msg, filetype)
|
|
||||||
default:
|
|
||||||
return b.PostDocumentMessage(msg, filetype)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
text := msg.Username + msg.Text
|
|
||||||
|
|
||||||
var message proto.Message
|
|
||||||
|
|
||||||
message.Conversation = &text
|
|
||||||
|
|
||||||
ID := whatsmeow.GenerateMessageID()
|
|
||||||
_, err := b.wc.SendMessage(context.TODO(), groupJID, &message, whatsmeow.SendRequestExtra{ID: ID})
|
|
||||||
|
|
||||||
return ID, err
|
|
||||||
}
|
|
||||||
157
changelog.md
157
changelog.md
@@ -1,154 +1,3 @@
|
|||||||
# v1.26.0
|
|
||||||
|
|
||||||
## New features
|
|
||||||
|
|
||||||
- irc: Allow substitution of bot's nick in RunCommands (irc) (#1890)
|
|
||||||
- matrix: Add Matrix username spoofing (#1875)
|
|
||||||
|
|
||||||
## Enhancements
|
|
||||||
|
|
||||||
- general: Update dependencies (#1951)
|
|
||||||
- mattermost: Remove mattermost 5 support (#1936)
|
|
||||||
- mumble: Implement sending of EventJoinLeave both to and from Mumble (#1915)
|
|
||||||
- whatsappmulti: Improve attachment handling (whatsapp) (#1928)
|
|
||||||
- whatsappmulti: Handle incoming document captions from whatsapp (#1935)
|
|
||||||
|
|
||||||
## Bugfix
|
|
||||||
|
|
||||||
- irc: Fix empty messages on IRC (#1897)
|
|
||||||
- telegram: Fix message html entities escaping when sending to Telegram (#1855)
|
|
||||||
- telegram/slack: Fix error messages in telegram and slack bridges (#1862)
|
|
||||||
- telegram: Fix telegram attachment comment formatting and escaping (#1920)
|
|
||||||
- telegram: Make the cgo lottie a build tag (-tag cgolottie) (#1955)
|
|
||||||
- whatsappmulti: Update dependencies and fix whatsmeow API changes (#1887)
|
|
||||||
- whatsappmulti: Fix the "Someone" nickname problem (whatsapp) (#1931)
|
|
||||||
|
|
||||||
This release couldn't exist without the following contributors:
|
|
||||||
@s3lph, @sas1024, @Glandos, @jx11r, @Lucki, @BuckarooBanzay, @ilmaisin, @Kufat
|
|
||||||
|
|
||||||
# v1.25.2
|
|
||||||
|
|
||||||
## Enhancements
|
|
||||||
|
|
||||||
- general: Update dependencies (#1851,#1841)
|
|
||||||
- mattermost: Support mattermost v7.x (#1852)
|
|
||||||
|
|
||||||
## Bugfix
|
|
||||||
|
|
||||||
- discord: Fix Unwanted join notifications from one Discord server to another (#1612)
|
|
||||||
- discord: Ignore events from other guilds, add nosendjoinpart support (#1846)
|
|
||||||
|
|
||||||
This release couldn't exist without the following contributors:
|
|
||||||
@wlcx
|
|
||||||
|
|
||||||
# v1.25.1
|
|
||||||
|
|
||||||
## Enhancements
|
|
||||||
|
|
||||||
- matrix: Add KeepQuotedReply option for matrix to fix regression (#1823)
|
|
||||||
- slack: Improve Slack attachments formatting (slack) (#1807)
|
|
||||||
|
|
||||||
## Bugfix
|
|
||||||
|
|
||||||
- general: Update dependencies (#1813,#1822,#1833)
|
|
||||||
- mattermost: Add space between filename and URL (mattermost). Fixes #1820
|
|
||||||
- matrix: Update matterbridge/gomatrix. Fixes #1772 (#1803)
|
|
||||||
- telegram: Do not modify .webm files (telegram). Fixes #17**88 (#1802)
|
|
||||||
- telegram: Do not apply any markup to URL entities (telegram) (#1808)
|
|
||||||
- telegram: Fix telegram message deletion request (#1818)
|
|
||||||
- vk: Fix UploadMessagesPhoto for vk community chat (vk) (#1812)
|
|
||||||
|
|
||||||
This release couldn't exist without the following contributors:
|
|
||||||
@bd808, @chugunov, @sas1024, @SevereCloud, @ValdikSS
|
|
||||||
|
|
||||||
# v1.25.0
|
|
||||||
|
|
||||||
## Breaking changes
|
|
||||||
|
|
||||||
- whatsapp: deprecated, the library <https://github.com/Rhymen/go-whatsapp> isn't maintained anymore.
|
|
||||||
We're switching to <https://github.com/tulir/whatsmeow> but as this uses a GPL3 licensed library we can't provide you with binaries.
|
|
||||||
You'll need to build it yourself. More information about this can be found here: <https://github.com/42wim/matterbridge#building-with-whatsapp-beta-multidevice-support>
|
|
||||||
|
|
||||||
## New features
|
|
||||||
|
|
||||||
- whatsappmulti: whatsapp multidevice support added - more info <https://github.com/42wim/matterbridge#building-with-whatsapp-beta-multidevice-support>
|
|
||||||
- general: Add Dockerfile_whatsappmulti for building with WhatsApp Multi-Device support (Whatsmeow) (#1774)
|
|
||||||
- telegram: Add UseFullName option (telegram) (#1777)
|
|
||||||
- slack: Use slack real name as user name (slack) (#1775)
|
|
||||||
|
|
||||||
## Enhancements
|
|
||||||
|
|
||||||
- general: Ignore sending file with comment, if comment contains IgnoreMessages value (#1783)
|
|
||||||
- general: Update dependencies (#1784)
|
|
||||||
- irc: Update lrstanley/girc dep (#1773)
|
|
||||||
- slack: Preserve threading for messages with files (slack) (#1781)
|
|
||||||
- telegram: Preserve threading from telegram replies (telegram) (#1776)
|
|
||||||
- telegram: Multiple media in one message (telegram) (#1779)
|
|
||||||
- whatsapp: Add whatsapp deprecation warning (#1792)
|
|
||||||
|
|
||||||
## Bugfix
|
|
||||||
|
|
||||||
- discord: Change discord non-native threading behaviour (discord) (#1791)
|
|
||||||
|
|
||||||
This release couldn't exist without the following contributors:
|
|
||||||
@sas1024, @tpxtron
|
|
||||||
|
|
||||||
# v1.24.1
|
|
||||||
|
|
||||||
## Enhancements
|
|
||||||
|
|
||||||
- discord: Switch to discordgo upstream again (#1759)
|
|
||||||
- general: Update dependencies and vendor (#1761)
|
|
||||||
- general: Create inmessage-logger.tengo (#1688) (#1747)
|
|
||||||
- general: Add OpenRC service file (#1746)
|
|
||||||
- irc: Refactor utf-8 conversion (irc) (#1767)
|
|
||||||
|
|
||||||
## Bugfixes
|
|
||||||
|
|
||||||
- irc: Fix panic in irc. Closes #1751 (#1760)
|
|
||||||
- mumble: Implement a workaround to signal Opus support (mumble) (#1764)
|
|
||||||
- telegram: Fix for complex-formatted Telegram text (#1765)
|
|
||||||
- telegram: Fix Telegram channel title in forwards (#1753)
|
|
||||||
- telegram: Fix Telegram Problem (unforwarded formatting and skipping of linebreaks) (#1749)
|
|
||||||
|
|
||||||
This release couldn't exist without the following contributors:
|
|
||||||
@s3lph, @ValdikSS, @reckel-jm, @CyberTailor
|
|
||||||
|
|
||||||
# v1.24.0
|
|
||||||
|
|
||||||
## New features
|
|
||||||
|
|
||||||
- harmony: new protocol added: Add support for Harmony (#1656)
|
|
||||||
- irc: Allow binding to IP on IRC (#1640)
|
|
||||||
- irc: Add support for client certificate (irc) (#1710)
|
|
||||||
- mattermost: Add UseUsername option (mattermost). Fixes #1665 (#1714)
|
|
||||||
- mattermost: Add support for using ID in channel config (mattermost) (#1715)
|
|
||||||
- matrix: Reply support for Matrix (#1664)
|
|
||||||
- telegram: Add Telegram Bot Command /chatId (telegram) (#1703)
|
|
||||||
|
|
||||||
## Enhancements
|
|
||||||
|
|
||||||
- general: Update dependencies/vendor (#1659)
|
|
||||||
- discord: Add more debug options for discord (#1712)
|
|
||||||
- docker: Use Alpine stable again in Dockerfile (#1643)
|
|
||||||
- mattermost: Log eventtype in debug (mattermost) (#1676)
|
|
||||||
- mattermost: Add more ignore debug messages (mattermost) (#1678)
|
|
||||||
- slack: Add support for deleting files from slack to discord. Fixes #1705 (#1709)
|
|
||||||
- telegram: Add support for code blocks in telegram (#1650)
|
|
||||||
- telegram: Update telegram-bot-api to v5 (#1660)
|
|
||||||
- telegram: Add comments to messages (telegram) (#1652)
|
|
||||||
- telegram: Add support for sender_chat (telegram) (#1677)
|
|
||||||
- vk: Remove GroupID (vk) (#1668)
|
|
||||||
|
|
||||||
## Bugfix
|
|
||||||
|
|
||||||
- mattermost: Use current parentID if rootId is not set (mattermost) (#1675)
|
|
||||||
- matrix: Make HTMLDisable work correct (matrix) (#1716)
|
|
||||||
- whatsapp: Make EditSuffix option actually work (whatsapp). Fixes #1510 (#1728)
|
|
||||||
|
|
||||||
This release couldn't exist without the following contributors:
|
|
||||||
@DavyJohnesev, @GoliathLabs, @pontaoski, @PeGaSuS-Coder, @dependabot[bot], @vpzomtrrfrt, @SevereCloud, @soloam, @YashRE42, @danwalmsley, @SuperSandro2000, @inzanity
|
|
||||||
|
|
||||||
# v1.23.2
|
# v1.23.2
|
||||||
|
|
||||||
If you're running whatsapp you should update.
|
If you're running whatsapp you should update.
|
||||||
@@ -157,9 +6,6 @@ If you're running whatsapp you should update.
|
|||||||
|
|
||||||
- whatsapp: Update go-whatsapp version (#1630)
|
- whatsapp: Update go-whatsapp version (#1630)
|
||||||
|
|
||||||
This release couldn't exist without the following contributors:
|
|
||||||
@snikpic
|
|
||||||
|
|
||||||
# v1.23.1
|
# v1.23.1
|
||||||
|
|
||||||
If you're running mattermost 6 you should update.
|
If you're running mattermost 6 you should update.
|
||||||
@@ -174,9 +20,6 @@ If you're running mattermost 6 you should update.
|
|||||||
- xmpp: Use a new msgID when replacing messages (xmpp). Fixes #1584 (#1623)
|
- xmpp: Use a new msgID when replacing messages (xmpp). Fixes #1584 (#1623)
|
||||||
- zulip: Add better error handling on Zulip (#1589)
|
- zulip: Add better error handling on Zulip (#1589)
|
||||||
|
|
||||||
This release couldn't exist without the following contributors:
|
|
||||||
@Polynomdivision, @minecraftchest1, @alexmv
|
|
||||||
|
|
||||||
# v1.23.0
|
# v1.23.0
|
||||||
|
|
||||||
## New features
|
## New features
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
fmt := import("fmt")
|
|
||||||
os := import("os")
|
|
||||||
times := import("times")
|
|
||||||
|
|
||||||
if msgText != "" && msgUsername != "system" {
|
|
||||||
os.chdir("/var/www/matterbridge")
|
|
||||||
file := os.open_file("inmessage.log", os.o_append|os.o_wronly|os.o_create, 0644)
|
|
||||||
file.write_string(fmt.sprintf(
|
|
||||||
"[%s] <%s> %s\n",
|
|
||||||
times.time_format(times.now(), times.format_rfc1123),
|
|
||||||
msgUsername,
|
|
||||||
msgText
|
|
||||||
))
|
|
||||||
file.close()
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/sbin/openrc-run
|
|
||||||
# Copyright 2021-2022 Gentoo Authors
|
|
||||||
# Distributed under the terms of the GNU General Public License v2
|
|
||||||
|
|
||||||
command=/usr/bin/matterbridge
|
|
||||||
command_args="-conf ${MATTERBRIDGE_CONF:-/etc/matterbridge/bridge.toml} ${MATTERBRIDGE_ARGS}"
|
|
||||||
command_user="matterbridge:matterbridge"
|
|
||||||
pidfile="/run/${RC_SVCNAME}.pid"
|
|
||||||
command_background=1
|
|
||||||
output_log="/var/log/${RC_SVCNAME}.log"
|
|
||||||
error_log="${output_log}"
|
|
||||||
|
|
||||||
depend() {
|
|
||||||
need net
|
|
||||||
}
|
|
||||||
|
|
||||||
start_pre() {
|
|
||||||
checkpath -f "${output_log}" -o "${command_user}" || return 1
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
// +build !nowhatsapp
|
// +build !nowhatsapp
|
||||||
// +build !whatsappmulti
|
|
||||||
|
|
||||||
package bridgemap
|
package bridgemap
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
// +build whatsappmulti
|
|
||||||
|
|
||||||
package bridgemap
|
|
||||||
|
|
||||||
import (
|
|
||||||
bwhatsapp "github.com/42wim/matterbridge/bridge/whatsappmulti"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
FullMap["whatsapp"] = bwhatsapp.New
|
|
||||||
}
|
|
||||||
@@ -299,30 +299,13 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
|
|||||||
|
|
||||||
igNicks := strings.Fields(gw.Bridges[msg.Account].GetString("IgnoreNicks"))
|
igNicks := strings.Fields(gw.Bridges[msg.Account].GetString("IgnoreNicks"))
|
||||||
igMessages := strings.Fields(gw.Bridges[msg.Account].GetString("IgnoreMessages"))
|
igMessages := strings.Fields(gw.Bridges[msg.Account].GetString("IgnoreMessages"))
|
||||||
if gw.ignoreTextEmpty(msg) || gw.ignoreText(msg.Username, igNicks) || gw.ignoreText(msg.Text, igMessages) || gw.ignoreFilesComment(msg.Extra, igMessages) {
|
if gw.ignoreTextEmpty(msg) || gw.ignoreText(msg.Username, igNicks) || gw.ignoreText(msg.Text, igMessages) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignoreFilesComment returns true if we need to ignore a file with matched comment.
|
|
||||||
func (gw *Gateway) ignoreFilesComment(extra map[string][]interface{}, igMessages []string) bool {
|
|
||||||
if extra == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, f := range extra["file"] {
|
|
||||||
fi, ok := f.(config.FileInfo)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if gw.ignoreText(fi.Comment, igMessages) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) string {
|
func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) string {
|
||||||
if dest.GetBool("StripNick") {
|
if dest.GetBool("StripNick") {
|
||||||
re := regexp.MustCompile("[^a-zA-Z0-9]+")
|
re := regexp.MustCompile("[^a-zA-Z0-9]+")
|
||||||
|
|||||||
131
go.mod
131
go.mod
@@ -6,60 +6,55 @@ require (
|
|||||||
github.com/Benau/tgsconverter v0.0.0-20210809170556-99f4a4f6337f
|
github.com/Benau/tgsconverter v0.0.0-20210809170556-99f4a4f6337f
|
||||||
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
|
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
|
||||||
github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c
|
github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c
|
||||||
github.com/SevereCloud/vksdk/v2 v2.15.0
|
github.com/SevereCloud/vksdk/v2 v2.13.0
|
||||||
github.com/bwmarrin/discordgo v0.27.0
|
github.com/d5/tengo/v2 v2.10.0
|
||||||
github.com/d5/tengo/v2 v2.13.0
|
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.5.1
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||||
github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c
|
github.com/gomarkdown/markdown v0.0.0-20211207152620-5d6539fd8bfc
|
||||||
github.com/google/gops v0.3.26
|
github.com/google/gops v0.3.22
|
||||||
github.com/gorilla/schema v1.2.0
|
github.com/gorilla/schema v1.2.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/harmony-development/shibshib v0.0.0-20220101224523-c98059d09cfa
|
github.com/harmony-development/shibshib v0.0.0-20211127182844-512296f7c548
|
||||||
github.com/hashicorp/golang-lru v0.6.0
|
github.com/hashicorp/golang-lru v0.5.4
|
||||||
github.com/jpillora/backoff v1.0.0
|
github.com/jpillora/backoff v1.0.0
|
||||||
github.com/keybase/go-keybase-chat-bot v0.0.0-20221220212439-e48d9abd2c20
|
github.com/keybase/go-keybase-chat-bot v0.0.0-20211201215354-ee4b23828b55
|
||||||
github.com/kyokomi/emoji/v2 v2.2.11
|
github.com/kyokomi/emoji/v2 v2.2.8
|
||||||
github.com/labstack/echo/v4 v4.10.0
|
github.com/labstack/echo/v4 v4.6.3
|
||||||
github.com/lrstanley/girc v0.0.0-20221222153823-a92667a5c9b4
|
github.com/lrstanley/girc v0.0.0-20211023233735-147f0ff77566
|
||||||
|
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
|
||||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696
|
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696
|
||||||
|
github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7
|
||||||
github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be
|
github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be
|
||||||
github.com/matterbridge/gomatrix v0.0.0-20220411225302-271e5088ea27
|
|
||||||
github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75
|
github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75
|
||||||
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
|
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
|
||||||
github.com/matterbridge/matterclient v0.0.0-20220624224459-272af20c7ddf
|
github.com/matterbridge/matterclient v0.0.0-20211107234719-faca3cd42315
|
||||||
github.com/mattermost/mattermost-server/v5 v5.39.3
|
github.com/mattermost/mattermost-server/v5 v5.39.3
|
||||||
github.com/mattermost/mattermost-server/v6 v6.7.2
|
github.com/mattermost/mattermost-server/v6 v6.3.0
|
||||||
github.com/mattn/godown v0.0.1
|
github.com/mattn/godown v0.0.1
|
||||||
github.com/mdp/qrterminal v1.0.1
|
github.com/missdeer/golib v1.0.4
|
||||||
github.com/nelsonken/gomf v0.0.0-20190423072027-c65cc0469e94
|
github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9
|
||||||
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
|
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
|
||||||
github.com/rs/xid v1.4.0
|
github.com/rs/xid v1.3.0
|
||||||
github.com/russross/blackfriday v1.6.0
|
github.com/russross/blackfriday v1.6.0
|
||||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
|
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
|
||||||
github.com/shazow/ssh-chat v1.10.1
|
github.com/shazow/ssh-chat v1.10.1
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/slack-go/slack v0.12.1
|
github.com/slack-go/slack v0.10.0
|
||||||
github.com/spf13/viper v1.15.0
|
github.com/spf13/viper v1.10.1
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/vincent-petithory/dataurl v1.0.0
|
github.com/vincent-petithory/dataurl v1.0.0
|
||||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible
|
github.com/writeas/go-strip-markdown v2.0.1+incompatible
|
||||||
github.com/yaegashi/msgraph.go v0.1.4
|
github.com/yaegashi/msgraph.go v0.1.4
|
||||||
github.com/zfjagann/golang-ring v0.0.0-20220330170733-19bcea1b6289
|
github.com/zfjagann/golang-ring v0.0.0-20210116075443-7c86fdb43134
|
||||||
go.mau.fi/whatsmeow v0.0.0-20230128195103-dcbc8dd31a22
|
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
|
||||||
golang.org/x/image v0.3.0
|
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||||
golang.org/x/oauth2 v0.4.0
|
|
||||||
golang.org/x/text v0.6.0
|
|
||||||
gomod.garykim.dev/nc-talk v0.3.0
|
gomod.garykim.dev/nc-talk v0.3.0
|
||||||
google.golang.org/protobuf v1.28.1
|
|
||||||
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376
|
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376
|
||||||
layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14
|
layeh.com/gumble v0.0.0-20200818122324-146f9205029b
|
||||||
modernc.org/sqlite v1.20.3
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.0.0 // indirect
|
|
||||||
github.com/Benau/go_rlottie v0.0.0-20210807002906-98c1b2421989 // indirect
|
github.com/Benau/go_rlottie v0.0.0-20210807002906-98c1b2421989 // indirect
|
||||||
github.com/Jeffail/gabs v1.4.0 // indirect
|
github.com/Jeffail/gabs v1.4.0 // indirect
|
||||||
github.com/apex/log v1.9.0 // indirect
|
github.com/apex/log v1.9.0 // indirect
|
||||||
@@ -73,90 +68,74 @@ require (
|
|||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/gopackage/ddp v0.0.3 // indirect
|
github.com/gopackage/ddp v0.0.3 // indirect
|
||||||
github.com/graph-gophers/graphql-go v1.3.0 // indirect
|
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
|
||||||
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f // indirect
|
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f // indirect
|
||||||
github.com/klauspost/compress v1.15.8 // indirect
|
github.com/klauspost/compress v1.14.1 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||||
github.com/labstack/gommon v0.4.0 // indirect
|
github.com/labstack/gommon v0.3.1 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.5 // indirect
|
||||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
|
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
|
||||||
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect
|
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect
|
||||||
github.com/mattermost/logr v1.0.13 // indirect
|
github.com/mattermost/logr v1.0.13 // indirect
|
||||||
github.com/mattermost/logr/v2 v2.0.15 // indirect
|
github.com/mattermost/logr/v2 v2.0.15 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
github.com/minio/minio-go/v7 v7.0.24 // indirect
|
github.com/minio/minio-go/v7 v7.0.16 // indirect
|
||||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/monaco-io/request v1.0.5 // indirect
|
github.com/monaco-io/request v1.0.5 // indirect
|
||||||
github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d // indirect
|
github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d // indirect
|
||||||
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect
|
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
|
||||||
github.com/pborman/uuid v1.2.1 // indirect
|
github.com/pborman/uuid v1.2.1 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
|
||||||
github.com/philhofer/fwd v1.1.1 // indirect
|
github.com/philhofer/fwd v1.1.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
|
||||||
github.com/rickb777/date v1.12.4 // indirect
|
github.com/rickb777/date v1.12.4 // indirect
|
||||||
github.com/rickb777/plural v1.2.0 // indirect
|
github.com/rickb777/plural v1.2.0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/shazow/rateio v0.0.0-20200113175441-4461efc8bdc4 // indirect
|
github.com/shazow/rateio v0.0.0-20200113175441-4461efc8bdc4 // indirect
|
||||||
github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 // indirect
|
github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 // indirect
|
||||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 // indirect
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||||
github.com/spf13/afero v1.9.3 // indirect
|
github.com/spf13/afero v1.6.0 // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.4.1 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.4.2 // indirect
|
github.com/subosito/gotenv v1.2.0 // indirect
|
||||||
github.com/tinylib/msgp v1.1.6 // indirect
|
github.com/tinylib/msgp v1.1.6 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
github.com/wiggin77/cfg v1.0.2 // indirect
|
github.com/wiggin77/cfg v1.0.2 // indirect
|
||||||
github.com/wiggin77/merror v1.0.3 // indirect
|
github.com/wiggin77/merror v1.0.3 // indirect
|
||||||
github.com/wiggin77/srslog v1.0.1 // indirect
|
github.com/wiggin77/srslog v1.0.1 // indirect
|
||||||
go.mau.fi/libsignal v0.1.0 // indirect
|
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.8.0 // indirect
|
go.uber.org/multierr v1.7.0 // indirect
|
||||||
go.uber.org/zap v1.21.0 // indirect
|
go.uber.org/zap v1.17.0 // indirect
|
||||||
golang.org/x/crypto v0.4.0 // indirect
|
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect
|
||||||
golang.org/x/net v0.5.0 // indirect
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||||
golang.org/x/sys v0.4.0 // indirect
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
|
||||||
golang.org/x/term v0.4.0 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
golang.org/x/time v0.2.0 // indirect
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
||||||
golang.org/x/tools v0.1.12 // indirect
|
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
lukechampine.com/uint128 v1.2.0 // indirect
|
|
||||||
modernc.org/cc/v3 v3.40.0 // indirect
|
|
||||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
|
||||||
modernc.org/libc v1.22.2 // indirect
|
|
||||||
modernc.org/mathutil v1.5.0 // indirect
|
|
||||||
modernc.org/memory v1.4.0 // indirect
|
|
||||||
modernc.org/opt v0.1.3 // indirect
|
|
||||||
modernc.org/strutil v1.1.3 // indirect
|
|
||||||
modernc.org/token v1.0.1 // indirect
|
|
||||||
rsc.io/qr v0.2.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//replace github.com/matrix-org/gomatrix => github.com/matterbridge/gomatrix v0.0.0-20220205235239-607eb9ee6419
|
replace github.com/matrix-org/gomatrix => github.com/matterbridge/gomatrix v0.0.0-20220205235239-607eb9ee6419
|
||||||
|
|
||||||
go 1.18
|
go 1.17
|
||||||
|
|||||||
@@ -122,11 +122,10 @@ RejoinDelay=0
|
|||||||
#Only works in IRC right now.
|
#Only works in IRC right now.
|
||||||
ColorNicks=false
|
ColorNicks=false
|
||||||
|
|
||||||
#RunCommands allows you to send RAW irc commands after connection.
|
#RunCommands allows you to send RAW irc commands after connection
|
||||||
#The string {BOTNICK} (case sensitive) will be replaced with the bot's current nickname.
|
|
||||||
#Array of strings
|
#Array of strings
|
||||||
#OPTIONAL (default empty)
|
#OPTIONAL (default empty)
|
||||||
RunCommands=["PRIVMSG user hello","PRIVMSG chanserv something", "MODE {BOTNICK} +B"]
|
RunCommands=["PRIVMSG user hello","PRIVMSG chanserv something"]
|
||||||
|
|
||||||
#PingDelay specifies how long to wait to send a ping to the irc server.
|
#PingDelay specifies how long to wait to send a ping to the irc server.
|
||||||
#You can use s for second, m for minute
|
#You can use s for second, m for minute
|
||||||
@@ -188,7 +187,7 @@ Label=""
|
|||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
#Enable to show users joins/parts from other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=false
|
||||||
|
|
||||||
@@ -198,7 +197,7 @@ ShowJoinPart=false
|
|||||||
VerboseJoinPart=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, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
NoSendJoinPart=false
|
NoSendJoinPart=false
|
||||||
|
|
||||||
@@ -325,7 +324,7 @@ Label=""
|
|||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
#Enable to show users joins/parts from other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=false
|
||||||
|
|
||||||
@@ -492,12 +491,12 @@ Label=""
|
|||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
#Enable to show users joins/parts from other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=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, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
NoSendJoinPart=false
|
NoSendJoinPart=false
|
||||||
|
|
||||||
@@ -579,7 +578,7 @@ Label=""
|
|||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
#Enable to show users joins/parts from other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=false
|
||||||
|
|
||||||
@@ -687,7 +686,7 @@ Label=""
|
|||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
#Enable to show users joins/parts from other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=false
|
||||||
|
|
||||||
@@ -826,12 +825,12 @@ Label=""
|
|||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
#Enable to show users joins/parts from other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=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, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
NoSendJoinPart=false
|
NoSendJoinPart=false
|
||||||
|
|
||||||
@@ -863,10 +862,6 @@ ShowUserTyping=false
|
|||||||
#Default "<clipped message>"
|
#Default "<clipped message>"
|
||||||
MessageClipped="<clipped message>"
|
MessageClipped="<clipped message>"
|
||||||
|
|
||||||
#If enabled use the slack "Real Name" as username.
|
|
||||||
#OPTIONAL (default false)
|
|
||||||
UseFullName=false
|
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
#discord section
|
#discord section
|
||||||
###################################################################
|
###################################################################
|
||||||
@@ -1041,12 +1036,6 @@ DisableWebPagePreview=false
|
|||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
UseFirstName=false
|
UseFirstName=false
|
||||||
|
|
||||||
#If enabled use the "Full Name" as username. If this is empty use the Username
|
|
||||||
#If disabled use the "Username" as username. If this is empty use the First Name and Last Name as Full Name
|
|
||||||
#If all names are empty, username will be "unknown"
|
|
||||||
#OPTIONAL (default false)
|
|
||||||
UseFullName=false
|
|
||||||
|
|
||||||
#WARNING! If enabled this will relay GIF/stickers/documents and other attachments as URLs
|
#WARNING! If enabled this will relay GIF/stickers/documents and other attachments as URLs
|
||||||
#Those URLs will contain your bot-token. This may not be what you want.
|
#Those URLs will contain your bot-token. This may not be what you want.
|
||||||
#For now there is no secure way to relay GIF/stickers/documents without seeing your token.
|
#For now there is no secure way to relay GIF/stickers/documents without seeing your token.
|
||||||
@@ -1138,7 +1127,7 @@ Label=""
|
|||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
#Enable to show users joins/parts from other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=false
|
||||||
|
|
||||||
@@ -1152,12 +1141,6 @@ StripNick=false
|
|||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowTopicChange=false
|
ShowTopicChange=false
|
||||||
|
|
||||||
#Opportunistically preserve threaded replies between Telegram groups.
|
|
||||||
#This only works if the parent message is still in the cache.
|
|
||||||
#Cache is flushed between restarts.
|
|
||||||
#OPTIONAL (default false)
|
|
||||||
PreserveThreading=false
|
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
#rocketchat section
|
#rocketchat section
|
||||||
###################################################################
|
###################################################################
|
||||||
@@ -1275,7 +1258,7 @@ Label=""
|
|||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
#Enable to show users joins/parts from other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=false
|
||||||
|
|
||||||
@@ -1329,15 +1312,6 @@ HTMLDisable=false
|
|||||||
# UseUserName shows the username instead of the server nickname
|
# UseUserName shows the username instead of the server nickname
|
||||||
UseUserName=false
|
UseUserName=false
|
||||||
|
|
||||||
# Matrix quotes replies and as of matterbridge 1.24.0 we strip those as this causes
|
|
||||||
# issues with bridges support threading and have PreserveThreading enabled.
|
|
||||||
# But if you for example use mattermost or discord with webhooks you'll need to enable
|
|
||||||
# this (and keep PreserveThreading disabled) if you want something that looks like a reply from matrix.
|
|
||||||
# See issues:
|
|
||||||
# - https://github.com/42wim/matterbridge/issues/1819
|
|
||||||
# - https://github.com/42wim/matterbridge/issues/1780
|
|
||||||
KeepQuotedReply=false
|
|
||||||
|
|
||||||
#Nicks you want to ignore.
|
#Nicks you want to ignore.
|
||||||
#Regular expressions supported
|
#Regular expressions supported
|
||||||
#Messages from those users will not be sent to other bridges.
|
#Messages from those users will not be sent to other bridges.
|
||||||
@@ -1387,15 +1361,10 @@ Label=""
|
|||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
#Enable to show users joins/parts from other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=false
|
||||||
|
|
||||||
#Rename the bot in the current room to the username of the message
|
|
||||||
#This will make an additional API request per message and will probably count towards rate limits
|
|
||||||
#OPTIONAL (default false)
|
|
||||||
SpoofUsername=false
|
|
||||||
|
|
||||||
#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
|
#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
|
||||||
#It will strip other characters from the nick
|
#It will strip other characters from the nick
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
@@ -1484,7 +1453,7 @@ Label=""
|
|||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
#Enable to show users joins/parts from other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=false
|
||||||
|
|
||||||
@@ -1572,15 +1541,6 @@ SkipTLSVerify=false
|
|||||||
#Default "<clipped message>"
|
#Default "<clipped message>"
|
||||||
MessageClipped="<clipped message>"
|
MessageClipped="<clipped message>"
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
|
||||||
#OPTIONAL (default false)
|
|
||||||
ShowJoinPart=false
|
|
||||||
|
|
||||||
#Do not send joins/parts to other bridges
|
|
||||||
#OPTIONAL (default false)
|
|
||||||
NoSendJoinPart=false
|
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
#VK
|
#VK
|
||||||
###################################################################
|
###################################################################
|
||||||
@@ -1693,7 +1653,7 @@ Label=""
|
|||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
#Enable to show users joins/parts from other bridges
|
#Enable to show users joins/parts from other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
|
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=false
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#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
|
||||||
[irc]
|
[irc]
|
||||||
[irc.libera]
|
[irc.freenode]
|
||||||
Server="irc.libera.chat:6667"
|
Server="irc.freenode.net:6667"
|
||||||
Nick="matterbot"
|
Nick="matterbot"
|
||||||
|
|
||||||
[mattermost]
|
[mattermost]
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
name="gateway1"
|
name="gateway1"
|
||||||
enable=true
|
enable=true
|
||||||
[[gateway.inout]]
|
[[gateway.inout]]
|
||||||
account="irc.libera"
|
account="irc.freenode"
|
||||||
channel="#testing"
|
channel="#testing"
|
||||||
|
|
||||||
[[gateway.inout]]
|
[[gateway.inout]]
|
||||||
@@ -29,6 +29,6 @@ enable=true
|
|||||||
#name="gateway2"
|
#name="gateway2"
|
||||||
#enable=true
|
#enable=true
|
||||||
#inout = [
|
#inout = [
|
||||||
# { account="irc.libera", channel="#testing", options={key="channelkey"}},
|
# { account="irc.freenode", channel="#testing", options={key="channelkey"}},
|
||||||
# { account="mattermost.work", channel="off-topic" },
|
# { account="mattermost.work", channel="off-topic" },
|
||||||
#]
|
#]
|
||||||
|
|||||||
27
vendor/filippo.io/edwards25519/LICENSE
generated
vendored
27
vendor/filippo.io/edwards25519/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
|||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
14
vendor/filippo.io/edwards25519/README.md
generated
vendored
14
vendor/filippo.io/edwards25519/README.md
generated
vendored
@@ -1,14 +0,0 @@
|
|||||||
# filippo.io/edwards25519
|
|
||||||
|
|
||||||
```
|
|
||||||
import "filippo.io/edwards25519"
|
|
||||||
```
|
|
||||||
|
|
||||||
This library implements the edwards25519 elliptic curve, exposing the necessary APIs to build a wide array of higher-level primitives.
|
|
||||||
Read the docs at [pkg.go.dev/filippo.io/edwards25519](https://pkg.go.dev/filippo.io/edwards25519).
|
|
||||||
|
|
||||||
The code is originally derived from Adam Langley's internal implementation in the Go standard library, and includes George Tankersley's [performance improvements](https://golang.org/cl/71950). It was then further developed by Henry de Valence for use in ristretto255, and was finally [merged back into the Go standard library](https://golang.org/cl/276272) as of Go 1.17. It now tracks the upstream codebase and extends it with additional functionality.
|
|
||||||
|
|
||||||
Most users don't need this package, and should instead use `crypto/ed25519` for signatures, `golang.org/x/crypto/curve25519` for Diffie-Hellman, or `github.com/gtank/ristretto255` for prime order group logic. However, for anyone currently using a fork of `crypto/ed25519/internal/edwards25519` or `github.com/agl/edwards25519`, this package should be a safer, faster, and more powerful alternative.
|
|
||||||
|
|
||||||
Since this package is meant to curb proliferation of edwards25519 implementations in the Go ecosystem, it welcomes requests for new APIs or reviewable performance improvements.
|
|
||||||
20
vendor/filippo.io/edwards25519/doc.go
generated
vendored
20
vendor/filippo.io/edwards25519/doc.go
generated
vendored
@@ -1,20 +0,0 @@
|
|||||||
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package edwards25519 implements group logic for the twisted Edwards curve
|
|
||||||
//
|
|
||||||
// -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2
|
|
||||||
//
|
|
||||||
// This is better known as the Edwards curve equivalent to Curve25519, and is
|
|
||||||
// the curve used by the Ed25519 signature scheme.
|
|
||||||
//
|
|
||||||
// Most users don't need this package, and should instead use crypto/ed25519 for
|
|
||||||
// signatures, golang.org/x/crypto/curve25519 for Diffie-Hellman, or
|
|
||||||
// github.com/gtank/ristretto255 for prime order group logic.
|
|
||||||
//
|
|
||||||
// However, developers who do need to interact with low-level edwards25519
|
|
||||||
// operations can use this package, which is an extended version of
|
|
||||||
// crypto/ed25519/internal/edwards25519 from the standard library repackaged as
|
|
||||||
// an importable module.
|
|
||||||
package edwards25519
|
|
||||||
428
vendor/filippo.io/edwards25519/edwards25519.go
generated
vendored
428
vendor/filippo.io/edwards25519/edwards25519.go
generated
vendored
@@ -1,428 +0,0 @@
|
|||||||
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package edwards25519
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"filippo.io/edwards25519/field"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Point types.
|
|
||||||
|
|
||||||
type projP1xP1 struct {
|
|
||||||
X, Y, Z, T field.Element
|
|
||||||
}
|
|
||||||
|
|
||||||
type projP2 struct {
|
|
||||||
X, Y, Z field.Element
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point represents a point on the edwards25519 curve.
|
|
||||||
//
|
|
||||||
// This type works similarly to math/big.Int, and all arguments and receivers
|
|
||||||
// are allowed to alias.
|
|
||||||
//
|
|
||||||
// The zero value is NOT valid, and it may be used only as a receiver.
|
|
||||||
type Point struct {
|
|
||||||
// The point is internally represented in extended coordinates (X, Y, Z, T)
|
|
||||||
// where x = X/Z, y = Y/Z, and xy = T/Z per https://eprint.iacr.org/2008/522.
|
|
||||||
x, y, z, t field.Element
|
|
||||||
|
|
||||||
// Make the type not comparable (i.e. used with == or as a map key), as
|
|
||||||
// equivalent points can be represented by different Go values.
|
|
||||||
_ incomparable
|
|
||||||
}
|
|
||||||
|
|
||||||
type incomparable [0]func()
|
|
||||||
|
|
||||||
func checkInitialized(points ...*Point) {
|
|
||||||
for _, p := range points {
|
|
||||||
if p.x == (field.Element{}) && p.y == (field.Element{}) {
|
|
||||||
panic("edwards25519: use of uninitialized Point")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type projCached struct {
|
|
||||||
YplusX, YminusX, Z, T2d field.Element
|
|
||||||
}
|
|
||||||
|
|
||||||
type affineCached struct {
|
|
||||||
YplusX, YminusX, T2d field.Element
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructors.
|
|
||||||
|
|
||||||
func (v *projP2) Zero() *projP2 {
|
|
||||||
v.X.Zero()
|
|
||||||
v.Y.One()
|
|
||||||
v.Z.One()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// identity is the point at infinity.
|
|
||||||
var identity, _ = new(Point).SetBytes([]byte{
|
|
||||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
|
||||||
|
|
||||||
// NewIdentityPoint returns a new Point set to the identity.
|
|
||||||
func NewIdentityPoint() *Point {
|
|
||||||
return new(Point).Set(identity)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generator is the canonical curve basepoint. See TestGenerator for the
|
|
||||||
// correspondence of this encoding with the values in RFC 8032.
|
|
||||||
var generator, _ = new(Point).SetBytes([]byte{
|
|
||||||
0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
|
||||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
|
||||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
|
||||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})
|
|
||||||
|
|
||||||
// NewGeneratorPoint returns a new Point set to the canonical generator.
|
|
||||||
func NewGeneratorPoint() *Point {
|
|
||||||
return new(Point).Set(generator)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *projCached) Zero() *projCached {
|
|
||||||
v.YplusX.One()
|
|
||||||
v.YminusX.One()
|
|
||||||
v.Z.One()
|
|
||||||
v.T2d.Zero()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *affineCached) Zero() *affineCached {
|
|
||||||
v.YplusX.One()
|
|
||||||
v.YminusX.One()
|
|
||||||
v.T2d.Zero()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assignments.
|
|
||||||
|
|
||||||
// Set sets v = u, and returns v.
|
|
||||||
func (v *Point) Set(u *Point) *Point {
|
|
||||||
*v = *u
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encoding.
|
|
||||||
|
|
||||||
// Bytes returns the canonical 32-byte encoding of v, according to RFC 8032,
|
|
||||||
// Section 5.1.2.
|
|
||||||
func (v *Point) Bytes() []byte {
|
|
||||||
// This function is outlined to make the allocations inline in the caller
|
|
||||||
// rather than happen on the heap.
|
|
||||||
var buf [32]byte
|
|
||||||
return v.bytes(&buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Point) bytes(buf *[32]byte) []byte {
|
|
||||||
checkInitialized(v)
|
|
||||||
|
|
||||||
var zInv, x, y field.Element
|
|
||||||
zInv.Invert(&v.z) // zInv = 1 / Z
|
|
||||||
x.Multiply(&v.x, &zInv) // x = X / Z
|
|
||||||
y.Multiply(&v.y, &zInv) // y = Y / Z
|
|
||||||
|
|
||||||
out := copyFieldElement(buf, &y)
|
|
||||||
out[31] |= byte(x.IsNegative() << 7)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
var feOne = new(field.Element).One()
|
|
||||||
|
|
||||||
// SetBytes sets v = x, where x is a 32-byte encoding of v. If x does not
|
|
||||||
// represent a valid point on the curve, SetBytes returns nil and an error and
|
|
||||||
// the receiver is unchanged. Otherwise, SetBytes returns v.
|
|
||||||
//
|
|
||||||
// Note that SetBytes accepts all non-canonical encodings of valid points.
|
|
||||||
// That is, it follows decoding rules that match most implementations in
|
|
||||||
// the ecosystem rather than RFC 8032.
|
|
||||||
func (v *Point) SetBytes(x []byte) (*Point, error) {
|
|
||||||
// Specifically, the non-canonical encodings that are accepted are
|
|
||||||
// 1) the ones where the field element is not reduced (see the
|
|
||||||
// (*field.Element).SetBytes docs) and
|
|
||||||
// 2) the ones where the x-coordinate is zero and the sign bit is set.
|
|
||||||
//
|
|
||||||
// This is consistent with crypto/ed25519/internal/edwards25519. Read more
|
|
||||||
// at https://hdevalence.ca/blog/2020-10-04-its-25519am, specifically the
|
|
||||||
// "Canonical A, R" section.
|
|
||||||
|
|
||||||
y, err := new(field.Element).SetBytes(x)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("edwards25519: invalid point encoding length")
|
|
||||||
}
|
|
||||||
|
|
||||||
// -x² + y² = 1 + dx²y²
|
|
||||||
// x² + dx²y² = x²(dy² + 1) = y² - 1
|
|
||||||
// x² = (y² - 1) / (dy² + 1)
|
|
||||||
|
|
||||||
// u = y² - 1
|
|
||||||
y2 := new(field.Element).Square(y)
|
|
||||||
u := new(field.Element).Subtract(y2, feOne)
|
|
||||||
|
|
||||||
// v = dy² + 1
|
|
||||||
vv := new(field.Element).Multiply(y2, d)
|
|
||||||
vv = vv.Add(vv, feOne)
|
|
||||||
|
|
||||||
// x = +√(u/v)
|
|
||||||
xx, wasSquare := new(field.Element).SqrtRatio(u, vv)
|
|
||||||
if wasSquare == 0 {
|
|
||||||
return nil, errors.New("edwards25519: invalid point encoding")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select the negative square root if the sign bit is set.
|
|
||||||
xxNeg := new(field.Element).Negate(xx)
|
|
||||||
xx = xx.Select(xxNeg, xx, int(x[31]>>7))
|
|
||||||
|
|
||||||
v.x.Set(xx)
|
|
||||||
v.y.Set(y)
|
|
||||||
v.z.One()
|
|
||||||
v.t.Multiply(xx, y) // xy = T / Z
|
|
||||||
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyFieldElement(buf *[32]byte, v *field.Element) []byte {
|
|
||||||
copy(buf[:], v.Bytes())
|
|
||||||
return buf[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conversions.
|
|
||||||
|
|
||||||
func (v *projP2) FromP1xP1(p *projP1xP1) *projP2 {
|
|
||||||
v.X.Multiply(&p.X, &p.T)
|
|
||||||
v.Y.Multiply(&p.Y, &p.Z)
|
|
||||||
v.Z.Multiply(&p.Z, &p.T)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *projP2) FromP3(p *Point) *projP2 {
|
|
||||||
v.X.Set(&p.x)
|
|
||||||
v.Y.Set(&p.y)
|
|
||||||
v.Z.Set(&p.z)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Point) fromP1xP1(p *projP1xP1) *Point {
|
|
||||||
v.x.Multiply(&p.X, &p.T)
|
|
||||||
v.y.Multiply(&p.Y, &p.Z)
|
|
||||||
v.z.Multiply(&p.Z, &p.T)
|
|
||||||
v.t.Multiply(&p.X, &p.Y)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Point) fromP2(p *projP2) *Point {
|
|
||||||
v.x.Multiply(&p.X, &p.Z)
|
|
||||||
v.y.Multiply(&p.Y, &p.Z)
|
|
||||||
v.z.Square(&p.Z)
|
|
||||||
v.t.Multiply(&p.X, &p.Y)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// d is a constant in the curve equation.
|
|
||||||
var d, _ = new(field.Element).SetBytes([]byte{
|
|
||||||
0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75,
|
|
||||||
0xab, 0xd8, 0x41, 0x41, 0x4d, 0x0a, 0x70, 0x00,
|
|
||||||
0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c,
|
|
||||||
0x73, 0xfe, 0x6f, 0x2b, 0xee, 0x6c, 0x03, 0x52})
|
|
||||||
var d2 = new(field.Element).Add(d, d)
|
|
||||||
|
|
||||||
func (v *projCached) FromP3(p *Point) *projCached {
|
|
||||||
v.YplusX.Add(&p.y, &p.x)
|
|
||||||
v.YminusX.Subtract(&p.y, &p.x)
|
|
||||||
v.Z.Set(&p.z)
|
|
||||||
v.T2d.Multiply(&p.t, d2)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *affineCached) FromP3(p *Point) *affineCached {
|
|
||||||
v.YplusX.Add(&p.y, &p.x)
|
|
||||||
v.YminusX.Subtract(&p.y, &p.x)
|
|
||||||
v.T2d.Multiply(&p.t, d2)
|
|
||||||
|
|
||||||
var invZ field.Element
|
|
||||||
invZ.Invert(&p.z)
|
|
||||||
v.YplusX.Multiply(&v.YplusX, &invZ)
|
|
||||||
v.YminusX.Multiply(&v.YminusX, &invZ)
|
|
||||||
v.T2d.Multiply(&v.T2d, &invZ)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// (Re)addition and subtraction.
|
|
||||||
|
|
||||||
// Add sets v = p + q, and returns v.
|
|
||||||
func (v *Point) Add(p, q *Point) *Point {
|
|
||||||
checkInitialized(p, q)
|
|
||||||
qCached := new(projCached).FromP3(q)
|
|
||||||
result := new(projP1xP1).Add(p, qCached)
|
|
||||||
return v.fromP1xP1(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtract sets v = p - q, and returns v.
|
|
||||||
func (v *Point) Subtract(p, q *Point) *Point {
|
|
||||||
checkInitialized(p, q)
|
|
||||||
qCached := new(projCached).FromP3(q)
|
|
||||||
result := new(projP1xP1).Sub(p, qCached)
|
|
||||||
return v.fromP1xP1(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *projP1xP1) Add(p *Point, q *projCached) *projP1xP1 {
|
|
||||||
var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element
|
|
||||||
|
|
||||||
YplusX.Add(&p.y, &p.x)
|
|
||||||
YminusX.Subtract(&p.y, &p.x)
|
|
||||||
|
|
||||||
PP.Multiply(&YplusX, &q.YplusX)
|
|
||||||
MM.Multiply(&YminusX, &q.YminusX)
|
|
||||||
TT2d.Multiply(&p.t, &q.T2d)
|
|
||||||
ZZ2.Multiply(&p.z, &q.Z)
|
|
||||||
|
|
||||||
ZZ2.Add(&ZZ2, &ZZ2)
|
|
||||||
|
|
||||||
v.X.Subtract(&PP, &MM)
|
|
||||||
v.Y.Add(&PP, &MM)
|
|
||||||
v.Z.Add(&ZZ2, &TT2d)
|
|
||||||
v.T.Subtract(&ZZ2, &TT2d)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *projP1xP1) Sub(p *Point, q *projCached) *projP1xP1 {
|
|
||||||
var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element
|
|
||||||
|
|
||||||
YplusX.Add(&p.y, &p.x)
|
|
||||||
YminusX.Subtract(&p.y, &p.x)
|
|
||||||
|
|
||||||
PP.Multiply(&YplusX, &q.YminusX) // flipped sign
|
|
||||||
MM.Multiply(&YminusX, &q.YplusX) // flipped sign
|
|
||||||
TT2d.Multiply(&p.t, &q.T2d)
|
|
||||||
ZZ2.Multiply(&p.z, &q.Z)
|
|
||||||
|
|
||||||
ZZ2.Add(&ZZ2, &ZZ2)
|
|
||||||
|
|
||||||
v.X.Subtract(&PP, &MM)
|
|
||||||
v.Y.Add(&PP, &MM)
|
|
||||||
v.Z.Subtract(&ZZ2, &TT2d) // flipped sign
|
|
||||||
v.T.Add(&ZZ2, &TT2d) // flipped sign
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *projP1xP1) AddAffine(p *Point, q *affineCached) *projP1xP1 {
|
|
||||||
var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element
|
|
||||||
|
|
||||||
YplusX.Add(&p.y, &p.x)
|
|
||||||
YminusX.Subtract(&p.y, &p.x)
|
|
||||||
|
|
||||||
PP.Multiply(&YplusX, &q.YplusX)
|
|
||||||
MM.Multiply(&YminusX, &q.YminusX)
|
|
||||||
TT2d.Multiply(&p.t, &q.T2d)
|
|
||||||
|
|
||||||
Z2.Add(&p.z, &p.z)
|
|
||||||
|
|
||||||
v.X.Subtract(&PP, &MM)
|
|
||||||
v.Y.Add(&PP, &MM)
|
|
||||||
v.Z.Add(&Z2, &TT2d)
|
|
||||||
v.T.Subtract(&Z2, &TT2d)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *projP1xP1) SubAffine(p *Point, q *affineCached) *projP1xP1 {
|
|
||||||
var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element
|
|
||||||
|
|
||||||
YplusX.Add(&p.y, &p.x)
|
|
||||||
YminusX.Subtract(&p.y, &p.x)
|
|
||||||
|
|
||||||
PP.Multiply(&YplusX, &q.YminusX) // flipped sign
|
|
||||||
MM.Multiply(&YminusX, &q.YplusX) // flipped sign
|
|
||||||
TT2d.Multiply(&p.t, &q.T2d)
|
|
||||||
|
|
||||||
Z2.Add(&p.z, &p.z)
|
|
||||||
|
|
||||||
v.X.Subtract(&PP, &MM)
|
|
||||||
v.Y.Add(&PP, &MM)
|
|
||||||
v.Z.Subtract(&Z2, &TT2d) // flipped sign
|
|
||||||
v.T.Add(&Z2, &TT2d) // flipped sign
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doubling.
|
|
||||||
|
|
||||||
func (v *projP1xP1) Double(p *projP2) *projP1xP1 {
|
|
||||||
var XX, YY, ZZ2, XplusYsq field.Element
|
|
||||||
|
|
||||||
XX.Square(&p.X)
|
|
||||||
YY.Square(&p.Y)
|
|
||||||
ZZ2.Square(&p.Z)
|
|
||||||
ZZ2.Add(&ZZ2, &ZZ2)
|
|
||||||
XplusYsq.Add(&p.X, &p.Y)
|
|
||||||
XplusYsq.Square(&XplusYsq)
|
|
||||||
|
|
||||||
v.Y.Add(&YY, &XX)
|
|
||||||
v.Z.Subtract(&YY, &XX)
|
|
||||||
|
|
||||||
v.X.Subtract(&XplusYsq, &v.Y)
|
|
||||||
v.T.Subtract(&ZZ2, &v.Z)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Negation.
|
|
||||||
|
|
||||||
// Negate sets v = -p, and returns v.
|
|
||||||
func (v *Point) Negate(p *Point) *Point {
|
|
||||||
checkInitialized(p)
|
|
||||||
v.x.Negate(&p.x)
|
|
||||||
v.y.Set(&p.y)
|
|
||||||
v.z.Set(&p.z)
|
|
||||||
v.t.Negate(&p.t)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal returns 1 if v is equivalent to u, and 0 otherwise.
|
|
||||||
func (v *Point) Equal(u *Point) int {
|
|
||||||
checkInitialized(v, u)
|
|
||||||
|
|
||||||
var t1, t2, t3, t4 field.Element
|
|
||||||
t1.Multiply(&v.x, &u.z)
|
|
||||||
t2.Multiply(&u.x, &v.z)
|
|
||||||
t3.Multiply(&v.y, &u.z)
|
|
||||||
t4.Multiply(&u.y, &v.z)
|
|
||||||
|
|
||||||
return t1.Equal(&t2) & t3.Equal(&t4)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constant-time operations
|
|
||||||
|
|
||||||
// Select sets v to a if cond == 1 and to b if cond == 0.
|
|
||||||
func (v *projCached) Select(a, b *projCached, cond int) *projCached {
|
|
||||||
v.YplusX.Select(&a.YplusX, &b.YplusX, cond)
|
|
||||||
v.YminusX.Select(&a.YminusX, &b.YminusX, cond)
|
|
||||||
v.Z.Select(&a.Z, &b.Z, cond)
|
|
||||||
v.T2d.Select(&a.T2d, &b.T2d, cond)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select sets v to a if cond == 1 and to b if cond == 0.
|
|
||||||
func (v *affineCached) Select(a, b *affineCached, cond int) *affineCached {
|
|
||||||
v.YplusX.Select(&a.YplusX, &b.YplusX, cond)
|
|
||||||
v.YminusX.Select(&a.YminusX, &b.YminusX, cond)
|
|
||||||
v.T2d.Select(&a.T2d, &b.T2d, cond)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
|
||||||
func (v *projCached) CondNeg(cond int) *projCached {
|
|
||||||
v.YplusX.Swap(&v.YminusX, cond)
|
|
||||||
v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
|
||||||
func (v *affineCached) CondNeg(cond int) *affineCached {
|
|
||||||
v.YplusX.Swap(&v.YminusX, cond)
|
|
||||||
v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
343
vendor/filippo.io/edwards25519/extra.go
generated
vendored
343
vendor/filippo.io/edwards25519/extra.go
generated
vendored
@@ -1,343 +0,0 @@
|
|||||||
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package edwards25519
|
|
||||||
|
|
||||||
// This file contains additional functionality that is not included in the
|
|
||||||
// upstream crypto/ed25519/internal/edwards25519 package.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"filippo.io/edwards25519/field"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ExtendedCoordinates returns v in extended coordinates (X:Y:Z:T) where
|
|
||||||
// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
|
|
||||||
func (v *Point) ExtendedCoordinates() (X, Y, Z, T *field.Element) {
|
|
||||||
// This function is outlined to make the allocations inline in the caller
|
|
||||||
// rather than happen on the heap. Don't change the style without making
|
|
||||||
// sure it doesn't increase the inliner cost.
|
|
||||||
var e [4]field.Element
|
|
||||||
X, Y, Z, T = v.extendedCoordinates(&e)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Point) extendedCoordinates(e *[4]field.Element) (X, Y, Z, T *field.Element) {
|
|
||||||
checkInitialized(v)
|
|
||||||
X = e[0].Set(&v.x)
|
|
||||||
Y = e[1].Set(&v.y)
|
|
||||||
Z = e[2].Set(&v.z)
|
|
||||||
T = e[3].Set(&v.t)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetExtendedCoordinates sets v = (X:Y:Z:T) in extended coordinates where
|
|
||||||
// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
|
|
||||||
//
|
|
||||||
// If the coordinates are invalid or don't represent a valid point on the curve,
|
|
||||||
// SetExtendedCoordinates returns nil and an error and the receiver is
|
|
||||||
// unchanged. Otherwise, SetExtendedCoordinates returns v.
|
|
||||||
func (v *Point) SetExtendedCoordinates(X, Y, Z, T *field.Element) (*Point, error) {
|
|
||||||
if !isOnCurve(X, Y, Z, T) {
|
|
||||||
return nil, errors.New("edwards25519: invalid point coordinates")
|
|
||||||
}
|
|
||||||
v.x.Set(X)
|
|
||||||
v.y.Set(Y)
|
|
||||||
v.z.Set(Z)
|
|
||||||
v.t.Set(T)
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isOnCurve(X, Y, Z, T *field.Element) bool {
|
|
||||||
var lhs, rhs field.Element
|
|
||||||
XX := new(field.Element).Square(X)
|
|
||||||
YY := new(field.Element).Square(Y)
|
|
||||||
ZZ := new(field.Element).Square(Z)
|
|
||||||
TT := new(field.Element).Square(T)
|
|
||||||
// -x² + y² = 1 + dx²y²
|
|
||||||
// -(X/Z)² + (Y/Z)² = 1 + d(T/Z)²
|
|
||||||
// -X² + Y² = Z² + dT²
|
|
||||||
lhs.Subtract(YY, XX)
|
|
||||||
rhs.Multiply(d, TT).Add(&rhs, ZZ)
|
|
||||||
if lhs.Equal(&rhs) != 1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// xy = T/Z
|
|
||||||
// XY/Z² = T/Z
|
|
||||||
// XY = TZ
|
|
||||||
lhs.Multiply(X, Y)
|
|
||||||
rhs.Multiply(T, Z)
|
|
||||||
return lhs.Equal(&rhs) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// BytesMontgomery converts v to a point on the birationally-equivalent
|
|
||||||
// Curve25519 Montgomery curve, and returns its canonical 32 bytes encoding
|
|
||||||
// according to RFC 7748.
|
|
||||||
//
|
|
||||||
// Note that BytesMontgomery only encodes the u-coordinate, so v and -v encode
|
|
||||||
// to the same value. If v is the identity point, BytesMontgomery returns 32
|
|
||||||
// zero bytes, analogously to the X25519 function.
|
|
||||||
func (v *Point) BytesMontgomery() []byte {
|
|
||||||
// This function is outlined to make the allocations inline in the caller
|
|
||||||
// rather than happen on the heap.
|
|
||||||
var buf [32]byte
|
|
||||||
return v.bytesMontgomery(&buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Point) bytesMontgomery(buf *[32]byte) []byte {
|
|
||||||
checkInitialized(v)
|
|
||||||
|
|
||||||
// RFC 7748, Section 4.1 provides the bilinear map to calculate the
|
|
||||||
// Montgomery u-coordinate
|
|
||||||
//
|
|
||||||
// u = (1 + y) / (1 - y)
|
|
||||||
//
|
|
||||||
// where y = Y / Z.
|
|
||||||
|
|
||||||
var y, recip, u field.Element
|
|
||||||
|
|
||||||
y.Multiply(&v.y, y.Invert(&v.z)) // y = Y / Z
|
|
||||||
recip.Invert(recip.Subtract(feOne, &y)) // r = 1/(1 - y)
|
|
||||||
u.Multiply(u.Add(feOne, &y), &recip) // u = (1 + y)*r
|
|
||||||
|
|
||||||
return copyFieldElement(buf, &u)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultByCofactor sets v = 8 * p, and returns v.
|
|
||||||
func (v *Point) MultByCofactor(p *Point) *Point {
|
|
||||||
checkInitialized(p)
|
|
||||||
result := projP1xP1{}
|
|
||||||
pp := (&projP2{}).FromP3(p)
|
|
||||||
result.Double(pp)
|
|
||||||
pp.FromP1xP1(&result)
|
|
||||||
result.Double(pp)
|
|
||||||
pp.FromP1xP1(&result)
|
|
||||||
result.Double(pp)
|
|
||||||
return v.fromP1xP1(&result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given k > 0, set s = s**(2*i).
|
|
||||||
func (s *Scalar) pow2k(k int) {
|
|
||||||
for i := 0; i < k; i++ {
|
|
||||||
s.Multiply(s, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invert sets s to the inverse of a nonzero scalar v, and returns s.
|
|
||||||
//
|
|
||||||
// If t is zero, Invert returns zero.
|
|
||||||
func (s *Scalar) Invert(t *Scalar) *Scalar {
|
|
||||||
// Uses a hardcoded sliding window of width 4.
|
|
||||||
var table [8]Scalar
|
|
||||||
var tt Scalar
|
|
||||||
tt.Multiply(t, t)
|
|
||||||
table[0] = *t
|
|
||||||
for i := 0; i < 7; i++ {
|
|
||||||
table[i+1].Multiply(&table[i], &tt)
|
|
||||||
}
|
|
||||||
// Now table = [t**1, t**3, t**7, t**11, t**13, t**15]
|
|
||||||
// so t**k = t[k/2] for odd k
|
|
||||||
|
|
||||||
// To compute the sliding window digits, use the following Sage script:
|
|
||||||
|
|
||||||
// sage: import itertools
|
|
||||||
// sage: def sliding_window(w,k):
|
|
||||||
// ....: digits = []
|
|
||||||
// ....: while k > 0:
|
|
||||||
// ....: if k % 2 == 1:
|
|
||||||
// ....: kmod = k % (2**w)
|
|
||||||
// ....: digits.append(kmod)
|
|
||||||
// ....: k = k - kmod
|
|
||||||
// ....: else:
|
|
||||||
// ....: digits.append(0)
|
|
||||||
// ....: k = k // 2
|
|
||||||
// ....: return digits
|
|
||||||
|
|
||||||
// Now we can compute s roughly as follows:
|
|
||||||
|
|
||||||
// sage: s = 1
|
|
||||||
// sage: for coeff in reversed(sliding_window(4,l-2)):
|
|
||||||
// ....: s = s*s
|
|
||||||
// ....: if coeff > 0 :
|
|
||||||
// ....: s = s*t**coeff
|
|
||||||
|
|
||||||
// This works on one bit at a time, with many runs of zeros.
|
|
||||||
// The digits can be collapsed into [(count, coeff)] as follows:
|
|
||||||
|
|
||||||
// sage: [(len(list(group)),d) for d,group in itertools.groupby(sliding_window(4,l-2))]
|
|
||||||
|
|
||||||
// Entries of the form (k, 0) turn into pow2k(k)
|
|
||||||
// Entries of the form (1, coeff) turn into a squaring and then a table lookup.
|
|
||||||
// We can fold the squaring into the previous pow2k(k) as pow2k(k+1).
|
|
||||||
|
|
||||||
*s = table[1/2]
|
|
||||||
s.pow2k(127 + 1)
|
|
||||||
s.Multiply(s, &table[1/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[9/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[11/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[13/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[15/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[7/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[15/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[5/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[1/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[15/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[15/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[7/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[3/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[11/2])
|
|
||||||
s.pow2k(5 + 1)
|
|
||||||
s.Multiply(s, &table[11/2])
|
|
||||||
s.pow2k(9 + 1)
|
|
||||||
s.Multiply(s, &table[9/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[3/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[3/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[3/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[9/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[7/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[3/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[13/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[7/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[9/2])
|
|
||||||
s.pow2k(3 + 1)
|
|
||||||
s.Multiply(s, &table[15/2])
|
|
||||||
s.pow2k(4 + 1)
|
|
||||||
s.Multiply(s, &table[11/2])
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v.
|
|
||||||
//
|
|
||||||
// Execution time depends only on the lengths of the two slices, which must match.
|
|
||||||
func (v *Point) MultiScalarMult(scalars []*Scalar, points []*Point) *Point {
|
|
||||||
if len(scalars) != len(points) {
|
|
||||||
panic("edwards25519: called MultiScalarMult with different size inputs")
|
|
||||||
}
|
|
||||||
checkInitialized(points...)
|
|
||||||
|
|
||||||
// Proceed as in the single-base case, but share doublings
|
|
||||||
// between each point in the multiscalar equation.
|
|
||||||
|
|
||||||
// Build lookup tables for each point
|
|
||||||
tables := make([]projLookupTable, len(points))
|
|
||||||
for i := range tables {
|
|
||||||
tables[i].FromP3(points[i])
|
|
||||||
}
|
|
||||||
// Compute signed radix-16 digits for each scalar
|
|
||||||
digits := make([][64]int8, len(scalars))
|
|
||||||
for i := range digits {
|
|
||||||
digits[i] = scalars[i].signedRadix16()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unwrap first loop iteration to save computing 16*identity
|
|
||||||
multiple := &projCached{}
|
|
||||||
tmp1 := &projP1xP1{}
|
|
||||||
tmp2 := &projP2{}
|
|
||||||
// Lookup-and-add the appropriate multiple of each input point
|
|
||||||
for j := range tables {
|
|
||||||
tables[j].SelectInto(multiple, digits[j][63])
|
|
||||||
tmp1.Add(v, multiple) // tmp1 = v + x_(j,63)*Q in P1xP1 coords
|
|
||||||
v.fromP1xP1(tmp1) // update v
|
|
||||||
}
|
|
||||||
tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration
|
|
||||||
for i := 62; i >= 0; i-- {
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords
|
|
||||||
tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords
|
|
||||||
tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords
|
|
||||||
tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords
|
|
||||||
v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords
|
|
||||||
// Lookup-and-add the appropriate multiple of each input point
|
|
||||||
for j := range tables {
|
|
||||||
tables[j].SelectInto(multiple, digits[j][i])
|
|
||||||
tmp1.Add(v, multiple) // tmp1 = v + x_(j,i)*Q in P1xP1 coords
|
|
||||||
v.fromP1xP1(tmp1) // update v
|
|
||||||
}
|
|
||||||
tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// VarTimeMultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v.
|
|
||||||
//
|
|
||||||
// Execution time depends on the inputs.
|
|
||||||
func (v *Point) VarTimeMultiScalarMult(scalars []*Scalar, points []*Point) *Point {
|
|
||||||
if len(scalars) != len(points) {
|
|
||||||
panic("edwards25519: called VarTimeMultiScalarMult with different size inputs")
|
|
||||||
}
|
|
||||||
checkInitialized(points...)
|
|
||||||
|
|
||||||
// Generalize double-base NAF computation to arbitrary sizes.
|
|
||||||
// Here all the points are dynamic, so we only use the smaller
|
|
||||||
// tables.
|
|
||||||
|
|
||||||
// Build lookup tables for each point
|
|
||||||
tables := make([]nafLookupTable5, len(points))
|
|
||||||
for i := range tables {
|
|
||||||
tables[i].FromP3(points[i])
|
|
||||||
}
|
|
||||||
// Compute a NAF for each scalar
|
|
||||||
nafs := make([][256]int8, len(scalars))
|
|
||||||
for i := range nafs {
|
|
||||||
nafs[i] = scalars[i].nonAdjacentForm(5)
|
|
||||||
}
|
|
||||||
|
|
||||||
multiple := &projCached{}
|
|
||||||
tmp1 := &projP1xP1{}
|
|
||||||
tmp2 := &projP2{}
|
|
||||||
tmp2.Zero()
|
|
||||||
|
|
||||||
// Move from high to low bits, doubling the accumulator
|
|
||||||
// at each iteration and checking whether there is a nonzero
|
|
||||||
// coefficient to look up a multiple of.
|
|
||||||
//
|
|
||||||
// Skip trying to find the first nonzero coefficent, because
|
|
||||||
// searching might be more work than a few extra doublings.
|
|
||||||
for i := 255; i >= 0; i-- {
|
|
||||||
tmp1.Double(tmp2)
|
|
||||||
|
|
||||||
for j := range nafs {
|
|
||||||
if nafs[j][i] > 0 {
|
|
||||||
v.fromP1xP1(tmp1)
|
|
||||||
tables[j].SelectInto(multiple, nafs[j][i])
|
|
||||||
tmp1.Add(v, multiple)
|
|
||||||
} else if nafs[j][i] < 0 {
|
|
||||||
v.fromP1xP1(tmp1)
|
|
||||||
tables[j].SelectInto(multiple, -nafs[j][i])
|
|
||||||
tmp1.Sub(v, multiple)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp2.FromP1xP1(tmp1)
|
|
||||||
}
|
|
||||||
|
|
||||||
v.fromP2(tmp2)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
420
vendor/filippo.io/edwards25519/field/fe.go
generated
vendored
420
vendor/filippo.io/edwards25519/field/fe.go
generated
vendored
@@ -1,420 +0,0 @@
|
|||||||
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package field implements fast arithmetic modulo 2^255-19.
|
|
||||||
package field
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/subtle"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"math/bits"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Element represents an element of the field GF(2^255-19). Note that this
|
|
||||||
// is not a cryptographically secure group, and should only be used to interact
|
|
||||||
// with edwards25519.Point coordinates.
|
|
||||||
//
|
|
||||||
// This type works similarly to math/big.Int, and all arguments and receivers
|
|
||||||
// are allowed to alias.
|
|
||||||
//
|
|
||||||
// The zero value is a valid zero element.
|
|
||||||
type Element struct {
|
|
||||||
// An element t represents the integer
|
|
||||||
// t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204
|
|
||||||
//
|
|
||||||
// Between operations, all limbs are expected to be lower than 2^52.
|
|
||||||
l0 uint64
|
|
||||||
l1 uint64
|
|
||||||
l2 uint64
|
|
||||||
l3 uint64
|
|
||||||
l4 uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
const maskLow51Bits uint64 = (1 << 51) - 1
|
|
||||||
|
|
||||||
var feZero = &Element{0, 0, 0, 0, 0}
|
|
||||||
|
|
||||||
// Zero sets v = 0, and returns v.
|
|
||||||
func (v *Element) Zero() *Element {
|
|
||||||
*v = *feZero
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
var feOne = &Element{1, 0, 0, 0, 0}
|
|
||||||
|
|
||||||
// One sets v = 1, and returns v.
|
|
||||||
func (v *Element) One() *Element {
|
|
||||||
*v = *feOne
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// reduce reduces v modulo 2^255 - 19 and returns it.
|
|
||||||
func (v *Element) reduce() *Element {
|
|
||||||
v.carryPropagate()
|
|
||||||
|
|
||||||
// After the light reduction we now have a field element representation
|
|
||||||
// v < 2^255 + 2^13 * 19, but need v < 2^255 - 19.
|
|
||||||
|
|
||||||
// If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1,
|
|
||||||
// generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise.
|
|
||||||
c := (v.l0 + 19) >> 51
|
|
||||||
c = (v.l1 + c) >> 51
|
|
||||||
c = (v.l2 + c) >> 51
|
|
||||||
c = (v.l3 + c) >> 51
|
|
||||||
c = (v.l4 + c) >> 51
|
|
||||||
|
|
||||||
// If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's
|
|
||||||
// effectively applying the reduction identity to the carry.
|
|
||||||
v.l0 += 19 * c
|
|
||||||
|
|
||||||
v.l1 += v.l0 >> 51
|
|
||||||
v.l0 = v.l0 & maskLow51Bits
|
|
||||||
v.l2 += v.l1 >> 51
|
|
||||||
v.l1 = v.l1 & maskLow51Bits
|
|
||||||
v.l3 += v.l2 >> 51
|
|
||||||
v.l2 = v.l2 & maskLow51Bits
|
|
||||||
v.l4 += v.l3 >> 51
|
|
||||||
v.l3 = v.l3 & maskLow51Bits
|
|
||||||
// no additional carry
|
|
||||||
v.l4 = v.l4 & maskLow51Bits
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add sets v = a + b, and returns v.
|
|
||||||
func (v *Element) Add(a, b *Element) *Element {
|
|
||||||
v.l0 = a.l0 + b.l0
|
|
||||||
v.l1 = a.l1 + b.l1
|
|
||||||
v.l2 = a.l2 + b.l2
|
|
||||||
v.l3 = a.l3 + b.l3
|
|
||||||
v.l4 = a.l4 + b.l4
|
|
||||||
// Using the generic implementation here is actually faster than the
|
|
||||||
// assembly. Probably because the body of this function is so simple that
|
|
||||||
// the compiler can figure out better optimizations by inlining the carry
|
|
||||||
// propagation.
|
|
||||||
return v.carryPropagateGeneric()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtract sets v = a - b, and returns v.
|
|
||||||
func (v *Element) Subtract(a, b *Element) *Element {
|
|
||||||
// We first add 2 * p, to guarantee the subtraction won't underflow, and
|
|
||||||
// then subtract b (which can be up to 2^255 + 2^13 * 19).
|
|
||||||
v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0
|
|
||||||
v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1
|
|
||||||
v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2
|
|
||||||
v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3
|
|
||||||
v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4
|
|
||||||
return v.carryPropagate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Negate sets v = -a, and returns v.
|
|
||||||
func (v *Element) Negate(a *Element) *Element {
|
|
||||||
return v.Subtract(feZero, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invert sets v = 1/z mod p, and returns v.
|
|
||||||
//
|
|
||||||
// If z == 0, Invert returns v = 0.
|
|
||||||
func (v *Element) Invert(z *Element) *Element {
|
|
||||||
// Inversion is implemented as exponentiation with exponent p − 2. It uses the
|
|
||||||
// same sequence of 255 squarings and 11 multiplications as [Curve25519].
|
|
||||||
var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element
|
|
||||||
|
|
||||||
z2.Square(z) // 2
|
|
||||||
t.Square(&z2) // 4
|
|
||||||
t.Square(&t) // 8
|
|
||||||
z9.Multiply(&t, z) // 9
|
|
||||||
z11.Multiply(&z9, &z2) // 11
|
|
||||||
t.Square(&z11) // 22
|
|
||||||
z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0
|
|
||||||
|
|
||||||
t.Square(&z2_5_0) // 2^6 - 2^1
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
t.Square(&t) // 2^10 - 2^5
|
|
||||||
}
|
|
||||||
z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0
|
|
||||||
|
|
||||||
t.Square(&z2_10_0) // 2^11 - 2^1
|
|
||||||
for i := 0; i < 9; i++ {
|
|
||||||
t.Square(&t) // 2^20 - 2^10
|
|
||||||
}
|
|
||||||
z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0
|
|
||||||
|
|
||||||
t.Square(&z2_20_0) // 2^21 - 2^1
|
|
||||||
for i := 0; i < 19; i++ {
|
|
||||||
t.Square(&t) // 2^40 - 2^20
|
|
||||||
}
|
|
||||||
t.Multiply(&t, &z2_20_0) // 2^40 - 2^0
|
|
||||||
|
|
||||||
t.Square(&t) // 2^41 - 2^1
|
|
||||||
for i := 0; i < 9; i++ {
|
|
||||||
t.Square(&t) // 2^50 - 2^10
|
|
||||||
}
|
|
||||||
z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0
|
|
||||||
|
|
||||||
t.Square(&z2_50_0) // 2^51 - 2^1
|
|
||||||
for i := 0; i < 49; i++ {
|
|
||||||
t.Square(&t) // 2^100 - 2^50
|
|
||||||
}
|
|
||||||
z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0
|
|
||||||
|
|
||||||
t.Square(&z2_100_0) // 2^101 - 2^1
|
|
||||||
for i := 0; i < 99; i++ {
|
|
||||||
t.Square(&t) // 2^200 - 2^100
|
|
||||||
}
|
|
||||||
t.Multiply(&t, &z2_100_0) // 2^200 - 2^0
|
|
||||||
|
|
||||||
t.Square(&t) // 2^201 - 2^1
|
|
||||||
for i := 0; i < 49; i++ {
|
|
||||||
t.Square(&t) // 2^250 - 2^50
|
|
||||||
}
|
|
||||||
t.Multiply(&t, &z2_50_0) // 2^250 - 2^0
|
|
||||||
|
|
||||||
t.Square(&t) // 2^251 - 2^1
|
|
||||||
t.Square(&t) // 2^252 - 2^2
|
|
||||||
t.Square(&t) // 2^253 - 2^3
|
|
||||||
t.Square(&t) // 2^254 - 2^4
|
|
||||||
t.Square(&t) // 2^255 - 2^5
|
|
||||||
|
|
||||||
return v.Multiply(&t, &z11) // 2^255 - 21
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets v = a, and returns v.
|
|
||||||
func (v *Element) Set(a *Element) *Element {
|
|
||||||
*v = *a
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBytes sets v to x, where x is a 32-byte little-endian encoding. If x is
|
|
||||||
// not of the right length, SetBytes returns nil and an error, and the
|
|
||||||
// receiver is unchanged.
|
|
||||||
//
|
|
||||||
// Consistent with RFC 7748, the most significant bit (the high bit of the
|
|
||||||
// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1)
|
|
||||||
// are accepted. Note that this is laxer than specified by RFC 8032, but
|
|
||||||
// consistent with most Ed25519 implementations.
|
|
||||||
func (v *Element) SetBytes(x []byte) (*Element, error) {
|
|
||||||
if len(x) != 32 {
|
|
||||||
return nil, errors.New("edwards25519: invalid field element input size")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51).
|
|
||||||
v.l0 = binary.LittleEndian.Uint64(x[0:8])
|
|
||||||
v.l0 &= maskLow51Bits
|
|
||||||
// Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51).
|
|
||||||
v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3
|
|
||||||
v.l1 &= maskLow51Bits
|
|
||||||
// Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51).
|
|
||||||
v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6
|
|
||||||
v.l2 &= maskLow51Bits
|
|
||||||
// Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51).
|
|
||||||
v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1
|
|
||||||
v.l3 &= maskLow51Bits
|
|
||||||
// Bits 204:255 (bytes 24:32, bits 192:256, shift 12, mask 51).
|
|
||||||
// Note: not bytes 25:33, shift 4, to avoid overread.
|
|
||||||
v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12
|
|
||||||
v.l4 &= maskLow51Bits
|
|
||||||
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the canonical 32-byte little-endian encoding of v.
|
|
||||||
func (v *Element) Bytes() []byte {
|
|
||||||
// This function is outlined to make the allocations inline in the caller
|
|
||||||
// rather than happen on the heap.
|
|
||||||
var out [32]byte
|
|
||||||
return v.bytes(&out)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Element) bytes(out *[32]byte) []byte {
|
|
||||||
t := *v
|
|
||||||
t.reduce()
|
|
||||||
|
|
||||||
var buf [8]byte
|
|
||||||
for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} {
|
|
||||||
bitsOffset := i * 51
|
|
||||||
binary.LittleEndian.PutUint64(buf[:], l<<uint(bitsOffset%8))
|
|
||||||
for i, bb := range buf {
|
|
||||||
off := bitsOffset/8 + i
|
|
||||||
if off >= len(out) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
out[off] |= bb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal returns 1 if v and u are equal, and 0 otherwise.
|
|
||||||
func (v *Element) Equal(u *Element) int {
|
|
||||||
sa, sv := u.Bytes(), v.Bytes()
|
|
||||||
return subtle.ConstantTimeCompare(sa, sv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise.
|
|
||||||
func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) }
|
|
||||||
|
|
||||||
// Select sets v to a if cond == 1, and to b if cond == 0.
|
|
||||||
func (v *Element) Select(a, b *Element, cond int) *Element {
|
|
||||||
m := mask64Bits(cond)
|
|
||||||
v.l0 = (m & a.l0) | (^m & b.l0)
|
|
||||||
v.l1 = (m & a.l1) | (^m & b.l1)
|
|
||||||
v.l2 = (m & a.l2) | (^m & b.l2)
|
|
||||||
v.l3 = (m & a.l3) | (^m & b.l3)
|
|
||||||
v.l4 = (m & a.l4) | (^m & b.l4)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v.
|
|
||||||
func (v *Element) Swap(u *Element, cond int) {
|
|
||||||
m := mask64Bits(cond)
|
|
||||||
t := m & (v.l0 ^ u.l0)
|
|
||||||
v.l0 ^= t
|
|
||||||
u.l0 ^= t
|
|
||||||
t = m & (v.l1 ^ u.l1)
|
|
||||||
v.l1 ^= t
|
|
||||||
u.l1 ^= t
|
|
||||||
t = m & (v.l2 ^ u.l2)
|
|
||||||
v.l2 ^= t
|
|
||||||
u.l2 ^= t
|
|
||||||
t = m & (v.l3 ^ u.l3)
|
|
||||||
v.l3 ^= t
|
|
||||||
u.l3 ^= t
|
|
||||||
t = m & (v.l4 ^ u.l4)
|
|
||||||
v.l4 ^= t
|
|
||||||
u.l4 ^= t
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNegative returns 1 if v is negative, and 0 otherwise.
|
|
||||||
func (v *Element) IsNegative() int {
|
|
||||||
return int(v.Bytes()[0] & 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Absolute sets v to |u|, and returns v.
|
|
||||||
func (v *Element) Absolute(u *Element) *Element {
|
|
||||||
return v.Select(new(Element).Negate(u), u, u.IsNegative())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiply sets v = x * y, and returns v.
|
|
||||||
func (v *Element) Multiply(x, y *Element) *Element {
|
|
||||||
feMul(v, x, y)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Square sets v = x * x, and returns v.
|
|
||||||
func (v *Element) Square(x *Element) *Element {
|
|
||||||
feSquare(v, x)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mult32 sets v = x * y, and returns v.
|
|
||||||
func (v *Element) Mult32(x *Element, y uint32) *Element {
|
|
||||||
x0lo, x0hi := mul51(x.l0, y)
|
|
||||||
x1lo, x1hi := mul51(x.l1, y)
|
|
||||||
x2lo, x2hi := mul51(x.l2, y)
|
|
||||||
x3lo, x3hi := mul51(x.l3, y)
|
|
||||||
x4lo, x4hi := mul51(x.l4, y)
|
|
||||||
v.l0 = x0lo + 19*x4hi // carried over per the reduction identity
|
|
||||||
v.l1 = x1lo + x0hi
|
|
||||||
v.l2 = x2lo + x1hi
|
|
||||||
v.l3 = x3lo + x2hi
|
|
||||||
v.l4 = x4lo + x3hi
|
|
||||||
// The hi portions are going to be only 32 bits, plus any previous excess,
|
|
||||||
// so we can skip the carry propagation.
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// mul51 returns lo + hi * 2⁵¹ = a * b.
|
|
||||||
func mul51(a uint64, b uint32) (lo uint64, hi uint64) {
|
|
||||||
mh, ml := bits.Mul64(a, uint64(b))
|
|
||||||
lo = ml & maskLow51Bits
|
|
||||||
hi = (mh << 13) | (ml >> 51)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3.
|
|
||||||
func (v *Element) Pow22523(x *Element) *Element {
|
|
||||||
var t0, t1, t2 Element
|
|
||||||
|
|
||||||
t0.Square(x) // x^2
|
|
||||||
t1.Square(&t0) // x^4
|
|
||||||
t1.Square(&t1) // x^8
|
|
||||||
t1.Multiply(x, &t1) // x^9
|
|
||||||
t0.Multiply(&t0, &t1) // x^11
|
|
||||||
t0.Square(&t0) // x^22
|
|
||||||
t0.Multiply(&t1, &t0) // x^31
|
|
||||||
t1.Square(&t0) // x^62
|
|
||||||
for i := 1; i < 5; i++ { // x^992
|
|
||||||
t1.Square(&t1)
|
|
||||||
}
|
|
||||||
t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1
|
|
||||||
t1.Square(&t0) // 2^11 - 2
|
|
||||||
for i := 1; i < 10; i++ { // 2^20 - 2^10
|
|
||||||
t1.Square(&t1)
|
|
||||||
}
|
|
||||||
t1.Multiply(&t1, &t0) // 2^20 - 1
|
|
||||||
t2.Square(&t1) // 2^21 - 2
|
|
||||||
for i := 1; i < 20; i++ { // 2^40 - 2^20
|
|
||||||
t2.Square(&t2)
|
|
||||||
}
|
|
||||||
t1.Multiply(&t2, &t1) // 2^40 - 1
|
|
||||||
t1.Square(&t1) // 2^41 - 2
|
|
||||||
for i := 1; i < 10; i++ { // 2^50 - 2^10
|
|
||||||
t1.Square(&t1)
|
|
||||||
}
|
|
||||||
t0.Multiply(&t1, &t0) // 2^50 - 1
|
|
||||||
t1.Square(&t0) // 2^51 - 2
|
|
||||||
for i := 1; i < 50; i++ { // 2^100 - 2^50
|
|
||||||
t1.Square(&t1)
|
|
||||||
}
|
|
||||||
t1.Multiply(&t1, &t0) // 2^100 - 1
|
|
||||||
t2.Square(&t1) // 2^101 - 2
|
|
||||||
for i := 1; i < 100; i++ { // 2^200 - 2^100
|
|
||||||
t2.Square(&t2)
|
|
||||||
}
|
|
||||||
t1.Multiply(&t2, &t1) // 2^200 - 1
|
|
||||||
t1.Square(&t1) // 2^201 - 2
|
|
||||||
for i := 1; i < 50; i++ { // 2^250 - 2^50
|
|
||||||
t1.Square(&t1)
|
|
||||||
}
|
|
||||||
t0.Multiply(&t1, &t0) // 2^250 - 1
|
|
||||||
t0.Square(&t0) // 2^251 - 2
|
|
||||||
t0.Square(&t0) // 2^252 - 4
|
|
||||||
return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion.
|
|
||||||
var sqrtM1 = &Element{1718705420411056, 234908883556509,
|
|
||||||
2233514472574048, 2117202627021982, 765476049583133}
|
|
||||||
|
|
||||||
// SqrtRatio sets r to the non-negative square root of the ratio of u and v.
|
|
||||||
//
|
|
||||||
// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio
|
|
||||||
// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00,
|
|
||||||
// and returns r and 0.
|
|
||||||
func (r *Element) SqrtRatio(u, v *Element) (R *Element, wasSquare int) {
|
|
||||||
t0 := new(Element)
|
|
||||||
|
|
||||||
// r = (u * v3) * (u * v7)^((p-5)/8)
|
|
||||||
v2 := new(Element).Square(v)
|
|
||||||
uv3 := new(Element).Multiply(u, t0.Multiply(v2, v))
|
|
||||||
uv7 := new(Element).Multiply(uv3, t0.Square(v2))
|
|
||||||
rr := new(Element).Multiply(uv3, t0.Pow22523(uv7))
|
|
||||||
|
|
||||||
check := new(Element).Multiply(v, t0.Square(rr)) // check = v * r^2
|
|
||||||
|
|
||||||
uNeg := new(Element).Negate(u)
|
|
||||||
correctSignSqrt := check.Equal(u)
|
|
||||||
flippedSignSqrt := check.Equal(uNeg)
|
|
||||||
flippedSignSqrtI := check.Equal(t0.Multiply(uNeg, sqrtM1))
|
|
||||||
|
|
||||||
rPrime := new(Element).Multiply(rr, sqrtM1) // r_prime = SQRT_M1 * r
|
|
||||||
// r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r)
|
|
||||||
rr.Select(rPrime, rr, flippedSignSqrt|flippedSignSqrtI)
|
|
||||||
|
|
||||||
r.Absolute(rr) // Choose the nonnegative square root.
|
|
||||||
return r, correctSignSqrt | flippedSignSqrt
|
|
||||||
}
|
|
||||||
13
vendor/filippo.io/edwards25519/field/fe_amd64.go
generated
vendored
13
vendor/filippo.io/edwards25519/field/fe_amd64.go
generated
vendored
@@ -1,13 +0,0 @@
|
|||||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
|
||||||
|
|
||||||
// +build amd64,gc,!purego
|
|
||||||
|
|
||||||
package field
|
|
||||||
|
|
||||||
// feMul sets out = a * b. It works like feMulGeneric.
|
|
||||||
//go:noescape
|
|
||||||
func feMul(out *Element, a *Element, b *Element)
|
|
||||||
|
|
||||||
// feSquare sets out = a * a. It works like feSquareGeneric.
|
|
||||||
//go:noescape
|
|
||||||
func feSquare(out *Element, a *Element)
|
|
||||||
378
vendor/filippo.io/edwards25519/field/fe_amd64.s
generated
vendored
378
vendor/filippo.io/edwards25519/field/fe_amd64.s
generated
vendored
@@ -1,378 +0,0 @@
|
|||||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
|
||||||
|
|
||||||
// +build amd64,gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
// func feMul(out *Element, a *Element, b *Element)
|
|
||||||
TEXT ·feMul(SB), NOSPLIT, $0-24
|
|
||||||
MOVQ a+8(FP), CX
|
|
||||||
MOVQ b+16(FP), BX
|
|
||||||
|
|
||||||
// r0 = a0×b0
|
|
||||||
MOVQ (CX), AX
|
|
||||||
MULQ (BX)
|
|
||||||
MOVQ AX, DI
|
|
||||||
MOVQ DX, SI
|
|
||||||
|
|
||||||
// r0 += 19×a1×b4
|
|
||||||
MOVQ 8(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 32(BX)
|
|
||||||
ADDQ AX, DI
|
|
||||||
ADCQ DX, SI
|
|
||||||
|
|
||||||
// r0 += 19×a2×b3
|
|
||||||
MOVQ 16(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 24(BX)
|
|
||||||
ADDQ AX, DI
|
|
||||||
ADCQ DX, SI
|
|
||||||
|
|
||||||
// r0 += 19×a3×b2
|
|
||||||
MOVQ 24(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 16(BX)
|
|
||||||
ADDQ AX, DI
|
|
||||||
ADCQ DX, SI
|
|
||||||
|
|
||||||
// r0 += 19×a4×b1
|
|
||||||
MOVQ 32(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 8(BX)
|
|
||||||
ADDQ AX, DI
|
|
||||||
ADCQ DX, SI
|
|
||||||
|
|
||||||
// r1 = a0×b1
|
|
||||||
MOVQ (CX), AX
|
|
||||||
MULQ 8(BX)
|
|
||||||
MOVQ AX, R9
|
|
||||||
MOVQ DX, R8
|
|
||||||
|
|
||||||
// r1 += a1×b0
|
|
||||||
MOVQ 8(CX), AX
|
|
||||||
MULQ (BX)
|
|
||||||
ADDQ AX, R9
|
|
||||||
ADCQ DX, R8
|
|
||||||
|
|
||||||
// r1 += 19×a2×b4
|
|
||||||
MOVQ 16(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 32(BX)
|
|
||||||
ADDQ AX, R9
|
|
||||||
ADCQ DX, R8
|
|
||||||
|
|
||||||
// r1 += 19×a3×b3
|
|
||||||
MOVQ 24(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 24(BX)
|
|
||||||
ADDQ AX, R9
|
|
||||||
ADCQ DX, R8
|
|
||||||
|
|
||||||
// r1 += 19×a4×b2
|
|
||||||
MOVQ 32(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 16(BX)
|
|
||||||
ADDQ AX, R9
|
|
||||||
ADCQ DX, R8
|
|
||||||
|
|
||||||
// r2 = a0×b2
|
|
||||||
MOVQ (CX), AX
|
|
||||||
MULQ 16(BX)
|
|
||||||
MOVQ AX, R11
|
|
||||||
MOVQ DX, R10
|
|
||||||
|
|
||||||
// r2 += a1×b1
|
|
||||||
MOVQ 8(CX), AX
|
|
||||||
MULQ 8(BX)
|
|
||||||
ADDQ AX, R11
|
|
||||||
ADCQ DX, R10
|
|
||||||
|
|
||||||
// r2 += a2×b0
|
|
||||||
MOVQ 16(CX), AX
|
|
||||||
MULQ (BX)
|
|
||||||
ADDQ AX, R11
|
|
||||||
ADCQ DX, R10
|
|
||||||
|
|
||||||
// r2 += 19×a3×b4
|
|
||||||
MOVQ 24(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 32(BX)
|
|
||||||
ADDQ AX, R11
|
|
||||||
ADCQ DX, R10
|
|
||||||
|
|
||||||
// r2 += 19×a4×b3
|
|
||||||
MOVQ 32(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 24(BX)
|
|
||||||
ADDQ AX, R11
|
|
||||||
ADCQ DX, R10
|
|
||||||
|
|
||||||
// r3 = a0×b3
|
|
||||||
MOVQ (CX), AX
|
|
||||||
MULQ 24(BX)
|
|
||||||
MOVQ AX, R13
|
|
||||||
MOVQ DX, R12
|
|
||||||
|
|
||||||
// r3 += a1×b2
|
|
||||||
MOVQ 8(CX), AX
|
|
||||||
MULQ 16(BX)
|
|
||||||
ADDQ AX, R13
|
|
||||||
ADCQ DX, R12
|
|
||||||
|
|
||||||
// r3 += a2×b1
|
|
||||||
MOVQ 16(CX), AX
|
|
||||||
MULQ 8(BX)
|
|
||||||
ADDQ AX, R13
|
|
||||||
ADCQ DX, R12
|
|
||||||
|
|
||||||
// r3 += a3×b0
|
|
||||||
MOVQ 24(CX), AX
|
|
||||||
MULQ (BX)
|
|
||||||
ADDQ AX, R13
|
|
||||||
ADCQ DX, R12
|
|
||||||
|
|
||||||
// r3 += 19×a4×b4
|
|
||||||
MOVQ 32(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 32(BX)
|
|
||||||
ADDQ AX, R13
|
|
||||||
ADCQ DX, R12
|
|
||||||
|
|
||||||
// r4 = a0×b4
|
|
||||||
MOVQ (CX), AX
|
|
||||||
MULQ 32(BX)
|
|
||||||
MOVQ AX, R15
|
|
||||||
MOVQ DX, R14
|
|
||||||
|
|
||||||
// r4 += a1×b3
|
|
||||||
MOVQ 8(CX), AX
|
|
||||||
MULQ 24(BX)
|
|
||||||
ADDQ AX, R15
|
|
||||||
ADCQ DX, R14
|
|
||||||
|
|
||||||
// r4 += a2×b2
|
|
||||||
MOVQ 16(CX), AX
|
|
||||||
MULQ 16(BX)
|
|
||||||
ADDQ AX, R15
|
|
||||||
ADCQ DX, R14
|
|
||||||
|
|
||||||
// r4 += a3×b1
|
|
||||||
MOVQ 24(CX), AX
|
|
||||||
MULQ 8(BX)
|
|
||||||
ADDQ AX, R15
|
|
||||||
ADCQ DX, R14
|
|
||||||
|
|
||||||
// r4 += a4×b0
|
|
||||||
MOVQ 32(CX), AX
|
|
||||||
MULQ (BX)
|
|
||||||
ADDQ AX, R15
|
|
||||||
ADCQ DX, R14
|
|
||||||
|
|
||||||
// First reduction chain
|
|
||||||
MOVQ $0x0007ffffffffffff, AX
|
|
||||||
SHLQ $0x0d, DI, SI
|
|
||||||
SHLQ $0x0d, R9, R8
|
|
||||||
SHLQ $0x0d, R11, R10
|
|
||||||
SHLQ $0x0d, R13, R12
|
|
||||||
SHLQ $0x0d, R15, R14
|
|
||||||
ANDQ AX, DI
|
|
||||||
IMUL3Q $0x13, R14, R14
|
|
||||||
ADDQ R14, DI
|
|
||||||
ANDQ AX, R9
|
|
||||||
ADDQ SI, R9
|
|
||||||
ANDQ AX, R11
|
|
||||||
ADDQ R8, R11
|
|
||||||
ANDQ AX, R13
|
|
||||||
ADDQ R10, R13
|
|
||||||
ANDQ AX, R15
|
|
||||||
ADDQ R12, R15
|
|
||||||
|
|
||||||
// Second reduction chain (carryPropagate)
|
|
||||||
MOVQ DI, SI
|
|
||||||
SHRQ $0x33, SI
|
|
||||||
MOVQ R9, R8
|
|
||||||
SHRQ $0x33, R8
|
|
||||||
MOVQ R11, R10
|
|
||||||
SHRQ $0x33, R10
|
|
||||||
MOVQ R13, R12
|
|
||||||
SHRQ $0x33, R12
|
|
||||||
MOVQ R15, R14
|
|
||||||
SHRQ $0x33, R14
|
|
||||||
ANDQ AX, DI
|
|
||||||
IMUL3Q $0x13, R14, R14
|
|
||||||
ADDQ R14, DI
|
|
||||||
ANDQ AX, R9
|
|
||||||
ADDQ SI, R9
|
|
||||||
ANDQ AX, R11
|
|
||||||
ADDQ R8, R11
|
|
||||||
ANDQ AX, R13
|
|
||||||
ADDQ R10, R13
|
|
||||||
ANDQ AX, R15
|
|
||||||
ADDQ R12, R15
|
|
||||||
|
|
||||||
// Store output
|
|
||||||
MOVQ out+0(FP), AX
|
|
||||||
MOVQ DI, (AX)
|
|
||||||
MOVQ R9, 8(AX)
|
|
||||||
MOVQ R11, 16(AX)
|
|
||||||
MOVQ R13, 24(AX)
|
|
||||||
MOVQ R15, 32(AX)
|
|
||||||
RET
|
|
||||||
|
|
||||||
// func feSquare(out *Element, a *Element)
|
|
||||||
TEXT ·feSquare(SB), NOSPLIT, $0-16
|
|
||||||
MOVQ a+8(FP), CX
|
|
||||||
|
|
||||||
// r0 = l0×l0
|
|
||||||
MOVQ (CX), AX
|
|
||||||
MULQ (CX)
|
|
||||||
MOVQ AX, SI
|
|
||||||
MOVQ DX, BX
|
|
||||||
|
|
||||||
// r0 += 38×l1×l4
|
|
||||||
MOVQ 8(CX), AX
|
|
||||||
IMUL3Q $0x26, AX, AX
|
|
||||||
MULQ 32(CX)
|
|
||||||
ADDQ AX, SI
|
|
||||||
ADCQ DX, BX
|
|
||||||
|
|
||||||
// r0 += 38×l2×l3
|
|
||||||
MOVQ 16(CX), AX
|
|
||||||
IMUL3Q $0x26, AX, AX
|
|
||||||
MULQ 24(CX)
|
|
||||||
ADDQ AX, SI
|
|
||||||
ADCQ DX, BX
|
|
||||||
|
|
||||||
// r1 = 2×l0×l1
|
|
||||||
MOVQ (CX), AX
|
|
||||||
SHLQ $0x01, AX
|
|
||||||
MULQ 8(CX)
|
|
||||||
MOVQ AX, R8
|
|
||||||
MOVQ DX, DI
|
|
||||||
|
|
||||||
// r1 += 38×l2×l4
|
|
||||||
MOVQ 16(CX), AX
|
|
||||||
IMUL3Q $0x26, AX, AX
|
|
||||||
MULQ 32(CX)
|
|
||||||
ADDQ AX, R8
|
|
||||||
ADCQ DX, DI
|
|
||||||
|
|
||||||
// r1 += 19×l3×l3
|
|
||||||
MOVQ 24(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 24(CX)
|
|
||||||
ADDQ AX, R8
|
|
||||||
ADCQ DX, DI
|
|
||||||
|
|
||||||
// r2 = 2×l0×l2
|
|
||||||
MOVQ (CX), AX
|
|
||||||
SHLQ $0x01, AX
|
|
||||||
MULQ 16(CX)
|
|
||||||
MOVQ AX, R10
|
|
||||||
MOVQ DX, R9
|
|
||||||
|
|
||||||
// r2 += l1×l1
|
|
||||||
MOVQ 8(CX), AX
|
|
||||||
MULQ 8(CX)
|
|
||||||
ADDQ AX, R10
|
|
||||||
ADCQ DX, R9
|
|
||||||
|
|
||||||
// r2 += 38×l3×l4
|
|
||||||
MOVQ 24(CX), AX
|
|
||||||
IMUL3Q $0x26, AX, AX
|
|
||||||
MULQ 32(CX)
|
|
||||||
ADDQ AX, R10
|
|
||||||
ADCQ DX, R9
|
|
||||||
|
|
||||||
// r3 = 2×l0×l3
|
|
||||||
MOVQ (CX), AX
|
|
||||||
SHLQ $0x01, AX
|
|
||||||
MULQ 24(CX)
|
|
||||||
MOVQ AX, R12
|
|
||||||
MOVQ DX, R11
|
|
||||||
|
|
||||||
// r3 += 2×l1×l2
|
|
||||||
MOVQ 8(CX), AX
|
|
||||||
IMUL3Q $0x02, AX, AX
|
|
||||||
MULQ 16(CX)
|
|
||||||
ADDQ AX, R12
|
|
||||||
ADCQ DX, R11
|
|
||||||
|
|
||||||
// r3 += 19×l4×l4
|
|
||||||
MOVQ 32(CX), AX
|
|
||||||
IMUL3Q $0x13, AX, AX
|
|
||||||
MULQ 32(CX)
|
|
||||||
ADDQ AX, R12
|
|
||||||
ADCQ DX, R11
|
|
||||||
|
|
||||||
// r4 = 2×l0×l4
|
|
||||||
MOVQ (CX), AX
|
|
||||||
SHLQ $0x01, AX
|
|
||||||
MULQ 32(CX)
|
|
||||||
MOVQ AX, R14
|
|
||||||
MOVQ DX, R13
|
|
||||||
|
|
||||||
// r4 += 2×l1×l3
|
|
||||||
MOVQ 8(CX), AX
|
|
||||||
IMUL3Q $0x02, AX, AX
|
|
||||||
MULQ 24(CX)
|
|
||||||
ADDQ AX, R14
|
|
||||||
ADCQ DX, R13
|
|
||||||
|
|
||||||
// r4 += l2×l2
|
|
||||||
MOVQ 16(CX), AX
|
|
||||||
MULQ 16(CX)
|
|
||||||
ADDQ AX, R14
|
|
||||||
ADCQ DX, R13
|
|
||||||
|
|
||||||
// First reduction chain
|
|
||||||
MOVQ $0x0007ffffffffffff, AX
|
|
||||||
SHLQ $0x0d, SI, BX
|
|
||||||
SHLQ $0x0d, R8, DI
|
|
||||||
SHLQ $0x0d, R10, R9
|
|
||||||
SHLQ $0x0d, R12, R11
|
|
||||||
SHLQ $0x0d, R14, R13
|
|
||||||
ANDQ AX, SI
|
|
||||||
IMUL3Q $0x13, R13, R13
|
|
||||||
ADDQ R13, SI
|
|
||||||
ANDQ AX, R8
|
|
||||||
ADDQ BX, R8
|
|
||||||
ANDQ AX, R10
|
|
||||||
ADDQ DI, R10
|
|
||||||
ANDQ AX, R12
|
|
||||||
ADDQ R9, R12
|
|
||||||
ANDQ AX, R14
|
|
||||||
ADDQ R11, R14
|
|
||||||
|
|
||||||
// Second reduction chain (carryPropagate)
|
|
||||||
MOVQ SI, BX
|
|
||||||
SHRQ $0x33, BX
|
|
||||||
MOVQ R8, DI
|
|
||||||
SHRQ $0x33, DI
|
|
||||||
MOVQ R10, R9
|
|
||||||
SHRQ $0x33, R9
|
|
||||||
MOVQ R12, R11
|
|
||||||
SHRQ $0x33, R11
|
|
||||||
MOVQ R14, R13
|
|
||||||
SHRQ $0x33, R13
|
|
||||||
ANDQ AX, SI
|
|
||||||
IMUL3Q $0x13, R13, R13
|
|
||||||
ADDQ R13, SI
|
|
||||||
ANDQ AX, R8
|
|
||||||
ADDQ BX, R8
|
|
||||||
ANDQ AX, R10
|
|
||||||
ADDQ DI, R10
|
|
||||||
ANDQ AX, R12
|
|
||||||
ADDQ R9, R12
|
|
||||||
ANDQ AX, R14
|
|
||||||
ADDQ R11, R14
|
|
||||||
|
|
||||||
// Store output
|
|
||||||
MOVQ out+0(FP), AX
|
|
||||||
MOVQ SI, (AX)
|
|
||||||
MOVQ R8, 8(AX)
|
|
||||||
MOVQ R10, 16(AX)
|
|
||||||
MOVQ R12, 24(AX)
|
|
||||||
MOVQ R14, 32(AX)
|
|
||||||
RET
|
|
||||||
12
vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go
generated
vendored
12
vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go
generated
vendored
@@ -1,12 +0,0 @@
|
|||||||
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !amd64 || !gc || purego
|
|
||||||
// +build !amd64 !gc purego
|
|
||||||
|
|
||||||
package field
|
|
||||||
|
|
||||||
func feMul(v, x, y *Element) { feMulGeneric(v, x, y) }
|
|
||||||
|
|
||||||
func feSquare(v, x *Element) { feSquareGeneric(v, x) }
|
|
||||||
16
vendor/filippo.io/edwards25519/field/fe_arm64.go
generated
vendored
16
vendor/filippo.io/edwards25519/field/fe_arm64.go
generated
vendored
@@ -1,16 +0,0 @@
|
|||||||
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build arm64 && gc && !purego
|
|
||||||
// +build arm64,gc,!purego
|
|
||||||
|
|
||||||
package field
|
|
||||||
|
|
||||||
//go:noescape
|
|
||||||
func carryPropagate(v *Element)
|
|
||||||
|
|
||||||
func (v *Element) carryPropagate() *Element {
|
|
||||||
carryPropagate(v)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
42
vendor/filippo.io/edwards25519/field/fe_arm64.s
generated
vendored
42
vendor/filippo.io/edwards25519/field/fe_arm64.s
generated
vendored
@@ -1,42 +0,0 @@
|
|||||||
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build arm64,gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
// carryPropagate works exactly like carryPropagateGeneric and uses the
|
|
||||||
// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but
|
|
||||||
// avoids loading R0-R4 twice and uses LDP and STP.
|
|
||||||
//
|
|
||||||
// See https://golang.org/issues/43145 for the main compiler issue.
|
|
||||||
//
|
|
||||||
// func carryPropagate(v *Element)
|
|
||||||
TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8
|
|
||||||
MOVD v+0(FP), R20
|
|
||||||
|
|
||||||
LDP 0(R20), (R0, R1)
|
|
||||||
LDP 16(R20), (R2, R3)
|
|
||||||
MOVD 32(R20), R4
|
|
||||||
|
|
||||||
AND $0x7ffffffffffff, R0, R10
|
|
||||||
AND $0x7ffffffffffff, R1, R11
|
|
||||||
AND $0x7ffffffffffff, R2, R12
|
|
||||||
AND $0x7ffffffffffff, R3, R13
|
|
||||||
AND $0x7ffffffffffff, R4, R14
|
|
||||||
|
|
||||||
ADD R0>>51, R11, R11
|
|
||||||
ADD R1>>51, R12, R12
|
|
||||||
ADD R2>>51, R13, R13
|
|
||||||
ADD R3>>51, R14, R14
|
|
||||||
// R4>>51 * 19 + R10 -> R10
|
|
||||||
LSR $51, R4, R21
|
|
||||||
MOVD $19, R22
|
|
||||||
MADD R22, R10, R21, R10
|
|
||||||
|
|
||||||
STP (R10, R11), 0(R20)
|
|
||||||
STP (R12, R13), 16(R20)
|
|
||||||
MOVD R14, 32(R20)
|
|
||||||
|
|
||||||
RET
|
|
||||||
12
vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go
generated
vendored
12
vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go
generated
vendored
@@ -1,12 +0,0 @@
|
|||||||
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !arm64 || !gc || purego
|
|
||||||
// +build !arm64 !gc purego
|
|
||||||
|
|
||||||
package field
|
|
||||||
|
|
||||||
func (v *Element) carryPropagate() *Element {
|
|
||||||
return v.carryPropagateGeneric()
|
|
||||||
}
|
|
||||||
50
vendor/filippo.io/edwards25519/field/fe_extra.go
generated
vendored
50
vendor/filippo.io/edwards25519/field/fe_extra.go
generated
vendored
@@ -1,50 +0,0 @@
|
|||||||
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package field
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
// This file contains additional functionality that is not included in the
|
|
||||||
// upstream crypto/ed25519/internal/edwards25519/field package.
|
|
||||||
|
|
||||||
// SetWideBytes sets v to x, where x is a 64-byte little-endian encoding, which
|
|
||||||
// is reduced modulo the field order. If x is not of the right length,
|
|
||||||
// SetWideBytes returns nil and an error, and the receiver is unchanged.
|
|
||||||
//
|
|
||||||
// SetWideBytes is not necessary to select a uniformly distributed value, and is
|
|
||||||
// only provided for compatibility: SetBytes can be used instead as the chance
|
|
||||||
// of bias is less than 2⁻²⁵⁰.
|
|
||||||
func (v *Element) SetWideBytes(x []byte) (*Element, error) {
|
|
||||||
if len(x) != 64 {
|
|
||||||
return nil, errors.New("edwards25519: invalid SetWideBytes input size")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split the 64 bytes into two elements, and extract the most significant
|
|
||||||
// bit of each, which is ignored by SetBytes.
|
|
||||||
lo, _ := new(Element).SetBytes(x[:32])
|
|
||||||
loMSB := uint64(x[31] >> 7)
|
|
||||||
hi, _ := new(Element).SetBytes(x[32:])
|
|
||||||
hiMSB := uint64(x[63] >> 7)
|
|
||||||
|
|
||||||
// The output we want is
|
|
||||||
//
|
|
||||||
// v = lo + loMSB * 2²⁵⁵ + hi * 2²⁵⁶ + hiMSB * 2⁵¹¹
|
|
||||||
//
|
|
||||||
// which applying the reduction identity comes out to
|
|
||||||
//
|
|
||||||
// v = lo + loMSB * 19 + hi * 2 * 19 + hiMSB * 2 * 19²
|
|
||||||
//
|
|
||||||
// l0 will be the sum of a 52 bits value (lo.l0), plus a 5 bits value
|
|
||||||
// (loMSB * 19), a 6 bits value (hi.l0 * 2 * 19), and a 10 bits value
|
|
||||||
// (hiMSB * 2 * 19²), so it fits in a uint64.
|
|
||||||
|
|
||||||
v.l0 = lo.l0 + loMSB*19 + hi.l0*2*19 + hiMSB*2*19*19
|
|
||||||
v.l1 = lo.l1 + hi.l1*2*19
|
|
||||||
v.l2 = lo.l2 + hi.l2*2*19
|
|
||||||
v.l3 = lo.l3 + hi.l3*2*19
|
|
||||||
v.l4 = lo.l4 + hi.l4*2*19
|
|
||||||
|
|
||||||
return v.carryPropagate(), nil
|
|
||||||
}
|
|
||||||
266
vendor/filippo.io/edwards25519/field/fe_generic.go
generated
vendored
266
vendor/filippo.io/edwards25519/field/fe_generic.go
generated
vendored
@@ -1,266 +0,0 @@
|
|||||||
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package field
|
|
||||||
|
|
||||||
import "math/bits"
|
|
||||||
|
|
||||||
// uint128 holds a 128-bit number as two 64-bit limbs, for use with the
|
|
||||||
// bits.Mul64 and bits.Add64 intrinsics.
|
|
||||||
type uint128 struct {
|
|
||||||
lo, hi uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// mul64 returns a * b.
|
|
||||||
func mul64(a, b uint64) uint128 {
|
|
||||||
hi, lo := bits.Mul64(a, b)
|
|
||||||
return uint128{lo, hi}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addMul64 returns v + a * b.
|
|
||||||
func addMul64(v uint128, a, b uint64) uint128 {
|
|
||||||
hi, lo := bits.Mul64(a, b)
|
|
||||||
lo, c := bits.Add64(lo, v.lo, 0)
|
|
||||||
hi, _ = bits.Add64(hi, v.hi, c)
|
|
||||||
return uint128{lo, hi}
|
|
||||||
}
|
|
||||||
|
|
||||||
// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits.
|
|
||||||
func shiftRightBy51(a uint128) uint64 {
|
|
||||||
return (a.hi << (64 - 51)) | (a.lo >> 51)
|
|
||||||
}
|
|
||||||
|
|
||||||
func feMulGeneric(v, a, b *Element) {
|
|
||||||
a0 := a.l0
|
|
||||||
a1 := a.l1
|
|
||||||
a2 := a.l2
|
|
||||||
a3 := a.l3
|
|
||||||
a4 := a.l4
|
|
||||||
|
|
||||||
b0 := b.l0
|
|
||||||
b1 := b.l1
|
|
||||||
b2 := b.l2
|
|
||||||
b3 := b.l3
|
|
||||||
b4 := b.l4
|
|
||||||
|
|
||||||
// Limb multiplication works like pen-and-paper columnar multiplication, but
|
|
||||||
// with 51-bit limbs instead of digits.
|
|
||||||
//
|
|
||||||
// a4 a3 a2 a1 a0 x
|
|
||||||
// b4 b3 b2 b1 b0 =
|
|
||||||
// ------------------------
|
|
||||||
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
|
||||||
// a4b1 a3b1 a2b1 a1b1 a0b1 +
|
|
||||||
// a4b2 a3b2 a2b2 a1b2 a0b2 +
|
|
||||||
// a4b3 a3b3 a2b3 a1b3 a0b3 +
|
|
||||||
// a4b4 a3b4 a2b4 a1b4 a0b4 =
|
|
||||||
// ----------------------------------------------
|
|
||||||
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
|
||||||
//
|
|
||||||
// We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to
|
|
||||||
// reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5,
|
|
||||||
// r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc.
|
|
||||||
//
|
|
||||||
// Reduction can be carried out simultaneously to multiplication. For
|
|
||||||
// example, we do not compute r5: whenever the result of a multiplication
|
|
||||||
// belongs to r5, like a1b4, we multiply it by 19 and add the result to r0.
|
|
||||||
//
|
|
||||||
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
|
||||||
// a3b1 a2b1 a1b1 a0b1 19×a4b1 +
|
|
||||||
// a2b2 a1b2 a0b2 19×a4b2 19×a3b2 +
|
|
||||||
// a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 +
|
|
||||||
// a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 =
|
|
||||||
// --------------------------------------
|
|
||||||
// r4 r3 r2 r1 r0
|
|
||||||
//
|
|
||||||
// Finally we add up the columns into wide, overlapping limbs.
|
|
||||||
|
|
||||||
a1_19 := a1 * 19
|
|
||||||
a2_19 := a2 * 19
|
|
||||||
a3_19 := a3 * 19
|
|
||||||
a4_19 := a4 * 19
|
|
||||||
|
|
||||||
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
|
||||||
r0 := mul64(a0, b0)
|
|
||||||
r0 = addMul64(r0, a1_19, b4)
|
|
||||||
r0 = addMul64(r0, a2_19, b3)
|
|
||||||
r0 = addMul64(r0, a3_19, b2)
|
|
||||||
r0 = addMul64(r0, a4_19, b1)
|
|
||||||
|
|
||||||
// r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2)
|
|
||||||
r1 := mul64(a0, b1)
|
|
||||||
r1 = addMul64(r1, a1, b0)
|
|
||||||
r1 = addMul64(r1, a2_19, b4)
|
|
||||||
r1 = addMul64(r1, a3_19, b3)
|
|
||||||
r1 = addMul64(r1, a4_19, b2)
|
|
||||||
|
|
||||||
// r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3)
|
|
||||||
r2 := mul64(a0, b2)
|
|
||||||
r2 = addMul64(r2, a1, b1)
|
|
||||||
r2 = addMul64(r2, a2, b0)
|
|
||||||
r2 = addMul64(r2, a3_19, b4)
|
|
||||||
r2 = addMul64(r2, a4_19, b3)
|
|
||||||
|
|
||||||
// r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4
|
|
||||||
r3 := mul64(a0, b3)
|
|
||||||
r3 = addMul64(r3, a1, b2)
|
|
||||||
r3 = addMul64(r3, a2, b1)
|
|
||||||
r3 = addMul64(r3, a3, b0)
|
|
||||||
r3 = addMul64(r3, a4_19, b4)
|
|
||||||
|
|
||||||
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
|
||||||
r4 := mul64(a0, b4)
|
|
||||||
r4 = addMul64(r4, a1, b3)
|
|
||||||
r4 = addMul64(r4, a2, b2)
|
|
||||||
r4 = addMul64(r4, a3, b1)
|
|
||||||
r4 = addMul64(r4, a4, b0)
|
|
||||||
|
|
||||||
// After the multiplication, we need to reduce (carry) the five coefficients
|
|
||||||
// to obtain a result with limbs that are at most slightly larger than 2⁵¹,
|
|
||||||
// to respect the Element invariant.
|
|
||||||
//
|
|
||||||
// Overall, the reduction works the same as carryPropagate, except with
|
|
||||||
// wider inputs: we take the carry for each coefficient by shifting it right
|
|
||||||
// by 51, and add it to the limb above it. The top carry is multiplied by 19
|
|
||||||
// according to the reduction identity and added to the lowest limb.
|
|
||||||
//
|
|
||||||
// The largest coefficient (r0) will be at most 111 bits, which guarantees
|
|
||||||
// that all carries are at most 111 - 51 = 60 bits, which fits in a uint64.
|
|
||||||
//
|
|
||||||
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
|
||||||
// r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²)
|
|
||||||
// r0 < (1 + 19 × 4) × 2⁵² × 2⁵²
|
|
||||||
// r0 < 2⁷ × 2⁵² × 2⁵²
|
|
||||||
// r0 < 2¹¹¹
|
|
||||||
//
|
|
||||||
// Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most
|
|
||||||
// 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and
|
|
||||||
// allows us to easily apply the reduction identity.
|
|
||||||
//
|
|
||||||
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
|
||||||
// r4 < 5 × 2⁵² × 2⁵²
|
|
||||||
// r4 < 2¹⁰⁷
|
|
||||||
//
|
|
||||||
|
|
||||||
c0 := shiftRightBy51(r0)
|
|
||||||
c1 := shiftRightBy51(r1)
|
|
||||||
c2 := shiftRightBy51(r2)
|
|
||||||
c3 := shiftRightBy51(r3)
|
|
||||||
c4 := shiftRightBy51(r4)
|
|
||||||
|
|
||||||
rr0 := r0.lo&maskLow51Bits + c4*19
|
|
||||||
rr1 := r1.lo&maskLow51Bits + c0
|
|
||||||
rr2 := r2.lo&maskLow51Bits + c1
|
|
||||||
rr3 := r3.lo&maskLow51Bits + c2
|
|
||||||
rr4 := r4.lo&maskLow51Bits + c3
|
|
||||||
|
|
||||||
// Now all coefficients fit into 64-bit registers but are still too large to
|
|
||||||
// be passed around as a Element. We therefore do one last carry chain,
|
|
||||||
// where the carries will be small enough to fit in the wiggle room above 2⁵¹.
|
|
||||||
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
|
||||||
v.carryPropagate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func feSquareGeneric(v, a *Element) {
|
|
||||||
l0 := a.l0
|
|
||||||
l1 := a.l1
|
|
||||||
l2 := a.l2
|
|
||||||
l3 := a.l3
|
|
||||||
l4 := a.l4
|
|
||||||
|
|
||||||
// Squaring works precisely like multiplication above, but thanks to its
|
|
||||||
// symmetry we get to group a few terms together.
|
|
||||||
//
|
|
||||||
// l4 l3 l2 l1 l0 x
|
|
||||||
// l4 l3 l2 l1 l0 =
|
|
||||||
// ------------------------
|
|
||||||
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
|
||||||
// l4l1 l3l1 l2l1 l1l1 l0l1 +
|
|
||||||
// l4l2 l3l2 l2l2 l1l2 l0l2 +
|
|
||||||
// l4l3 l3l3 l2l3 l1l3 l0l3 +
|
|
||||||
// l4l4 l3l4 l2l4 l1l4 l0l4 =
|
|
||||||
// ----------------------------------------------
|
|
||||||
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
|
||||||
//
|
|
||||||
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
|
||||||
// l3l1 l2l1 l1l1 l0l1 19×l4l1 +
|
|
||||||
// l2l2 l1l2 l0l2 19×l4l2 19×l3l2 +
|
|
||||||
// l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 +
|
|
||||||
// l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 =
|
|
||||||
// --------------------------------------
|
|
||||||
// r4 r3 r2 r1 r0
|
|
||||||
//
|
|
||||||
// With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with
|
|
||||||
// only three Mul64 and four Add64, instead of five and eight.
|
|
||||||
|
|
||||||
l0_2 := l0 * 2
|
|
||||||
l1_2 := l1 * 2
|
|
||||||
|
|
||||||
l1_38 := l1 * 38
|
|
||||||
l2_38 := l2 * 38
|
|
||||||
l3_38 := l3 * 38
|
|
||||||
|
|
||||||
l3_19 := l3 * 19
|
|
||||||
l4_19 := l4 * 19
|
|
||||||
|
|
||||||
// r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3)
|
|
||||||
r0 := mul64(l0, l0)
|
|
||||||
r0 = addMul64(r0, l1_38, l4)
|
|
||||||
r0 = addMul64(r0, l2_38, l3)
|
|
||||||
|
|
||||||
// r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3
|
|
||||||
r1 := mul64(l0_2, l1)
|
|
||||||
r1 = addMul64(r1, l2_38, l4)
|
|
||||||
r1 = addMul64(r1, l3_19, l3)
|
|
||||||
|
|
||||||
// r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4
|
|
||||||
r2 := mul64(l0_2, l2)
|
|
||||||
r2 = addMul64(r2, l1, l1)
|
|
||||||
r2 = addMul64(r2, l3_38, l4)
|
|
||||||
|
|
||||||
// r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4
|
|
||||||
r3 := mul64(l0_2, l3)
|
|
||||||
r3 = addMul64(r3, l1_2, l2)
|
|
||||||
r3 = addMul64(r3, l4_19, l4)
|
|
||||||
|
|
||||||
// r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2
|
|
||||||
r4 := mul64(l0_2, l4)
|
|
||||||
r4 = addMul64(r4, l1_2, l3)
|
|
||||||
r4 = addMul64(r4, l2, l2)
|
|
||||||
|
|
||||||
c0 := shiftRightBy51(r0)
|
|
||||||
c1 := shiftRightBy51(r1)
|
|
||||||
c2 := shiftRightBy51(r2)
|
|
||||||
c3 := shiftRightBy51(r3)
|
|
||||||
c4 := shiftRightBy51(r4)
|
|
||||||
|
|
||||||
rr0 := r0.lo&maskLow51Bits + c4*19
|
|
||||||
rr1 := r1.lo&maskLow51Bits + c0
|
|
||||||
rr2 := r2.lo&maskLow51Bits + c1
|
|
||||||
rr3 := r3.lo&maskLow51Bits + c2
|
|
||||||
rr4 := r4.lo&maskLow51Bits + c3
|
|
||||||
|
|
||||||
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
|
||||||
v.carryPropagate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// carryPropagate brings the limbs below 52 bits by applying the reduction
|
|
||||||
// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry.
|
|
||||||
func (v *Element) carryPropagateGeneric() *Element {
|
|
||||||
c0 := v.l0 >> 51
|
|
||||||
c1 := v.l1 >> 51
|
|
||||||
c2 := v.l2 >> 51
|
|
||||||
c3 := v.l3 >> 51
|
|
||||||
c4 := v.l4 >> 51
|
|
||||||
|
|
||||||
// c4 is at most 64 - 51 = 13 bits, so c4*19 is at most 18 bits, and
|
|
||||||
// the final l0 will be at most 52 bits. Similarly for the rest.
|
|
||||||
v.l0 = v.l0&maskLow51Bits + c4*19
|
|
||||||
v.l1 = v.l1&maskLow51Bits + c0
|
|
||||||
v.l2 = v.l2&maskLow51Bits + c1
|
|
||||||
v.l3 = v.l3&maskLow51Bits + c2
|
|
||||||
v.l4 = v.l4&maskLow51Bits + c3
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
1030
vendor/filippo.io/edwards25519/scalar.go
generated
vendored
1030
vendor/filippo.io/edwards25519/scalar.go
generated
vendored
File diff suppressed because it is too large
Load Diff
214
vendor/filippo.io/edwards25519/scalarmult.go
generated
vendored
214
vendor/filippo.io/edwards25519/scalarmult.go
generated
vendored
@@ -1,214 +0,0 @@
|
|||||||
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package edwards25519
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
// basepointTable is a set of 32 affineLookupTables, where table i is generated
|
|
||||||
// from 256i * basepoint. It is precomputed the first time it's used.
|
|
||||||
func basepointTable() *[32]affineLookupTable {
|
|
||||||
basepointTablePrecomp.initOnce.Do(func() {
|
|
||||||
p := NewGeneratorPoint()
|
|
||||||
for i := 0; i < 32; i++ {
|
|
||||||
basepointTablePrecomp.table[i].FromP3(p)
|
|
||||||
for j := 0; j < 8; j++ {
|
|
||||||
p.Add(p, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return &basepointTablePrecomp.table
|
|
||||||
}
|
|
||||||
|
|
||||||
var basepointTablePrecomp struct {
|
|
||||||
table [32]affineLookupTable
|
|
||||||
initOnce sync.Once
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScalarBaseMult sets v = x * B, where B is the canonical generator, and
|
|
||||||
// returns v.
|
|
||||||
//
|
|
||||||
// The scalar multiplication is done in constant time.
|
|
||||||
func (v *Point) ScalarBaseMult(x *Scalar) *Point {
|
|
||||||
basepointTable := basepointTable()
|
|
||||||
|
|
||||||
// Write x = sum(x_i * 16^i) so x*B = sum( B*x_i*16^i )
|
|
||||||
// as described in the Ed25519 paper
|
|
||||||
//
|
|
||||||
// Group even and odd coefficients
|
|
||||||
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
|
|
||||||
// + x_1*16^1*B + x_3*16^3*B + ... + x_63*16^63*B
|
|
||||||
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
|
|
||||||
// + 16*( x_1*16^0*B + x_3*16^2*B + ... + x_63*16^62*B)
|
|
||||||
//
|
|
||||||
// We use a lookup table for each i to get x_i*16^(2*i)*B
|
|
||||||
// and do four doublings to multiply by 16.
|
|
||||||
digits := x.signedRadix16()
|
|
||||||
|
|
||||||
multiple := &affineCached{}
|
|
||||||
tmp1 := &projP1xP1{}
|
|
||||||
tmp2 := &projP2{}
|
|
||||||
|
|
||||||
// Accumulate the odd components first
|
|
||||||
v.Set(NewIdentityPoint())
|
|
||||||
for i := 1; i < 64; i += 2 {
|
|
||||||
basepointTable[i/2].SelectInto(multiple, digits[i])
|
|
||||||
tmp1.AddAffine(v, multiple)
|
|
||||||
v.fromP1xP1(tmp1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiply by 16
|
|
||||||
tmp2.FromP3(v) // tmp2 = v in P2 coords
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 2*v in P1xP1 coords
|
|
||||||
tmp2.FromP1xP1(tmp1) // tmp2 = 2*v in P2 coords
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 4*v in P1xP1 coords
|
|
||||||
tmp2.FromP1xP1(tmp1) // tmp2 = 4*v in P2 coords
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 8*v in P1xP1 coords
|
|
||||||
tmp2.FromP1xP1(tmp1) // tmp2 = 8*v in P2 coords
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 16*v in P1xP1 coords
|
|
||||||
v.fromP1xP1(tmp1) // now v = 16*(odd components)
|
|
||||||
|
|
||||||
// Accumulate the even components
|
|
||||||
for i := 0; i < 64; i += 2 {
|
|
||||||
basepointTable[i/2].SelectInto(multiple, digits[i])
|
|
||||||
tmp1.AddAffine(v, multiple)
|
|
||||||
v.fromP1xP1(tmp1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScalarMult sets v = x * q, and returns v.
|
|
||||||
//
|
|
||||||
// The scalar multiplication is done in constant time.
|
|
||||||
func (v *Point) ScalarMult(x *Scalar, q *Point) *Point {
|
|
||||||
checkInitialized(q)
|
|
||||||
|
|
||||||
var table projLookupTable
|
|
||||||
table.FromP3(q)
|
|
||||||
|
|
||||||
// Write x = sum(x_i * 16^i)
|
|
||||||
// so x*Q = sum( Q*x_i*16^i )
|
|
||||||
// = Q*x_0 + 16*(Q*x_1 + 16*( ... + Q*x_63) ... )
|
|
||||||
// <------compute inside out---------
|
|
||||||
//
|
|
||||||
// We use the lookup table to get the x_i*Q values
|
|
||||||
// and do four doublings to compute 16*Q
|
|
||||||
digits := x.signedRadix16()
|
|
||||||
|
|
||||||
// Unwrap first loop iteration to save computing 16*identity
|
|
||||||
multiple := &projCached{}
|
|
||||||
tmp1 := &projP1xP1{}
|
|
||||||
tmp2 := &projP2{}
|
|
||||||
table.SelectInto(multiple, digits[63])
|
|
||||||
|
|
||||||
v.Set(NewIdentityPoint())
|
|
||||||
tmp1.Add(v, multiple) // tmp1 = x_63*Q in P1xP1 coords
|
|
||||||
for i := 62; i >= 0; i-- {
|
|
||||||
tmp2.FromP1xP1(tmp1) // tmp2 = (prev) in P2 coords
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords
|
|
||||||
tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords
|
|
||||||
tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords
|
|
||||||
tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords
|
|
||||||
tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords
|
|
||||||
v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords
|
|
||||||
table.SelectInto(multiple, digits[i])
|
|
||||||
tmp1.Add(v, multiple) // tmp1 = x_i*Q + 16*(prev) in P1xP1 coords
|
|
||||||
}
|
|
||||||
v.fromP1xP1(tmp1)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// basepointNafTable is the nafLookupTable8 for the basepoint.
|
|
||||||
// It is precomputed the first time it's used.
|
|
||||||
func basepointNafTable() *nafLookupTable8 {
|
|
||||||
basepointNafTablePrecomp.initOnce.Do(func() {
|
|
||||||
basepointNafTablePrecomp.table.FromP3(NewGeneratorPoint())
|
|
||||||
})
|
|
||||||
return &basepointNafTablePrecomp.table
|
|
||||||
}
|
|
||||||
|
|
||||||
var basepointNafTablePrecomp struct {
|
|
||||||
table nafLookupTable8
|
|
||||||
initOnce sync.Once
|
|
||||||
}
|
|
||||||
|
|
||||||
// VarTimeDoubleScalarBaseMult sets v = a * A + b * B, where B is the canonical
|
|
||||||
// generator, and returns v.
|
|
||||||
//
|
|
||||||
// Execution time depends on the inputs.
|
|
||||||
func (v *Point) VarTimeDoubleScalarBaseMult(a *Scalar, A *Point, b *Scalar) *Point {
|
|
||||||
checkInitialized(A)
|
|
||||||
|
|
||||||
// Similarly to the single variable-base approach, we compute
|
|
||||||
// digits and use them with a lookup table. However, because
|
|
||||||
// we are allowed to do variable-time operations, we don't
|
|
||||||
// need constant-time lookups or constant-time digit
|
|
||||||
// computations.
|
|
||||||
//
|
|
||||||
// So we use a non-adjacent form of some width w instead of
|
|
||||||
// radix 16. This is like a binary representation (one digit
|
|
||||||
// for each binary place) but we allow the digits to grow in
|
|
||||||
// magnitude up to 2^{w-1} so that the nonzero digits are as
|
|
||||||
// sparse as possible. Intuitively, this "condenses" the
|
|
||||||
// "mass" of the scalar onto sparse coefficients (meaning
|
|
||||||
// fewer additions).
|
|
||||||
|
|
||||||
basepointNafTable := basepointNafTable()
|
|
||||||
var aTable nafLookupTable5
|
|
||||||
aTable.FromP3(A)
|
|
||||||
// Because the basepoint is fixed, we can use a wider NAF
|
|
||||||
// corresponding to a bigger table.
|
|
||||||
aNaf := a.nonAdjacentForm(5)
|
|
||||||
bNaf := b.nonAdjacentForm(8)
|
|
||||||
|
|
||||||
// Find the first nonzero coefficient.
|
|
||||||
i := 255
|
|
||||||
for j := i; j >= 0; j-- {
|
|
||||||
if aNaf[j] != 0 || bNaf[j] != 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
multA := &projCached{}
|
|
||||||
multB := &affineCached{}
|
|
||||||
tmp1 := &projP1xP1{}
|
|
||||||
tmp2 := &projP2{}
|
|
||||||
tmp2.Zero()
|
|
||||||
|
|
||||||
// Move from high to low bits, doubling the accumulator
|
|
||||||
// at each iteration and checking whether there is a nonzero
|
|
||||||
// coefficient to look up a multiple of.
|
|
||||||
for ; i >= 0; i-- {
|
|
||||||
tmp1.Double(tmp2)
|
|
||||||
|
|
||||||
// Only update v if we have a nonzero coeff to add in.
|
|
||||||
if aNaf[i] > 0 {
|
|
||||||
v.fromP1xP1(tmp1)
|
|
||||||
aTable.SelectInto(multA, aNaf[i])
|
|
||||||
tmp1.Add(v, multA)
|
|
||||||
} else if aNaf[i] < 0 {
|
|
||||||
v.fromP1xP1(tmp1)
|
|
||||||
aTable.SelectInto(multA, -aNaf[i])
|
|
||||||
tmp1.Sub(v, multA)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bNaf[i] > 0 {
|
|
||||||
v.fromP1xP1(tmp1)
|
|
||||||
basepointNafTable.SelectInto(multB, bNaf[i])
|
|
||||||
tmp1.AddAffine(v, multB)
|
|
||||||
} else if bNaf[i] < 0 {
|
|
||||||
v.fromP1xP1(tmp1)
|
|
||||||
basepointNafTable.SelectInto(multB, -bNaf[i])
|
|
||||||
tmp1.SubAffine(v, multB)
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp2.FromP1xP1(tmp1)
|
|
||||||
}
|
|
||||||
|
|
||||||
v.fromP2(tmp2)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
129
vendor/filippo.io/edwards25519/tables.go
generated
vendored
129
vendor/filippo.io/edwards25519/tables.go
generated
vendored
@@ -1,129 +0,0 @@
|
|||||||
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package edwards25519
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/subtle"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A dynamic lookup table for variable-base, constant-time scalar muls.
|
|
||||||
type projLookupTable struct {
|
|
||||||
points [8]projCached
|
|
||||||
}
|
|
||||||
|
|
||||||
// A precomputed lookup table for fixed-base, constant-time scalar muls.
|
|
||||||
type affineLookupTable struct {
|
|
||||||
points [8]affineCached
|
|
||||||
}
|
|
||||||
|
|
||||||
// A dynamic lookup table for variable-base, variable-time scalar muls.
|
|
||||||
type nafLookupTable5 struct {
|
|
||||||
points [8]projCached
|
|
||||||
}
|
|
||||||
|
|
||||||
// A precomputed lookup table for fixed-base, variable-time scalar muls.
|
|
||||||
type nafLookupTable8 struct {
|
|
||||||
points [64]affineCached
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructors.
|
|
||||||
|
|
||||||
// Builds a lookup table at runtime. Fast.
|
|
||||||
func (v *projLookupTable) FromP3(q *Point) {
|
|
||||||
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
|
||||||
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
|
||||||
v.points[0].FromP3(q)
|
|
||||||
tmpP3 := Point{}
|
|
||||||
tmpP1xP1 := projP1xP1{}
|
|
||||||
for i := 0; i < 7; i++ {
|
|
||||||
// Compute (i+1)*Q as Q + i*Q and convert to a ProjCached
|
|
||||||
// This is needlessly complicated because the API has explicit
|
|
||||||
// recievers instead of creating stack objects and relying on RVO
|
|
||||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(q, &v.points[i])))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is not optimised for speed; fixed-base tables should be precomputed.
|
|
||||||
func (v *affineLookupTable) FromP3(q *Point) {
|
|
||||||
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
|
||||||
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
|
||||||
v.points[0].FromP3(q)
|
|
||||||
tmpP3 := Point{}
|
|
||||||
tmpP1xP1 := projP1xP1{}
|
|
||||||
for i := 0; i < 7; i++ {
|
|
||||||
// Compute (i+1)*Q as Q + i*Q and convert to AffineCached
|
|
||||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(q, &v.points[i])))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Builds a lookup table at runtime. Fast.
|
|
||||||
func (v *nafLookupTable5) FromP3(q *Point) {
|
|
||||||
// Goal: v.points[i] = (2*i+1)*Q, i.e., Q, 3Q, 5Q, ..., 15Q
|
|
||||||
// This allows lookup of -15Q, ..., -3Q, -Q, 0, Q, 3Q, ..., 15Q
|
|
||||||
v.points[0].FromP3(q)
|
|
||||||
q2 := Point{}
|
|
||||||
q2.Add(q, q)
|
|
||||||
tmpP3 := Point{}
|
|
||||||
tmpP1xP1 := projP1xP1{}
|
|
||||||
for i := 0; i < 7; i++ {
|
|
||||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(&q2, &v.points[i])))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is not optimised for speed; fixed-base tables should be precomputed.
|
|
||||||
func (v *nafLookupTable8) FromP3(q *Point) {
|
|
||||||
v.points[0].FromP3(q)
|
|
||||||
q2 := Point{}
|
|
||||||
q2.Add(q, q)
|
|
||||||
tmpP3 := Point{}
|
|
||||||
tmpP1xP1 := projP1xP1{}
|
|
||||||
for i := 0; i < 63; i++ {
|
|
||||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(&q2, &v.points[i])))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Selectors.
|
|
||||||
|
|
||||||
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
|
||||||
func (v *projLookupTable) SelectInto(dest *projCached, x int8) {
|
|
||||||
// Compute xabs = |x|
|
|
||||||
xmask := x >> 7
|
|
||||||
xabs := uint8((x + xmask) ^ xmask)
|
|
||||||
|
|
||||||
dest.Zero()
|
|
||||||
for j := 1; j <= 8; j++ {
|
|
||||||
// Set dest = j*Q if |x| = j
|
|
||||||
cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
|
|
||||||
dest.Select(&v.points[j-1], dest, cond)
|
|
||||||
}
|
|
||||||
// Now dest = |x|*Q, conditionally negate to get x*Q
|
|
||||||
dest.CondNeg(int(xmask & 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
|
||||||
func (v *affineLookupTable) SelectInto(dest *affineCached, x int8) {
|
|
||||||
// Compute xabs = |x|
|
|
||||||
xmask := x >> 7
|
|
||||||
xabs := uint8((x + xmask) ^ xmask)
|
|
||||||
|
|
||||||
dest.Zero()
|
|
||||||
for j := 1; j <= 8; j++ {
|
|
||||||
// Set dest = j*Q if |x| = j
|
|
||||||
cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
|
|
||||||
dest.Select(&v.points[j-1], dest, cond)
|
|
||||||
}
|
|
||||||
// Now dest = |x|*Q, conditionally negate to get x*Q
|
|
||||||
dest.CondNeg(int(xmask & 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given odd x with 0 < x < 2^4, return x*Q (in variable time).
|
|
||||||
func (v *nafLookupTable5) SelectInto(dest *projCached, x int8) {
|
|
||||||
*dest = v.points[x/2]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given odd x with 0 < x < 2^7, return x*Q (in variable time).
|
|
||||||
func (v *nafLookupTable8) SelectInto(dest *affineCached, x int8) {
|
|
||||||
*dest = v.points[x/2]
|
|
||||||
}
|
|
||||||
8
vendor/github.com/SevereCloud/vksdk/v2/.golangci.yml
generated
vendored
8
vendor/github.com/SevereCloud/vksdk/v2/.golangci.yml
generated
vendored
@@ -54,11 +54,6 @@ linters:
|
|||||||
- nilnil
|
- nilnil
|
||||||
- tenv
|
- tenv
|
||||||
- nestif
|
- nestif
|
||||||
- grouper
|
|
||||||
- decorder
|
|
||||||
- containedctx
|
|
||||||
# - execinquery # FIXME: panic in 1.46.0
|
|
||||||
- nosprintfhostport
|
|
||||||
|
|
||||||
# - wrapcheck # TODO: v3 Fix
|
# - wrapcheck # TODO: v3 Fix
|
||||||
# - testpackage # TODO: Fix testpackage
|
# - testpackage # TODO: Fix testpackage
|
||||||
@@ -87,9 +82,6 @@ linters:
|
|||||||
# - tagliatelle
|
# - tagliatelle
|
||||||
# - errname
|
# - errname
|
||||||
# - varnamelen
|
# - varnamelen
|
||||||
# - errchkjson
|
|
||||||
# - maintidx
|
|
||||||
# - nonamedreturns
|
|
||||||
|
|
||||||
# depricated
|
# depricated
|
||||||
# - maligned
|
# - maligned
|
||||||
|
|||||||
2
vendor/github.com/SevereCloud/vksdk/v2/README.md
generated
vendored
2
vendor/github.com/SevereCloud/vksdk/v2/README.md
generated
vendored
@@ -1,7 +1,7 @@
|
|||||||
# VK SDK for Golang
|
# VK SDK for Golang
|
||||||
|
|
||||||
[](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2?tab=subdirectories)
|
[](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2?tab=subdirectories)
|
||||||
[](https://dev.vk.com/)
|
[](https://vk.com/dev/)
|
||||||
[](https://codecov.io/gh/SevereCloud/vksdk)
|
[](https://codecov.io/gh/SevereCloud/vksdk)
|
||||||
[](https://vk.me/join/AJQ1d6Or8Q00Y_CSOESfbqGt)
|
[](https://vk.me/join/AJQ1d6Or8Q00Y_CSOESfbqGt)
|
||||||
[](https://github.com/SevereCloud/vksdk/releases)
|
[](https://github.com/SevereCloud/vksdk/releases)
|
||||||
|
|||||||
2
vendor/github.com/SevereCloud/vksdk/v2/api/account.go
generated
vendored
2
vendor/github.com/SevereCloud/vksdk/v2/api/account.go
generated
vendored
@@ -141,8 +141,6 @@ func (vk *VK) AccountSetInfo(params Params) (response int, err error) {
|
|||||||
// AccountSetNameInMenu sets an application screen name
|
// AccountSetNameInMenu sets an application screen name
|
||||||
// (up to 17 characters), that is shown to the user in the left menu.
|
// (up to 17 characters), that is shown to the user in the left menu.
|
||||||
//
|
//
|
||||||
// Deprecated: This method is deprecated and may be disabled soon, please avoid
|
|
||||||
//
|
|
||||||
// https://vk.com/dev/account.setNameInMenu
|
// https://vk.com/dev/account.setNameInMenu
|
||||||
func (vk *VK) AccountSetNameInMenu(params Params) (response int, err error) {
|
func (vk *VK) AccountSetNameInMenu(params Params) (response int, err error) {
|
||||||
err = vk.RequestUnmarshal("account.setNameInMenu", &response, params)
|
err = vk.RequestUnmarshal("account.setNameInMenu", &response, params)
|
||||||
|
|||||||
22
vendor/github.com/SevereCloud/vksdk/v2/api/api.go
generated
vendored
22
vendor/github.com/SevereCloud/vksdk/v2/api/api.go
generated
vendored
@@ -203,13 +203,10 @@ func buildQuery(sliceParams ...Params) (context.Context, url.Values) {
|
|||||||
|
|
||||||
for _, params := range sliceParams {
|
for _, params := range sliceParams {
|
||||||
for key, value := range params {
|
for key, value := range params {
|
||||||
switch key {
|
if key != ":context" {
|
||||||
case "access_token":
|
|
||||||
continue
|
|
||||||
case ":context":
|
|
||||||
ctx = value.(context.Context)
|
|
||||||
default:
|
|
||||||
query.Set(key, FmtValue(value, 0))
|
query.Set(key, FmtValue(value, 0))
|
||||||
|
} else {
|
||||||
|
ctx = value.(context.Context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,9 +255,6 @@ func (vk *VK) DefaultHandler(method string, sliceParams ...Params) (Response, er
|
|||||||
acceptEncoding = "zstd"
|
acceptEncoding = "zstd"
|
||||||
}
|
}
|
||||||
|
|
||||||
token := sliceParams[len(sliceParams)-1]["access_token"].(string)
|
|
||||||
req.Header.Set("Authorization", "Bearer "+token)
|
|
||||||
|
|
||||||
req.Header.Set("User-Agent", vk.UserAgent)
|
req.Header.Set("User-Agent", vk.UserAgent)
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
@@ -275,15 +269,9 @@ func (vk *VK) DefaultHandler(method string, sliceParams ...Params) (Response, er
|
|||||||
|
|
||||||
switch resp.Header.Get("Content-Encoding") {
|
switch resp.Header.Get("Content-Encoding") {
|
||||||
case "zstd":
|
case "zstd":
|
||||||
zstdReader, _ := zstd.NewReader(resp.Body)
|
reader, _ = zstd.NewReader(resp.Body)
|
||||||
defer zstdReader.Close()
|
|
||||||
|
|
||||||
reader = zstdReader
|
|
||||||
case "gzip":
|
case "gzip":
|
||||||
gzipReader, _ := gzip.NewReader(resp.Body)
|
reader, _ = gzip.NewReader(resp.Body)
|
||||||
defer gzipReader.Close()
|
|
||||||
|
|
||||||
reader = gzipReader
|
|
||||||
default:
|
default:
|
||||||
reader = resp.Body
|
reader = resp.Body
|
||||||
}
|
}
|
||||||
|
|||||||
48
vendor/github.com/SevereCloud/vksdk/v2/api/apps.go
generated
vendored
48
vendor/github.com/SevereCloud/vksdk/v2/api/apps.go
generated
vendored
@@ -4,14 +4,6 @@ import (
|
|||||||
"github.com/SevereCloud/vksdk/v2/object"
|
"github.com/SevereCloud/vksdk/v2/object"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AppsAddUsersToTestingGroup method.
|
|
||||||
//
|
|
||||||
// https://vk.com/dev/apps.addUsersToTestingGroup
|
|
||||||
func (vk *VK) AppsAddUsersToTestingGroup(params Params) (response int, err error) {
|
|
||||||
err = vk.RequestUnmarshal("apps.addUsersToTestingGroup", &response, params)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppsDeleteAppRequests deletes all request notifications from the current app.
|
// AppsDeleteAppRequests deletes all request notifications from the current app.
|
||||||
//
|
//
|
||||||
// https://vk.com/dev/apps.deleteAppRequests
|
// https://vk.com/dev/apps.deleteAppRequests
|
||||||
@@ -148,33 +140,6 @@ func (vk *VK) AppsGetScore(params Params) (response string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppsGetTestingGroupsResponse struct.
|
|
||||||
type AppsGetTestingGroupsResponse []object.AppsTestingGroup
|
|
||||||
|
|
||||||
// AppsGetTestingGroups method.
|
|
||||||
//
|
|
||||||
// https://vk.com/dev/apps.getTestingGroups
|
|
||||||
func (vk *VK) AppsGetTestingGroups(params Params) (response AppsGetTestingGroupsResponse, err error) {
|
|
||||||
err = vk.RequestUnmarshal("apps.getTestingGroups", &response, params)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppsRemoveTestingGroup method.
|
|
||||||
//
|
|
||||||
// https://vk.com/dev/apps.removeTestingGroup
|
|
||||||
func (vk *VK) AppsRemoveTestingGroup(params Params) (response int, err error) {
|
|
||||||
err = vk.RequestUnmarshal("apps.removeTestingGroup", &response, params)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppsRemoveUsersFromTestingGroups method.
|
|
||||||
//
|
|
||||||
// https://vk.com/dev/apps.removeUsersFromTestingGroups
|
|
||||||
func (vk *VK) AppsRemoveUsersFromTestingGroups(params Params) (response int, err error) {
|
|
||||||
err = vk.RequestUnmarshal("apps.removeUsersFromTestingGroups", &response, params)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppsSendRequest sends a request to another user in an app that uses VK authorization.
|
// AppsSendRequest sends a request to another user in an app that uses VK authorization.
|
||||||
//
|
//
|
||||||
// https://vk.com/dev/apps.sendRequest
|
// https://vk.com/dev/apps.sendRequest
|
||||||
@@ -182,16 +147,3 @@ func (vk *VK) AppsSendRequest(params Params) (response int, err error) {
|
|||||||
err = vk.RequestUnmarshal("apps.sendRequest", &response, params)
|
err = vk.RequestUnmarshal("apps.sendRequest", &response, params)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppsUpdateMetaForTestingGroupResponse struct.
|
|
||||||
type AppsUpdateMetaForTestingGroupResponse struct {
|
|
||||||
GroupID int `json:"group_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppsUpdateMetaForTestingGroup method.
|
|
||||||
//
|
|
||||||
// https://vk.com/dev/apps.updateMetaForTestingGroup
|
|
||||||
func (vk *VK) AppsUpdateMetaForTestingGroup(params Params) (response AppsUpdateMetaForTestingGroupResponse, err error) {
|
|
||||||
err = vk.RequestUnmarshal("apps.updateMetaForTestingGroup", &response, params)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
6
vendor/github.com/SevereCloud/vksdk/v2/api/errors.go
generated
vendored
6
vendor/github.com/SevereCloud/vksdk/v2/api/errors.go
generated
vendored
@@ -622,12 +622,6 @@ const (
|
|||||||
// Anonymous token is invalid.
|
// Anonymous token is invalid.
|
||||||
ErrAnonymousTokenInvalid ErrorType = 1116
|
ErrAnonymousTokenInvalid ErrorType = 1116
|
||||||
|
|
||||||
// Access token has expired.
|
|
||||||
ErrAuthAccessTokenHasExpired ErrorType = 1117
|
|
||||||
|
|
||||||
// Anonymous token ip mismatch.
|
|
||||||
ErrAuthAnonymousTokenIPMismatch ErrorType = 1118
|
|
||||||
|
|
||||||
// Invalid document id.
|
// Invalid document id.
|
||||||
ErrParamDocID ErrorType = 1150
|
ErrParamDocID ErrorType = 1150
|
||||||
|
|
||||||
|
|||||||
22
vendor/github.com/SevereCloud/vksdk/v2/api/messages.go
generated
vendored
22
vendor/github.com/SevereCloud/vksdk/v2/api/messages.go
generated
vendored
@@ -116,14 +116,6 @@ func (vk *VK) MessagesEditChat(params Params) (response int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessagesForceCallFinish method.
|
|
||||||
//
|
|
||||||
// https://vk.com/dev/messages.forceCallFinish
|
|
||||||
func (vk *VK) MessagesForceCallFinish(params Params) (response int, err error) {
|
|
||||||
err = vk.RequestUnmarshal("messages.forceCallFinish", &response, params)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessagesGetByConversationMessageIDResponse struct.
|
// MessagesGetByConversationMessageIDResponse struct.
|
||||||
type MessagesGetByConversationMessageIDResponse struct {
|
type MessagesGetByConversationMessageIDResponse struct {
|
||||||
Count int `json:"count"`
|
Count int `json:"count"`
|
||||||
@@ -641,20 +633,6 @@ func (vk *VK) MessagesSetChatPhoto(params Params) (response MessagesSetChatPhoto
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessagesStartCallResponse struct.
|
|
||||||
type MessagesStartCallResponse struct {
|
|
||||||
JoinLink string `json:"join_link"`
|
|
||||||
CallID string `json:"call_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessagesStartCall method.
|
|
||||||
//
|
|
||||||
// https://vk.com/dev/messages.startCall
|
|
||||||
func (vk *VK) MessagesStartCall(params Params) (response MessagesStartCallResponse, err error) {
|
|
||||||
err = vk.RequestUnmarshal("messages.startCall", &response, params)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessagesUnpin messages.unpin.
|
// MessagesUnpin messages.unpin.
|
||||||
//
|
//
|
||||||
// https://vk.com/dev/messages.unpin
|
// https://vk.com/dev/messages.unpin
|
||||||
|
|||||||
2
vendor/github.com/SevereCloud/vksdk/v2/api/store.go
generated
vendored
2
vendor/github.com/SevereCloud/vksdk/v2/api/store.go
generated
vendored
@@ -1,4 +1,4 @@
|
|||||||
package api // import "github.com/SevereCloud/vksdk/v2/api"
|
package api // import "github.com/SevereCloud/vksdk/api"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/SevereCloud/vksdk/v2/object"
|
"github.com/SevereCloud/vksdk/v2/object"
|
||||||
|
|||||||
35
vendor/github.com/SevereCloud/vksdk/v2/api/video.go
generated
vendored
35
vendor/github.com/SevereCloud/vksdk/v2/api/video.go
generated
vendored
@@ -235,17 +235,6 @@ func (vk *VK) VideoGetCommentsExtended(params Params) (response VideoGetComments
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// VideoLiveGetCategoriesResponse struct.
|
|
||||||
type VideoLiveGetCategoriesResponse []object.VideoLiveCategory
|
|
||||||
|
|
||||||
// VideoLiveGetCategories method.
|
|
||||||
//
|
|
||||||
// https://vk.com/dev/video.liveGetCategories
|
|
||||||
func (vk *VK) VideoLiveGetCategories(params Params) (response VideoLiveGetCategoriesResponse, err error) {
|
|
||||||
err = vk.RequestUnmarshal("video.liveGetCategories", &response, params)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoRemoveFromAlbum allows you to remove the video from the album.
|
// VideoRemoveFromAlbum allows you to remove the video from the album.
|
||||||
//
|
//
|
||||||
// https://vk.com/dev/video.removeFromAlbum
|
// https://vk.com/dev/video.removeFromAlbum
|
||||||
@@ -347,27 +336,3 @@ func (vk *VK) VideoSearchExtended(params Params) (response VideoSearchExtendedRe
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// VideoStartStreamingResponse struct.
|
|
||||||
type VideoStartStreamingResponse object.VideoLive
|
|
||||||
|
|
||||||
// VideoStartStreaming method.
|
|
||||||
//
|
|
||||||
// https://vk.com/dev/video.startStreaming
|
|
||||||
func (vk *VK) VideoStartStreaming(params Params) (response VideoStartStreamingResponse, err error) {
|
|
||||||
err = vk.RequestUnmarshal("video.startStreaming", &response, params)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoStopStreamingResponse struct.
|
|
||||||
type VideoStopStreamingResponse struct {
|
|
||||||
UniqueViewers int `json:"unique_viewers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoStopStreaming method.
|
|
||||||
//
|
|
||||||
// https://vk.com/dev/video.stopStreaming
|
|
||||||
func (vk *VK) VideoStopStreaming(params Params) (response VideoStopStreamingResponse, err error) {
|
|
||||||
err = vk.RequestUnmarshal("video.stopStreaming", &response, params)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
2
vendor/github.com/SevereCloud/vksdk/v2/doc.go
generated
vendored
2
vendor/github.com/SevereCloud/vksdk/v2/doc.go
generated
vendored
@@ -7,6 +7,6 @@ package vksdk
|
|||||||
|
|
||||||
// Module constants.
|
// Module constants.
|
||||||
const (
|
const (
|
||||||
Version = "2.15.0"
|
Version = "2.13.0"
|
||||||
API = "5.131"
|
API = "5.131"
|
||||||
)
|
)
|
||||||
|
|||||||
5
vendor/github.com/SevereCloud/vksdk/v2/events/context.go
generated
vendored
5
vendor/github.com/SevereCloud/vksdk/v2/events/context.go
generated
vendored
@@ -15,8 +15,3 @@ func GroupIDFromContext(ctx context.Context) int {
|
|||||||
func EventIDFromContext(ctx context.Context) string {
|
func EventIDFromContext(ctx context.Context) string {
|
||||||
return ctx.Value(internal.EventIDKey).(string)
|
return ctx.Value(internal.EventIDKey).(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VersionFromContext returns the version from context.
|
|
||||||
func VersionFromContext(ctx context.Context) string {
|
|
||||||
return ctx.Value(internal.EventVersionKey).(string)
|
|
||||||
}
|
|
||||||
|
|||||||
2
vendor/github.com/SevereCloud/vksdk/v2/events/events.go
generated
vendored
2
vendor/github.com/SevereCloud/vksdk/v2/events/events.go
generated
vendored
@@ -81,7 +81,6 @@ type GroupEvent struct {
|
|||||||
Object json.RawMessage `json:"object"`
|
Object json.RawMessage `json:"object"`
|
||||||
GroupID int `json:"group_id"`
|
GroupID int `json:"group_id"`
|
||||||
EventID string `json:"event_id"`
|
EventID string `json:"event_id"`
|
||||||
V string `json:"v"`
|
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +158,6 @@ func NewFuncList() *FuncList {
|
|||||||
func (fl FuncList) Handler(ctx context.Context, e GroupEvent) error { // nolint:gocyclo
|
func (fl FuncList) Handler(ctx context.Context, e GroupEvent) error { // nolint:gocyclo
|
||||||
ctx = context.WithValue(ctx, internal.GroupIDKey, e.GroupID)
|
ctx = context.WithValue(ctx, internal.GroupIDKey, e.GroupID)
|
||||||
ctx = context.WithValue(ctx, internal.EventIDKey, e.EventID)
|
ctx = context.WithValue(ctx, internal.EventIDKey, e.EventID)
|
||||||
ctx = context.WithValue(ctx, internal.EventVersionKey, e.V)
|
|
||||||
|
|
||||||
if sliceFunc, ok := fl.special[e.Type]; ok {
|
if sliceFunc, ok := fl.special[e.Type]; ok {
|
||||||
for _, f := range sliceFunc {
|
for _, f := range sliceFunc {
|
||||||
|
|||||||
1
vendor/github.com/SevereCloud/vksdk/v2/internal/transport.go
generated
vendored
1
vendor/github.com/SevereCloud/vksdk/v2/internal/transport.go
generated
vendored
@@ -28,7 +28,6 @@ const (
|
|||||||
CallbackRetryCounterKey
|
CallbackRetryCounterKey
|
||||||
CallbackRetryAfterKey
|
CallbackRetryAfterKey
|
||||||
CallbackRemove
|
CallbackRemove
|
||||||
EventVersionKey
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContextClient return *http.Client.
|
// ContextClient return *http.Client.
|
||||||
|
|||||||
1
vendor/github.com/SevereCloud/vksdk/v2/object/account.go
generated
vendored
1
vendor/github.com/SevereCloud/vksdk/v2/object/account.go
generated
vendored
@@ -84,6 +84,7 @@ type AccountAccountCounters struct {
|
|||||||
|
|
||||||
// AccountInfo struct.
|
// AccountInfo struct.
|
||||||
type AccountInfo struct {
|
type AccountInfo struct {
|
||||||
|
|
||||||
// Country code.
|
// Country code.
|
||||||
Country string `json:"country"`
|
Country string `json:"country"`
|
||||||
|
|
||||||
|
|||||||
9
vendor/github.com/SevereCloud/vksdk/v2/object/apps.go
generated
vendored
9
vendor/github.com/SevereCloud/vksdk/v2/object/apps.go
generated
vendored
@@ -100,12 +100,3 @@ type AppsScope struct {
|
|||||||
Name string `json:"name"` // Scope name
|
Name string `json:"name"` // Scope name
|
||||||
Title string `json:"title"` // Scope title
|
Title string `json:"title"` // Scope title
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppsTestingGroup testing group description.
|
|
||||||
type AppsTestingGroup struct {
|
|
||||||
GroupID int `json:"group_id"`
|
|
||||||
UserIDs []int `json:"user_ids"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Webview string `json:"webview"`
|
|
||||||
Platforms []string `json:"platforms"`
|
|
||||||
}
|
|
||||||
|
|||||||
10
vendor/github.com/SevereCloud/vksdk/v2/object/groups.go
generated
vendored
10
vendor/github.com/SevereCloud/vksdk/v2/object/groups.go
generated
vendored
@@ -210,7 +210,7 @@ type GroupsGroup struct {
|
|||||||
MainSection int `json:"main_section,omitempty"`
|
MainSection int `json:"main_section,omitempty"`
|
||||||
OnlineStatus GroupsOnlineStatus `json:"online_status,omitempty"` // Status of replies in community messages
|
OnlineStatus GroupsOnlineStatus `json:"online_status,omitempty"` // Status of replies in community messages
|
||||||
AgeLimits int `json:"age_limits,omitempty"` // Information whether age limit
|
AgeLimits int `json:"age_limits,omitempty"` // Information whether age limit
|
||||||
BanInfo *GroupsGroupBanInfo `json:"ban_info,omitempty"` // User ban info
|
BanInfo GroupsGroupBanInfo `json:"ban_info,omitempty"` // User ban info
|
||||||
Addresses GroupsAddressesInfo `json:"addresses,omitempty"` // Info about addresses in Groups
|
Addresses GroupsAddressesInfo `json:"addresses,omitempty"` // Info about addresses in Groups
|
||||||
LiveCovers GroupsLiveCovers `json:"live_covers,omitempty"`
|
LiveCovers GroupsLiveCovers `json:"live_covers,omitempty"`
|
||||||
CropPhoto UsersCropPhoto `json:"crop_photo,omitempty"`
|
CropPhoto UsersCropPhoto `json:"crop_photo,omitempty"`
|
||||||
@@ -963,10 +963,10 @@ type GroupsOnlineStatus struct {
|
|||||||
|
|
||||||
// GroupsOwnerXtrBanInfo struct.
|
// GroupsOwnerXtrBanInfo struct.
|
||||||
type GroupsOwnerXtrBanInfo struct {
|
type GroupsOwnerXtrBanInfo struct {
|
||||||
BanInfo *GroupsBanInfo `json:"ban_info"`
|
BanInfo GroupsBanInfo `json:"ban_info"`
|
||||||
Group GroupsGroup `json:"group"`
|
Group GroupsGroup `json:"group"`
|
||||||
Profile UsersUser `json:"profile"`
|
Profile UsersUser `json:"profile"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupsSubjectItem struct.
|
// GroupsSubjectItem struct.
|
||||||
|
|||||||
1
vendor/github.com/SevereCloud/vksdk/v2/object/messages.go
generated
vendored
1
vendor/github.com/SevereCloud/vksdk/v2/object/messages.go
generated
vendored
@@ -409,6 +409,7 @@ type MessageContentSource struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
MessageContentSourceMessage // type message
|
MessageContentSourceMessage // type message
|
||||||
MessageContentSourceURL // type url
|
MessageContentSourceURL // type url
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMessageContentSourceMessage ...
|
// NewMessageContentSourceMessage ...
|
||||||
|
|||||||
211
vendor/github.com/SevereCloud/vksdk/v2/object/users.go
generated
vendored
211
vendor/github.com/SevereCloud/vksdk/v2/object/users.go
generated
vendored
@@ -24,113 +24,110 @@ const (
|
|||||||
|
|
||||||
// UsersUser struct.
|
// UsersUser struct.
|
||||||
type UsersUser struct {
|
type UsersUser struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
FirstNameNom string `json:"first_name_nom"`
|
FirstNameNom string `json:"first_name_nom"`
|
||||||
FirstNameGen string `json:"first_name_gen"`
|
FirstNameGen string `json:"first_name_gen"`
|
||||||
FirstNameDat string `json:"first_name_dat"`
|
FirstNameDat string `json:"first_name_dat"`
|
||||||
FirstNameAcc string `json:"first_name_acc"`
|
FirstNameAcc string `json:"first_name_acc"`
|
||||||
FirstNameIns string `json:"first_name_ins"`
|
FirstNameIns string `json:"first_name_ins"`
|
||||||
FirstNameAbl string `json:"first_name_abl"`
|
FirstNameAbl string `json:"first_name_abl"`
|
||||||
LastNameNom string `json:"last_name_nom"`
|
LastNameNom string `json:"last_name_nom"`
|
||||||
LastNameGen string `json:"last_name_gen"`
|
LastNameGen string `json:"last_name_gen"`
|
||||||
LastNameDat string `json:"last_name_dat"`
|
LastNameDat string `json:"last_name_dat"`
|
||||||
LastNameAcc string `json:"last_name_acc"`
|
LastNameAcc string `json:"last_name_acc"`
|
||||||
LastNameIns string `json:"last_name_ins"`
|
LastNameIns string `json:"last_name_ins"`
|
||||||
LastNameAbl string `json:"last_name_abl"`
|
LastNameAbl string `json:"last_name_abl"`
|
||||||
MaidenName string `json:"maiden_name"`
|
MaidenName string `json:"maiden_name"`
|
||||||
Sex int `json:"sex"`
|
Sex int `json:"sex"`
|
||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname"`
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
ScreenName string `json:"screen_name"`
|
ScreenName string `json:"screen_name"`
|
||||||
Bdate string `json:"bdate"`
|
Bdate string `json:"bdate"`
|
||||||
City BaseObject `json:"city"`
|
City BaseObject `json:"city"`
|
||||||
Country BaseObject `json:"country"`
|
Country BaseObject `json:"country"`
|
||||||
Photo50 string `json:"photo_50"`
|
Photo50 string `json:"photo_50"`
|
||||||
Photo100 string `json:"photo_100"`
|
Photo100 string `json:"photo_100"`
|
||||||
Photo200 string `json:"photo_200"`
|
Photo200 string `json:"photo_200"`
|
||||||
PhotoMax string `json:"photo_max"`
|
PhotoMax string `json:"photo_max"`
|
||||||
Photo200Orig string `json:"photo_200_orig"`
|
Photo200Orig string `json:"photo_200_orig"`
|
||||||
Photo400Orig string `json:"photo_400_orig"`
|
Photo400Orig string `json:"photo_400_orig"`
|
||||||
PhotoMaxOrig string `json:"photo_max_orig"`
|
PhotoMaxOrig string `json:"photo_max_orig"`
|
||||||
PhotoID string `json:"photo_id"`
|
PhotoID string `json:"photo_id"`
|
||||||
FriendStatus int `json:"friend_status"` // see FriendStatus const
|
FriendStatus int `json:"friend_status"` // see FriendStatus const
|
||||||
OnlineApp int `json:"online_app"`
|
OnlineApp int `json:"online_app"`
|
||||||
Online BaseBoolInt `json:"online"`
|
Online BaseBoolInt `json:"online"`
|
||||||
OnlineMobile BaseBoolInt `json:"online_mobile"`
|
OnlineMobile BaseBoolInt `json:"online_mobile"`
|
||||||
HasPhoto BaseBoolInt `json:"has_photo"`
|
HasPhoto BaseBoolInt `json:"has_photo"`
|
||||||
HasMobile BaseBoolInt `json:"has_mobile"`
|
HasMobile BaseBoolInt `json:"has_mobile"`
|
||||||
IsClosed BaseBoolInt `json:"is_closed"`
|
IsClosed BaseBoolInt `json:"is_closed"`
|
||||||
IsFriend BaseBoolInt `json:"is_friend"`
|
IsFriend BaseBoolInt `json:"is_friend"`
|
||||||
IsFavorite BaseBoolInt `json:"is_favorite"`
|
IsFavorite BaseBoolInt `json:"is_favorite"`
|
||||||
IsHiddenFromFeed BaseBoolInt `json:"is_hidden_from_feed"`
|
IsHiddenFromFeed BaseBoolInt `json:"is_hidden_from_feed"`
|
||||||
CanAccessClosed BaseBoolInt `json:"can_access_closed"`
|
CanAccessClosed BaseBoolInt `json:"can_access_closed"`
|
||||||
CanBeInvitedGroup BaseBoolInt `json:"can_be_invited_group"`
|
CanBeInvitedGroup BaseBoolInt `json:"can_be_invited_group"`
|
||||||
CanPost BaseBoolInt `json:"can_post"`
|
CanPost BaseBoolInt `json:"can_post"`
|
||||||
CanSeeAllPosts BaseBoolInt `json:"can_see_all_posts"`
|
CanSeeAllPosts BaseBoolInt `json:"can_see_all_posts"`
|
||||||
CanSeeAudio BaseBoolInt `json:"can_see_audio"`
|
CanSeeAudio BaseBoolInt `json:"can_see_audio"`
|
||||||
CanWritePrivateMessage BaseBoolInt `json:"can_write_private_message"`
|
CanWritePrivateMessage BaseBoolInt `json:"can_write_private_message"`
|
||||||
CanSendFriendRequest BaseBoolInt `json:"can_send_friend_request"`
|
CanSendFriendRequest BaseBoolInt `json:"can_send_friend_request"`
|
||||||
CanCallFromGroup BaseBoolInt `json:"can_call_from_group"`
|
CanCallFromGroup BaseBoolInt `json:"can_call_from_group"`
|
||||||
Verified BaseBoolInt `json:"verified"`
|
Verified BaseBoolInt `json:"verified"`
|
||||||
Trending BaseBoolInt `json:"trending"`
|
Trending BaseBoolInt `json:"trending"`
|
||||||
Blacklisted BaseBoolInt `json:"blacklisted"`
|
Blacklisted BaseBoolInt `json:"blacklisted"`
|
||||||
BlacklistedByMe BaseBoolInt `json:"blacklisted_by_me"`
|
BlacklistedByMe BaseBoolInt `json:"blacklisted_by_me"`
|
||||||
// Deprecated: Facebook и Instagram запрещены в России, Meta признана экстремистской организацией...
|
Facebook string `json:"facebook"`
|
||||||
Facebook string `json:"facebook"`
|
FacebookName string `json:"facebook_name"`
|
||||||
// Deprecated: Facebook и Instagram запрещены в России, Meta признана экстремистской организацией...
|
Twitter string `json:"twitter"`
|
||||||
FacebookName string `json:"facebook_name"`
|
Instagram string `json:"instagram"`
|
||||||
// Deprecated: Facebook и Instagram запрещены в России, Meta признана экстремистской организацией...
|
Site string `json:"site"`
|
||||||
Instagram string `json:"instagram"`
|
Status string `json:"status"`
|
||||||
Twitter string `json:"twitter"`
|
StatusAudio AudioAudio `json:"status_audio"`
|
||||||
Site string `json:"site"`
|
LastSeen UsersLastSeen `json:"last_seen"`
|
||||||
Status string `json:"status"`
|
CropPhoto UsersCropPhoto `json:"crop_photo"`
|
||||||
StatusAudio AudioAudio `json:"status_audio"`
|
FollowersCount int `json:"followers_count"`
|
||||||
LastSeen UsersLastSeen `json:"last_seen"`
|
CommonCount int `json:"common_count"`
|
||||||
CropPhoto UsersCropPhoto `json:"crop_photo"`
|
Occupation UsersOccupation `json:"occupation"`
|
||||||
FollowersCount int `json:"followers_count"`
|
Career []UsersCareer `json:"career"`
|
||||||
CommonCount int `json:"common_count"`
|
Military []UsersMilitary `json:"military"`
|
||||||
Occupation UsersOccupation `json:"occupation"`
|
University int `json:"university"`
|
||||||
Career []UsersCareer `json:"career"`
|
UniversityName string `json:"university_name"`
|
||||||
Military []UsersMilitary `json:"military"`
|
Faculty int `json:"faculty"`
|
||||||
University int `json:"university"`
|
FacultyName string `json:"faculty_name"`
|
||||||
UniversityName string `json:"university_name"`
|
Graduation int `json:"graduation"`
|
||||||
Faculty int `json:"faculty"`
|
EducationForm string `json:"education_form"`
|
||||||
FacultyName string `json:"faculty_name"`
|
EducationStatus string `json:"education_status"`
|
||||||
Graduation int `json:"graduation"`
|
HomeTown string `json:"home_town"`
|
||||||
EducationForm string `json:"education_form"`
|
Relation int `json:"relation"`
|
||||||
EducationStatus string `json:"education_status"`
|
Personal UsersPersonal `json:"personal"`
|
||||||
HomeTown string `json:"home_town"`
|
Interests string `json:"interests"`
|
||||||
Relation int `json:"relation"`
|
Music string `json:"music"`
|
||||||
Personal UsersPersonal `json:"personal"`
|
Activities string `json:"activities"`
|
||||||
Interests string `json:"interests"`
|
Movies string `json:"movies"`
|
||||||
Music string `json:"music"`
|
Tv string `json:"tv"`
|
||||||
Activities string `json:"activities"`
|
Books string `json:"books"`
|
||||||
Movies string `json:"movies"`
|
Games string `json:"games"`
|
||||||
Tv string `json:"tv"`
|
Universities []UsersUniversity `json:"universities"`
|
||||||
Books string `json:"books"`
|
Schools []UsersSchool `json:"schools"`
|
||||||
Games string `json:"games"`
|
About string `json:"about"`
|
||||||
Universities []UsersUniversity `json:"universities"`
|
Relatives []UsersRelative `json:"relatives"`
|
||||||
Schools []UsersSchool `json:"schools"`
|
Quotes string `json:"quotes"`
|
||||||
About string `json:"about"`
|
Lists []int `json:"lists"`
|
||||||
Relatives []UsersRelative `json:"relatives"`
|
Deactivated string `json:"deactivated"`
|
||||||
Quotes string `json:"quotes"`
|
WallDefault string `json:"wall_default"`
|
||||||
Lists []int `json:"lists"`
|
Timezone int `json:"timezone"`
|
||||||
Deactivated string `json:"deactivated"`
|
Exports UsersExports `json:"exports"`
|
||||||
WallDefault string `json:"wall_default"`
|
Counters UsersUserCounters `json:"counters"`
|
||||||
Timezone int `json:"timezone"`
|
MobilePhone string `json:"mobile_phone"`
|
||||||
Exports UsersExports `json:"exports"`
|
HomePhone string `json:"home_phone"`
|
||||||
Counters UsersUserCounters `json:"counters"`
|
FoundWith int `json:"found_with"` // TODO: check it
|
||||||
MobilePhone string `json:"mobile_phone"`
|
OnlineInfo UsersOnlineInfo `json:"online_info"`
|
||||||
HomePhone string `json:"home_phone"`
|
Mutual FriendsRequestsMutual `json:"mutual"`
|
||||||
FoundWith int `json:"found_with"` // TODO: check it
|
TrackCode string `json:"track_code"`
|
||||||
OnlineInfo UsersOnlineInfo `json:"online_info"`
|
RelationPartner UsersUserMin `json:"relation_partner"`
|
||||||
Mutual FriendsRequestsMutual `json:"mutual"`
|
Type string `json:"type"`
|
||||||
TrackCode string `json:"track_code"`
|
Skype string `json:"skype"`
|
||||||
RelationPartner UsersUserMin `json:"relation_partner"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Skype string `json:"skype"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToMention return mention.
|
// ToMention return mention.
|
||||||
|
|||||||
26
vendor/github.com/SevereCloud/vksdk/v2/object/video.go
generated
vendored
26
vendor/github.com/SevereCloud/vksdk/v2/object/video.go
generated
vendored
@@ -31,7 +31,7 @@ type VideoVideo struct {
|
|||||||
CanLike BaseBoolInt `json:"can_like"`
|
CanLike BaseBoolInt `json:"can_like"`
|
||||||
|
|
||||||
// Information whether current user can download the video.
|
// Information whether current user can download the video.
|
||||||
CanDownload int `json:"can_download"`
|
CanDownload BaseBoolInt `json:"can_download"`
|
||||||
|
|
||||||
// Information whether current user can repost this video.
|
// Information whether current user can repost this video.
|
||||||
CanRepost BaseBoolInt `json:"can_repost"`
|
CanRepost BaseBoolInt `json:"can_repost"`
|
||||||
@@ -297,27 +297,3 @@ type VideoVideoImage struct {
|
|||||||
BaseImage
|
BaseImage
|
||||||
WithPadding BaseBoolInt `json:"with_padding"`
|
WithPadding BaseBoolInt `json:"with_padding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// VideoLive struct.
|
|
||||||
type VideoLive struct {
|
|
||||||
OwnerID int `json:"owner_id"`
|
|
||||||
VideoID int `json:"video_id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
AccessKey string `json:"access_key"`
|
|
||||||
Stream VideoLiveStream `json:"stream"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoLiveStream struct.
|
|
||||||
type VideoLiveStream struct {
|
|
||||||
URL string `json:"url"`
|
|
||||||
Key string `json:"key"`
|
|
||||||
OKMPURL string `json:"okmp_url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoLiveCategory struct.
|
|
||||||
type VideoLiveCategory struct {
|
|
||||||
ID int `json:"id"`
|
|
||||||
Label string `json:"label"`
|
|
||||||
Sublist []VideoLiveCategory `json:"sublist,omitempty"`
|
|
||||||
}
|
|
||||||
|
|||||||
5
vendor/github.com/bwmarrin/discordgo/.gitignore
generated
vendored
5
vendor/github.com/bwmarrin/discordgo/.gitignore
generated
vendored
@@ -1,5 +0,0 @@
|
|||||||
# IDE-specific metadata
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# Environment variables. Useful for examples.
|
|
||||||
.env
|
|
||||||
19
vendor/github.com/bwmarrin/discordgo/.golangci.yml
generated
vendored
19
vendor/github.com/bwmarrin/discordgo/.golangci.yml
generated
vendored
@@ -1,19 +0,0 @@
|
|||||||
linters:
|
|
||||||
disable-all: true
|
|
||||||
enable:
|
|
||||||
# - staticcheck
|
|
||||||
# - unused
|
|
||||||
- golint
|
|
||||||
|
|
||||||
linters-settings:
|
|
||||||
staticcheck:
|
|
||||||
go: "1.13"
|
|
||||||
|
|
||||||
checks: ["all"]
|
|
||||||
|
|
||||||
unused:
|
|
||||||
go: "1.13"
|
|
||||||
|
|
||||||
issues:
|
|
||||||
include:
|
|
||||||
- EXC0002
|
|
||||||
87
vendor/github.com/bwmarrin/discordgo/CONTRIBUTING.md
generated
vendored
87
vendor/github.com/bwmarrin/discordgo/CONTRIBUTING.md
generated
vendored
@@ -1,87 +0,0 @@
|
|||||||
# Getting started
|
|
||||||
|
|
||||||
To start off you can check out existing Pull Requests and Issues to get a gasp of what problems we’re currently solving and what features you can implement.
|
|
||||||
|
|
||||||
## Issues
|
|
||||||
|
|
||||||
Our issues are mostly used for bugs, however we welcome refactoring and conceptual issues.
|
|
||||||
|
|
||||||
Any other conversation would belong and would be moved into “Discussions”.
|
|
||||||
|
|
||||||
## Discussions
|
|
||||||
|
|
||||||
We use discussions for ideas, polls, announcements and help questions.
|
|
||||||
|
|
||||||
Don’t hesitate to ask, we always would try to help.
|
|
||||||
|
|
||||||
## Pull Requests
|
|
||||||
|
|
||||||
If you want to help us by improving existing or adding new features, you create what’s called a Pull Request (aka PR). It allows us to review your code, suggest changes and merge it.
|
|
||||||
|
|
||||||
Here are some tips on how to make a good first PR:
|
|
||||||
|
|
||||||
- When creating a PR, please consider a distinctive name and description for it, so the maintainers can understand what your PR changes / adds / removes.
|
|
||||||
- It’s always a good idea to link documentation when implementing a new feature / endpoint
|
|
||||||
- If you’re resolving an issue, don’t forget to [link it](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) in the description.
|
|
||||||
- Enable the checkbox to allow maintainers to edit your PR and make commits in the PR branch when necessary.
|
|
||||||
- We may ask for changes, usually through suggestions or pull request comments. You can apply suggestions right in the UI. Any other change needs to be done manually.
|
|
||||||
- Don’t forget to mark PR comments resolved when you’re done applying the changes.
|
|
||||||
- Be patient and don’t close and reopen your PR when no one responds, sometimes it might be held for a while. There might be a lot of reasons: release preparation, the feature is not significant, maintainers are busy, etc.
|
|
||||||
|
|
||||||
|
|
||||||
When your changes are still incomplete (i.e. in Work In Progress state), you can still create a PR, but consider making it a draft.
|
|
||||||
To make a draft PR, you can change the type of PR by clicking to a triangle next to the “Create Pull Request” button.
|
|
||||||
|
|
||||||
Once you’re done, you can mark it as “Ready for review”, and we’ll get right on it.
|
|
||||||
|
|
||||||
|
|
||||||
# Code style
|
|
||||||
|
|
||||||
To standardize and make things less messy we have a certain code style, that is persistent throughout the codebase.
|
|
||||||
|
|
||||||
## Naming
|
|
||||||
|
|
||||||
### REST methods
|
|
||||||
|
|
||||||
When naming a REST method, while it might seem counterintuitive, we specify the entity before the action verb (for GET endpoints we don’t specify one however). Here’s an example:
|
|
||||||
|
|
||||||
> Endpoint name: Get Channel Message
|
|
||||||
>
|
|
||||||
> Method name: `ChannelMessage`
|
|
||||||
|
|
||||||
> Endpoint name: Edit Channel Message
|
|
||||||
>
|
|
||||||
> Method name: `ChannelMessageEdit`
|
|
||||||
|
|
||||||
### Parameter structures
|
|
||||||
|
|
||||||
When making a complex REST endpoint, sometimes you might need to implement a `Param` structure. This structure contains parameters for certain endpoint/set of endpoints.
|
|
||||||
|
|
||||||
- If an endpoint/set of endpoints have mostly same parameters, it’s a good idea to use a single `Param` structure for them. Here’s an example:
|
|
||||||
|
|
||||||
> Endpoint: `GuildMemberEdit`
|
|
||||||
>
|
|
||||||
> `Param` structure: `GuildMemberParams`
|
|
||||||
- If an endpoint/set of endpoints have differentiating parameters, `Param` structure can be named after the endpoint’s verb. Here’s an example:
|
|
||||||
|
|
||||||
> Endpoint: `ChannelMessageSendComplex`
|
|
||||||
>
|
|
||||||
> `Param` structure: `MessageSend`
|
|
||||||
|
|
||||||
> Endpoint: `ChannelMessageEditComplex`
|
|
||||||
>
|
|
||||||
> `Param` structure: `MessageEdit`
|
|
||||||
|
|
||||||
### Events
|
|
||||||
|
|
||||||
When naming an event, we follow gateway’s internal naming (which often matches with the official event name in the docs). Here’s an example:
|
|
||||||
|
|
||||||
> Event name: Interaction Create (`INTERACTION_CREATE`)
|
|
||||||
>
|
|
||||||
> Structure name: `InteractionCreate`
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
|
|
||||||
In our REST functions we usually favor named returns instead of regular anonymous returns. This helps readability.
|
|
||||||
|
|
||||||
Additionally we try to avoid naked return statements for functions with a long body. Since it’s easier to loose track of the return result.
|
|
||||||
267
vendor/github.com/bwmarrin/discordgo/components.go
generated
vendored
267
vendor/github.com/bwmarrin/discordgo/components.go
generated
vendored
@@ -1,267 +0,0 @@
|
|||||||
package discordgo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ComponentType is type of component.
|
|
||||||
type ComponentType uint
|
|
||||||
|
|
||||||
// MessageComponent types.
|
|
||||||
const (
|
|
||||||
ActionsRowComponent ComponentType = 1
|
|
||||||
ButtonComponent ComponentType = 2
|
|
||||||
SelectMenuComponent ComponentType = 3
|
|
||||||
TextInputComponent ComponentType = 4
|
|
||||||
UserSelectMenuComponent ComponentType = 5
|
|
||||||
RoleSelectMenuComponent ComponentType = 6
|
|
||||||
MentionableSelectMenuComponent ComponentType = 7
|
|
||||||
ChannelSelectMenuComponent ComponentType = 8
|
|
||||||
)
|
|
||||||
|
|
||||||
// MessageComponent is a base interface for all message components.
|
|
||||||
type MessageComponent interface {
|
|
||||||
json.Marshaler
|
|
||||||
Type() ComponentType
|
|
||||||
}
|
|
||||||
|
|
||||||
type unmarshalableMessageComponent struct {
|
|
||||||
MessageComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON is a helper function to unmarshal MessageComponent object.
|
|
||||||
func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error {
|
|
||||||
var v struct {
|
|
||||||
Type ComponentType `json:"type"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(src, &v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v.Type {
|
|
||||||
case ActionsRowComponent:
|
|
||||||
umc.MessageComponent = &ActionsRow{}
|
|
||||||
case ButtonComponent:
|
|
||||||
umc.MessageComponent = &Button{}
|
|
||||||
case SelectMenuComponent, ChannelSelectMenuComponent, UserSelectMenuComponent,
|
|
||||||
RoleSelectMenuComponent, MentionableSelectMenuComponent:
|
|
||||||
umc.MessageComponent = &SelectMenu{}
|
|
||||||
case TextInputComponent:
|
|
||||||
umc.MessageComponent = &TextInput{}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown component type: %d", v.Type)
|
|
||||||
}
|
|
||||||
return json.Unmarshal(src, umc.MessageComponent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageComponentFromJSON is a helper function for unmarshaling message components
|
|
||||||
func MessageComponentFromJSON(b []byte) (MessageComponent, error) {
|
|
||||||
var u unmarshalableMessageComponent
|
|
||||||
err := u.UnmarshalJSON(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to unmarshal into MessageComponent: %w", err)
|
|
||||||
}
|
|
||||||
return u.MessageComponent, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActionsRow is a container for components within one row.
|
|
||||||
type ActionsRow struct {
|
|
||||||
Components []MessageComponent `json:"components"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON is a method for marshaling ActionsRow to a JSON object.
|
|
||||||
func (r ActionsRow) MarshalJSON() ([]byte, error) {
|
|
||||||
type actionsRow ActionsRow
|
|
||||||
|
|
||||||
return Marshal(struct {
|
|
||||||
actionsRow
|
|
||||||
Type ComponentType `json:"type"`
|
|
||||||
}{
|
|
||||||
actionsRow: actionsRow(r),
|
|
||||||
Type: r.Type(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON is a helper function to unmarshal Actions Row.
|
|
||||||
func (r *ActionsRow) UnmarshalJSON(data []byte) error {
|
|
||||||
var v struct {
|
|
||||||
RawComponents []unmarshalableMessageComponent `json:"components"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(data, &v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r.Components = make([]MessageComponent, len(v.RawComponents))
|
|
||||||
for i, v := range v.RawComponents {
|
|
||||||
r.Components[i] = v.MessageComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type is a method to get the type of a component.
|
|
||||||
func (r ActionsRow) Type() ComponentType {
|
|
||||||
return ActionsRowComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
// ButtonStyle is style of button.
|
|
||||||
type ButtonStyle uint
|
|
||||||
|
|
||||||
// Button styles.
|
|
||||||
const (
|
|
||||||
// PrimaryButton is a button with blurple color.
|
|
||||||
PrimaryButton ButtonStyle = 1
|
|
||||||
// SecondaryButton is a button with grey color.
|
|
||||||
SecondaryButton ButtonStyle = 2
|
|
||||||
// SuccessButton is a button with green color.
|
|
||||||
SuccessButton ButtonStyle = 3
|
|
||||||
// DangerButton is a button with red color.
|
|
||||||
DangerButton ButtonStyle = 4
|
|
||||||
// LinkButton is a special type of button which navigates to a URL. Has grey color.
|
|
||||||
LinkButton ButtonStyle = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
// ComponentEmoji represents button emoji, if it does have one.
|
|
||||||
type ComponentEmoji struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
Animated bool `json:"animated,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Button represents button component.
|
|
||||||
type Button struct {
|
|
||||||
Label string `json:"label"`
|
|
||||||
Style ButtonStyle `json:"style"`
|
|
||||||
Disabled bool `json:"disabled"`
|
|
||||||
Emoji ComponentEmoji `json:"emoji"`
|
|
||||||
|
|
||||||
// NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID.
|
|
||||||
URL string `json:"url,omitempty"`
|
|
||||||
CustomID string `json:"custom_id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON is a method for marshaling Button to a JSON object.
|
|
||||||
func (b Button) MarshalJSON() ([]byte, error) {
|
|
||||||
type button Button
|
|
||||||
|
|
||||||
if b.Style == 0 {
|
|
||||||
b.Style = PrimaryButton
|
|
||||||
}
|
|
||||||
|
|
||||||
return Marshal(struct {
|
|
||||||
button
|
|
||||||
Type ComponentType `json:"type"`
|
|
||||||
}{
|
|
||||||
button: button(b),
|
|
||||||
Type: b.Type(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type is a method to get the type of a component.
|
|
||||||
func (Button) Type() ComponentType {
|
|
||||||
return ButtonComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectMenuOption represents an option for a select menu.
|
|
||||||
type SelectMenuOption struct {
|
|
||||||
Label string `json:"label,omitempty"`
|
|
||||||
Value string `json:"value"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Emoji ComponentEmoji `json:"emoji"`
|
|
||||||
// Determines whenever option is selected by default or not.
|
|
||||||
Default bool `json:"default"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectMenuType represents select menu type.
|
|
||||||
type SelectMenuType ComponentType
|
|
||||||
|
|
||||||
// SelectMenu types.
|
|
||||||
const (
|
|
||||||
StringSelectMenu = SelectMenuType(SelectMenuComponent)
|
|
||||||
UserSelectMenu = SelectMenuType(UserSelectMenuComponent)
|
|
||||||
RoleSelectMenu = SelectMenuType(RoleSelectMenuComponent)
|
|
||||||
MentionableSelectMenu = SelectMenuType(MentionableSelectMenuComponent)
|
|
||||||
ChannelSelectMenu = SelectMenuType(ChannelSelectMenuComponent)
|
|
||||||
)
|
|
||||||
|
|
||||||
// SelectMenu represents select menu component.
|
|
||||||
type SelectMenu struct {
|
|
||||||
// Type of the select menu.
|
|
||||||
MenuType SelectMenuType `json:"type,omitempty"`
|
|
||||||
// CustomID is a developer-defined identifier for the select menu.
|
|
||||||
CustomID string `json:"custom_id,omitempty"`
|
|
||||||
// The text which will be shown in the menu if there's no default options or all options was deselected and component was closed.
|
|
||||||
Placeholder string `json:"placeholder"`
|
|
||||||
// This value determines the minimal amount of selected items in the menu.
|
|
||||||
MinValues *int `json:"min_values,omitempty"`
|
|
||||||
// This value determines the maximal amount of selected items in the menu.
|
|
||||||
// If MaxValues or MinValues are greater than one then the user can select multiple items in the component.
|
|
||||||
MaxValues int `json:"max_values,omitempty"`
|
|
||||||
Options []SelectMenuOption `json:"options,omitempty"`
|
|
||||||
Disabled bool `json:"disabled"`
|
|
||||||
|
|
||||||
// NOTE: Can only be used in SelectMenu with Channel menu type.
|
|
||||||
ChannelTypes []ChannelType `json:"channel_types,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type is a method to get the type of a component.
|
|
||||||
func (s SelectMenu) Type() ComponentType {
|
|
||||||
if s.MenuType != 0 {
|
|
||||||
return ComponentType(s.MenuType)
|
|
||||||
}
|
|
||||||
return SelectMenuComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON is a method for marshaling SelectMenu to a JSON object.
|
|
||||||
func (s SelectMenu) MarshalJSON() ([]byte, error) {
|
|
||||||
type selectMenu SelectMenu
|
|
||||||
|
|
||||||
return Marshal(struct {
|
|
||||||
selectMenu
|
|
||||||
Type ComponentType `json:"type"`
|
|
||||||
}{
|
|
||||||
selectMenu: selectMenu(s),
|
|
||||||
Type: s.Type(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextInput represents text input component.
|
|
||||||
type TextInput struct {
|
|
||||||
CustomID string `json:"custom_id"`
|
|
||||||
Label string `json:"label"`
|
|
||||||
Style TextInputStyle `json:"style"`
|
|
||||||
Placeholder string `json:"placeholder,omitempty"`
|
|
||||||
Value string `json:"value,omitempty"`
|
|
||||||
Required bool `json:"required"`
|
|
||||||
MinLength int `json:"min_length,omitempty"`
|
|
||||||
MaxLength int `json:"max_length,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type is a method to get the type of a component.
|
|
||||||
func (TextInput) Type() ComponentType {
|
|
||||||
return TextInputComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON is a method for marshaling TextInput to a JSON object.
|
|
||||||
func (m TextInput) MarshalJSON() ([]byte, error) {
|
|
||||||
type inputText TextInput
|
|
||||||
|
|
||||||
return Marshal(struct {
|
|
||||||
inputText
|
|
||||||
Type ComponentType `json:"type"`
|
|
||||||
}{
|
|
||||||
inputText: inputText(m),
|
|
||||||
Type: m.Type(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextInputStyle is style of text in TextInput component.
|
|
||||||
type TextInputStyle uint
|
|
||||||
|
|
||||||
// Text styles
|
|
||||||
const (
|
|
||||||
TextInputShort TextInputStyle = 1
|
|
||||||
TextInputParagraph TextInputStyle = 2
|
|
||||||
)
|
|
||||||
63
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
63
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
@@ -1,63 +0,0 @@
|
|||||||
// Discordgo - Discord bindings for Go
|
|
||||||
// Available at https://github.com/bwmarrin/discordgo
|
|
||||||
|
|
||||||
// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This file contains high level helper functions and easy entry points for the
|
|
||||||
// entire discordgo package. These functions are being developed and are very
|
|
||||||
// experimental at this point. They will most likely change so please use the
|
|
||||||
// low level functions if that's a problem.
|
|
||||||
|
|
||||||
// Package discordgo provides Discord binding for Go
|
|
||||||
package discordgo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
|
|
||||||
const VERSION = "0.27.0"
|
|
||||||
|
|
||||||
// New creates a new Discord session with provided token.
|
|
||||||
// If the token is for a bot, it must be prefixed with "Bot "
|
|
||||||
// e.g. "Bot ..."
|
|
||||||
// Or if it is an OAuth2 token, it must be prefixed with "Bearer "
|
|
||||||
// e.g. "Bearer ..."
|
|
||||||
func New(token string) (s *Session, err error) {
|
|
||||||
|
|
||||||
// Create an empty Session interface.
|
|
||||||
s = &Session{
|
|
||||||
State: NewState(),
|
|
||||||
Ratelimiter: NewRatelimiter(),
|
|
||||||
StateEnabled: true,
|
|
||||||
Compress: true,
|
|
||||||
ShouldReconnectOnError: true,
|
|
||||||
ShouldRetryOnRateLimit: true,
|
|
||||||
ShardID: 0,
|
|
||||||
ShardCount: 1,
|
|
||||||
MaxRestRetries: 3,
|
|
||||||
Client: &http.Client{Timeout: (20 * time.Second)},
|
|
||||||
Dialer: websocket.DefaultDialer,
|
|
||||||
UserAgent: "DiscordBot (https://github.com/bwmarrin/discordgo, v" + VERSION + ")",
|
|
||||||
sequence: new(int64),
|
|
||||||
LastHeartbeatAck: time.Now().UTC(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the Identify Package with defaults
|
|
||||||
// These can be modified prior to calling Open()
|
|
||||||
s.Identify.Compress = true
|
|
||||||
s.Identify.LargeThreshold = 250
|
|
||||||
s.Identify.Properties.OS = runtime.GOOS
|
|
||||||
s.Identify.Properties.Browser = "DiscordGo v" + VERSION
|
|
||||||
s.Identify.Intents = IntentsAllWithoutPrivileged
|
|
||||||
s.Identify.Token = token
|
|
||||||
s.Token = token
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
218
vendor/github.com/bwmarrin/discordgo/endpoints.go
generated
vendored
218
vendor/github.com/bwmarrin/discordgo/endpoints.go
generated
vendored
@@ -1,218 +0,0 @@
|
|||||||
// Discordgo - Discord bindings for Go
|
|
||||||
// Available at https://github.com/bwmarrin/discordgo
|
|
||||||
|
|
||||||
// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This file contains variables for all known Discord end points. All functions
|
|
||||||
// throughout the Discordgo package use these variables for all connections
|
|
||||||
// to Discord. These are all exported and you may modify them if needed.
|
|
||||||
|
|
||||||
package discordgo
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
// APIVersion is the Discord API version used for the REST and Websocket API.
|
|
||||||
var APIVersion = "9"
|
|
||||||
|
|
||||||
// Known Discord API Endpoints.
|
|
||||||
var (
|
|
||||||
EndpointStatus = "https://status.discord.com/api/v2/"
|
|
||||||
EndpointSm = EndpointStatus + "scheduled-maintenances/"
|
|
||||||
EndpointSmActive = EndpointSm + "active.json"
|
|
||||||
EndpointSmUpcoming = EndpointSm + "upcoming.json"
|
|
||||||
|
|
||||||
EndpointDiscord = "https://discord.com/"
|
|
||||||
EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/"
|
|
||||||
EndpointGuilds = EndpointAPI + "guilds/"
|
|
||||||
EndpointChannels = EndpointAPI + "channels/"
|
|
||||||
EndpointUsers = EndpointAPI + "users/"
|
|
||||||
EndpointGateway = EndpointAPI + "gateway"
|
|
||||||
EndpointGatewayBot = EndpointGateway + "/bot"
|
|
||||||
EndpointWebhooks = EndpointAPI + "webhooks/"
|
|
||||||
EndpointStickers = EndpointAPI + "stickers/"
|
|
||||||
EndpointStageInstances = EndpointAPI + "stage-instances"
|
|
||||||
|
|
||||||
EndpointCDN = "https://cdn.discordapp.com/"
|
|
||||||
EndpointCDNAttachments = EndpointCDN + "attachments/"
|
|
||||||
EndpointCDNAvatars = EndpointCDN + "avatars/"
|
|
||||||
EndpointCDNIcons = EndpointCDN + "icons/"
|
|
||||||
EndpointCDNSplashes = EndpointCDN + "splashes/"
|
|
||||||
EndpointCDNChannelIcons = EndpointCDN + "channel-icons/"
|
|
||||||
EndpointCDNBanners = EndpointCDN + "banners/"
|
|
||||||
EndpointCDNGuilds = EndpointCDN + "guilds/"
|
|
||||||
|
|
||||||
EndpointVoice = EndpointAPI + "/voice/"
|
|
||||||
EndpointVoiceRegions = EndpointVoice + "regions"
|
|
||||||
|
|
||||||
EndpointUser = func(uID string) string { return EndpointUsers + uID }
|
|
||||||
EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" }
|
|
||||||
EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" }
|
|
||||||
EndpointDefaultUserAvatar = func(uDiscriminator string) string {
|
|
||||||
uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator)
|
|
||||||
return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png"
|
|
||||||
}
|
|
||||||
EndpointUserBanner = func(uID, cID string) string {
|
|
||||||
return EndpointCDNBanners + uID + "/" + cID + ".png"
|
|
||||||
}
|
|
||||||
EndpointUserBannerAnimated = func(uID, cID string) string {
|
|
||||||
return EndpointCDNBanners + uID + "/" + cID + ".gif"
|
|
||||||
}
|
|
||||||
|
|
||||||
EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" }
|
|
||||||
EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID }
|
|
||||||
EndpointUserGuildMember = func(uID, gID string) string { return EndpointUserGuild(uID, gID) + "/member" }
|
|
||||||
EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" }
|
|
||||||
EndpointUserApplicationRoleConnection = func(aID string) string { return EndpointUsers + "@me/applications/" + aID + "/role-connection" }
|
|
||||||
EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" }
|
|
||||||
|
|
||||||
EndpointGuild = func(gID string) string { return EndpointGuilds + gID }
|
|
||||||
EndpointGuildAutoModeration = func(gID string) string { return EndpointGuild(gID) + "/auto-moderation" }
|
|
||||||
EndpointGuildAutoModerationRules = func(gID string) string { return EndpointGuildAutoModeration(gID) + "/rules" }
|
|
||||||
EndpointGuildAutoModerationRule = func(gID, rID string) string { return EndpointGuildAutoModerationRules(gID) + "/" + rID }
|
|
||||||
EndpointGuildThreads = func(gID string) string { return EndpointGuild(gID) + "/threads" }
|
|
||||||
EndpointGuildActiveThreads = func(gID string) string { return EndpointGuildThreads(gID) + "/active" }
|
|
||||||
EndpointGuildPreview = func(gID string) string { return EndpointGuilds + gID + "/preview" }
|
|
||||||
EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" }
|
|
||||||
EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" }
|
|
||||||
EndpointGuildMembersSearch = func(gID string) string { return EndpointGuildMembers(gID) + "/search" }
|
|
||||||
EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID }
|
|
||||||
EndpointGuildMemberRole = func(gID, uID, rID string) string { return EndpointGuilds + gID + "/members/" + uID + "/roles/" + rID }
|
|
||||||
EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" }
|
|
||||||
EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID }
|
|
||||||
EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" }
|
|
||||||
EndpointGuildIntegration = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID }
|
|
||||||
EndpointGuildRoles = func(gID string) string { return EndpointGuilds + gID + "/roles" }
|
|
||||||
EndpointGuildRole = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID }
|
|
||||||
EndpointGuildInvites = func(gID string) string { return EndpointGuilds + gID + "/invites" }
|
|
||||||
EndpointGuildWidget = func(gID string) string { return EndpointGuilds + gID + "/widget" }
|
|
||||||
EndpointGuildEmbed = EndpointGuildWidget
|
|
||||||
EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" }
|
|
||||||
EndpointGuildIcon = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" }
|
|
||||||
EndpointGuildIconAnimated = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".gif" }
|
|
||||||
EndpointGuildSplash = func(gID, hash string) string { return EndpointCDNSplashes + gID + "/" + hash + ".png" }
|
|
||||||
EndpointGuildWebhooks = func(gID string) string { return EndpointGuilds + gID + "/webhooks" }
|
|
||||||
EndpointGuildAuditLogs = func(gID string) string { return EndpointGuilds + gID + "/audit-logs" }
|
|
||||||
EndpointGuildEmojis = func(gID string) string { return EndpointGuilds + gID + "/emojis" }
|
|
||||||
EndpointGuildEmoji = func(gID, eID string) string { return EndpointGuilds + gID + "/emojis/" + eID }
|
|
||||||
EndpointGuildBanner = func(gID, hash string) string { return EndpointCDNBanners + gID + "/" + hash + ".png" }
|
|
||||||
EndpointGuildBannerAnimated = func(gID, hash string) string { return EndpointCDNBanners + gID + "/" + hash + ".gif" }
|
|
||||||
EndpointGuildStickers = func(gID string) string { return EndpointGuilds + gID + "/stickers" }
|
|
||||||
EndpointGuildSticker = func(gID, sID string) string { return EndpointGuilds + gID + "/stickers/" + sID }
|
|
||||||
EndpointStageInstance = func(cID string) string { return EndpointStageInstances + "/" + cID }
|
|
||||||
EndpointGuildScheduledEvents = func(gID string) string { return EndpointGuilds + gID + "/scheduled-events" }
|
|
||||||
EndpointGuildScheduledEvent = func(gID, eID string) string { return EndpointGuilds + gID + "/scheduled-events/" + eID }
|
|
||||||
EndpointGuildScheduledEventUsers = func(gID, eID string) string { return EndpointGuildScheduledEvent(gID, eID) + "/users" }
|
|
||||||
EndpointGuildTemplate = func(tID string) string { return EndpointGuilds + "/templates/" + tID }
|
|
||||||
EndpointGuildTemplates = func(gID string) string { return EndpointGuilds + gID + "/templates" }
|
|
||||||
EndpointGuildTemplateSync = func(gID, tID string) string { return EndpointGuilds + gID + "/templates/" + tID }
|
|
||||||
EndpointGuildMemberAvatar = func(gId, uID, aID string) string {
|
|
||||||
return EndpointCDNGuilds + gId + "/users/" + uID + "/avatars/" + aID + ".png"
|
|
||||||
}
|
|
||||||
EndpointGuildMemberAvatarAnimated = func(gId, uID, aID string) string {
|
|
||||||
return EndpointCDNGuilds + gId + "/users/" + uID + "/avatars/" + aID + ".gif"
|
|
||||||
}
|
|
||||||
|
|
||||||
EndpointChannel = func(cID string) string { return EndpointChannels + cID }
|
|
||||||
EndpointChannelThreads = func(cID string) string { return EndpointChannel(cID) + "/threads" }
|
|
||||||
EndpointChannelActiveThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/active" }
|
|
||||||
EndpointChannelPublicArchivedThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/archived/public" }
|
|
||||||
EndpointChannelPrivateArchivedThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/archived/private" }
|
|
||||||
EndpointChannelJoinedPrivateArchivedThreads = func(cID string) string { return EndpointChannel(cID) + "/users/@me/threads/archived/private" }
|
|
||||||
EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" }
|
|
||||||
EndpointChannelPermission = func(cID, tID string) string { return EndpointChannels + cID + "/permissions/" + tID }
|
|
||||||
EndpointChannelInvites = func(cID string) string { return EndpointChannels + cID + "/invites" }
|
|
||||||
EndpointChannelTyping = func(cID string) string { return EndpointChannels + cID + "/typing" }
|
|
||||||
EndpointChannelMessages = func(cID string) string { return EndpointChannels + cID + "/messages" }
|
|
||||||
EndpointChannelMessage = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID }
|
|
||||||
EndpointChannelMessageThread = func(cID, mID string) string { return EndpointChannelMessage(cID, mID) + "/threads" }
|
|
||||||
EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk-delete" }
|
|
||||||
EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" }
|
|
||||||
EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
|
|
||||||
EndpointChannelMessageCrosspost = func(cID, mID string) string { return EndpointChannel(cID) + "/messages/" + mID + "/crosspost" }
|
|
||||||
EndpointChannelFollow = func(cID string) string { return EndpointChannel(cID) + "/followers" }
|
|
||||||
EndpointThreadMembers = func(tID string) string { return EndpointChannel(tID) + "/thread-members" }
|
|
||||||
EndpointThreadMember = func(tID, mID string) string { return EndpointThreadMembers(tID) + "/" + mID }
|
|
||||||
|
|
||||||
EndpointGroupIcon = func(cID, hash string) string { return EndpointCDNChannelIcons + cID + "/" + hash + ".png" }
|
|
||||||
|
|
||||||
EndpointSticker = func(sID string) string { return EndpointStickers + sID }
|
|
||||||
EndpointNitroStickersPacks = EndpointAPI + "/sticker-packs"
|
|
||||||
|
|
||||||
EndpointChannelWebhooks = func(cID string) string { return EndpointChannel(cID) + "/webhooks" }
|
|
||||||
EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID }
|
|
||||||
EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token }
|
|
||||||
EndpointWebhookMessage = func(wID, token, messageID string) string {
|
|
||||||
return EndpointWebhookToken(wID, token) + "/messages/" + messageID
|
|
||||||
}
|
|
||||||
|
|
||||||
EndpointMessageReactionsAll = func(cID, mID string) string {
|
|
||||||
return EndpointChannelMessage(cID, mID) + "/reactions"
|
|
||||||
}
|
|
||||||
EndpointMessageReactions = func(cID, mID, eID string) string {
|
|
||||||
return EndpointChannelMessage(cID, mID) + "/reactions/" + eID
|
|
||||||
}
|
|
||||||
EndpointMessageReaction = func(cID, mID, eID, uID string) string {
|
|
||||||
return EndpointMessageReactions(cID, mID, eID) + "/" + uID
|
|
||||||
}
|
|
||||||
|
|
||||||
EndpointApplicationGlobalCommands = func(aID string) string {
|
|
||||||
return EndpointApplication(aID) + "/commands"
|
|
||||||
}
|
|
||||||
EndpointApplicationGlobalCommand = func(aID, cID string) string {
|
|
||||||
return EndpointApplicationGlobalCommands(aID) + "/" + cID
|
|
||||||
}
|
|
||||||
|
|
||||||
EndpointApplicationGuildCommands = func(aID, gID string) string {
|
|
||||||
return EndpointApplication(aID) + "/guilds/" + gID + "/commands"
|
|
||||||
}
|
|
||||||
EndpointApplicationGuildCommand = func(aID, gID, cID string) string {
|
|
||||||
return EndpointApplicationGuildCommands(aID, gID) + "/" + cID
|
|
||||||
}
|
|
||||||
EndpointApplicationCommandPermissions = func(aID, gID, cID string) string {
|
|
||||||
return EndpointApplicationGuildCommand(aID, gID, cID) + "/permissions"
|
|
||||||
}
|
|
||||||
EndpointApplicationCommandsGuildPermissions = func(aID, gID string) string {
|
|
||||||
return EndpointApplicationGuildCommands(aID, gID) + "/permissions"
|
|
||||||
}
|
|
||||||
EndpointInteraction = func(aID, iToken string) string {
|
|
||||||
return EndpointAPI + "interactions/" + aID + "/" + iToken
|
|
||||||
}
|
|
||||||
EndpointInteractionResponse = func(iID, iToken string) string {
|
|
||||||
return EndpointInteraction(iID, iToken) + "/callback"
|
|
||||||
}
|
|
||||||
EndpointInteractionResponseActions = func(aID, iToken string) string {
|
|
||||||
return EndpointWebhookMessage(aID, iToken, "@original")
|
|
||||||
}
|
|
||||||
EndpointFollowupMessage = func(aID, iToken string) string {
|
|
||||||
return EndpointWebhookToken(aID, iToken)
|
|
||||||
}
|
|
||||||
EndpointFollowupMessageActions = func(aID, iToken, mID string) string {
|
|
||||||
return EndpointWebhookMessage(aID, iToken, mID)
|
|
||||||
}
|
|
||||||
|
|
||||||
EndpointGuildCreate = EndpointAPI + "guilds"
|
|
||||||
|
|
||||||
EndpointInvite = func(iID string) string { return EndpointAPI + "invites/" + iID }
|
|
||||||
|
|
||||||
EndpointEmoji = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".png" }
|
|
||||||
EndpointEmojiAnimated = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".gif" }
|
|
||||||
|
|
||||||
EndpointApplications = EndpointAPI + "applications"
|
|
||||||
EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID }
|
|
||||||
EndpointApplicationRoleConnectionMetadata = func(aID string) string { return EndpointApplication(aID) + "/role-connections/metadata" }
|
|
||||||
|
|
||||||
EndpointOAuth2 = EndpointAPI + "oauth2/"
|
|
||||||
EndpointOAuth2Applications = EndpointOAuth2 + "applications"
|
|
||||||
EndpointOAuth2Application = func(aID string) string { return EndpointOAuth2Applications + "/" + aID }
|
|
||||||
EndpointOAuth2ApplicationsBot = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/bot" }
|
|
||||||
EndpointOAuth2ApplicationAssets = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/assets" }
|
|
||||||
|
|
||||||
// TODO: Deprecated, remove in the next release
|
|
||||||
EndpointOauth2 = EndpointOAuth2
|
|
||||||
EndpointOauth2Applications = EndpointOAuth2Applications
|
|
||||||
EndpointOauth2Application = EndpointOAuth2Application
|
|
||||||
EndpointOauth2ApplicationsBot = EndpointOAuth2ApplicationsBot
|
|
||||||
EndpointOauth2ApplicationAssets = EndpointOAuth2ApplicationAssets
|
|
||||||
)
|
|
||||||
611
vendor/github.com/bwmarrin/discordgo/interactions.go
generated
vendored
611
vendor/github.com/bwmarrin/discordgo/interactions.go
generated
vendored
@@ -1,611 +0,0 @@
|
|||||||
package discordgo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/ed25519"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InteractionDeadline is the time allowed to respond to an interaction.
|
|
||||||
const InteractionDeadline = time.Second * 3
|
|
||||||
|
|
||||||
// ApplicationCommandType represents the type of application command.
|
|
||||||
type ApplicationCommandType uint8
|
|
||||||
|
|
||||||
// Application command types
|
|
||||||
const (
|
|
||||||
// ChatApplicationCommand is default command type. They are slash commands (i.e. called directly from the chat).
|
|
||||||
ChatApplicationCommand ApplicationCommandType = 1
|
|
||||||
// UserApplicationCommand adds command to user context menu.
|
|
||||||
UserApplicationCommand ApplicationCommandType = 2
|
|
||||||
// MessageApplicationCommand adds command to message context menu.
|
|
||||||
MessageApplicationCommand ApplicationCommandType = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
// ApplicationCommand represents an application's slash command.
|
|
||||||
type ApplicationCommand struct {
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
ApplicationID string `json:"application_id,omitempty"`
|
|
||||||
GuildID string `json:"guild_id,omitempty"`
|
|
||||||
Version string `json:"version,omitempty"`
|
|
||||||
Type ApplicationCommandType `json:"type,omitempty"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
NameLocalizations *map[Locale]string `json:"name_localizations,omitempty"`
|
|
||||||
// NOTE: DefaultPermission will be soon deprecated. Use DefaultMemberPermissions and DMPermission instead.
|
|
||||||
DefaultPermission *bool `json:"default_permission,omitempty"`
|
|
||||||
DefaultMemberPermissions *int64 `json:"default_member_permissions,string,omitempty"`
|
|
||||||
DMPermission *bool `json:"dm_permission,omitempty"`
|
|
||||||
NSFW *bool `json:"nsfw,omitempty"`
|
|
||||||
|
|
||||||
// NOTE: Chat commands only. Otherwise it mustn't be set.
|
|
||||||
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
DescriptionLocalizations *map[Locale]string `json:"description_localizations,omitempty"`
|
|
||||||
Options []*ApplicationCommandOption `json:"options"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplicationCommandOptionType indicates the type of a slash command's option.
|
|
||||||
type ApplicationCommandOptionType uint8
|
|
||||||
|
|
||||||
// Application command option types.
|
|
||||||
const (
|
|
||||||
ApplicationCommandOptionSubCommand ApplicationCommandOptionType = 1
|
|
||||||
ApplicationCommandOptionSubCommandGroup ApplicationCommandOptionType = 2
|
|
||||||
ApplicationCommandOptionString ApplicationCommandOptionType = 3
|
|
||||||
ApplicationCommandOptionInteger ApplicationCommandOptionType = 4
|
|
||||||
ApplicationCommandOptionBoolean ApplicationCommandOptionType = 5
|
|
||||||
ApplicationCommandOptionUser ApplicationCommandOptionType = 6
|
|
||||||
ApplicationCommandOptionChannel ApplicationCommandOptionType = 7
|
|
||||||
ApplicationCommandOptionRole ApplicationCommandOptionType = 8
|
|
||||||
ApplicationCommandOptionMentionable ApplicationCommandOptionType = 9
|
|
||||||
ApplicationCommandOptionNumber ApplicationCommandOptionType = 10
|
|
||||||
ApplicationCommandOptionAttachment ApplicationCommandOptionType = 11
|
|
||||||
)
|
|
||||||
|
|
||||||
func (t ApplicationCommandOptionType) String() string {
|
|
||||||
switch t {
|
|
||||||
case ApplicationCommandOptionSubCommand:
|
|
||||||
return "SubCommand"
|
|
||||||
case ApplicationCommandOptionSubCommandGroup:
|
|
||||||
return "SubCommandGroup"
|
|
||||||
case ApplicationCommandOptionString:
|
|
||||||
return "String"
|
|
||||||
case ApplicationCommandOptionInteger:
|
|
||||||
return "Integer"
|
|
||||||
case ApplicationCommandOptionBoolean:
|
|
||||||
return "Boolean"
|
|
||||||
case ApplicationCommandOptionUser:
|
|
||||||
return "User"
|
|
||||||
case ApplicationCommandOptionChannel:
|
|
||||||
return "Channel"
|
|
||||||
case ApplicationCommandOptionRole:
|
|
||||||
return "Role"
|
|
||||||
case ApplicationCommandOptionMentionable:
|
|
||||||
return "Mentionable"
|
|
||||||
case ApplicationCommandOptionNumber:
|
|
||||||
return "Number"
|
|
||||||
case ApplicationCommandOptionAttachment:
|
|
||||||
return "Attachment"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("ApplicationCommandOptionType(%d)", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplicationCommandOption represents an option/subcommand/subcommands group.
|
|
||||||
type ApplicationCommandOption struct {
|
|
||||||
Type ApplicationCommandOptionType `json:"type"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
NameLocalizations map[Locale]string `json:"name_localizations,omitempty"`
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
DescriptionLocalizations map[Locale]string `json:"description_localizations,omitempty"`
|
|
||||||
// NOTE: This feature was on the API, but at some point developers decided to remove it.
|
|
||||||
// So I commented it, until it will be officially on the docs.
|
|
||||||
// Default bool `json:"default"`
|
|
||||||
|
|
||||||
ChannelTypes []ChannelType `json:"channel_types"`
|
|
||||||
Required bool `json:"required"`
|
|
||||||
Options []*ApplicationCommandOption `json:"options"`
|
|
||||||
|
|
||||||
// NOTE: mutually exclusive with Choices.
|
|
||||||
Autocomplete bool `json:"autocomplete"`
|
|
||||||
Choices []*ApplicationCommandOptionChoice `json:"choices"`
|
|
||||||
// Minimal value of number/integer option.
|
|
||||||
MinValue *float64 `json:"min_value,omitempty"`
|
|
||||||
// Maximum value of number/integer option.
|
|
||||||
MaxValue float64 `json:"max_value,omitempty"`
|
|
||||||
// Minimum length of string option.
|
|
||||||
MinLength *int `json:"min_length,omitempty"`
|
|
||||||
// Maximum length of string option.
|
|
||||||
MaxLength int `json:"max_length,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplicationCommandOptionChoice represents a slash command option choice.
|
|
||||||
type ApplicationCommandOptionChoice struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
NameLocalizations map[Locale]string `json:"name_localizations,omitempty"`
|
|
||||||
Value interface{} `json:"value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplicationCommandPermissions represents a single user or role permission for a command.
|
|
||||||
type ApplicationCommandPermissions struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Type ApplicationCommandPermissionType `json:"type"`
|
|
||||||
Permission bool `json:"permission"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GuildAllChannelsID is a helper function which returns guild_id-1.
|
|
||||||
// It is used in ApplicationCommandPermissions to target all the channels within a guild.
|
|
||||||
func GuildAllChannelsID(guild string) (id string, err error) {
|
|
||||||
var v uint64
|
|
||||||
v, err = strconv.ParseUint(guild, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return strconv.FormatUint(v-1, 10), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplicationCommandPermissionsList represents a list of ApplicationCommandPermissions, needed for serializing to JSON.
|
|
||||||
type ApplicationCommandPermissionsList struct {
|
|
||||||
Permissions []*ApplicationCommandPermissions `json:"permissions"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GuildApplicationCommandPermissions represents all permissions for a single guild command.
|
|
||||||
type GuildApplicationCommandPermissions struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
ApplicationID string `json:"application_id"`
|
|
||||||
GuildID string `json:"guild_id"`
|
|
||||||
Permissions []*ApplicationCommandPermissions `json:"permissions"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplicationCommandPermissionType indicates whether a permission is user or role based.
|
|
||||||
type ApplicationCommandPermissionType uint8
|
|
||||||
|
|
||||||
// Application command permission types.
|
|
||||||
const (
|
|
||||||
ApplicationCommandPermissionTypeRole ApplicationCommandPermissionType = 1
|
|
||||||
ApplicationCommandPermissionTypeUser ApplicationCommandPermissionType = 2
|
|
||||||
ApplicationCommandPermissionTypeChannel ApplicationCommandPermissionType = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
// InteractionType indicates the type of an interaction event.
|
|
||||||
type InteractionType uint8
|
|
||||||
|
|
||||||
// Interaction types
|
|
||||||
const (
|
|
||||||
InteractionPing InteractionType = 1
|
|
||||||
InteractionApplicationCommand InteractionType = 2
|
|
||||||
InteractionMessageComponent InteractionType = 3
|
|
||||||
InteractionApplicationCommandAutocomplete InteractionType = 4
|
|
||||||
InteractionModalSubmit InteractionType = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
func (t InteractionType) String() string {
|
|
||||||
switch t {
|
|
||||||
case InteractionPing:
|
|
||||||
return "Ping"
|
|
||||||
case InteractionApplicationCommand:
|
|
||||||
return "ApplicationCommand"
|
|
||||||
case InteractionMessageComponent:
|
|
||||||
return "MessageComponent"
|
|
||||||
case InteractionModalSubmit:
|
|
||||||
return "ModalSubmit"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("InteractionType(%d)", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interaction represents data of an interaction.
|
|
||||||
type Interaction struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
AppID string `json:"application_id"`
|
|
||||||
Type InteractionType `json:"type"`
|
|
||||||
Data InteractionData `json:"data"`
|
|
||||||
GuildID string `json:"guild_id"`
|
|
||||||
ChannelID string `json:"channel_id"`
|
|
||||||
|
|
||||||
// The message on which interaction was used.
|
|
||||||
// NOTE: this field is only filled when a button click triggered the interaction. Otherwise it will be nil.
|
|
||||||
Message *Message `json:"message"`
|
|
||||||
|
|
||||||
// Bitwise set of permissions the app or bot has within the channel the interaction was sent from
|
|
||||||
AppPermissions int64 `json:"app_permissions,string"`
|
|
||||||
|
|
||||||
// The member who invoked this interaction.
|
|
||||||
// NOTE: this field is only filled when the slash command was invoked in a guild;
|
|
||||||
// if it was invoked in a DM, the `User` field will be filled instead.
|
|
||||||
// Make sure to check for `nil` before using this field.
|
|
||||||
Member *Member `json:"member"`
|
|
||||||
// The user who invoked this interaction.
|
|
||||||
// NOTE: this field is only filled when the slash command was invoked in a DM;
|
|
||||||
// if it was invoked in a guild, the `Member` field will be filled instead.
|
|
||||||
// Make sure to check for `nil` before using this field.
|
|
||||||
User *User `json:"user"`
|
|
||||||
|
|
||||||
// The user's discord client locale.
|
|
||||||
Locale Locale `json:"locale"`
|
|
||||||
// The guild's locale. This defaults to EnglishUS
|
|
||||||
// NOTE: this field is only filled when the interaction was invoked in a guild.
|
|
||||||
GuildLocale *Locale `json:"guild_locale"`
|
|
||||||
|
|
||||||
Token string `json:"token"`
|
|
||||||
Version int `json:"version"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type interaction Interaction
|
|
||||||
|
|
||||||
type rawInteraction struct {
|
|
||||||
interaction
|
|
||||||
Data json.RawMessage `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON is a method for unmarshalling JSON object to Interaction.
|
|
||||||
func (i *Interaction) UnmarshalJSON(raw []byte) error {
|
|
||||||
var tmp rawInteraction
|
|
||||||
err := json.Unmarshal(raw, &tmp)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*i = Interaction(tmp.interaction)
|
|
||||||
|
|
||||||
switch tmp.Type {
|
|
||||||
case InteractionApplicationCommand, InteractionApplicationCommandAutocomplete:
|
|
||||||
v := ApplicationCommandInteractionData{}
|
|
||||||
err = json.Unmarshal(tmp.Data, &v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i.Data = v
|
|
||||||
case InteractionMessageComponent:
|
|
||||||
v := MessageComponentInteractionData{}
|
|
||||||
err = json.Unmarshal(tmp.Data, &v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i.Data = v
|
|
||||||
case InteractionModalSubmit:
|
|
||||||
v := ModalSubmitInteractionData{}
|
|
||||||
err = json.Unmarshal(tmp.Data, &v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i.Data = v
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageComponentData is helper function to assert the inner InteractionData to MessageComponentInteractionData.
|
|
||||||
// Make sure to check that the Type of the interaction is InteractionMessageComponent before calling.
|
|
||||||
func (i Interaction) MessageComponentData() (data MessageComponentInteractionData) {
|
|
||||||
if i.Type != InteractionMessageComponent {
|
|
||||||
panic("MessageComponentData called on interaction of type " + i.Type.String())
|
|
||||||
}
|
|
||||||
return i.Data.(MessageComponentInteractionData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplicationCommandData is helper function to assert the inner InteractionData to ApplicationCommandInteractionData.
|
|
||||||
// Make sure to check that the Type of the interaction is InteractionApplicationCommand before calling.
|
|
||||||
func (i Interaction) ApplicationCommandData() (data ApplicationCommandInteractionData) {
|
|
||||||
if i.Type != InteractionApplicationCommand && i.Type != InteractionApplicationCommandAutocomplete {
|
|
||||||
panic("ApplicationCommandData called on interaction of type " + i.Type.String())
|
|
||||||
}
|
|
||||||
return i.Data.(ApplicationCommandInteractionData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ModalSubmitData is helper function to assert the inner InteractionData to ModalSubmitInteractionData.
|
|
||||||
// Make sure to check that the Type of the interaction is InteractionModalSubmit before calling.
|
|
||||||
func (i Interaction) ModalSubmitData() (data ModalSubmitInteractionData) {
|
|
||||||
if i.Type != InteractionModalSubmit {
|
|
||||||
panic("ModalSubmitData called on interaction of type " + i.Type.String())
|
|
||||||
}
|
|
||||||
return i.Data.(ModalSubmitInteractionData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InteractionData is a common interface for all types of interaction data.
|
|
||||||
type InteractionData interface {
|
|
||||||
Type() InteractionType
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplicationCommandInteractionData contains the data of application command interaction.
|
|
||||||
type ApplicationCommandInteractionData struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Resolved *ApplicationCommandInteractionDataResolved `json:"resolved"`
|
|
||||||
|
|
||||||
// Slash command options
|
|
||||||
Options []*ApplicationCommandInteractionDataOption `json:"options"`
|
|
||||||
// Target (user/message) id on which context menu command was called.
|
|
||||||
// The details are stored in Resolved according to command type.
|
|
||||||
TargetID string `json:"target_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplicationCommandInteractionDataResolved contains resolved data of command execution.
|
|
||||||
// Partial Member objects are missing user, deaf and mute fields.
|
|
||||||
// Partial Channel objects only have id, name, type and permissions fields.
|
|
||||||
type ApplicationCommandInteractionDataResolved struct {
|
|
||||||
Users map[string]*User `json:"users"`
|
|
||||||
Members map[string]*Member `json:"members"`
|
|
||||||
Roles map[string]*Role `json:"roles"`
|
|
||||||
Channels map[string]*Channel `json:"channels"`
|
|
||||||
Messages map[string]*Message `json:"messages"`
|
|
||||||
Attachments map[string]*MessageAttachment `json:"attachments"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the type of interaction data.
|
|
||||||
func (ApplicationCommandInteractionData) Type() InteractionType {
|
|
||||||
return InteractionApplicationCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageComponentInteractionData contains the data of message component interaction.
|
|
||||||
type MessageComponentInteractionData struct {
|
|
||||||
CustomID string `json:"custom_id"`
|
|
||||||
ComponentType ComponentType `json:"component_type"`
|
|
||||||
Resolved MessageComponentInteractionDataResolved `json:"resolved"`
|
|
||||||
|
|
||||||
// NOTE: Only filled when ComponentType is SelectMenuComponent (3). Otherwise is nil.
|
|
||||||
Values []string `json:"values"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageComponentInteractionDataResolved contains the resolved data of selected option.
|
|
||||||
type MessageComponentInteractionDataResolved struct {
|
|
||||||
Users map[string]*User `json:"users"`
|
|
||||||
Members map[string]*Member `json:"members"`
|
|
||||||
Roles map[string]*Role `json:"roles"`
|
|
||||||
Channels map[string]*Channel `json:"channels"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the type of interaction data.
|
|
||||||
func (MessageComponentInteractionData) Type() InteractionType {
|
|
||||||
return InteractionMessageComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
// ModalSubmitInteractionData contains the data of modal submit interaction.
|
|
||||||
type ModalSubmitInteractionData struct {
|
|
||||||
CustomID string `json:"custom_id"`
|
|
||||||
Components []MessageComponent `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the type of interaction data.
|
|
||||||
func (ModalSubmitInteractionData) Type() InteractionType {
|
|
||||||
return InteractionModalSubmit
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON is a helper function to correctly unmarshal Components.
|
|
||||||
func (d *ModalSubmitInteractionData) UnmarshalJSON(data []byte) error {
|
|
||||||
type modalSubmitInteractionData ModalSubmitInteractionData
|
|
||||||
var v struct {
|
|
||||||
modalSubmitInteractionData
|
|
||||||
RawComponents []unmarshalableMessageComponent `json:"components"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(data, &v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*d = ModalSubmitInteractionData(v.modalSubmitInteractionData)
|
|
||||||
d.Components = make([]MessageComponent, len(v.RawComponents))
|
|
||||||
for i, v := range v.RawComponents {
|
|
||||||
d.Components[i] = v.MessageComponent
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplicationCommandInteractionDataOption represents an option of a slash command.
|
|
||||||
type ApplicationCommandInteractionDataOption struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Type ApplicationCommandOptionType `json:"type"`
|
|
||||||
// NOTE: Contains the value specified by Type.
|
|
||||||
Value interface{} `json:"value,omitempty"`
|
|
||||||
Options []*ApplicationCommandInteractionDataOption `json:"options,omitempty"`
|
|
||||||
|
|
||||||
// NOTE: autocomplete interaction only.
|
|
||||||
Focused bool `json:"focused,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntValue is a utility function for casting option value to integer
|
|
||||||
func (o ApplicationCommandInteractionDataOption) IntValue() int64 {
|
|
||||||
if o.Type != ApplicationCommandOptionInteger {
|
|
||||||
panic("IntValue called on data option of type " + o.Type.String())
|
|
||||||
}
|
|
||||||
return int64(o.Value.(float64))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UintValue is a utility function for casting option value to unsigned integer
|
|
||||||
func (o ApplicationCommandInteractionDataOption) UintValue() uint64 {
|
|
||||||
if o.Type != ApplicationCommandOptionInteger {
|
|
||||||
panic("UintValue called on data option of type " + o.Type.String())
|
|
||||||
}
|
|
||||||
return uint64(o.Value.(float64))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FloatValue is a utility function for casting option value to float
|
|
||||||
func (o ApplicationCommandInteractionDataOption) FloatValue() float64 {
|
|
||||||
if o.Type != ApplicationCommandOptionNumber {
|
|
||||||
panic("FloatValue called on data option of type " + o.Type.String())
|
|
||||||
}
|
|
||||||
return o.Value.(float64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringValue is a utility function for casting option value to string
|
|
||||||
func (o ApplicationCommandInteractionDataOption) StringValue() string {
|
|
||||||
if o.Type != ApplicationCommandOptionString {
|
|
||||||
panic("StringValue called on data option of type " + o.Type.String())
|
|
||||||
}
|
|
||||||
return o.Value.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolValue is a utility function for casting option value to bool
|
|
||||||
func (o ApplicationCommandInteractionDataOption) BoolValue() bool {
|
|
||||||
if o.Type != ApplicationCommandOptionBoolean {
|
|
||||||
panic("BoolValue called on data option of type " + o.Type.String())
|
|
||||||
}
|
|
||||||
return o.Value.(bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChannelValue is a utility function for casting option value to channel object.
|
|
||||||
// s : Session object, if not nil, function additionally fetches all channel's data
|
|
||||||
func (o ApplicationCommandInteractionDataOption) ChannelValue(s *Session) *Channel {
|
|
||||||
if o.Type != ApplicationCommandOptionChannel {
|
|
||||||
panic("ChannelValue called on data option of type " + o.Type.String())
|
|
||||||
}
|
|
||||||
chanID := o.Value.(string)
|
|
||||||
|
|
||||||
if s == nil {
|
|
||||||
return &Channel{ID: chanID}
|
|
||||||
}
|
|
||||||
|
|
||||||
ch, err := s.State.Channel(chanID)
|
|
||||||
if err != nil {
|
|
||||||
ch, err = s.Channel(chanID)
|
|
||||||
if err != nil {
|
|
||||||
return &Channel{ID: chanID}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ch
|
|
||||||
}
|
|
||||||
|
|
||||||
// RoleValue is a utility function for casting option value to role object.
|
|
||||||
// s : Session object, if not nil, function additionally fetches all role's data
|
|
||||||
func (o ApplicationCommandInteractionDataOption) RoleValue(s *Session, gID string) *Role {
|
|
||||||
if o.Type != ApplicationCommandOptionRole && o.Type != ApplicationCommandOptionMentionable {
|
|
||||||
panic("RoleValue called on data option of type " + o.Type.String())
|
|
||||||
}
|
|
||||||
roleID := o.Value.(string)
|
|
||||||
|
|
||||||
if s == nil || gID == "" {
|
|
||||||
return &Role{ID: roleID}
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := s.State.Role(gID, roleID)
|
|
||||||
if err != nil {
|
|
||||||
roles, err := s.GuildRoles(gID)
|
|
||||||
if err == nil {
|
|
||||||
for _, r = range roles {
|
|
||||||
if r.ID == roleID {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &Role{ID: roleID}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserValue is a utility function for casting option value to user object.
|
|
||||||
// s : Session object, if not nil, function additionally fetches all user's data
|
|
||||||
func (o ApplicationCommandInteractionDataOption) UserValue(s *Session) *User {
|
|
||||||
if o.Type != ApplicationCommandOptionUser && o.Type != ApplicationCommandOptionMentionable {
|
|
||||||
panic("UserValue called on data option of type " + o.Type.String())
|
|
||||||
}
|
|
||||||
userID := o.Value.(string)
|
|
||||||
|
|
||||||
if s == nil {
|
|
||||||
return &User{ID: userID}
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := s.User(userID)
|
|
||||||
if err != nil {
|
|
||||||
return &User{ID: userID}
|
|
||||||
}
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// InteractionResponseType is type of interaction response.
|
|
||||||
type InteractionResponseType uint8
|
|
||||||
|
|
||||||
// Interaction response types.
|
|
||||||
const (
|
|
||||||
// InteractionResponsePong is for ACK ping event.
|
|
||||||
InteractionResponsePong InteractionResponseType = 1
|
|
||||||
// InteractionResponseChannelMessageWithSource is for responding with a message, showing the user's input.
|
|
||||||
InteractionResponseChannelMessageWithSource InteractionResponseType = 4
|
|
||||||
// InteractionResponseDeferredChannelMessageWithSource acknowledges that the event was received, and that a follow-up will come later.
|
|
||||||
InteractionResponseDeferredChannelMessageWithSource InteractionResponseType = 5
|
|
||||||
// InteractionResponseDeferredMessageUpdate acknowledges that the message component interaction event was received, and message will be updated later.
|
|
||||||
InteractionResponseDeferredMessageUpdate InteractionResponseType = 6
|
|
||||||
// InteractionResponseUpdateMessage is for updating the message to which message component was attached.
|
|
||||||
InteractionResponseUpdateMessage InteractionResponseType = 7
|
|
||||||
// InteractionApplicationCommandAutocompleteResult shows autocompletion results. Autocomplete interaction only.
|
|
||||||
InteractionApplicationCommandAutocompleteResult InteractionResponseType = 8
|
|
||||||
// InteractionResponseModal is for responding to an interaction with a modal window.
|
|
||||||
InteractionResponseModal InteractionResponseType = 9
|
|
||||||
)
|
|
||||||
|
|
||||||
// InteractionResponse represents a response for an interaction event.
|
|
||||||
type InteractionResponse struct {
|
|
||||||
Type InteractionResponseType `json:"type,omitempty"`
|
|
||||||
Data *InteractionResponseData `json:"data,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InteractionResponseData is response data for an interaction.
|
|
||||||
type InteractionResponseData struct {
|
|
||||||
TTS bool `json:"tts"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
Components []MessageComponent `json:"components"`
|
|
||||||
Embeds []*MessageEmbed `json:"embeds"`
|
|
||||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
|
||||||
Files []*File `json:"-"`
|
|
||||||
|
|
||||||
// NOTE: only MessageFlagsSuppressEmbeds and MessageFlagsEphemeral can be set.
|
|
||||||
Flags MessageFlags `json:"flags,omitempty"`
|
|
||||||
|
|
||||||
// NOTE: autocomplete interaction only.
|
|
||||||
Choices []*ApplicationCommandOptionChoice `json:"choices,omitempty"`
|
|
||||||
|
|
||||||
// NOTE: modal interaction only.
|
|
||||||
|
|
||||||
CustomID string `json:"custom_id,omitempty"`
|
|
||||||
Title string `json:"title,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// VerifyInteraction implements message verification of the discord interactions api
|
|
||||||
// signing algorithm, as documented here:
|
|
||||||
// https://discord.com/developers/docs/interactions/receiving-and-responding#security-and-authorization
|
|
||||||
func VerifyInteraction(r *http.Request, key ed25519.PublicKey) bool {
|
|
||||||
var msg bytes.Buffer
|
|
||||||
|
|
||||||
signature := r.Header.Get("X-Signature-Ed25519")
|
|
||||||
if signature == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := hex.DecodeString(signature)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(sig) != ed25519.SignatureSize {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp := r.Header.Get("X-Signature-Timestamp")
|
|
||||||
if timestamp == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.WriteString(timestamp)
|
|
||||||
|
|
||||||
defer r.Body.Close()
|
|
||||||
var body bytes.Buffer
|
|
||||||
|
|
||||||
// at the end of the function, copy the original body back into the request
|
|
||||||
defer func() {
|
|
||||||
r.Body = ioutil.NopCloser(&body)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// copy body into buffers
|
|
||||||
_, err = io.Copy(&msg, io.TeeReader(r.Body, &body))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return ed25519.Verify(key, msg.Bytes(), sig)
|
|
||||||
}
|
|
||||||
83
vendor/github.com/bwmarrin/discordgo/locales.go
generated
vendored
83
vendor/github.com/bwmarrin/discordgo/locales.go
generated
vendored
@@ -1,83 +0,0 @@
|
|||||||
package discordgo
|
|
||||||
|
|
||||||
// Locale represents the accepted languages for Discord.
|
|
||||||
// https://discord.com/developers/docs/reference#locales
|
|
||||||
type Locale string
|
|
||||||
|
|
||||||
// String returns the human-readable string of the locale
|
|
||||||
func (l Locale) String() string {
|
|
||||||
if name, ok := Locales[l]; ok {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
return Unknown.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// All defined locales in Discord
|
|
||||||
const (
|
|
||||||
EnglishUS Locale = "en-US"
|
|
||||||
EnglishGB Locale = "en-GB"
|
|
||||||
Bulgarian Locale = "bg"
|
|
||||||
ChineseCN Locale = "zh-CN"
|
|
||||||
ChineseTW Locale = "zh-TW"
|
|
||||||
Croatian Locale = "hr"
|
|
||||||
Czech Locale = "cs"
|
|
||||||
Danish Locale = "da"
|
|
||||||
Dutch Locale = "nl"
|
|
||||||
Finnish Locale = "fi"
|
|
||||||
French Locale = "fr"
|
|
||||||
German Locale = "de"
|
|
||||||
Greek Locale = "el"
|
|
||||||
Hindi Locale = "hi"
|
|
||||||
Hungarian Locale = "hu"
|
|
||||||
Italian Locale = "it"
|
|
||||||
Japanese Locale = "ja"
|
|
||||||
Korean Locale = "ko"
|
|
||||||
Lithuanian Locale = "lt"
|
|
||||||
Norwegian Locale = "no"
|
|
||||||
Polish Locale = "pl"
|
|
||||||
PortugueseBR Locale = "pt-BR"
|
|
||||||
Romanian Locale = "ro"
|
|
||||||
Russian Locale = "ru"
|
|
||||||
SpanishES Locale = "es-ES"
|
|
||||||
Swedish Locale = "sv-SE"
|
|
||||||
Thai Locale = "th"
|
|
||||||
Turkish Locale = "tr"
|
|
||||||
Ukrainian Locale = "uk"
|
|
||||||
Vietnamese Locale = "vi"
|
|
||||||
Unknown Locale = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
// Locales is a map of all the languages codes to their names.
|
|
||||||
var Locales = map[Locale]string{
|
|
||||||
EnglishUS: "English (United States)",
|
|
||||||
EnglishGB: "English (Great Britain)",
|
|
||||||
Bulgarian: "Bulgarian",
|
|
||||||
ChineseCN: "Chinese (China)",
|
|
||||||
ChineseTW: "Chinese (Taiwan)",
|
|
||||||
Croatian: "Croatian",
|
|
||||||
Czech: "Czech",
|
|
||||||
Danish: "Danish",
|
|
||||||
Dutch: "Dutch",
|
|
||||||
Finnish: "Finnish",
|
|
||||||
French: "French",
|
|
||||||
German: "German",
|
|
||||||
Greek: "Greek",
|
|
||||||
Hindi: "Hindi",
|
|
||||||
Hungarian: "Hungarian",
|
|
||||||
Italian: "Italian",
|
|
||||||
Japanese: "Japanese",
|
|
||||||
Korean: "Korean",
|
|
||||||
Lithuanian: "Lithuanian",
|
|
||||||
Norwegian: "Norwegian",
|
|
||||||
Polish: "Polish",
|
|
||||||
PortugueseBR: "Portuguese (Brazil)",
|
|
||||||
Romanian: "Romanian",
|
|
||||||
Russian: "Russian",
|
|
||||||
SpanishES: "Spanish (Spain)",
|
|
||||||
Swedish: "Swedish",
|
|
||||||
Thai: "Thai",
|
|
||||||
Turkish: "Turkish",
|
|
||||||
Ukrainian: "Ukrainian",
|
|
||||||
Vietnamese: "Vietnamese",
|
|
||||||
Unknown: "unknown",
|
|
||||||
}
|
|
||||||
3384
vendor/github.com/bwmarrin/discordgo/restapi.go
generated
vendored
3384
vendor/github.com/bwmarrin/discordgo/restapi.go
generated
vendored
File diff suppressed because it is too large
Load Diff
2421
vendor/github.com/bwmarrin/discordgo/structs.go
generated
vendored
2421
vendor/github.com/bwmarrin/discordgo/structs.go
generated
vendored
File diff suppressed because it is too large
Load Diff
125
vendor/github.com/bwmarrin/discordgo/util.go
generated
vendored
125
vendor/github.com/bwmarrin/discordgo/util.go
generated
vendored
@@ -1,125 +0,0 @@
|
|||||||
package discordgo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"mime/multipart"
|
|
||||||
"net/textproto"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SnowflakeTimestamp returns the creation time of a Snowflake ID relative to the creation of Discord.
|
|
||||||
func SnowflakeTimestamp(ID string) (t time.Time, err error) {
|
|
||||||
i, err := strconv.ParseInt(ID, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
timestamp := (i >> 22) + 1420070400000
|
|
||||||
t = time.Unix(0, timestamp*1000000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultipartBodyWithJSON returns the contentType and body for a discord request
|
|
||||||
// data : The object to encode for payload_json in the multipart request
|
|
||||||
// files : Files to include in the request
|
|
||||||
func MultipartBodyWithJSON(data interface{}, files []*File) (requestContentType string, requestBody []byte, err error) {
|
|
||||||
body := &bytes.Buffer{}
|
|
||||||
bodywriter := multipart.NewWriter(body)
|
|
||||||
|
|
||||||
payload, err := Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var p io.Writer
|
|
||||||
|
|
||||||
h := make(textproto.MIMEHeader)
|
|
||||||
h.Set("Content-Disposition", `form-data; name="payload_json"`)
|
|
||||||
h.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
p, err = bodywriter.CreatePart(h)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = p.Write(payload); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, file := range files {
|
|
||||||
h := make(textproto.MIMEHeader)
|
|
||||||
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="files[%d]"; filename="%s"`, i, quoteEscaper.Replace(file.Name)))
|
|
||||||
contentType := file.ContentType
|
|
||||||
if contentType == "" {
|
|
||||||
contentType = "application/octet-stream"
|
|
||||||
}
|
|
||||||
h.Set("Content-Type", contentType)
|
|
||||||
|
|
||||||
p, err = bodywriter.CreatePart(h)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = io.Copy(p, file.Reader); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bodywriter.Close()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return bodywriter.FormDataContentType(), body.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func avatarURL(avatarHash, defaultAvatarURL, staticAvatarURL, animatedAvatarURL, size string) string {
|
|
||||||
var URL string
|
|
||||||
if avatarHash == "" {
|
|
||||||
URL = defaultAvatarURL
|
|
||||||
} else if strings.HasPrefix(avatarHash, "a_") {
|
|
||||||
URL = animatedAvatarURL
|
|
||||||
} else {
|
|
||||||
URL = staticAvatarURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if size != "" {
|
|
||||||
return URL + "?size=" + size
|
|
||||||
}
|
|
||||||
return URL
|
|
||||||
}
|
|
||||||
|
|
||||||
func bannerURL(bannerHash, staticBannerURL, animatedBannerURL, size string) string {
|
|
||||||
var URL string
|
|
||||||
if bannerHash == "" {
|
|
||||||
return ""
|
|
||||||
} else if strings.HasPrefix(bannerHash, "a_") {
|
|
||||||
URL = animatedBannerURL
|
|
||||||
} else {
|
|
||||||
URL = staticBannerURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if size != "" {
|
|
||||||
return URL + "?size=" + size
|
|
||||||
}
|
|
||||||
return URL
|
|
||||||
}
|
|
||||||
|
|
||||||
func iconURL(iconHash, staticIconURL, animatedIconURL, size string) string {
|
|
||||||
var URL string
|
|
||||||
if iconHash == "" {
|
|
||||||
return ""
|
|
||||||
} else if strings.HasPrefix(iconHash, "a_") {
|
|
||||||
URL = animatedIconURL
|
|
||||||
} else {
|
|
||||||
URL = staticIconURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if size != "" {
|
|
||||||
return URL + "?size=" + size
|
|
||||||
}
|
|
||||||
return URL
|
|
||||||
}
|
|
||||||
50
vendor/github.com/bwmarrin/discordgo/webhook.go
generated
vendored
50
vendor/github.com/bwmarrin/discordgo/webhook.go
generated
vendored
@@ -1,50 +0,0 @@
|
|||||||
package discordgo
|
|
||||||
|
|
||||||
// Webhook stores the data for a webhook.
|
|
||||||
type Webhook struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Type WebhookType `json:"type"`
|
|
||||||
GuildID string `json:"guild_id"`
|
|
||||||
ChannelID string `json:"channel_id"`
|
|
||||||
User *User `json:"user"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Avatar string `json:"avatar"`
|
|
||||||
Token string `json:"token"`
|
|
||||||
|
|
||||||
// ApplicationID is the bot/OAuth2 application that created this webhook
|
|
||||||
ApplicationID string `json:"application_id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebhookType is the type of Webhook (see WebhookType* consts) in the Webhook struct
|
|
||||||
// https://discord.com/developers/docs/resources/webhook#webhook-object-webhook-types
|
|
||||||
type WebhookType int
|
|
||||||
|
|
||||||
// Valid WebhookType values
|
|
||||||
const (
|
|
||||||
WebhookTypeIncoming WebhookType = 1
|
|
||||||
WebhookTypeChannelFollower WebhookType = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// WebhookParams is a struct for webhook params, used in the WebhookExecute command.
|
|
||||||
type WebhookParams struct {
|
|
||||||
Content string `json:"content,omitempty"`
|
|
||||||
Username string `json:"username,omitempty"`
|
|
||||||
AvatarURL string `json:"avatar_url,omitempty"`
|
|
||||||
TTS bool `json:"tts,omitempty"`
|
|
||||||
Files []*File `json:"-"`
|
|
||||||
Components []MessageComponent `json:"components"`
|
|
||||||
Embeds []*MessageEmbed `json:"embeds,omitempty"`
|
|
||||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
|
||||||
// Only MessageFlagsSuppressEmbeds and MessageFlagsEphemeral can be set.
|
|
||||||
// MessageFlagsEphemeral can only be set when using Followup Message Create endpoint.
|
|
||||||
Flags MessageFlags `json:"flags,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebhookEdit stores data for editing of a webhook message.
|
|
||||||
type WebhookEdit struct {
|
|
||||||
Content *string `json:"content,omitempty"`
|
|
||||||
Components *[]MessageComponent `json:"components,omitempty"`
|
|
||||||
Embeds *[]*MessageEmbed `json:"embeds,omitempty"`
|
|
||||||
Files []*File `json:"-"`
|
|
||||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
|
||||||
}
|
|
||||||
5
vendor/github.com/d5/tengo/v2/README.md
generated
vendored
5
vendor/github.com/d5/tengo/v2/README.md
generated
vendored
@@ -144,7 +144,10 @@ fmt.Println(res) // "success"
|
|||||||
- [Interoperability](https://github.com/d5/tengo/blob/master/docs/interoperability.md)
|
- [Interoperability](https://github.com/d5/tengo/blob/master/docs/interoperability.md)
|
||||||
- [Tengo CLI](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md)
|
- [Tengo CLI](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md)
|
||||||
- [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md)
|
- [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md)
|
||||||
- Syntax Highlighters: [VSCode](https://github.com/lissein/vscode-tengo), [Atom](https://github.com/d5/tengo-atom), [Vim](https://github.com/geseq/tengo-vim)
|
- Syntax Highlighters: [VSCode](https://github.com/lissein/vscode-tengo), [Atom](https://github.com/d5/tengo-atom)
|
||||||
- **Why the name Tengo?** It's from [1Q84](https://en.wikipedia.org/wiki/1Q84).
|
- **Why the name Tengo?** It's from [1Q84](https://en.wikipedia.org/wiki/1Q84).
|
||||||
|
|
||||||
|
##
|
||||||
|
|
||||||
|
:hearts: Like writing Go code? Come work at Skool. [We're hiring!](https://jobs.lever.co/skool)
|
||||||
|
|
||||||
|
|||||||
40
vendor/github.com/d5/tengo/v2/compiler.go
generated
vendored
40
vendor/github.com/d5/tengo/v2/compiler.go
generated
vendored
@@ -141,7 +141,25 @@ func (c *Compiler) Compile(node parser.Node) error {
|
|||||||
if node.Token == token.LAnd || node.Token == token.LOr {
|
if node.Token == token.LAnd || node.Token == token.LOr {
|
||||||
return c.compileLogical(node)
|
return c.compileLogical(node)
|
||||||
}
|
}
|
||||||
|
if node.Token == token.Less {
|
||||||
|
if err := c.Compile(node.RHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Compile(node.LHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.emit(node, parser.OpBinaryOp, int(token.Greater))
|
||||||
|
return nil
|
||||||
|
} else if node.Token == token.LessEq {
|
||||||
|
if err := c.Compile(node.RHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Compile(node.LHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.emit(node, parser.OpBinaryOp, int(token.GreaterEq))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if err := c.Compile(node.LHS); err != nil {
|
if err := c.Compile(node.LHS); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -164,10 +182,6 @@ func (c *Compiler) Compile(node parser.Node) error {
|
|||||||
c.emit(node, parser.OpBinaryOp, int(token.Greater))
|
c.emit(node, parser.OpBinaryOp, int(token.Greater))
|
||||||
case token.GreaterEq:
|
case token.GreaterEq:
|
||||||
c.emit(node, parser.OpBinaryOp, int(token.GreaterEq))
|
c.emit(node, parser.OpBinaryOp, int(token.GreaterEq))
|
||||||
case token.Less:
|
|
||||||
c.emit(node, parser.OpBinaryOp, int(token.Less))
|
|
||||||
case token.LessEq:
|
|
||||||
c.emit(node, parser.OpBinaryOp, int(token.LessEq))
|
|
||||||
case token.Equal:
|
case token.Equal:
|
||||||
c.emit(node, parser.OpEqual)
|
c.emit(node, parser.OpEqual)
|
||||||
case token.NotEqual:
|
case token.NotEqual:
|
||||||
@@ -678,15 +692,12 @@ func (c *Compiler) compileAssign(
|
|||||||
return c.errorf(node, "operator ':=' not allowed with selector")
|
return c.errorf(node, "operator ':=' not allowed with selector")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, isFunc := rhs[0].(*parser.FuncLit)
|
|
||||||
symbol, depth, exists := c.symbolTable.Resolve(ident, false)
|
symbol, depth, exists := c.symbolTable.Resolve(ident, false)
|
||||||
if op == token.Define {
|
if op == token.Define {
|
||||||
if depth == 0 && exists {
|
if depth == 0 && exists {
|
||||||
return c.errorf(node, "'%s' redeclared in this block", ident)
|
return c.errorf(node, "'%s' redeclared in this block", ident)
|
||||||
}
|
}
|
||||||
if isFunc {
|
symbol = c.symbolTable.Define(ident)
|
||||||
symbol = c.symbolTable.Define(ident)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if !exists {
|
if !exists {
|
||||||
return c.errorf(node, "unresolved reference '%s'", ident)
|
return c.errorf(node, "unresolved reference '%s'", ident)
|
||||||
@@ -707,10 +718,6 @@ func (c *Compiler) compileAssign(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if op == token.Define && !isFunc {
|
|
||||||
symbol = c.symbolTable.Define(ident)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch op {
|
switch op {
|
||||||
case token.AddAssign:
|
case token.AddAssign:
|
||||||
c.emit(node, parser.OpBinaryOp, int(token.Add))
|
c.emit(node, parser.OpBinaryOp, int(token.Add))
|
||||||
@@ -1213,14 +1220,14 @@ func (c *Compiler) optimizeFunc(node parser.Node) {
|
|||||||
iterateInstructions(c.scopes[c.scopeIndex].Instructions,
|
iterateInstructions(c.scopes[c.scopeIndex].Instructions,
|
||||||
func(pos int, opcode parser.Opcode, operands []int) bool {
|
func(pos int, opcode parser.Opcode, operands []int) bool {
|
||||||
switch {
|
switch {
|
||||||
case dsts[pos]:
|
|
||||||
dstIdx++
|
|
||||||
deadCode = false
|
|
||||||
case opcode == parser.OpReturn:
|
case opcode == parser.OpReturn:
|
||||||
if deadCode {
|
if deadCode {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
deadCode = true
|
deadCode = true
|
||||||
|
case dsts[pos]:
|
||||||
|
dstIdx++
|
||||||
|
deadCode = false
|
||||||
case deadCode:
|
case deadCode:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -1235,7 +1242,6 @@ func (c *Compiler) optimizeFunc(node parser.Node) {
|
|||||||
var appendReturn bool
|
var appendReturn bool
|
||||||
endPos := len(c.scopes[c.scopeIndex].Instructions)
|
endPos := len(c.scopes[c.scopeIndex].Instructions)
|
||||||
newEndPost := len(newInsts)
|
newEndPost := len(newInsts)
|
||||||
|
|
||||||
iterateInstructions(newInsts,
|
iterateInstructions(newInsts,
|
||||||
func(pos int, opcode parser.Opcode, operands []int) bool {
|
func(pos int, opcode parser.Opcode, operands []int) bool {
|
||||||
switch opcode {
|
switch opcode {
|
||||||
|
|||||||
7
vendor/github.com/d5/tengo/v2/objects.go
generated
vendored
7
vendor/github.com/d5/tengo/v2/objects.go
generated
vendored
@@ -1577,8 +1577,9 @@ func (o *Undefined) Value() Object {
|
|||||||
// UserFunction represents a user function.
|
// UserFunction represents a user function.
|
||||||
type UserFunction struct {
|
type UserFunction struct {
|
||||||
ObjectImpl
|
ObjectImpl
|
||||||
Name string
|
Name string
|
||||||
Value CallableFunc
|
Value CallableFunc
|
||||||
|
EncodingID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeName returns the name of the type.
|
// TypeName returns the name of the type.
|
||||||
@@ -1592,7 +1593,7 @@ func (o *UserFunction) String() string {
|
|||||||
|
|
||||||
// Copy returns a copy of the type.
|
// Copy returns a copy of the type.
|
||||||
func (o *UserFunction) Copy() Object {
|
func (o *UserFunction) Copy() Object {
|
||||||
return &UserFunction{Value: o.Value, Name: o.Name}
|
return &UserFunction{Value: o.Value}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals returns true if the value of the type is equal to the value of
|
// Equals returns true if the value of the type is equal to the value of
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user