Compare commits

..

25 Commits

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

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

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

View File

@@ -176,6 +176,10 @@ linters:
- prealloc
- wsl
- gomnd
- godox
- goerr113
- testpackage
- godot
# rules to deal with reported isues

View File

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

View File

@@ -10,7 +10,7 @@ RUN apk update && apk add go git gcc musl-dev \
FROM alpine:edge
RUN apk --no-cache add ca-certificates mailcap
COPY --from=builder /bin/matterbridge /bin/matterbridge
RUN mkdir /etc/matterbridge
RUN touch /etc/matterbridge/matterbridge.toml
RUN ln -sf /matterbridge.toml /etc/matterbridge/matterbridge.toml
RUN mkdir /etc/matterbridge \
&& touch /etc/matterbridge/matterbridge.toml \
&& ln -sf /matterbridge.toml /etc/matterbridge/matterbridge.toml
ENTRYPOINT ["/bin/matterbridge", "-conf", "/etc/matterbridge/matterbridge.toml"]

View File

@@ -29,7 +29,6 @@ And more...
---
[![Download stable](https://img.shields.io/github/release/42wim/matterbridge.svg?label=download%20stable)](https://github.com/42wim/matterbridge/releases/latest)
[![Download dev](https://img.shields.io/bintray/v/42wim/nightly/Matterbridge.svg?label=download%20dev&colorB=007ec6)](https://bintray.com/42wim/nightly/Matterbridge/_latestVersion)
[![Maintainability](https://api.codeclimate.com/v1/badges/82dff70ef2ba85a6173a/maintainability)](https://codeclimate.com/github/42wim/matterbridge/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/82dff70ef2ba85a6173a/test_coverage)](https://codeclimate.com/github/42wim/matterbridge/test_coverage)<br />
@@ -94,6 +93,7 @@ And more...
- [Matrix](https://matrix.org)
- [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x
- [Microsoft Teams](https://teams.microsoft.com)
- [Nextcloud Talk](https://nextcloud.com/talk/)
- [Rocket.chat](https://rocket.chat)
- [Slack](https://slack.com)
- [Ssh-chat](https://github.com/shazow/ssh-chat)
@@ -111,6 +111,7 @@ And more...
- [Minecraft](https://github.com/elytra/MatterLink)
- [Reddit](https://github.com/bonehurtingjuice/mattereddit)
- [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
- [MatterAMXX](https://github.com/GabeIggy/MatterAMXX)
### API
@@ -151,14 +152,15 @@ See https://github.com/42wim/matterbridge/wiki
### Binaries
- Latest stable release [v1.17.5](https://github.com/42wim/matterbridge/releases/latest)
- Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
- Latest stable release [v1.18.0](https://github.com/42wim/matterbridge/releases/latest)
- Development releases (follows master) can be downloaded [here](https://github.com/42wim/matterbridge/actions) selecting the latest green build and then artifacts.
To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
### Packages
- [Overview](https://repology.org/metapackage/matterbridge/versions)
- [snap](https://snapcraft.io/matterbridge)
## Building
@@ -306,6 +308,7 @@ See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ)
- https://kopano.com/blog/matterbridge-bridging-mattermost-chat/
- https://www.stitcher.com/s/?eid=52382713
- https://daniele.tech/2019/02/how-to-use-matterbridge-to-connect-2-different-slack-workspaces/
- https://userlinux.net/mattermost-and-matterbridge.html
## Thanks

View File

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

View File

@@ -86,8 +86,16 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map
return nil
}
func (b *Bridge) GetConfigKey(key string) string {
return b.Account + "." + key
}
func (b *Bridge) IsKeySet(key string) bool {
return b.Config.IsKeySet(b.GetConfigKey(key)) || b.Config.IsKeySet("general."+key)
}
func (b *Bridge) GetBool(key string) bool {
val, ok := b.Config.GetBool(b.Account + "." + key)
val, ok := b.Config.GetBool(b.GetConfigKey(key))
if !ok {
val, _ = b.Config.GetBool("general." + key)
}
@@ -95,7 +103,7 @@ func (b *Bridge) GetBool(key string) bool {
}
func (b *Bridge) GetInt(key string) int {
val, ok := b.Config.GetInt(b.Account + "." + key)
val, ok := b.Config.GetInt(b.GetConfigKey(key))
if !ok {
val, _ = b.Config.GetInt("general." + key)
}
@@ -103,7 +111,7 @@ func (b *Bridge) GetInt(key string) int {
}
func (b *Bridge) GetString(key string) string {
val, ok := b.Config.GetString(b.Account + "." + key)
val, ok := b.Config.GetString(b.GetConfigKey(key))
if !ok {
val, _ = b.Config.GetString("general." + key)
}
@@ -111,7 +119,7 @@ func (b *Bridge) GetString(key string) string {
}
func (b *Bridge) GetStringSlice(key string) []string {
val, ok := b.Config.GetStringSlice(b.Account + "." + key)
val, ok := b.Config.GetStringSlice(b.GetConfigKey(key))
if !ok {
val, _ = b.Config.GetStringSlice("general." + key)
}
@@ -119,7 +127,7 @@ func (b *Bridge) GetStringSlice(key string) []string {
}
func (b *Bridge) GetStringSlice2D(key string) [][]string {
val, ok := b.Config.GetStringSlice2D(b.Account + "." + key)
val, ok := b.Config.GetStringSlice2D(b.GetConfigKey(key))
if !ok {
val, _ = b.Config.GetStringSlice2D("general." + key)
}

View File

@@ -3,6 +3,7 @@ package config
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
@@ -93,6 +94,7 @@ type Protocol struct {
JoinDelay string // all protocols
Label string // all protocols
Login string // mattermost, matrix
LogFile string // general
MediaDownloadBlackList []string
MediaDownloadPath string // Basically MediaServerUpload, but instead of uploading it, just write it to a file on the same server.
MediaDownloadSize int // all protocols
@@ -219,6 +221,7 @@ type BridgeValues struct {
type Config interface {
Viper() *viper.Viper
BridgeValues() *BridgeValues
IsKeySet(key string) bool
GetBool(key string) (bool, bool)
GetInt(key string) (int, bool)
GetString(key string) (string, bool)
@@ -246,6 +249,15 @@ func NewConfig(rootLogger *logrus.Logger, cfgfile string) Config {
cfgtype := detectConfigType(cfgfile)
mycfg := newConfigFromString(logger, input, cfgtype)
if mycfg.cv.General.LogFile != "" {
logfile, err := os.OpenFile(mycfg.cv.General.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if err == nil {
logger.Info("Opening log file ", mycfg.cv.General.LogFile)
rootLogger.Out = logfile
} else {
logger.Warn("Failed to open ", mycfg.cv.General.LogFile)
}
}
if mycfg.cv.General.MediaDownloadSize == 0 {
mycfg.cv.General.MediaDownloadSize = 1000000
}
@@ -303,6 +315,12 @@ func (c *config) Viper() *viper.Viper {
return c.v
}
func (c *config) IsKeySet(key string) bool {
c.RLock()
defer c.RUnlock()
return c.v.IsSet(key)
}
func (c *config) GetBool(key string) (bool, bool) {
c.RLock()
defer c.RUnlock()
@@ -362,6 +380,11 @@ type TestConfig struct {
Overrides map[string]interface{}
}
func (c *TestConfig) IsKeySet(key string) bool {
_, ok := c.Overrides[key]
return ok || c.Config.IsKeySet(key)
}
func (c *TestConfig) GetBool(key string) (bool, bool) {
val, ok := c.Overrides[key]
if ok {

View File

@@ -206,7 +206,7 @@ func (b *Birc) doSend() {
for msg := range b.Local {
<-throttle.C
username := msg.Username
if b.GetBool("Colornicks") {
if b.GetBool("Colornicks") && len(username) > 1 {
checksum := crc32.ChecksumIEEE([]byte(msg.Username))
colorCode := checksum%14 + 2 // quick fix - prevent white or black color codes
username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, msg.Username)
@@ -250,6 +250,8 @@ func (b *Birc) getClient() (*girc.Client, error) {
SSL: b.GetBool("UseTLS"),
TLSConfig: &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server}, //nolint:gosec
PingDelay: time.Minute,
// skip gIRC internal rate limiting, since we have our own throttling
AllowFlood: true,
})
return i, nil
}

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

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

View File

@@ -322,6 +322,9 @@ func (b *Btelegram) handleEdit(msg *config.Message, chatid int64) (string, error
case "Markdown":
b.Log.Debug("Using mode markdown")
m.ParseMode = tgbotapi.ModeMarkdown
case MarkdownV2:
b.Log.Debug("Using mode MarkdownV2")
m.ParseMode = MarkdownV2
}
if strings.ToLower(b.GetString("MessageFormat")) == HTMLNick {
b.Log.Debug("Using mode HTML - nick only")

View File

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

View File

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

View File

@@ -1,3 +1,29 @@
# v1.18.0
## New features
- nctalk: new protocol added. Add Nextcloud Talk support #1167
- general: Add an option to log into a file rather than stdout (#1168)
- api: Add websocket to API (#970)
## Enhancements
- telegram: Fix MarkdownV2 support in Telegram (#1169)
- whatsapp: Reload user information when a new contact is detected (whatsapp) (#1160)
- api: Add sane RemoteNickFormat default for API (#1157)
- irc: Skip gIRC built-in rate limiting (irc) (#1164)
- irc: Only colour IRC nicks if there is one. (#1161)
- docker: Combine runs to one layer (#1151)
## Bugfix
- general: Update dependencies for 1.18.0 release (#1175)
Discord users are encouraged to upgrade, this release works with the move to the discord.com domain.
This release couldn't exist without the following contributors:
@42wim, @jlu5, @qaisjp, @TheHolyRoger, @SuperSandro2000, @gary-kim, @z3bra, @greenx, @haykam821, @nathanaelhoun
# v1.17.5
## Enhancements

View File

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

View File

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

View File

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

View File

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

20
go.mod
View File

@@ -6,12 +6,12 @@ require (
github.com/Jeffail/gabs v1.1.1 // indirect
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0
github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334
github.com/d5/tengo/v2 v2.4.2
github.com/d5/tengo/v2 v2.6.0
github.com/davecgh/go-spew v1.1.1
github.com/fsnotify/fsnotify v1.4.9
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497
github.com/google/gops v0.3.6
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81
github.com/gomarkdown/markdown v0.0.0-20200609195525-3f9352745725
github.com/google/gops v0.3.10
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect
github.com/gorilla/schema v1.1.0
github.com/gorilla/websocket v1.4.2
@@ -22,7 +22,7 @@ require (
github.com/labstack/echo/v4 v4.1.16
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3
github.com/matterbridge/discordgo v0.21.2-0.20200718144317-01fe5db6c78d
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible
github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6
@@ -45,21 +45,19 @@ require (
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98
github.com/sirupsen/logrus v1.6.0
github.com/slack-go/slack v0.6.4
github.com/slack-go/slack v0.6.5
github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.5.1
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/writeas/go-strip-markdown v2.0.1+incompatible
github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
github.com/yaegashi/msgraph.go v0.1.2
github.com/yaegashi/msgraph.go v0.1.3
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2
golang.org/x/image v0.0.0-20200430140353-33d19683fad8
golang.org/x/image v0.0.0-20200618115811-c13761719519
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
gomod.garykim.dev/nc-talk v0.0.1
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)
//replace github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43
go 1.13

78
go.sum
View File

@@ -30,10 +30,9 @@ github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/g
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU=
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw=
github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM=
github.com/StackExchange/wmi v0.0.0-20170410192909-ea383cf3ba6e/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc=
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -49,8 +48,8 @@ github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/d5/tengo/v2 v2.4.2 h1:Sj+v0CS8jtrZrXyJLzWof6QTnE6S5k3tYP0y6YVPGkw=
github.com/d5/tengo/v2 v2.4.2/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
github.com/d5/tengo/v2 v2.6.0 h1:D0cJtpiBzaLJ/Smv6nnUc/LIfO46oKwDx85NZtIRNRI=
github.com/d5/tengo/v2 v2.6.0/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -58,7 +57,6 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -67,10 +65,10 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81 h1:FdZThbRF0R+2qgyBl3KCVNWWBmKm68E+stT3rnQ02Ww=
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81/go.mod h1:lDm2E64X4OjFdBUA4hlN4mEvbSitvhJdKw7rsA8KHgI=
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -82,18 +80,17 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497 h1:wJkj+x9gPYlDyM34C6r3SXPs270coWeh85wu1CsusDo=
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
github.com/gomarkdown/markdown v0.0.0-20200609195525-3f9352745725 h1:X6sZdr+t2E2jwajTy/FfXbmAKPFTYxEq9hiFgzMiuPQ=
github.com/gomarkdown/markdown v0.0.0-20200609195525-3f9352745725/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gops v0.3.6 h1:6akvbMlpZrEYOuoebn2kR+ZJekbZqJ28fJXTs84+8to=
github.com/google/gops v0.3.6/go.mod h1:RZ1rH95wsAGX4vMWKmqBOIWynmWisBf4QFdgT/k/xOI=
github.com/google/gops v0.3.10 h1:M2XZYgfUW+P7AOCLiu4CRb0rQfwnslLyB4B9Mp0vXmE=
github.com/google/gops v0.3.10/go.mod h1:38bMPVKFh+1X106CPpbLAWtZIR1+xwgzT9gew0kn6w4=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -107,9 +104,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@@ -151,15 +146,11 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da h1:LK+8uBG3kNikj664cjFt88RBmuGmonxkXv2rUVfbqz4=
github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da/go.mod h1:xJA+X9ZVyT/irGldcb7q1XnJBq5F9s5H9h2L44Y+poY=
github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999 h1:2d+FLQbz4xRTi36DO1qYNUwfORax9XcQ0jhbO81Vago=
github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -179,8 +170,8 @@ github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzR
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048 h1:B9HaistmV+MD8/33BXmZe1zPIn+RImAFVXNNSOrwU2E=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 h1:VP/DNRn2HtrVRN6+X3h4FDcQI2OOKT+88WUi21ZD1Kw=
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3/go.mod h1:5a1bHtG/38ofcx9cgwM5eTW/Pl4SpbQksNDnTRcGA2Y=
github.com/matterbridge/discordgo v0.21.2-0.20200718144317-01fe5db6c78d h1:NBckP4nw7qVspbt7cOZYsrOrEbq7tATdMjSjc1hW63A=
github.com/matterbridge/discordgo v0.21.2-0.20200718144317-01fe5db6c78d/go.mod h1:411nZYv0UMMrtppR5glXop1foboJiFAowy+42U+Ahvw=
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible h1:oaOqwbg5HxHRxvAbd84ks0Okwoc1ISyUZ87EiVJFhGI=
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible/go.mod h1:igE6rUAn3jai2wCdsjFHfhUoekjrFthoEjFObKKwSb4=
github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050 h1:kWkP1lXpkvtoNL08jkP3XQH/zvDOEXJpdCJd/DlIvMw=
@@ -224,6 +215,8 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/monaco-io/request v1.0.3 h1:FsiIwXCCbHEyWx9A7lgg6JBTMHhHlEEsADsgAOvZ9HA=
github.com/monaco-io/request v1.0.3/go.mod h1:EmggwHktBsbJmCgwZXqy7o0H1NNsAstQBWZrFVd3xtQ=
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 h1:oKIteTqeSpenyTrOVj5zkiyCaflLa8B+CD0324otT+o=
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff h1:HLGD5/9UxxfEuO9DtP8gnTmNtMxbPyhYltfxsITel8g=
@@ -278,15 +271,14 @@ github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zK
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI=
github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98 h1:sN07ff+PSRsUNhpSod4uGKAQ+Nc0FXsBPG9FmYMNg4w=
github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98/go.mod h1:xkTgfD+WP+KR4HuG76oal25BBEeu5kJyi2EOsgiu/4Q=
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shirou/gopsutil v2.20.4+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
github.com/slack-go/slack v0.6.4 h1:cxOqFgM5RW6mdEyDqAJutFk3qiORK9oHRKi5bPqkY9o=
github.com/slack-go/slack v0.6.4/go.mod h1:sGRjv3w+ERAUMMMbldHObQPBcNSyVB7KLKYfnwUFBfw=
github.com/slack-go/slack v0.6.5 h1:IkDKtJ2IROJNoe3d6mW870/NRKvq2fhLB/Q5XmzWk00=
github.com/slack-go/slack v0.6.5/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
@@ -307,7 +299,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -326,9 +317,11 @@ github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9p
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/yaegashi/msgraph.go v0.1.2 h1:83uVRQaj8YBsVqOUGj0WRwzxdgGF69jRpg5IQYaTvoY=
github.com/yaegashi/msgraph.go v0.1.2/go.mod h1:Lp39e9oo596G5FcmMKI0cXR3mg/QikSdabgZdbMqbAM=
github.com/xlab/treeprint v1.0.0/go.mod h1:IoImgRak9i3zJyuxOKUP1v4UZd1tMoKkq/Cimt1uhCg=
github.com/yaegashi/msgraph.go v0.1.3 h1:xeknrGbPGqUVvjjtXGuxvesHLNJ6jEfiOtqJToZe0qw=
github.com/yaegashi/msgraph.go v0.1.3/go.mod h1:dpty8G9hMEC1xBQeXp6Z2hCXKalqczk2BLvK9me/TUU=
github.com/yaegashi/wtz.go v0.0.2/go.mod h1:nOLA5QXsmdkRxBkP5tljhua13ADHCKirLBrzPf4PEJc=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2 h1:UQwvu7FjUEdVYofx0U6bsc5odNE7wa5TSA0fl559GcA=
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -343,14 +336,15 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -358,8 +352,8 @@ golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm0
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34=
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -371,6 +365,7 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -400,8 +395,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20171017063910-8dbc5d05d6ed/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -422,8 +417,9 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
@@ -447,14 +443,19 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200529172331-a64b76657301/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomod.garykim.dev/nc-talk v0.0.1 h1:6mgjcAf5/HMkV0CFGeXVfYHG7FAUCQcGR8eg9oM6fCc=
gomod.garykim.dev/nc-talk v0.0.1/go.mod h1:0/Ksg0osAYmnWKs1OcCG+gBQ4HU1xiF1699g9B6jWZw=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
@@ -489,7 +490,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -498,4 +498,4 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/goversion v1.0.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=

View File

@@ -16,7 +16,7 @@ import (
)
var (
version = "1.17.5"
version = "1.18.0"
githash string
flagConfig = flag.String("conf", "matterbridge.toml", "config file")
@@ -51,6 +51,15 @@ func main() {
cfg := config.NewConfig(rootLogger, *flagConfig)
cfg.BridgeValues().General.Debug = *flagDebug
// if logging to a file, ensure it is closed when the program terminates
// nolint:errcheck
defer func() {
if f, ok := rootLogger.Out.(*os.File); ok {
f.Sync()
f.Close()
}
}()
r, err := gateway.NewRouter(rootLogger, cfg, bridgemap.FullMap)
if err != nil {
logger.Fatalf("Starting gateway failed: %s", err)

View File

@@ -1383,7 +1383,22 @@ StripNick=false
#OPTIONAL (default false)
ShowTopicChange=false
###################################################################
#
# NCTalk (Nextcloud Talk)
#
###################################################################
[nctalk.bridge]
# Url of your Nextcloud server
Server = "https://cloud.youdomain.me"
# Username of the bot
Login = "talkuser"
# Password of the bot
Password = "talkuserpass"
###################################################################
#
@@ -1604,6 +1619,14 @@ MediaDownloadBlacklist=[".html$",".htm$"]
#OPTIONAL (default false)
IgnoreFailureOnStart=false
#LogFile defines the location of a file to write logs into, rather
#than stdout.
#Logging will still happen on stdout if the file cannot be open for
#writing, or if the value is empty. Note that the log won't roll, so
#you might want to use logrotate(8) with this feature.
#OPTIONAL (default empty)
LogFile=/var/log/matterbridge.log
###################################################################
#Tengo configuration
###################################################################
@@ -1694,7 +1717,7 @@ enable=true
# REQUIRED
account="irc.freenode"
# The channel key in each gateway is mapped to a similar group chat ID on the chat platform
# The channel key in each gateway is mapped to a similar group chat ID on the chat platform
# To find the group chat ID for different platforms, refer to the table below
#
# Platform | Identifier name | Example | Description
@@ -1722,6 +1745,8 @@ enable=true
# -------------------------------------------------------------------------------------------------------------------------------------
# steam | chatid | example needed | The number in the URL when you click "enter chat room" in the browser
# -------------------------------------------------------------------------------------------------------------------------------------
# nctalk | token | xs25tz5y | The token in the URL when you are in a chat. It will be the last part of the URL.
# -------------------------------------------------------------------------------------------------------------------------------------
# telegram | chatid | -123456789 | A large negative number. see https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau
# -------------------------------------------------------------------------------------------------------------------------------------
# whatsapp | group JID | 48111222333-123455678999@g.us | A unique group JID. If you specify an empty string, bridge will list all the possibilities

View File

@@ -506,7 +506,11 @@ func (c *Compiler) Compile(node parser.Node) error {
return err
}
}
c.emit(node, parser.OpCall, len(node.Args))
ellipsis := 0
if node.Ellipsis.IsValid() {
ellipsis = 1
}
c.emit(node, parser.OpCall, len(node.Args), ellipsis)
case *parser.ImportExpr:
if node.ModuleName == "" {
return c.errorf(node, "empty module name")
@@ -526,7 +530,7 @@ func (c *Compiler) Compile(node parser.Node) error {
return err
}
c.emit(node, parser.OpConstant, c.addConstant(compiled))
c.emit(node, parser.OpCall, 0)
c.emit(node, parser.OpCall, 0, 0)
case Object: // builtin module
c.emit(node, parser.OpConstant, c.addConstant(v))
default:
@@ -556,7 +560,7 @@ func (c *Compiler) Compile(node parser.Node) error {
return err
}
c.emit(node, parser.OpConstant, c.addConstant(compiled))
c.emit(node, parser.OpCall, 0)
c.emit(node, parser.OpCall, 0, 0)
} else {
return c.errorf(node, "module '%s' not found", node.ModuleName)
}

View File

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

View File

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

View File

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

View File

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

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

@@ -537,12 +537,36 @@ func (v *VM) run() {
}
case parser.OpCall:
numArgs := int(v.curInsts[v.ip+1])
v.ip++
spread := int(v.curInsts[v.ip+2])
v.ip += 2
value := v.stack[v.sp-1-numArgs]
if !value.CanCall() {
v.err = fmt.Errorf("not callable: %s", value.TypeName())
return
}
if spread == 1 {
v.sp--
switch arr := v.stack[v.sp].(type) {
case *Array:
for _, item := range arr.Value {
v.stack[v.sp] = item
v.sp++
}
numArgs += len(arr.Value) - 1
case *ImmutableArray:
for _, item := range arr.Value {
v.stack[v.sp] = item
v.sp++
}
numArgs += len(arr.Value) - 1
default:
v.err = fmt.Errorf("not an array: %s", arr.TypeName())
return
}
}
if callee, ok := value.(*CompiledFunction); ok {
if callee.VarArgs {
// if the closure is variadic,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -326,6 +326,7 @@ func link(p *Parser, data []byte, offset int) (int, ast.Node) {
i = skipSpace(data, i)
linkB := i
brace := 0
// look for link end: ' " )
findlinkend:
@@ -334,7 +335,18 @@ func link(p *Parser, data []byte, offset int) (int, ast.Node) {
case data[i] == '\\':
i += 2
case data[i] == ')' || data[i] == '\'' || data[i] == '"':
case data[i] == '(':
brace++
i++
case data[i] == ')':
if brace <= 0 {
break findlinkend
}
brace--
i++
case data[i] == '\'' || data[i] == '"':
break findlinkend
default:
@@ -352,19 +364,21 @@ func link(p *Parser, data []byte, offset int) (int, ast.Node) {
if data[i] == '\'' || data[i] == '"' {
i++
titleB = i
titleEndCharFound := false
findtitleend:
for i < len(data) {
switch {
case data[i] == '\\':
i += 2
case data[i] == ')':
break findtitleend
default:
i++
case data[i] == data[titleB-1]: // matching title delimiter
titleEndCharFound = true
case titleEndCharFound && data[i] == ')':
break findtitleend
}
i++
}
if i >= len(data) {

View File

@@ -20,12 +20,13 @@ import (
"runtime/pprof"
"runtime/trace"
"strconv"
"strings"
"sync"
"syscall"
"time"
"github.com/google/gops/internal"
"github.com/google/gops/signal"
"github.com/kardianos/osext"
)
const defaultAddr = "127.0.0.1:0"
@@ -116,18 +117,21 @@ func listen() {
for {
fd, err := listener.Accept()
if err != nil {
fmt.Fprintf(os.Stderr, "gops: %v", err)
// No great way to check for this, see https://golang.org/issues/4373.
if !strings.Contains(err.Error(), "use of closed network connection") {
fmt.Fprintf(os.Stderr, "gops: %v\n", err)
}
if netErr, ok := err.(net.Error); ok && !netErr.Temporary() {
break
}
continue
}
if _, err := fd.Read(buf); err != nil {
fmt.Fprintf(os.Stderr, "gops: %v", err)
fmt.Fprintf(os.Stderr, "gops: %v\n", err)
continue
}
if err := handle(fd, buf); err != nil {
fmt.Fprintf(os.Stderr, "gops: %v", err)
fmt.Fprintf(os.Stderr, "gops: %v\n", err)
continue
}
fd.Close()
@@ -136,12 +140,16 @@ func listen() {
func gracefulShutdown() {
c := make(chan os.Signal, 1)
gosignal.Notify(c, os.Interrupt)
gosignal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
go func() {
// cleanup the socket on shutdown.
<-c
sig := <-c
Close()
os.Exit(1)
ret := 1
if sig == syscall.SIGTERM {
ret = 0
}
os.Exit(ret)
}()
}
@@ -220,7 +228,7 @@ func handle(conn io.ReadWriter, msg []byte) error {
case signal.Version:
fmt.Fprintf(conn, "%v\n", runtime.Version())
case signal.HeapProfile:
pprof.WriteHeapProfile(conn)
return pprof.WriteHeapProfile(conn)
case signal.CPUProfile:
if err := pprof.StartCPUProfile(conn); err != nil {
return err
@@ -233,7 +241,7 @@ func handle(conn io.ReadWriter, msg []byte) error {
fmt.Fprintf(conn, "GOMAXPROCS: %v\n", runtime.GOMAXPROCS(0))
fmt.Fprintf(conn, "num CPU: %v\n", runtime.NumCPU())
case signal.BinaryDump:
path, err := osext.Executable()
path, err := os.Executable()
if err != nil {
return err
}
@@ -246,7 +254,9 @@ func handle(conn io.ReadWriter, msg []byte) error {
_, err = bufio.NewReader(f).WriteTo(conn)
return err
case signal.Trace:
trace.Start(conn)
if err := trace.Start(conn); err != nil {
return err
}
time.Sleep(5 * time.Second)
trace.Stop()
case signal.SetGCPercent:

View File

@@ -6,12 +6,12 @@ package internal
import (
"errors"
"fmt"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"runtime"
"strconv"
"strings"
)
@@ -22,9 +22,18 @@ func ConfigDir() (string, error) {
return configDir, nil
}
if osUserConfigDir := getOSUserConfigDir(); osUserConfigDir != "" {
return filepath.Join(osUserConfigDir, "gops"), nil
}
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), "gops"), nil
}
if xdgConfigDir := os.Getenv("XDG_CONFIG_HOME"); xdgConfigDir != "" {
return filepath.Join(xdgConfigDir, "gops"), nil
}
homeDir := guessUnixHomeDir()
if homeDir == "" {
return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty")
@@ -45,7 +54,7 @@ func PIDFile(pid int) (string, error) {
if err != nil {
return "", err
}
return fmt.Sprintf("%s/%d", gopsdir, pid), nil
return filepath.Join(gopsdir, strconv.Itoa(pid)), nil
}
func GetPort(pid int) (string, error) {

View File

@@ -0,0 +1,19 @@
// Copyright 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 go1.13
package internal
import (
"os"
)
func getOSUserConfigDir() string {
configDir, err := os.UserConfigDir()
if err != nil {
return ""
}
return configDir
}

View File

@@ -0,0 +1,11 @@
// Copyright 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 !go1.13
package internal
func getOSUserConfigDir() string {
return ""
}

View File

@@ -1,27 +0,0 @@
Copyright (c) 2012 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.

View File

@@ -1,21 +0,0 @@
### Extensions to the "os" package.
[![GoDoc](https://godoc.org/github.com/kardianos/osext?status.svg)](https://godoc.org/github.com/kardianos/osext)
## Find the current Executable and ExecutableFolder.
As of go1.8 the Executable function may be found in `os`. The Executable function
in the std lib `os` package is used if available.
There is sometimes utility in finding the current executable file
that is running. This can be used for upgrading the current executable
or finding resources located relative to the executable file. Both
working directory and the os.Args[0] value are arbitrary and cannot
be relied on; os.Args[0] can be "faked".
Multi-platform and supports:
* Linux
* OS X
* Windows
* Plan 9
* BSDs.

View File

@@ -1,33 +0,0 @@
// Copyright 2012 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.
// Extensions to the standard "os" package.
package osext // import "github.com/kardianos/osext"
import "path/filepath"
var cx, ce = executableClean()
func executableClean() (string, error) {
p, err := executable()
return filepath.Clean(p), err
}
// Executable returns an absolute path that can be used to
// re-invoke the current program.
// It may not be valid after the current program exits.
func Executable() (string, error) {
return cx, ce
}
// Returns same path as Executable, returns just the folder
// path. Excludes the executable name and any trailing slash.
func ExecutableFolder() (string, error) {
p, err := Executable()
if err != nil {
return "", err
}
return filepath.Dir(p), nil
}

View File

@@ -1,9 +0,0 @@
//+build go1.8,!openbsd
package osext
import "os"
func executable() (string, error) {
return os.Executable()
}

View File

@@ -1,22 +0,0 @@
// Copyright 2012 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 !go1.8
package osext
import (
"os"
"strconv"
"syscall"
)
func executable() (string, error) {
f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
if err != nil {
return "", err
}
defer f.Close()
return syscall.Fd2path(int(f.Fd()))
}

View File

@@ -1,36 +0,0 @@
// Copyright 2012 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 !go1.8,android !go1.8,linux !go1.8,netbsd !go1.8,solaris !go1.8,dragonfly
package osext
import (
"errors"
"fmt"
"os"
"runtime"
"strings"
)
func executable() (string, error) {
switch runtime.GOOS {
case "linux", "android":
const deletedTag = " (deleted)"
execpath, err := os.Readlink("/proc/self/exe")
if err != nil {
return execpath, err
}
execpath = strings.TrimSuffix(execpath, deletedTag)
execpath = strings.TrimPrefix(execpath, deletedTag)
return execpath, nil
case "netbsd":
return os.Readlink("/proc/curproc/exe")
case "dragonfly":
return os.Readlink("/proc/curproc/file")
case "solaris":
return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid()))
}
return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
}

View File

@@ -1,126 +0,0 @@
// Copyright 2012 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 !go1.8,darwin !go1.8,freebsd openbsd
package osext
import (
"os"
"os/exec"
"path/filepath"
"runtime"
"syscall"
"unsafe"
)
var initCwd, initCwdErr = os.Getwd()
func executable() (string, error) {
var mib [4]int32
switch runtime.GOOS {
case "freebsd":
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
case "darwin":
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
case "openbsd":
mib = [4]int32{1 /* CTL_KERN */, 55 /* KERN_PROC_ARGS */, int32(os.Getpid()), 1 /* KERN_PROC_ARGV */}
}
n := uintptr(0)
// Get length.
_, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
if errNum != 0 {
return "", errNum
}
if n == 0 { // This shouldn't happen.
return "", nil
}
buf := make([]byte, n)
_, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
if errNum != 0 {
return "", errNum
}
if n == 0 { // This shouldn't happen.
return "", nil
}
var execPath string
switch runtime.GOOS {
case "openbsd":
// buf now contains **argv, with pointers to each of the C-style
// NULL terminated arguments.
var args []string
argv := uintptr(unsafe.Pointer(&buf[0]))
Loop:
for {
argp := *(**[1 << 20]byte)(unsafe.Pointer(argv))
if argp == nil {
break
}
for i := 0; uintptr(i) < n; i++ {
// we don't want the full arguments list
if string(argp[i]) == " " {
break Loop
}
if argp[i] != 0 {
continue
}
args = append(args, string(argp[:i]))
n -= uintptr(i)
break
}
if n < unsafe.Sizeof(argv) {
break
}
argv += unsafe.Sizeof(argv)
n -= unsafe.Sizeof(argv)
}
execPath = args[0]
// There is no canonical way to get an executable path on
// OpenBSD, so check PATH in case we are called directly
if execPath[0] != '/' && execPath[0] != '.' {
execIsInPath, err := exec.LookPath(execPath)
if err == nil {
execPath = execIsInPath
}
}
default:
for i, v := range buf {
if v == 0 {
buf = buf[:i]
break
}
}
execPath = string(buf)
}
var err error
// execPath will not be empty due to above checks.
// Try to get the absolute path if the execPath is not rooted.
if execPath[0] != '/' {
execPath, err = getAbs(execPath)
if err != nil {
return execPath, err
}
}
// For darwin KERN_PROCARGS may return the path to a symlink rather than the
// actual executable.
if runtime.GOOS == "darwin" {
if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
return execPath, err
}
}
return execPath, nil
}
func getAbs(execPath string) (string, error) {
if initCwdErr != nil {
return execPath, initCwdErr
}
// The execPath may begin with a "../" or a "./" so clean it first.
// Join the two paths, trailing and starting slashes undetermined, so use
// the generic Join function.
return filepath.Join(initCwd, filepath.Clean(execPath)), nil
}

View File

@@ -1,36 +0,0 @@
// Copyright 2012 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 !go1.8
package osext
import (
"syscall"
"unicode/utf16"
"unsafe"
)
var (
kernel = syscall.MustLoadDLL("kernel32.dll")
getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
)
// GetModuleFileName() with hModule = NULL
func executable() (exePath string, err error) {
return getModuleFileName()
}
func getModuleFileName() (string, error) {
var n uint32
b := make([]uint16, syscall.MAX_PATH)
size := uint32(len(b))
r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
n = uint32(r0)
if n == 0 {
return "", e1
}
return string(utf16.Decode(b[0:n])), nil
}

2
vendor/github.com/matterbridge/discordgo/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
# IDE-specific metadata
.idea/

View File

@@ -1,8 +1,9 @@
language: go
go:
- 1.9.x
- 1.10.x
- 1.11.x
- 1.12.x
- 1.13.x
install:
- go get github.com/bwmarrin/discordgo
- go get -v .

View File

@@ -1,16 +1,16 @@
# DiscordGo
[![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) [![Go report](http://goreportcard.com/badge/bwmarrin/discordgo)](http://goreportcard.com/report/bwmarrin/discordgo) [![Build Status](https://travis-ci.org/bwmarrin/discordgo.svg?branch=master)](https://travis-ci.org/bwmarrin/discordgo) [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)](https://discord.gg/0f1SbxBZjYoCtNPP) [![Discord API](https://img.shields.io/badge/Discord%20API-%23go_discordgo-blue.svg)](https://discordapp.com/invite/discord-api)
[![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) [![Go report](http://goreportcard.com/badge/bwmarrin/discordgo)](http://goreportcard.com/report/bwmarrin/discordgo) [![Build Status](https://travis-ci.org/bwmarrin/discordgo.svg?branch=master)](https://travis-ci.org/bwmarrin/discordgo) [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)](https://discord.gg/0f1SbxBZjYoCtNPP) [![Discord API](https://img.shields.io/badge/Discord%20API-%23go_discordgo-blue.svg)](https://discord.com/invite/discord-api)
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">
DiscordGo is a [Go](https://golang.org/) package that provides low level
bindings to the [Discord](https://discordapp.com/) chat client API. DiscordGo
bindings to the [Discord](https://discord.com/) chat client API. DiscordGo
has nearly complete support for all of the Discord API endpoints, websocket
interface, and voice interface.
If you would like to help the DiscordGo package please use
[this link](https://discordapp.com/oauth2/authorize?client_id=173113690092994561&scope=bot)
[this link](https://discord.com/oauth2/authorize?client_id=173113690092994561&scope=bot)
to add the official DiscordGo test bot **dgo** to your server. This provides
indispensable help to this project.

View File

@@ -17,11 +17,12 @@ import (
"errors"
"fmt"
"net/http"
"runtime"
"time"
)
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
const VERSION = "0.20.2"
const VERSION = "0.21.1"
// ErrMFA will be risen by New when the user has 2FA.
var ErrMFA = errors.New("account has 2FA enabled")
@@ -30,10 +31,13 @@ var ErrMFA = errors.New("account has 2FA enabled")
// tasks if given enough information to do so. Currently you can pass zero
// arguments and it will return an empty Discord session.
// There are 3 ways to call New:
// With a single auth token - All requests will use the token blindly,
// With a single auth token - All requests will use the token blindly
// (just tossing it into the HTTP Authorization header);
// no verification of the token will be done and requests may fail.
// IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT `
// eg: `"Bot <token>"`
// IF IT IS AN OAUTH2 ACCESS TOKEN, IT MUST BE PREFIXED WITH `Bearer `
// eg: `"Bearer <token>"`
// With an email and password - Discord will sign in with the provided
// credentials.
// With an email, password and auth token - Discord will verify the auth
@@ -63,6 +67,15 @@ func New(args ...interface{}) (s *Session, err error) {
LastHeartbeatAck: time.Now().UTC(),
}
// Initilize the Identify Package with defaults
// These can be modified prior to calling Open()
s.Identify.Compress = true
s.Identify.LargeThreshold = 250
s.Identify.GuildSubscriptions = true
s.Identify.Properties.OS = runtime.GOOS
s.Identify.Properties.Browser = "DiscordGo v" + VERSION
s.Identify.Intents = MakeIntent(IntentsAllWithoutPrivileged)
// If no arguments are passed return the empty Session interface.
if args == nil {
return
@@ -94,7 +107,8 @@ func New(args ...interface{}) (s *Session, err error) {
// If third string exists, it must be an auth token.
if len(v) > 2 {
s.Token = v[2]
s.Identify.Token = v[2]
s.Token = v[2] // TODO: Remove, Deprecated - Kept for backwards compatibility.
}
case string:
@@ -107,7 +121,8 @@ func New(args ...interface{}) (s *Session, err error) {
} else if pass == "" {
pass = v
} else if s.Token == "" {
s.Token = v
s.Identify.Token = v
s.Token = v // TODO: Remove, Deprecated - Kept for backwards compatibility.
} else {
err = fmt.Errorf("too many string parameters provided")
return
@@ -127,10 +142,12 @@ func New(args ...interface{}) (s *Session, err error) {
// Discord will verify it for free, or log the user in if it is
// invalid.
if pass == "" {
s.Token = auth
s.Identify.Token = auth
s.Token = auth // TODO: Remove, Deprecated - Kept for backwards compatibility.
} else {
err = s.Login(auth, pass)
if err != nil || s.Token == "" {
// TODO: Remove last s.Token part, Deprecated - Kept for backwards compatibility.
if err != nil || s.Identify.Token == "" || s.Token == "" {
if s.MFA {
err = ErrMFA
} else {
@@ -140,8 +157,5 @@ func New(args ...interface{}) (s *Session, err error) {
}
}
// The Session is now able to have RestAPI methods called on it.
// It is recommended that you now call Open() so that events will trigger.
return
}

View File

@@ -18,12 +18,12 @@ var APIVersion = "6"
// Known Discord API Endpoints.
var (
EndpointStatus = "https://status.discordapp.com/api/v2/"
EndpointStatus = "https://status.discord.com/api/v2/"
EndpointSm = EndpointStatus + "scheduled-maintenances/"
EndpointSmActive = EndpointSm + "active.json"
EndpointSmUpcoming = EndpointSm + "upcoming.json"
EndpointDiscord = "https://discordapp.com/"
EndpointDiscord = "https://discord.com/"
EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/"
EndpointGuilds = EndpointAPI + "guilds/"
EndpointChannels = EndpointAPI + "channels/"

View File

@@ -110,7 +110,7 @@ func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() {
// })
//
// List of events can be found at this page, with corresponding names in the
// library for each event: https://discordapp.com/developers/docs/topics/gateway#event-names
// library for each event: https://discord.com/developers/docs/topics/gateway#event-names
// There are also synthetic events fired by the library internally which are
// available for handling, like Connect, Disconnect, and RateLimit.
// events.go contains all of the Discord WSAPI and synthetic events that can be handled.

View File

@@ -139,8 +139,11 @@ type GuildEmojisUpdate struct {
// A GuildMembersChunk is the data for a GuildMembersChunk event.
type GuildMembersChunk struct {
GuildID string `json:"guild_id"`
Members []*Member `json:"members"`
GuildID string `json:"guild_id"`
Members []*Member `json:"members"`
ChunkIndex int `json:"chunk_index"`
ChunkCount int `json:"chunk_count"`
Presences []*Presence `json:"presences,omitempty"`
}
// GuildIntegrationsUpdate is the data for a GuildIntegrationsUpdate event.
@@ -169,6 +172,7 @@ type MessageUpdate struct {
// MessageDelete is the data for a MessageDelete event.
type MessageDelete struct {
*Message
BeforeDelete *Message `json:"-"`
}
// MessageReactionAdd is the data for a MessageReactionAdd event.

View File

@@ -1,6 +1,8 @@
module github.com/matterbridge/discordgo
require (
github.com/gorilla/websocket v1.4.0
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16
github.com/gorilla/websocket v1.4.2
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
)
go 1.10

View File

@@ -1,4 +1,10 @@
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@@ -37,7 +37,7 @@ const (
// Logger can be used to replace the standard logging for discordgo
var Logger func(msgL, caller int, format string, a ...interface{})
// msglog provides package wide logging consistancy for discordgo
// msglog provides package wide logging consistency for discordgo
// the format, a... portion this command follows that of fmt.Printf
// msgL : LogLevel of the message
// caller : 1 + the number of callers away from the message source

View File

@@ -63,7 +63,7 @@ type Message struct {
MentionRoles []string `json:"mention_roles"`
// Whether the message is text-to-speech.
Tts bool `json:"tts"`
TTS bool `json:"tts"`
// Whether the message mentions everyone.
MentionEveryone bool `json:"mention_everyone"`
@@ -129,10 +129,11 @@ type File struct {
// MessageSend stores all parameters you can send with ChannelMessageSendComplex.
type MessageSend struct {
Content string `json:"content,omitempty"`
Embed *MessageEmbed `json:"embed,omitempty"`
Tts bool `json:"tts"`
Files []*File `json:"-"`
Content string `json:"content,omitempty"`
Embed *MessageEmbed `json:"embed,omitempty"`
TTS bool `json:"tts"`
Files []*File `json:"-"`
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
// TODO: Remove this when compatibility is not required.
File *File `json:"-"`
@@ -141,8 +142,9 @@ type MessageSend struct {
// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which
// is also where you should get the instance from.
type MessageEdit struct {
Content *string `json:"content,omitempty"`
Embed *MessageEmbed `json:"embed,omitempty"`
Content *string `json:"content,omitempty"`
Embed *MessageEmbed `json:"embed,omitempty"`
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
ID string
Channel string
@@ -171,6 +173,42 @@ func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit {
return m
}
// AllowedMentionType describes the types of mentions used
// in the MessageAllowedMentions type.
type AllowedMentionType string
// The types of mentions used in MessageAllowedMentions.
const (
AllowedMentionTypeRoles AllowedMentionType = "roles"
AllowedMentionTypeUsers AllowedMentionType = "users"
AllowedMentionTypeEveryone AllowedMentionType = "everyone"
)
// MessageAllowedMentions allows the user to specify which mentions
// Discord is allowed to parse in this message. This is useful when
// sending user input as a message, as it prevents unwanted mentions.
// If this type is used, all mentions must be explicitly whitelisted,
// either by putting an AllowedMentionType in the Parse slice
// (allowing all mentions of that type) or, in the case of roles and
// users, explicitly allowing those mentions on an ID-by-ID basis.
// For more information on this functionality, see:
// https://discordapp.com/developers/docs/resources/channel#allowed-mentions-object-allowed-mentions-reference
type MessageAllowedMentions struct {
// The mention types that are allowed to be parsed in this message.
// Please note that this is purposely **not** marked as omitempty,
// so if a zero-value MessageAllowedMentions object is provided no
// mentions will be allowed.
Parse []AllowedMentionType `json:"parse"`
// A list of role IDs to allow. This cannot be used when specifying
// AllowedMentionTypeRoles in the Parse slice.
Roles []string `json:"roles,omitempty"`
// A list of user IDs to allow. This cannot be used when specifying
// AllowedMentionTypeUsers in the Parse slice.
Users []string `json:"users,omitempty"`
}
// A MessageAttachment stores data for message attachments.
type MessageAttachment struct {
ID string `json:"id"`

View File

@@ -38,7 +38,7 @@ var (
ErrPruneDaysBounds = errors.New("the number of days should be more than or equal to 1")
ErrGuildNoIcon = errors.New("guild does not have an icon set")
ErrGuildNoSplash = errors.New("guild does not have a splash set")
ErrUnauthorized = errors.New("HTTP request was unauthorized. This could be because the provided token was not a bot token. Please add \"Bot \" to the start of your token. https://discordapp.com/developers/docs/reference#authentication-example-bot-token-authorization-header")
ErrUnauthorized = errors.New("HTTP request was unauthorized. This could be because the provided token was not a bot token. Please add \"Bot \" to the start of your token. https://discord.com/developers/docs/reference#authentication-example-bot-token-authorization-header")
)
// Request is the same as RequestWithBucketID but the bucket id is the same as the urlStr
@@ -506,7 +506,7 @@ func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions
}
// Calculates the permissions for a member.
// https://support.discordapp.com/hc/en-us/articles/206141927-How-is-the-permission-hierarchy-structured-
// https://support.discord.com/hc/en-us/articles/206141927-How-is-the-permission-hierarchy-structured-
func memberPermissions(guild *Guild, channel *Channel, member *Member) (apermissions int) {
userID := member.User.ID
@@ -583,14 +583,6 @@ func memberPermissions(guild *Guild, channel *Channel, member *Member) (apermiss
// Guild returns a Guild structure of a specific Guild.
// guildID : The ID of a Guild
func (s *Session) Guild(guildID string) (st *Guild, err error) {
if s.StateEnabled {
// Attempt to grab the guild from State first.
st, err = s.State.Guild(guildID)
if err == nil && !st.Unavailable {
return
}
}
body, err := s.RequestWithBucketID("GET", EndpointGuild(guildID), nil, EndpointGuild(guildID))
if err != nil {
return
@@ -931,6 +923,8 @@ type GuildChannelCreateData struct {
Topic string `json:"topic,omitempty"`
Bitrate int `json:"bitrate,omitempty"`
UserLimit int `json:"user_limit,omitempty"`
RateLimitPerUser int `json:"rate_limit_per_user,omitempty"`
Position int `json:"position,omitempty"`
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"`
ParentID string `json:"parent_id,omitempty"`
NSFW bool `json:"nsfw,omitempty"`
@@ -1593,7 +1587,7 @@ func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend)
func (s *Session) ChannelMessageSendTTS(channelID string, content string) (*Message, error) {
return s.ChannelMessageSendComplex(channelID, &MessageSend{
Content: content,
Tts: true,
TTS: true,
})
}
@@ -2191,7 +2185,9 @@ func (s *Session) MessageReactionsRemoveAll(channelID, messageID string) error {
// messageID : The message ID.
// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier.
// limit : max number of users to return (max 100)
func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit int) (st []*User, err error) {
// beforeID : If provided all reactions returned will be before given ID.
// afterID : If provided all reactions returned will be after given ID.
func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit int, beforeID, afterID string) (st []*User, err error) {
// emoji such as #⃣ need to have # escaped
emojiID = strings.Replace(emojiID, "#", "%23", -1)
uri := EndpointMessageReactions(channelID, messageID, emojiID)
@@ -2202,6 +2198,13 @@ func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit i
v.Set("limit", strconv.Itoa(limit))
}
if afterID != "" {
v.Set("after", afterID)
}
if beforeID != "" {
v.Set("before", beforeID)
}
if len(v) > 0 {
uri += "?" + v.Encode()
}

View File

@@ -848,6 +848,12 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
err = s.MemberAdd(t.Members[i])
}
}
if s.TrackPresences {
for _, p := range t.Presences {
err = s.PresenceAdd(t.GuildID, p)
}
}
case *GuildRoleCreate:
if s.TrackRoles {
err = s.RoleAdd(t.GuildID, t.Role)
@@ -893,6 +899,13 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
}
case *MessageDelete:
if s.MaxMessageCount != 0 {
var old *Message
old, err = s.Message(t.ChannelID, t.ID)
if err == nil {
oldCopy := *old
t.BeforeDelete = &oldCopy
}
err = s.MessageRemove(t.Message)
}
case *MessageDeleteBulk:

View File

@@ -29,8 +29,10 @@ type Session struct {
// General configurable settings.
// Authentication token for this session
// TODO: Remove Below, Deprecated, Use Identify struct
Token string
MFA bool
MFA bool
// Debug for printing JSON request/responses
Debug bool // Deprecated, will be removed.
@@ -39,6 +41,11 @@ type Session struct {
// Should the session reconnect the websocket on errors.
ShouldReconnectOnError bool
// Identify is sent during initial handshake with the discord gateway.
// https://discord.com/developers/docs/topics/gateway#identify
Identify Identify
// TODO: Remove Below, Deprecated, Use Identify struct
// Should the session request compressed websocket data.
Compress bool
@@ -587,12 +594,13 @@ type VoiceState struct {
// A Presence stores the online, offline, or idle and game status of Guild members.
type Presence struct {
User *User `json:"user"`
Status Status `json:"status"`
Game *Game `json:"game"`
Nick string `json:"nick"`
Roles []string `json:"roles"`
Since *int `json:"since"`
User *User `json:"user"`
Status Status `json:"status"`
Game *Game `json:"game"`
Activities []*Game `json:"activities"`
Nick string `json:"nick"`
Roles []string `json:"roles"`
Since *int `json:"since"`
}
// GameType is the type of "game" (see GameType* consts) in the Game struct
@@ -604,6 +612,7 @@ const (
GameTypeStreaming
GameTypeListening
GameTypeWatching
GameTypeCustom
)
// A Game struct holds the name of the "playing .." game for a user
@@ -687,7 +696,7 @@ type Settings struct {
RenderEmbeds bool `json:"render_embeds"`
InlineEmbedMedia bool `json:"inline_embed_media"`
InlineAttachmentMedia bool `json:"inline_attachment_media"`
EnableTtsCommand bool `json:"enable_tts_command"`
EnableTTSCommand bool `json:"enable_tts_command"`
MessageDisplayCompact bool `json:"message_display_compact"`
ShowCurrentGame bool `json:"show_current_game"`
ConvertEmoticons bool `json:"convert_emoticons"`
@@ -909,8 +918,63 @@ type GatewayBotResponse struct {
Shards int `json:"shards"`
}
// GatewayStatusUpdate is sent by the client to indicate a presence or status update
// https://discord.com/developers/docs/topics/gateway#update-status-gateway-status-update-structure
type GatewayStatusUpdate struct {
Since int `json:"since"`
Game Activity `json:"game"`
Status string `json:"status"`
AFK bool `json:"afk"`
}
// Activity defines the Activity sent with GatewayStatusUpdate
// https://discord.com/developers/docs/topics/gateway#activity-object
type Activity struct {
Name string
Type ActivityType
URL string
}
// ActivityType is the type of Activity (see ActivityType* consts) in the Activity struct
// https://discord.com/developers/docs/topics/gateway#activity-object-activity-types
type ActivityType int
// Valid ActivityType values
// https://discord.com/developers/docs/topics/gateway#activity-object-activity-types
const (
ActivityTypeGame GameType = iota
ActivityTypeStreaming
ActivityTypeListening
// ActivityTypeWatching // not valid in this use case?
ActivityTypeCustom = 4
)
// Identify is sent during initial handshake with the discord gateway.
// https://discord.com/developers/docs/topics/gateway#identify
type Identify struct {
Token string `json:"token"`
Properties IdentifyProperties `json:"properties"`
Compress bool `json:"compress"`
LargeThreshold int `json:"large_threshold"`
Shard *[2]int `json:"shard,omitempty"`
Presence GatewayStatusUpdate `json:"presence,omitempty"`
GuildSubscriptions bool `json:"guild_subscriptions"`
Intents *Intent `json:"intents,omitempty"`
}
// IdentifyProperties contains the "properties" portion of an Identify packet
// https://discord.com/developers/docs/topics/gateway#identify-identify-connection-properties
type IdentifyProperties struct {
OS string `json:"$os"`
Browser string `json:"$browser"`
Device string `json:"$device"`
Referer string `json:"$referer"`
ReferringDomain string `json:"$referring_domain"`
}
// Constants for the different bit offsets of text channel permissions
const (
// Deprecated: PermissionReadMessages has been replaced with PermissionViewChannel for text and voice channels
PermissionReadMessages = 1 << (iota + 10)
PermissionSendMessages
PermissionSendTTSMessages
@@ -952,8 +1016,9 @@ const (
PermissionManageServer
PermissionAddReactions
PermissionViewAuditLogs
PermissionViewChannel = 1 << (iota + 2)
PermissionAllText = PermissionReadMessages |
PermissionAllText = PermissionViewChannel |
PermissionSendMessages |
PermissionSendTTSMessages |
PermissionManageMessages |
@@ -961,7 +1026,8 @@ const (
PermissionAttachFiles |
PermissionReadMessageHistory |
PermissionMentionEveryone
PermissionAllVoice = PermissionVoiceConnect |
PermissionAllVoice = PermissionViewChannel |
PermissionVoiceConnect |
PermissionVoiceSpeak |
PermissionVoiceMuteMembers |
PermissionVoiceDeafenMembers |
@@ -1037,3 +1103,49 @@ const (
ErrCodeReactionBlocked = 90001
)
// Intent is the type of a Gateway Intent
// https://discord.com/developers/docs/topics/gateway#gateway-intents
type Intent int
// Constants for the different bit offsets of intents
const (
IntentsGuilds Intent = 1 << iota
IntentsGuildMembers
IntentsGuildBans
IntentsGuildEmojis
IntentsGuildIntegrations
IntentsGuildWebhooks
IntentsGuildInvites
IntentsGuildVoiceStates
IntentsGuildPresences
IntentsGuildMessages
IntentsGuildMessageReactions
IntentsGuildMessageTyping
IntentsDirectMessages
IntentsDirectMessageReactions
IntentsDirectMessageTyping
IntentsAllWithoutPrivileged = IntentsGuilds |
IntentsGuildBans |
IntentsGuildEmojis |
IntentsGuildIntegrations |
IntentsGuildWebhooks |
IntentsGuildInvites |
IntentsGuildVoiceStates |
IntentsGuildMessages |
IntentsGuildMessageReactions |
IntentsGuildMessageTyping |
IntentsDirectMessages |
IntentsDirectMessageReactions |
IntentsDirectMessageTyping
IntentsAll = IntentsAllWithoutPrivileged |
IntentsGuildMembers |
IntentsGuildPresences
IntentsNone Intent = 0
)
// MakeIntent helps convert a gateway intent value for use in the Identify structure.
func MakeIntent(intents Intent) *Intent {
return &intents
}

View File

@@ -12,6 +12,6 @@ func SnowflakeTimestamp(ID string) (t time.Time, err error) {
return
}
timestamp := (i >> 22) + 1420070400000
t = time.Unix(timestamp/1000, 0)
t = time.Unix(0, timestamp*1000000)
return
}

View File

@@ -346,6 +346,25 @@ func (v *VoiceConnection) wsListen(wsConn *websocket.Conn, close <-chan struct{}
for {
_, message, err := v.wsConn.ReadMessage()
if err != nil {
// 4014 indicates a manual disconnection by someone in the guild;
// we shouldn't reconnect.
if websocket.IsCloseError(err, 4014) {
v.log(LogInformational, "received 4014 manual disconnection")
// Abandon the voice WS connection
v.Lock()
v.wsConn = nil
v.Unlock()
v.session.Lock()
delete(v.session.VoiceConnections, v.GuildID)
v.session.Unlock()
v.Close()
return
}
// Detect if we have been closed manually. If a Close() has already
// happened, the websocket we are listening on will be different to the
// current session.

View File

@@ -18,7 +18,6 @@ import (
"fmt"
"io"
"net/http"
"runtime"
"sync/atomic"
"time"
@@ -47,7 +46,7 @@ type resumePacket struct {
}
// Open creates a websocket connection to Discord.
// See: https://discordapp.com/developers/docs/topics/gateway#connecting
// See: https://discord.com/developers/docs/topics/gateway#connecting
func (s *Session) Open() error {
s.log(LogInformational, "called")
@@ -80,7 +79,7 @@ func (s *Session) Open() error {
header.Add("accept-encoding", "zlib")
s.wsConn, _, err = websocket.DefaultDialer.Dial(s.gateway, header)
if err != nil {
s.log(LogWarning, "error connecting to gateway %s, %s", s.gateway, err)
s.log(LogError, "error connecting to gateway %s, %s", s.gateway, err)
s.gateway = "" // clear cached gateway
s.wsConn = nil // Just to be safe.
return err
@@ -399,9 +398,10 @@ func (s *Session) UpdateStatusComplex(usd UpdateStatusData) (err error) {
}
type requestGuildMembersData struct {
GuildID string `json:"guild_id"`
Query string `json:"query"`
Limit int `json:"limit"`
GuildIDs []string `json:"guild_id"`
Query string `json:"query"`
Limit int `json:"limit"`
Presences bool `json:"presences"`
}
type requestGuildMembersOp struct {
@@ -411,10 +411,39 @@ type requestGuildMembersOp struct {
// RequestGuildMembers requests guild members from the gateway
// The gateway responds with GuildMembersChunk events
// guildID : The ID of the guild to request members of
// query : String that username starts with, leave empty to return all members
// limit : Max number of items to return, or 0 to request all members matched
func (s *Session) RequestGuildMembers(guildID, query string, limit int) (err error) {
// guildID : Single Guild ID to request members of
// query : String that username starts with, leave empty to return all members
// limit : Max number of items to return, or 0 to request all members matched
// presences : Whether to request presences of guild members
func (s *Session) RequestGuildMembers(guildID string, query string, limit int, presences bool) (err error) {
data := requestGuildMembersData{
GuildIDs: []string{guildID},
Query: query,
Limit: limit,
Presences: presences,
}
err = s.requestGuildMembers(data)
return
}
// RequestGuildMembersBatch requests guild members from the gateway
// The gateway responds with GuildMembersChunk events
// guildID : Slice of guild IDs to request members of
// query : String that username starts with, leave empty to return all members
// limit : Max number of items to return, or 0 to request all members matched
// presences : Whether to request presences of guild members
func (s *Session) RequestGuildMembersBatch(guildIDs []string, query string, limit int, presences bool) (err error) {
data := requestGuildMembersData{
GuildIDs: guildIDs,
Query: query,
Limit: limit,
Presences: presences,
}
err = s.requestGuildMembers(data)
return
}
func (s *Session) requestGuildMembers(data requestGuildMembersData) (err error) {
s.log(LogInformational, "called")
s.RLock()
@@ -423,12 +452,6 @@ func (s *Session) RequestGuildMembers(guildID, query string, limit int) (err err
return ErrWSNotFound
}
data := requestGuildMembersData{
GuildID: guildID,
Query: query,
Limit: limit,
}
s.wsMutex.Lock()
err = s.wsConn.WriteJSON(requestGuildMembersOp{8, data})
s.wsMutex.Unlock()
@@ -498,7 +521,7 @@ func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
// Must immediately disconnect from gateway and reconnect to new gateway.
if e.Operation == 7 {
s.log(LogInformational, "Closing and reconnecting in response to Op7")
s.Close()
s.CloseWithCode(websocket.CloseServiceRestart)
s.reconnect()
return e, nil
}
@@ -722,55 +745,42 @@ func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {
}
}
type identifyProperties struct {
OS string `json:"$os"`
Browser string `json:"$browser"`
Device string `json:"$device"`
Referer string `json:"$referer"`
ReferringDomain string `json:"$referring_domain"`
}
type identifyData struct {
Token string `json:"token"`
Properties identifyProperties `json:"properties"`
LargeThreshold int `json:"large_threshold"`
Compress bool `json:"compress"`
Shard *[2]int `json:"shard,omitempty"`
}
type identifyOp struct {
Op int `json:"op"`
Data identifyData `json:"d"`
Op int `json:"op"`
Data Identify `json:"d"`
}
// identify sends the identify packet to the gateway
func (s *Session) identify() error {
s.log(LogDebug, "called")
properties := identifyProperties{runtime.GOOS,
"Discordgo v" + VERSION,
"",
"",
"",
// TODO: This is a temporary block of code to help
// maintain backwards compatability
if s.Compress == false {
s.Identify.Compress = false
}
data := identifyData{s.Token,
properties,
250,
s.Compress,
nil,
// TODO: This is a temporary block of code to help
// maintain backwards compatability
if s.Token != "" && s.Identify.Token == "" {
s.Identify.Token = s.Token
}
// TODO: Below block should be refactored so ShardID and ShardCount
// can be deprecated and their usage moved to the Session.Identify
// struct
if s.ShardCount > 1 {
if s.ShardID >= s.ShardCount {
return ErrWSShardBounds
}
data.Shard = &[2]int{s.ShardID, s.ShardCount}
s.Identify.Shard = &[2]int{s.ShardID, s.ShardCount}
}
op := identifyOp{2, data}
// Send Identify packet to Discord
op := identifyOp{2, s.Identify}
s.log(LogDebug, "Identify Packet: \n%#v", op)
s.wsMutex.Lock()
err := s.wsConn.WriteJSON(op)
s.wsMutex.Unlock()
@@ -834,8 +844,15 @@ func (s *Session) reconnect() {
}
// Close closes a websocket and stops all listening/heartbeat goroutines.
// TODO: Add support for Voice WS/UDP
func (s *Session) Close() error {
return s.CloseWithCode(websocket.CloseNormalClosure)
}
// CloseWithCode closes a websocket using the provided closeCode and stops all
// listening/heartbeat goroutines.
// TODO: Add support for Voice WS/UDP connections
func (s *Session) Close() (err error) {
func (s *Session) CloseWithCode(closeCode int) (err error) {
s.log(LogInformational, "called")
s.Lock()
@@ -857,7 +874,7 @@ func (s *Session) Close() (err error) {
// To cleanly close a connection, a client should send a close
// frame and wait for the server to close the connection.
s.wsMutex.Lock()
err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(closeCode, ""))
s.wsMutex.Unlock()
if err != nil {
s.log(LogInformational, "error closing websocket, %s", err)

20
vendor/github.com/monaco-io/request/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,20 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
.idea
.vscode
coverage.txt

16
vendor/github.com/monaco-io/request/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,16 @@
language: go
go:
- 1.14.x
- tip
sudo: false
before_install:
- go get -t -v ./...
script:
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash) -t 6cf6f7ab-26e6-429c-ac44-f0ad85c1e586

76
vendor/github.com/monaco-io/request/CODE_OF_CONDUCT.md generated vendored Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at luxuze@agora.io. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at <https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
<https://www.contributor-covenant.org/faq>

1
vendor/github.com/monaco-io/request/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1 @@
# CONTRIBUTING

21
vendor/github.com/monaco-io/request/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Monaco.HappyHacking
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

197
vendor/github.com/monaco-io/request/README.md generated vendored Normal file
View File

@@ -0,0 +1,197 @@
# Request [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go) [![Go Report Card](https://goreportcard.com/badge/github.com/monaco-io/request)](https://goreportcard.com/report/github.com/monaco-io/request) ![Go](https://github.com/monaco-io/request/workflows/Go/badge.svg)
<img align="right" width="159px" src="https://raw.githubusercontent.com/gin-gonic/logo/master/color.png">
[![Build Status](https://travis-ci.org/monaco-io/request.svg?branch=master)](https://travis-ci.org/monaco-io/request)
[![GoDoc](https://godoc.org/github.com/monaco-io/request?status.svg)](https://pkg.go.dev/github.com/monaco-io/request?tab=doc)
[![codecov](https://codecov.io/gh/monaco-io/request/branch/master/graph/badge.svg)](https://codecov.io/gh/monaco-io/request)
[![Release](https://img.shields.io/github/release/monaco-io/request.svg?style=flat-square)](https://github.com/monaco-io/request/releases)
[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/monaco-io/request)](https://www.tickgit.com/browse?repo=github.com/monaco-io/request)
[![License](https://img.shields.io/github/license/monaco-io/request?style=plastic)](https://github.com/monaco-io/request/blob/master/LICENSE)
<!-- [![Sourcegraph](https://sourcegraph.com/github.com/monaco-io/request/-/badge.svg)](https://sourcegraph.com/github.com/monaco-io/request?badge) -->
<!-- [![Open Source Helpers](https://www.codetriage.com/monaco-io/request/badges/users.svg)](https://www.codetriage.com/monaco-io/request) -->
<!-- [![Join the chat at https://gitter.im/monaco-io/request](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/monaco-io/request?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -->
HTTP client for golang, Inspired by [Javascript-axios](https://github.com/axios/axios) [Python-request](https://github.com/psf/requests).
If you have experience about axios or requests, you will love it.
No 3rd dependency.
## Features
- Make [http](https://golang.org) requests from Golang
- Intercept request and response
- Transform request and response data
## Installing
go mod:
```bash
go get github.com/monaco-io/request
```
## Methods
- OPTIONS
- GET
- HEAD
- POST
- PUT
- DELETE
- TRACE
- CONNECT
## Example
### GET
```go
package main
import (
"log"
"github.com/monaco-io/request"
)
func main() {
client := request.Client{
URL: "https://google.com",
Method: "GET",
Params: map[string]string{"hello": "world"},
}
resp, err := client.Do()
log.Println(resp.Code, string(resp.Data), err)
}
```
### POST
```go
package main
import (
"log"
"github.com/monaco-io/request"
)
func main() {
client := request.Client{
URL: "https://google.com",
Method: "POST",
Params: map[string]string{"hello": "world"},
Body: []byte(`{"hello": "world"}`),
}
resp, err := client.Do()
log.Println(resp.Code, string(resp.Data), err)
}
```
### Content-Type
```go
package main
import (
"log"
"github.com/monaco-io/request"
)
func main() {
client := request.Client{
URL: "https://google.com",
Method: "POST",
ContentType: request.ApplicationXWwwFormURLEncoded, // default is "application/json"
}
resp, err := client.Do()
log.Println(resp.Code, string(resp.Data), err)
}
```
### Authorization
```go
package main
import (
"log"
"github.com/monaco-io/request"
)
func main() {
client := request.Client{
URL: "https://google.com",
Method: "POST",
BasicAuth: request.BasicAuth{
Username:"user_xxx",
Password:"pwd_xxx",
}, // xxx:xxx
}
resp, err := client.Do()
log.Println(resp.Code, string(resp.Data), err)
}
```
### Timeout
```go
package main
import (
"log"
"github.com/monaco-io/request"
)
func main() {
client := request.Client{
URL: "https://google.com",
Method: "POST",
Timeout: 10, // seconds
}
resp, err := client.Do()
log.Println(resp.Code, string(resp.Data), err)
}
```
### Cookies
```go
package main
import (
"log"
"github.com/monaco-io/request"
)
func main() {
client := request.Client{
URL: "https://google.com",
Cookies:[]*http.Cookie{
{
Name: "cookie_name",
Value: "cookie_value",
},
},
}
resp, err := client.Do()
log.Println(resp.Code, string(resp.Data), err)
}
```
## License
[MIT](LICENSE)

59
vendor/github.com/monaco-io/request/doc.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
// Package request HTTP client for golang
// - Make http requests from Golang
// - Intercept request and response
// - Transform request and response data
//
// GET
//
// client := request.Client{
// URL: "https://google.com",
// Method: "GET",
// Params: map[string]string{"hello": "world"},
// }
// resp, err := client.Do()
//
// POST
//
// client := request.Client{
// URL: "https://google.com",
// Method: "POST",
// Params: map[string]string{"hello": "world"},
// Body: []byte(`{"hello": "world"}`),
// }
// resp, err := client.Do()
//
// Content-Type
//
// client := request.Client{
// URL: "https://google.com",
// Method: "POST",
// ContentType: request.ApplicationXWwwFormURLEncoded, // default is "application/json"
// }
// resp, err := client.Do()
//
// Authorization
//
// client := request.Client{
// URL: "https://google.com",
// Method: "POST",
// BasicAuth: request.BasicAuth{
// Username:"user_xxx",
// Password:"pwd_xxx",
// }, // xxx:xxx
// }
//
// resp, err := client.Do()
//
// Cookies
// client := request.Client{
// URL: "https://google.com",
// Cookies:[]*http.Cookie{
// {
// Name: "cookie_name",
// Value: "cookie_value",
// },
// },
// }
//
// resp, err := client.Do()
package request

3
vendor/github.com/monaco-io/request/go.mod generated vendored Normal file
View File

@@ -0,0 +1,3 @@
module github.com/monaco-io/request
go 1.14

0
vendor/github.com/monaco-io/request/go.sum generated vendored Normal file
View File

66
vendor/github.com/monaco-io/request/model.go generated vendored Normal file
View File

@@ -0,0 +1,66 @@
package request
import (
"net/http"
"time"
)
const (
// ApplicationJSON application/json
ApplicationJSON ContentType = "application/json"
// ApplicationXWwwFormURLEncoded application/x-www-form-urlencoded
ApplicationXWwwFormURLEncoded ContentType = "application/x-www-form-urlencoded"
// MultipartFormData multipart/form-data
MultipartFormData ContentType = "multipart/form-data"
)
// ContentType Content-Type
type ContentType string
// Client Method
/*
Method = "OPTIONS" ; Section 9.2
| "GET" ; Section 9.3
| "HEAD" ; Section 9.4
| "POST" ; Section 9.5
| "PUT" ; Section 9.6
| "DELETE" ; Section 9.7
| "TRACE" ; Section 9.8
| "CONNECT" ; Section 9.9
| extension-method
extension-method = token
token = 1*<any CHAR except CTLs or separators>
*/
type Client struct {
URL string
Method string
Header map[string]string
Params map[string]string
Body []byte
BasicAuth BasicAuth
Timeout time.Duration // second
ProxyURL string
ContentType ContentType
Cookies []*http.Cookie
// private
client *http.Client
req *http.Request
}
// BasicAuth Add Username:Password as Basic Auth
type BasicAuth struct {
Username string
Password string
}
// SugaredResp Sugared response with status code and body data
type SugaredResp struct {
Data []byte
Code int
// private
resp *http.Response
}

98
vendor/github.com/monaco-io/request/request.go generated vendored Normal file
View File

@@ -0,0 +1,98 @@
package request
import (
"bytes"
"crypto/tls"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/url"
"time"
)
// Do send http request
func (c *Client) Do() (resp SugaredResp, err error) {
defer resp.Close()
if err := c.buildRequest(); err != nil {
return resp, err
}
// send request and close on func call end
if resp.resp, err = c.client.Do(c.req); err != nil {
return resp, err
}
// read response data form resp
resp.Data, err = ioutil.ReadAll(resp.resp.Body)
resp.Code = resp.resp.StatusCode
return resp, err
}
func (c *Client) buildRequest() (err error) {
// encode requestURL.httpURL like https://google.com?hello=world&package=request
ru := requestURL{
urlString: c.URL,
parameters: c.Params,
}
if err := ru.EncodeURL(); err != nil {
return err
}
// build request
c.req, err = http.NewRequest(c.Method, ru.string(), bytes.NewReader(c.Body))
if err != nil {
return err
}
// apply Header to request
if c.Method == "POST" {
if c.ContentType == "" {
c.ContentType = ApplicationJSON
}
c.req.Header.Set("Content-Type", string(c.ContentType))
}
for k, v := range c.Header {
c.req.Header.Add(k, v)
}
// apply basic Auth of request header
if c.BasicAuth.Username != "" && c.BasicAuth.Password != "" {
c.req.SetBasicAuth(c.BasicAuth.Username, c.BasicAuth.Password)
}
c.client = &http.Client{}
// apply timeout
if c.Timeout > 0 {
c.client.Timeout = c.Timeout * time.Second
}
// apply cookies
if c.Cookies != nil {
jar, _ := cookiejar.New(nil)
jar.SetCookies(&url.URL{Scheme: ru.scheme(), Host: ru.host()}, c.Cookies)
c.client.Jar = jar
}
// apply proxy
if c.ProxyURL != "" {
if proxy, err := url.Parse(c.ProxyURL); err == nil && proxy != nil {
c.client.Transport = &http.Transport{
Proxy: http.ProxyURL(proxy),
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
}
return err
}
// Resp do request and get original http response struct
func (c *Client) Resp() (resp *http.Response, err error) {
if err = c.buildRequest(); err != nil {
return resp, err
}
return c.client.Do(c.req)
}

18
vendor/github.com/monaco-io/request/resp.go generated vendored Normal file
View File

@@ -0,0 +1,18 @@
package request
// StatusCode get response status code
func (s *SugaredResp) StatusCode() (code int) {
return s.resp.StatusCode
}
// Status get response status code and text, like 200 ok
func (s *SugaredResp) Status() (status string) {
return s.resp.Status
}
// Close close response body
func (s *SugaredResp) Close() {
if s.resp != nil {
_ = s.resp.Body.Close()
}
}

36
vendor/github.com/monaco-io/request/url.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
package request
import "net/url"
type requestURL struct {
httpURL *url.URL
urlString string
parameters map[string]string
}
// EncodeURL add and encoded parameters.
func (ru *requestURL) EncodeURL() (err error) {
ru.httpURL, err = url.Parse(ru.urlString)
if err != nil {
return err
}
query := ru.httpURL.Query()
for k := range ru.parameters {
query.Set(k, ru.parameters[k])
}
ru.httpURL.RawQuery = query.Encode()
return err
}
// String return example: https://www.google.com/search?a=1&b=2
func (ru requestURL) string() string {
return ru.httpURL.String()
}
func (ru requestURL) scheme() string {
return ru.httpURL.Scheme
}
func (ru requestURL) host() string {
return ru.httpURL.Host
}

View File

@@ -32,6 +32,8 @@ matrix:
script: go test -v -mod=vendor ./...
- go: "1.13.x"
script: go test -v -mod=vendor ./...
- go: "1.14.x"
script: go test -v -mod=vendor ./...
- go: "tip"
script: go test -v -mod=vendor ./...

View File

@@ -32,23 +32,26 @@ type Blocks struct {
// BlockAction is the action callback sent when a block is interacted with
type BlockAction struct {
ActionID string `json:"action_id"`
BlockID string `json:"block_id"`
Type actionType `json:"type"`
Text TextBlockObject `json:"text"`
Value string `json:"value"`
ActionTs string `json:"action_ts"`
SelectedOption OptionBlockObject `json:"selected_option"`
SelectedOptions []OptionBlockObject `json:"selected_options"`
SelectedUser string `json:"selected_user"`
SelectedChannel string `json:"selected_channel"`
SelectedConversation string `json:"selected_conversation"`
SelectedDate string `json:"selected_date"`
InitialOption OptionBlockObject `json:"initial_option"`
InitialUser string `json:"initial_user"`
InitialChannel string `json:"initial_channel"`
InitialConversation string `json:"initial_conversation"`
InitialDate string `json:"initial_date"`
ActionID string `json:"action_id"`
BlockID string `json:"block_id"`
Type actionType `json:"type"`
Text TextBlockObject `json:"text"`
Value string `json:"value"`
ActionTs string `json:"action_ts"`
SelectedOption OptionBlockObject `json:"selected_option"`
SelectedOptions []OptionBlockObject `json:"selected_options"`
SelectedUser string `json:"selected_user"`
SelectedUsers []string `json:"selected_users"`
SelectedChannel string `json:"selected_channel"`
SelectedChannels []string `json:"selected_channels"`
SelectedConversation string `json:"selected_conversation"`
SelectedConversations []string `json:"selected_conversations"`
SelectedDate string `json:"selected_date"`
InitialOption OptionBlockObject `json:"initial_option"`
InitialUser string `json:"initial_user"`
InitialChannel string `json:"initial_channel"`
InitialConversation string `json:"initial_conversation"`
InitialDate string `json:"initial_date"`
}
// actionType returns the type of the action

View File

@@ -2,6 +2,7 @@ package slack
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
)
@@ -172,10 +173,12 @@ func (b *BlockElements) UnmarshalJSON(data []byte) error {
blockElement = &DatePickerBlockElement{}
case "plain_text_input":
blockElement = &PlainTextInputBlockElement{}
case "checkboxes":
blockElement = &CheckboxGroupsBlockElement{}
case "static_select", "external_select", "users_select", "conversations_select", "channels_select":
blockElement = &SelectBlockElement{}
default:
return errors.New("unsupported block element type")
return fmt.Errorf("unsupported block element type %v", blockElementType)
}
err = json.Unmarshal(r, blockElement)
@@ -275,6 +278,12 @@ func (a *Accessory) UnmarshalJSON(data []byte) error {
return err
}
a.MultiSelectElement = element.(*MultiSelectBlockElement)
case "checkboxes":
element, err := unmarshalBlockElement(r, &CheckboxGroupsBlockElement{})
if err != nil {
return err
}
a.CheckboxGroupsBlockElement = element.(*CheckboxGroupsBlockElement)
default:
element, err := unmarshalBlockElement(r, &UnknownBlockElement{})
if err != nil {
@@ -313,6 +322,9 @@ func toBlockElement(element *Accessory) BlockElement {
if element.RadioButtonsElement != nil {
return element.RadioButtonsElement
}
if element.CheckboxGroupsBlockElement != nil {
return element.CheckboxGroupsBlockElement
}
if element.SelectElement != nil {
return element.SelectElement
}

View File

@@ -40,15 +40,16 @@ type MixedElement interface {
}
type Accessory struct {
ImageElement *ImageBlockElement
ButtonElement *ButtonBlockElement
OverflowElement *OverflowBlockElement
DatePickerElement *DatePickerBlockElement
PlainTextInputElement *PlainTextInputBlockElement
RadioButtonsElement *RadioButtonsBlockElement
SelectElement *SelectBlockElement
MultiSelectElement *MultiSelectBlockElement
UnknownElement *UnknownBlockElement
ImageElement *ImageBlockElement
ButtonElement *ButtonBlockElement
OverflowElement *OverflowBlockElement
DatePickerElement *DatePickerBlockElement
PlainTextInputElement *PlainTextInputBlockElement
RadioButtonsElement *RadioButtonsBlockElement
SelectElement *SelectBlockElement
MultiSelectElement *MultiSelectBlockElement
CheckboxGroupsBlockElement *CheckboxGroupsBlockElement
UnknownElement *UnknownBlockElement
}
// NewAccessory returns a new Accessory for a given block element
@@ -70,6 +71,8 @@ func NewAccessory(element BlockElement) *Accessory {
return &Accessory{SelectElement: element.(*SelectBlockElement)}
case *MultiSelectBlockElement:
return &Accessory{MultiSelectElement: element.(*MultiSelectBlockElement)}
case *CheckboxGroupsBlockElement:
return &Accessory{CheckboxGroupsBlockElement: element.(*CheckboxGroupsBlockElement)}
default:
return &Accessory{UnknownElement: element.(*UnknownBlockElement)}
}
@@ -152,9 +155,10 @@ func (s ButtonBlockElement) ElementType() MessageElementType {
return s.Type
}
// add styling to button object
func (s *ButtonBlockElement) WithStyle(style Style) {
// WithStyling adds styling to the button object and returns the modified ButtonBlockElement
func (s *ButtonBlockElement) WithStyle(style Style) *ButtonBlockElement {
s.Style = style
return s
}
// NewButtonBlockElement returns an instance of a new button element to be used within a block
@@ -186,17 +190,19 @@ type OptionGroupsResponse struct {
//
// More Information: https://api.slack.com/reference/messaging/block-elements#select
type SelectBlockElement struct {
Type string `json:"type,omitempty"`
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
ActionID string `json:"action_id,omitempty"`
Options []*OptionBlockObject `json:"options,omitempty"`
OptionGroups []*OptionGroupBlockObject `json:"option_groups,omitempty"`
InitialOption *OptionBlockObject `json:"initial_option,omitempty"`
InitialUser string `json:"initial_user,omitempty"`
InitialConversation string `json:"initial_conversation,omitempty"`
InitialChannel string `json:"initial_channel,omitempty"`
MinQueryLength *int `json:"min_query_length,omitempty"`
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
Type string `json:"type,omitempty"`
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
ActionID string `json:"action_id,omitempty"`
Options []*OptionBlockObject `json:"options,omitempty"`
OptionGroups []*OptionGroupBlockObject `json:"option_groups,omitempty"`
InitialOption *OptionBlockObject `json:"initial_option,omitempty"`
InitialUser string `json:"initial_user,omitempty"`
InitialConversation string `json:"initial_conversation,omitempty"`
InitialChannel string `json:"initial_channel,omitempty"`
DefaultToCurrentConversation bool `json:"default_to_current_conversation,omitempty"`
ResponseURLEnabled bool `json:"response_url_enabled,omitempty"`
MinQueryLength *int `json:"min_query_length,omitempty"`
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
}
// ElementType returns the type of the Element
@@ -315,7 +321,7 @@ func NewOverflowBlockElement(actionID string, options ...*OptionBlockObject) *Ov
// More Information: https://api.slack.com/reference/messaging/block-elements#datepicker
type DatePickerBlockElement struct {
Type MessageElementType `json:"type"`
ActionID string `json:"action_id"`
ActionID string `json:"action_id,omitempty"`
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
InitialDate string `json:"initial_date,omitempty"`
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
@@ -341,7 +347,7 @@ func NewDatePickerBlockElement(actionID string) *DatePickerBlockElement {
// More Information: https://api.slack.com/reference/block-kit/block-elements#input
type PlainTextInputBlockElement struct {
Type MessageElementType `json:"type"`
ActionID string `json:"action_id"`
ActionID string `json:"action_id,omitempty"`
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
InitialValue string `json:"initial_value,omitempty"`
Multiline bool `json:"multiline,omitempty"`
@@ -370,7 +376,7 @@ func NewPlainTextInputBlockElement(placeholder *TextBlockObject, actionID string
// More Information: https://api.slack.com/reference/block-kit/block-elements#checkboxes
type CheckboxGroupsBlockElement struct {
Type MessageElementType `json:"type"`
ActionID string `json:"action_id"`
ActionID string `json:"action_id,omitempty"`
Options []*OptionBlockObject `json:"options"`
InitialOptions []*OptionBlockObject `json:"initial_options,omitempty"`
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
@@ -381,7 +387,7 @@ func (c CheckboxGroupsBlockElement) ElementType() MessageElementType {
return c.Type
}
// NewRadioButtonsBlockElement returns an instance of a radio block element
// NewCheckboxGroupsBlockElement returns an instance of a radio block element
func NewCheckboxGroupsBlockElement(actionID string, options ...*OptionBlockObject) *CheckboxGroupsBlockElement {
return &CheckboxGroupsBlockElement{
Type: METCheckboxGroups,
@@ -396,7 +402,7 @@ func NewCheckboxGroupsBlockElement(actionID string, options ...*OptionBlockObjec
// More Information: https://api.slack.com/reference/block-kit/block-elements#radio
type RadioButtonsBlockElement struct {
Type MessageElementType `json:"type"`
ActionID string `json:"action_id"`
ActionID string `json:"action_id,omitempty"`
Options []*OptionBlockObject `json:"options"`
InitialOption *OptionBlockObject `json:"initial_option,omitempty"`
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`

View File

@@ -8,7 +8,7 @@ type ImageBlock struct {
ImageURL string `json:"image_url"`
AltText string `json:"alt_text"`
BlockID string `json:"block_id,omitempty"`
Title *TextBlockObject `json:"title"`
Title *TextBlockObject `json:"title,omitempty"`
}
// BlockType returns the type of the block

View File

@@ -163,6 +163,7 @@ type ConfirmationBlockObject struct {
Text *TextBlockObject `json:"text"`
Confirm *TextBlockObject `json:"confirm"`
Deny *TextBlockObject `json:"deny"`
Style Style `json:"style,omitempty"`
}
// validateType enforces block objects for element and block parameters
@@ -170,6 +171,11 @@ func (s ConfirmationBlockObject) validateType() MessageObjectType {
return motConfirmation
}
// add styling to confirmation object
func (s *ConfirmationBlockObject) WithStyle(style Style) {
s.Style = style
}
// NewConfirmationBlockObject returns an instance of a new Confirmation Block Object
func NewConfirmationBlockObject(title, text, confirm, deny *TextBlockObject) *ConfirmationBlockObject {
return &ConfirmationBlockObject{

View File

@@ -1,8 +1,10 @@
package slack
import (
"bytes"
"context"
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"strconv"
@@ -206,6 +208,15 @@ func (api *Client) SendMessageContext(ctx context.Context, channelID string, opt
return "", "", "", err
}
if api.Debug() {
reqBody, err := ioutil.ReadAll(req.Body)
if err != nil {
return "", "", "", err
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody))
api.Debugf("Sending request: %s", string(reqBody))
}
if err = doPost(ctx, api.httpclient, req, parser(&response), api); err != nil {
return "", "", "", err
}

View File

@@ -1,9 +1,11 @@
module github.com/slack-go/slack
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-test/deep v1.0.4
github.com/gorilla/websocket v1.2.0
github.com/gorilla/websocket v1.4.2
github.com/pkg/errors v0.8.0
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.2
)

View File

@@ -4,6 +4,8 @@ github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

View File

@@ -59,8 +59,13 @@ type InteractionCallback struct {
}
type Container struct {
Type string `json:"type"`
ViewID string `json:"view_id"`
Type string `json:"type"`
ViewID string `json:"view_id"`
MessageTs string `json:"message_ts"`
AttachmentID json.Number `json:"attachment_id"`
ChannelID string `json:"channel_id"`
IsEphemeral bool `json:"is_ephemeral"`
IsAppUnfurl bool `json:"is_app_unfurl"`
}
// ActionCallback is a convenience struct defined to allow dynamic unmarshalling of
@@ -135,7 +140,7 @@ func (a *ActionCallbacks) UnmarshalJSON(data []byte) error {
}
a.BlockActions = append(a.BlockActions, action.(*BlockAction))
return nil
continue
}
action, err := unmarshalAction(r, &AttachmentAction{})

View File

@@ -78,10 +78,26 @@ func GetOAuthTokenContext(ctx context.Context, client httpClient, clientID, clie
return response.AccessToken, response.Scope, nil
}
// GetBotOAuthToken retrieves top-level and bot AccessToken - https://api.slack.com/legacy/oauth#bot_user_access_tokens
func GetBotOAuthToken(client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, bot OAuthResponseBot, err error) {
return GetBotOAuthTokenContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
}
// GetBotOAuthTokenContext retrieves top-level and bot AccessToken with a custom context
func GetBotOAuthTokenContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, bot OAuthResponseBot, err error) {
response, err := GetOAuthResponseContext(ctx, client, clientID, clientSecret, code, redirectURI)
if err != nil {
return "", "", OAuthResponseBot{}, err
}
return response.AccessToken, response.Scope, response.Bot, nil
}
// GetOAuthResponse retrieves OAuth response
func GetOAuthResponse(client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) {
return GetOAuthResponseContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
}
// GetOAuthResponseContext retrieves OAuth response with custom context
func GetOAuthResponseContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) {
values := url.Values{
"client_id": {clientID},

View File

@@ -14,6 +14,7 @@ type WebhookMessage struct {
Text string `json:"text,omitempty"`
Attachments []Attachment `json:"attachments,omitempty"`
Parse string `json:"parse,omitempty"`
Blocks *Blocks `json:"blocks,omitempty"`
}
func PostWebhook(url string, msg *WebhookMessage) error {

View File

@@ -58,6 +58,32 @@ var scopes = []string{msauth.DefaultMSGraphScope}
...
```
### Resource owner password credentials grant
- [OAuth 2.0 resource owner passowrd credentials grant flow]
```go
const (
tenantID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
clientID = "YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"
clientSecret = "ZZZZZZZZZZZZZZZZZZZZZZZZ"
username = "user.name@your-domain.com"
password = "secure-password"
)
var scopes = []string{msauth.DefaultMSGraphScope}
ctx := context.Background()
m := msauth.NewManager()
ts, err := m.ResourceOwnerPasswordGrant(ctx, tenantID, clientID, clientSecret, username, password, scopes)
if err != nil {
log.Fatal(err)
}
httpClient := oauth2.NewClient(ctx, ts)
...
```
### Authorization code grant
- [OAuth 2.0 authorization code grant flow]
@@ -67,4 +93,5 @@ var scopes = []string{msauth.DefaultMSGraphScope}
[v2.0 endpoint]: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-overview
[OAuth 2.0 device authorization grant flow]: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code
[OAuth 2.0 client credentials grant flow]: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
[OAuth 2.0 authorization code grant flow]: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
[OAuth 2.0 authorization code grant flow]: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
[OAuth 2.0 resource owner passowrd credentials grant flow]: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc

View File

@@ -39,9 +39,10 @@ func (m *Manager) DeviceAuthorizationGrant(ctx context.Context, tenantID, client
Endpoint: endpoint,
Scopes: scopes,
}
if t, ok := m.TokenCache[generateKey(tenantID, clientID)]; ok {
if t, ok := m.GetToken(CacheKey(tenantID, clientID)); ok {
tt, err := config.TokenSource(ctx, t).Token()
if err == nil {
m.PutToken(CacheKey(tenantID, clientID), tt)
return config.TokenSource(ctx, tt), nil
}
if _, ok := err.(*oauth2.RetrieveError); !ok {
@@ -85,7 +86,7 @@ func (m *Manager) DeviceAuthorizationGrant(ctx context.Context, tenantID, client
time.Sleep(time.Second * time.Duration(interval))
token, err := m.requestToken(ctx, tenantID, clientID, values)
if err == nil {
m.Cache(tenantID, clientID, token)
m.PutToken(CacheKey(tenantID, clientID), token)
return config.TokenSource(ctx, token), nil
}
tokenError, ok := err.(*TokenError)

View File

@@ -36,10 +36,6 @@ func (t *TokenError) Error() string {
return fmt.Sprintf("%s: %s", t.ErrorObject, t.ErrorDescription)
}
func generateKey(tenantID, clientID string) string {
return fmt.Sprintf("%s:%s", tenantID, clientID)
}
func deviceCodeURL(tenantID string) string {
return fmt.Sprintf(endpointURLFormat, tenantID, "devicecode")
}
@@ -65,6 +61,7 @@ func (e *tokenJSON) expiry() (t time.Time) {
// Manager is oauth2 token cache manager
type Manager struct {
mu sync.Mutex
Dirty bool
TokenCache map[string]*oauth2.Token
}
@@ -87,27 +84,64 @@ func (m *Manager) SaveBytes() ([]byte, error) {
return json.Marshal(m.TokenCache)
}
// LoadFile loads token cache from file
// LoadFile loads token cache from file with dirty state control
func (m *Manager) LoadFile(path string) error {
b, err := ioutil.ReadFile(path)
m.mu.Lock()
defer m.mu.Unlock()
b, err := ReadLocation(path)
if err != nil {
return err
}
return m.LoadBytes(b)
err = json.Unmarshal(b, &m.TokenCache)
if err != nil {
return err
}
m.Dirty = false
return nil
}
// SaveFile saves token cache to file
// SaveFile saves token cache to file with dirty state control
func (m *Manager) SaveFile(path string) error {
b, err := m.SaveBytes()
m.mu.Lock()
defer m.mu.Unlock()
if !m.Dirty {
return nil
}
b, err := json.Marshal(m.TokenCache)
if err != nil {
return err
}
return ioutil.WriteFile(path, b, 0644)
err = WriteLocation(path, b, 0644)
if err != nil {
return err
}
m.Dirty = false
return nil
}
// Cache stores a token into token cache
func (m *Manager) Cache(tenantID, clientID string, token *oauth2.Token) {
m.TokenCache[generateKey(tenantID, clientID)] = token
// CacheKey generates a token cache key from tenantID/clientID
func CacheKey(tenantID, clientID string) string {
return fmt.Sprintf("%s:%s", tenantID, clientID)
}
// GetToken gets a token from token cache
func (m *Manager) GetToken(cacheKey string) (*oauth2.Token, bool) {
m.mu.Lock()
defer m.mu.Unlock()
token, ok := m.TokenCache[cacheKey]
return token, ok
}
// PutToken puts a token into token cache
func (m *Manager) PutToken(cacheKey string, token *oauth2.Token) {
m.mu.Lock()
defer m.mu.Unlock()
oldToken, ok := m.TokenCache[cacheKey]
if ok && *oldToken == *token {
return
}
m.TokenCache[cacheKey] = token
m.Dirty = true
}
// requestToken requests a token from the token endpoint

View File

@@ -0,0 +1,26 @@
package msauth
import (
"context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/microsoft"
)
// ResourceOwnerPasswordGrant preforms OAuth 2.0 client resource owner password grant and returns a token.
func (m *Manager) ResourceOwnerPasswordGrant(ctx context.Context, tenantID, clientID, clientSecret, username, password string, scopes []string) (oauth2.TokenSource, error) {
endpoint := microsoft.AzureADEndpoint(tenantID)
endpoint.AuthStyle = oauth2.AuthStyleInParams
config := &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
Endpoint: endpoint,
Scopes: scopes,
}
t, err := config.PasswordCredentialsToken(ctx, username, password)
if err != nil {
return nil, err
}
ts := config.TokenSource(ctx, t)
return ts, nil
}

View File

@@ -0,0 +1,70 @@
package msauth
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
)
// ReadLocation reads data from file with path or URL
func ReadLocation(loc string) ([]byte, error) {
u, err := url.Parse(loc)
if err != nil {
return nil, err
}
switch u.Scheme {
case "", "file":
return ioutil.ReadFile(u.Path)
case "http", "https":
res, err := http.Get(loc)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%s", res.Status)
}
b, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
return b, nil
}
return nil, fmt.Errorf("Unsupported location to load: %s", loc)
}
// WriteLocation writes data to file with path or URL
func WriteLocation(loc string, b []byte, m os.FileMode) error {
u, err := url.Parse(loc)
if err != nil {
return err
}
switch u.Scheme {
case "", "file":
return ioutil.WriteFile(u.Path, b, m)
case "http", "https":
if strings.HasSuffix(u.Host, ".blob.core.windows.net") {
// Azure Blob Storage URL with SAS assumed here
cli := &http.Client{}
req, err := http.NewRequest(http.MethodPut, loc, bytes.NewBuffer(b))
if err != nil {
return err
}
req.Header.Set("x-ms-blob-type", "BlockBlob")
res, err := cli.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return fmt.Errorf("%s", res.Status)
}
return nil
}
}
return fmt.Errorf("Unsupported location to save: %s", loc)
}

View File

@@ -102,7 +102,12 @@ func (a *AuthorizationError) Error() string {
for i, err := range a.Errors {
e[i] = err.Error()
}
return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; "))
if a.Identifier != "" {
return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; "))
}
return fmt.Sprintf("acme: authorization error: %s", strings.Join(e, "; "))
}
// OrderError is returned from Client's order related methods.
@@ -407,6 +412,7 @@ type wireAuthz struct {
Wildcard bool
Challenges []wireChallenge
Combinations [][]int
Error *wireError
}
func (z *wireAuthz) authorization(uri string) *Authorization {
@@ -430,11 +436,17 @@ func (z *wireAuthz) error(uri string) *AuthorizationError {
URI: uri,
Identifier: z.Identifier.Value,
}
if z.Error != nil {
err.Errors = append(err.Errors, z.Error.error(nil))
}
for _, raw := range z.Challenges {
if raw.Error != nil {
err.Errors = append(err.Errors, raw.Error.error(nil))
}
}
return err
}

View File

@@ -42,10 +42,14 @@ type Cipher struct {
// The last len bytes of buf are leftover key stream bytes from the previous
// XORKeyStream invocation. The size of buf depends on how many blocks are
// computed at a time.
// computed at a time by xorKeyStreamBlocks.
buf [bufSize]byte
len int
// overflow is set when the counter overflowed, no more blocks can be
// generated, and the next XORKeyStream call should panic.
overflow bool
// The counter-independent results of the first round are cached after they
// are computed the first time.
precompDone bool
@@ -89,6 +93,7 @@ func newUnauthenticatedCipher(c *Cipher, key, nonce []byte) (*Cipher, error) {
return nil, errors.New("chacha20: wrong nonce size")
}
key, nonce = key[:KeySize], nonce[:NonceSize] // bounds check elimination hint
c.key = [8]uint32{
binary.LittleEndian.Uint32(key[0:4]),
binary.LittleEndian.Uint32(key[4:8]),
@@ -139,15 +144,18 @@ func quarterRound(a, b, c, d uint32) (uint32, uint32, uint32, uint32) {
// SetCounter sets the Cipher counter. The next invocation of XORKeyStream will
// behave as if (64 * counter) bytes had been encrypted so far.
//
// To prevent accidental counter reuse, SetCounter panics if counter is
// less than the current value.
// To prevent accidental counter reuse, SetCounter panics if counter is less
// than the current value.
//
// Note that the execution time of XORKeyStream is not independent of the
// counter value.
func (s *Cipher) SetCounter(counter uint32) {
// Internally, s may buffer multiple blocks, which complicates this
// implementation slightly. When checking whether the counter has rolled
// back, we must use both s.counter and s.len to determine how many blocks
// we have already output.
outputCounter := s.counter - uint32(s.len)/blockSize
if counter < outputCounter {
if s.overflow || counter < outputCounter {
panic("chacha20: SetCounter attempted to rollback counter")
}
@@ -196,34 +204,52 @@ func (s *Cipher) XORKeyStream(dst, src []byte) {
dst[i] = src[i] ^ b
}
s.len -= len(keyStream)
src = src[len(keyStream):]
dst = dst[len(keyStream):]
dst, src = dst[len(keyStream):], src[len(keyStream):]
}
if len(src) == 0 {
return
}
const blocksPerBuf = bufSize / blockSize
numBufs := (uint64(len(src)) + bufSize - 1) / bufSize
if uint64(s.counter)+numBufs*blocksPerBuf >= 1<<32 {
// If we'd need to let the counter overflow and keep generating output,
// panic immediately. If instead we'd only reach the last block, remember
// not to generate any more output after the buffer is drained.
numBlocks := (uint64(len(src)) + blockSize - 1) / blockSize
if s.overflow || uint64(s.counter)+numBlocks > 1<<32 {
panic("chacha20: counter overflow")
} else if uint64(s.counter)+numBlocks == 1<<32 {
s.overflow = true
}
// xorKeyStreamBlocks implementations expect input lengths that are a
// multiple of bufSize. Platform-specific ones process multiple blocks at a
// time, so have bufSizes that are a multiple of blockSize.
rem := len(src) % bufSize
full := len(src) - rem
full := len(src) - len(src)%bufSize
if full > 0 {
s.xorKeyStreamBlocks(dst[:full], src[:full])
}
dst, src = dst[full:], src[full:]
// If using a multi-block xorKeyStreamBlocks would overflow, use the generic
// one that does one block at a time.
const blocksPerBuf = bufSize / blockSize
if uint64(s.counter)+blocksPerBuf > 1<<32 {
s.buf = [bufSize]byte{}
numBlocks := (len(src) + blockSize - 1) / blockSize
buf := s.buf[bufSize-numBlocks*blockSize:]
copy(buf, src)
s.xorKeyStreamBlocksGeneric(buf, buf)
s.len = len(buf) - copy(dst, buf)
return
}
// If we have a partial (multi-)block, pad it for xorKeyStreamBlocks, and
// keep the leftover keystream for the next XORKeyStream invocation.
if rem > 0 {
if len(src) > 0 {
s.buf = [bufSize]byte{}
copy(s.buf[:], src[full:])
copy(s.buf[:], src)
s.xorKeyStreamBlocks(s.buf[:], s.buf[:])
s.len = bufSize - copy(dst[full:], s.buf[:])
s.len = bufSize - copy(dst, s.buf[:])
}
}
@@ -260,7 +286,9 @@ func (s *Cipher) xorKeyStreamBlocksGeneric(dst, src []byte) {
s.precompDone = true
}
for i := 0; i < len(src); i += blockSize {
// A condition of len(src) > 0 would be sufficient, but this also
// acts as a bounds check elimination hint.
for len(src) >= 64 && len(dst) >= 64 {
// The remainder of the first column round.
fcr0, fcr4, fcr8, fcr12 := quarterRound(c0, c4, c8, s.counter)
@@ -285,49 +313,28 @@ func (s *Cipher) xorKeyStreamBlocksGeneric(dst, src []byte) {
x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14)
}
// Finally, add back the initial state to generate the key stream.
x0 += c0
x1 += c1
x2 += c2
x3 += c3
x4 += c4
x5 += c5
x6 += c6
x7 += c7
x8 += c8
x9 += c9
x10 += c10
x11 += c11
x12 += s.counter
x13 += c13
x14 += c14
x15 += c15
// Add back the initial state to generate the key stream, then
// XOR the key stream with the source and write out the result.
addXor(dst[0:4], src[0:4], x0, c0)
addXor(dst[4:8], src[4:8], x1, c1)
addXor(dst[8:12], src[8:12], x2, c2)
addXor(dst[12:16], src[12:16], x3, c3)
addXor(dst[16:20], src[16:20], x4, c4)
addXor(dst[20:24], src[20:24], x5, c5)
addXor(dst[24:28], src[24:28], x6, c6)
addXor(dst[28:32], src[28:32], x7, c7)
addXor(dst[32:36], src[32:36], x8, c8)
addXor(dst[36:40], src[36:40], x9, c9)
addXor(dst[40:44], src[40:44], x10, c10)
addXor(dst[44:48], src[44:48], x11, c11)
addXor(dst[48:52], src[48:52], x12, s.counter)
addXor(dst[52:56], src[52:56], x13, c13)
addXor(dst[56:60], src[56:60], x14, c14)
addXor(dst[60:64], src[60:64], x15, c15)
s.counter += 1
if s.counter == 0 {
panic("chacha20: internal error: counter overflow")
}
in, out := src[i:], dst[i:]
in, out = in[:blockSize], out[:blockSize] // bounds check elimination hint
// XOR the key stream with the source and write out the result.
xor(out[0:], in[0:], x0)
xor(out[4:], in[4:], x1)
xor(out[8:], in[8:], x2)
xor(out[12:], in[12:], x3)
xor(out[16:], in[16:], x4)
xor(out[20:], in[20:], x5)
xor(out[24:], in[24:], x6)
xor(out[28:], in[28:], x7)
xor(out[32:], in[32:], x8)
xor(out[36:], in[36:], x9)
xor(out[40:], in[40:], x10)
xor(out[44:], in[44:], x11)
xor(out[48:], in[48:], x12)
xor(out[52:], in[52:], x13)
xor(out[56:], in[56:], x14)
xor(out[60:], in[60:], x15)
src, dst = src[blockSize:], dst[blockSize:]
}
}

View File

@@ -13,10 +13,10 @@ const unaligned = runtime.GOARCH == "386" ||
runtime.GOARCH == "ppc64le" ||
runtime.GOARCH == "s390x"
// xor reads a little endian uint32 from src, XORs it with u and
// addXor reads a little endian uint32 from src, XORs it with (a + b) and
// places the result in little endian byte order in dst.
func xor(dst, src []byte, u uint32) {
_, _ = src[3], dst[3] // eliminate bounds checks
func addXor(dst, src []byte, a, b uint32) {
_, _ = src[3], dst[3] // bounds check elimination hint
if unaligned {
// The compiler should optimize this code into
// 32-bit unaligned little endian loads and stores.
@@ -27,15 +27,16 @@ func xor(dst, src []byte, u uint32) {
v |= uint32(src[1]) << 8
v |= uint32(src[2]) << 16
v |= uint32(src[3]) << 24
v ^= u
v ^= a + b
dst[0] = byte(v)
dst[1] = byte(v >> 8)
dst[2] = byte(v >> 16)
dst[3] = byte(v >> 24)
} else {
dst[0] = src[0] ^ byte(u)
dst[1] = src[1] ^ byte(u>>8)
dst[2] = src[2] ^ byte(u>>16)
dst[3] = src[3] ^ byte(u>>24)
a += b
dst[0] = src[0] ^ byte(a)
dst[1] = src[1] ^ byte(a>>8)
dst[2] = src[2] ^ byte(a>>16)
dst[3] = src[3] ^ byte(a>>24)
}
}

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