5
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2024-11-25 23:41:35 +00:00

Update vendor (#1384)

This commit is contained in:
Wim 2021-02-01 21:29:04 +01:00 committed by GitHub
parent 1624f10773
commit 0452be0cb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1539 additions and 244 deletions

10
go.mod
View File

@ -4,7 +4,7 @@ require (
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560 github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
github.com/Rhymen/go-whatsapp v0.1.2-0.20201226125722-8029c28f5c5a github.com/Rhymen/go-whatsapp v0.1.2-0.20210126174449-3c094ebae0ce
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20200922220614-e4a51dfb52e4 // indirect github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20200922220614-e4a51dfb52e4 // indirect
github.com/SevereCloud/vksdk/v2 v2.9.0 github.com/SevereCloud/vksdk/v2 v2.9.0
github.com/d5/tengo/v2 v2.6.2 github.com/d5/tengo/v2 v2.6.2
@ -22,7 +22,7 @@ require (
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7 github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7
github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20201206215757-c1d86d75b9f8 github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20201206215757-c1d86d75b9f8
github.com/matterbridge/discordgo v0.22.1 github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible
github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050 github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050
github.com/matterbridge/gozulipbot v0.0.0-20200820220548-be5824faa913 github.com/matterbridge/gozulipbot v0.0.0-20200820220548-be5824faa913
@ -41,19 +41,19 @@ require (
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
github.com/shazow/ssh-chat v1.10.1 github.com/shazow/ssh-chat v1.10.1
github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus v1.7.0
github.com/slack-go/slack v0.7.4 github.com/slack-go/slack v0.8.0
github.com/spf13/afero v1.3.4 // indirect github.com/spf13/afero v1.3.4 // indirect
github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.7.1 github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.7.0
github.com/vincent-petithory/dataurl v0.0.0-20191104211930-d1553a71de50 github.com/vincent-petithory/dataurl v0.0.0-20191104211930-d1553a71de50
github.com/writeas/go-strip-markdown v2.0.1+incompatible github.com/writeas/go-strip-markdown v2.0.1+incompatible
github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
github.com/yaegashi/msgraph.go v0.1.4 github.com/yaegashi/msgraph.go v0.1.4
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2 github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 golang.org/x/image v0.0.0-20201208152932-35266b937fa6
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c
gomod.garykim.dev/nc-talk v0.1.7 gomod.garykim.dev/nc-talk v0.1.7
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376
layeh.com/gumble v0.0.0-20200818122324-146f9205029b layeh.com/gumble v0.0.0-20200818122324-146f9205029b

10
go.sum
View File

@ -79,6 +79,8 @@ github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBK
github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA= github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
github.com/Rhymen/go-whatsapp v0.1.2-0.20201226125722-8029c28f5c5a h1:xE1ogaIgFJQbEDoIkiAkMH9wVEAmlKOy/M+kf1xmtCY= github.com/Rhymen/go-whatsapp v0.1.2-0.20201226125722-8029c28f5c5a h1:xE1ogaIgFJQbEDoIkiAkMH9wVEAmlKOy/M+kf1xmtCY=
github.com/Rhymen/go-whatsapp v0.1.2-0.20201226125722-8029c28f5c5a/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk= github.com/Rhymen/go-whatsapp v0.1.2-0.20201226125722-8029c28f5c5a/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk=
github.com/Rhymen/go-whatsapp v0.1.2-0.20210126174449-3c094ebae0ce h1:qitALaMtz6i05smexphqLty1gGc7Viwhn8bdSRmp4UM=
github.com/Rhymen/go-whatsapp v0.1.2-0.20210126174449-3c094ebae0ce/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk=
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME= github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME=
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU= github.com/Rhymen/go-whatsapp/examples/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/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw=
@ -550,6 +552,8 @@ github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd h1:xVrqJK3xHRE
github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20201206215757-c1d86d75b9f8 h1:2iHni9FGPRRnaZrknLp+Kyr1CWbRIRUjp89TNpkqy3I= github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20201206215757-c1d86d75b9f8 h1:2iHni9FGPRRnaZrknLp+Kyr1CWbRIRUjp89TNpkqy3I=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20201206215757-c1d86d75b9f8/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A= github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20201206215757-c1d86d75b9f8/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7 h1:4J2YZuY8dIYrxbLMsWGqPZb/B59ygCwSBkyZHez5PSY=
github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7/go.mod h1:411nZYv0UMMrtppR5glXop1foboJiFAowy+42U+Ahvw=
github.com/matterbridge/discordgo v0.22.1 h1:Wh2NXfvF4egJDxX7jEvtgxJgT/ZOqD/5tfcIsNnHJ9o= github.com/matterbridge/discordgo v0.22.1 h1:Wh2NXfvF4egJDxX7jEvtgxJgT/ZOqD/5tfcIsNnHJ9o=
github.com/matterbridge/discordgo v0.22.1/go.mod h1:411nZYv0UMMrtppR5glXop1foboJiFAowy+42U+Ahvw= github.com/matterbridge/discordgo v0.22.1/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 h1:oaOqwbg5HxHRxvAbd84ks0Okwoc1ISyUZ87EiVJFhGI=
@ -846,6 +850,8 @@ github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTe
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
github.com/slack-go/slack v0.7.4 h1:Z+7CmUDV+ym4lYLA4NNLFIpr3+nDgViHrx8xsuXgrYs= github.com/slack-go/slack v0.7.4 h1:Z+7CmUDV+ym4lYLA4NNLFIpr3+nDgViHrx8xsuXgrYs=
github.com/slack-go/slack v0.7.4/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM= github.com/slack-go/slack v0.7.4/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM=
github.com/slack-go/slack v0.8.0 h1:ANyLY5KHLV+MxLJDQum2IuHTLwbCbDtaWY405X1EU9U=
github.com/slack-go/slack v0.8.0/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 h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
@ -901,6 +907,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
@ -1147,6 +1155,8 @@ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BG
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 h1:Lm4OryKCca1vehdsWogr9N4t7NfZxLbJoc/H0w4K4S4= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 h1:Lm4OryKCca1vehdsWogr9N4t7NfZxLbJoc/H0w4K4S4=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c h1:HiAZXo96zOhVhtFHchj/ojzoxCFiPrp9/j0GtS38V3g=
golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View File

@ -2,9 +2,11 @@ package whatsapp
import ( import (
"fmt" "fmt"
"github.com/Rhymen/go-whatsapp/binary"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/Rhymen/go-whatsapp/binary"
) )
type Presence string type Presence string
@ -241,3 +243,46 @@ func buildParticipantNodes(participants []string) []binary.Node {
} }
return p return p
} }
func (wac *Conn) BlockContact(jid string) (<-chan string, error) {
return wac.handleBlockContact("add", jid)
}
func (wac *Conn) UnblockContact(jid string) (<-chan string, error) {
return wac.handleBlockContact("remove", jid)
}
func (wac *Conn) handleBlockContact(action, jid string) (<-chan string, error) {
ts := time.Now().Unix()
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
netsplit := strings.Split(jid, "@")
cusjid := netsplit[0] + "@c.us"
n := binary.Node{
Description: "action",
Attributes: map[string]string{
"type": "set",
"epoch": strconv.Itoa(wac.msgCount),
},
Content: []interface{}{
binary.Node{
Description: "block",
Attributes: map[string]string{
"type": action,
},
Content: []binary.Node{
{
Description: "user",
Attributes: map[string]string{
"jid": cusjid,
},
Content: nil,
},
},
},
},
}
return wac.writeBinary(n, contact, ignore, tag)
}

View File

@ -83,10 +83,8 @@ func (wac *Conn) processReadData(msgType int, msg []byte) error {
// chan string to something like chan map[string]interface{}. The unmarshalling // chan string to something like chan map[string]interface{}. The unmarshalling
// in several places, especially in session.go, would then be gone. // in several places, especially in session.go, would then be gone.
listener <- data[1] listener <- data[1]
close(listener)
wac.listener.Lock() wac.removeListener(data[0])
delete(wac.listener.m, data[0])
wac.listener.Unlock()
} else if msgType == websocket.BinaryMessage { } else if msgType == websocket.BinaryMessage {
wac.loginSessionLock.RLock() wac.loginSessionLock.RLock()
sess := wac.session sess := wac.session

View File

@ -15,15 +15,30 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
func (wac *Conn) addListener(ch chan string, messageTag string) {
wac.listener.Lock()
wac.listener.m[messageTag] = ch
wac.listener.Unlock()
}
func (wac *Conn) removeListener(answerMessageTag string) {
wac.listener.Lock()
delete(wac.listener.m, answerMessageTag)
wac.listener.Unlock()
}
//writeJson enqueues a json message into the writeChan //writeJson enqueues a json message into the writeChan
func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) { func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) {
ch := make(chan string, 1)
wac.writerLock.Lock() wac.writerLock.Lock()
defer wac.writerLock.Unlock() defer wac.writerLock.Unlock()
d, err := json.Marshal(data) d, err := json.Marshal(data)
if err != nil { if err != nil {
return nil, err close(ch)
return ch, err
} }
ts := time.Now().Unix() ts := time.Now().Unix()
@ -35,9 +50,13 @@ func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) {
wac.timeTag = tss[len(tss)-3:] wac.timeTag = tss[len(tss)-3:]
} }
ch, err := wac.write(websocket.TextMessage, messageTag, bytes) wac.addListener(ch, messageTag)
err = wac.write(websocket.TextMessage, bytes)
if err != nil { if err != nil {
return nil, err close(ch)
wac.removeListener(messageTag)
return ch, err
} }
wac.msgCount++ wac.msgCount++
@ -45,8 +64,12 @@ func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) {
} }
func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, messageTag string) (<-chan string, error) { func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, messageTag string) (<-chan string, error) {
ch := make(chan string, 1)
if len(messageTag) < 2 { if len(messageTag) < 2 {
return nil, ErrMissingMessageTag close(ch)
return ch, ErrMissingMessageTag
} }
wac.writerLock.Lock() wac.writerLock.Lock()
@ -54,16 +77,21 @@ func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, message
data, err := wac.encryptBinaryMessage(node) data, err := wac.encryptBinaryMessage(node)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "encryptBinaryMessage(node) failed") close(ch)
return ch, errors.Wrap(err, "encryptBinaryMessage(node) failed")
} }
bytes := []byte(messageTag + ",") bytes := []byte(messageTag + ",")
bytes = append(bytes, byte(metric), byte(flag)) bytes = append(bytes, byte(metric), byte(flag))
bytes = append(bytes, data...) bytes = append(bytes, data...)
ch, err := wac.write(websocket.BinaryMessage, messageTag, bytes) wac.addListener(ch, messageTag)
err = wac.write(websocket.BinaryMessage, bytes)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to write message") close(ch)
wac.removeListener(messageTag)
return ch, errors.Wrap(err, "failed to write message")
} }
wac.msgCount++ wac.msgCount++
@ -71,9 +99,15 @@ func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, message
} }
func (wac *Conn) sendKeepAlive() error { func (wac *Conn) sendKeepAlive() error {
respChan := make(chan string, 1)
wac.addListener(respChan, "!")
bytes := []byte("?,,") bytes := []byte("?,,")
respChan, err := wac.write(websocket.TextMessage, "!", bytes) err := wac.write(websocket.TextMessage, bytes)
if err != nil { if err != nil {
close(respChan)
wac.removeListener("!")
return errors.Wrap(err, "error sending keepAlive") return errors.Wrap(err, "error sending keepAlive")
} }
@ -122,32 +156,21 @@ func (wac *Conn) sendAdminTest() (bool, error) {
} }
} }
func (wac *Conn) write(messageType int, answerMessageTag string, data []byte) (<-chan string, error) { func (wac *Conn) write(messageType int, data []byte) error {
var ch chan string
if answerMessageTag != "" {
ch = make(chan string, 1)
wac.listener.Lock()
wac.listener.m[answerMessageTag] = ch
wac.listener.Unlock()
}
if wac == nil || wac.ws == nil { if wac == nil || wac.ws == nil {
return nil, ErrInvalidWebsocket return ErrInvalidWebsocket
} }
wac.ws.Lock() wac.ws.Lock()
err := wac.ws.conn.WriteMessage(messageType, data) err := wac.ws.conn.WriteMessage(messageType, data)
wac.ws.Unlock() wac.ws.Unlock()
if err != nil { if err != nil {
if answerMessageTag != "" { return errors.Wrap(err, "error writing to websocket")
wac.listener.Lock()
delete(wac.listener.m, answerMessageTag)
wac.listener.Unlock()
} }
return nil, errors.Wrap(err, "error writing to websocket")
} return nil
return ch, nil
} }
func (wac *Conn) encryptBinaryMessage(node binary.Node) (data []byte, err error) { func (wac *Conn) encryptBinaryMessage(node binary.Node) (data []byte, err error) {

View File

@ -1,7 +1,5 @@
language: go language: go
go: go:
- 1.11.x
- 1.12.x
- 1.13.x - 1.13.x
- 1.14.x - 1.14.x
- 1.15.x - 1.15.x

View File

@ -22,7 +22,7 @@ import (
) )
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/) // VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
const VERSION = "0.22.0" const VERSION = "0.23.0"
// ErrMFA will be risen by New when the user has 2FA. // ErrMFA will be risen by New when the user has 2FA.
var ErrMFA = errors.New("account has 2FA enabled") var ErrMFA = errors.New("account has 2FA enabled")

View File

@ -14,7 +14,7 @@ package discordgo
import "strconv" import "strconv"
// APIVersion is the Discord API version used for the REST and Websocket API. // APIVersion is the Discord API version used for the REST and Websocket API.
var APIVersion = "6" var APIVersion = "8"
// Known Discord API Endpoints. // Known Discord API Endpoints.
var ( var (
@ -90,7 +90,8 @@ var (
EndpointGuildRoles = func(gID string) string { return EndpointGuilds + gID + "/roles" } EndpointGuildRoles = func(gID string) string { return EndpointGuilds + gID + "/roles" }
EndpointGuildRole = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID } EndpointGuildRole = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID }
EndpointGuildInvites = func(gID string) string { return EndpointGuilds + gID + "/invites" } EndpointGuildInvites = func(gID string) string { return EndpointGuilds + gID + "/invites" }
EndpointGuildEmbed = func(gID string) string { return EndpointGuilds + gID + "/embed" } EndpointGuildWidget = func(gID string) string { return EndpointGuilds + gID + "/widget" }
EndpointGuildEmbed = EndpointGuildWidget
EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" } EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" }
EndpointGuildIcon = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" } EndpointGuildIcon = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" }
EndpointGuildIconAnimated = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".gif" } EndpointGuildIconAnimated = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".gif" }

View File

@ -197,7 +197,6 @@ type PresencesReplace []*Presence
type PresenceUpdate struct { type PresenceUpdate struct {
Presence Presence
GuildID string `json:"guild_id"` GuildID string `json:"guild_id"`
Roles []string `json:"roles"`
} }
// Resumed is the data for a Resumed event. // Resumed is the data for a Resumed event.

View File

@ -0,0 +1,54 @@
package discordgo
import (
"bytes"
"crypto/ed25519"
"encoding/hex"
"io"
"io/ioutil"
"net/http"
)
// VerifyInteraction implements message verification of the discord interactions api
// signing algorithm, as documented here:
// https://discord.com/developers/docs/interactions/slash-commands#security-and-authorization
func VerifyInteraction(r *http.Request, key ed25519.PublicKey) bool {
var msg bytes.Buffer
signature := r.Header.Get("X-Signature-Ed25519")
if signature == "" {
return false
}
sig, err := hex.DecodeString(signature)
if err != nil {
return false
}
if len(sig) != ed25519.SignatureSize {
return false
}
timestamp := r.Header.Get("X-Signature-Timestamp")
if timestamp == "" {
return false
}
msg.WriteString(timestamp)
defer r.Body.Close()
var body bytes.Buffer
// at the end of the function, copy the original body back into the request
defer func() {
r.Body = ioutil.NopCloser(&body)
}()
// copy body into buffers
_, err = io.Copy(&msg, io.TeeReader(r.Body, &body))
if err != nil {
return false
}
return ed25519.Verify(key, msg.Bytes(), sig)
}

View File

@ -34,8 +34,10 @@ const (
MessageTypeUserPremiumGuildSubscriptionTierTwo MessageTypeUserPremiumGuildSubscriptionTierTwo
MessageTypeUserPremiumGuildSubscriptionTierThree MessageTypeUserPremiumGuildSubscriptionTierThree
MessageTypeChannelFollowAdd MessageTypeChannelFollowAdd
MessageTypeGuildDiscoveryDisqualified MessageTypeGuildDiscoveryDisqualified = iota + 1
MessageTypeGuildDiscoveryRequalified MessageTypeGuildDiscoveryRequalified
MessageTypeReply = iota + 4
MessageTypeApplicationCommand
) )
// A Message stores all data related to a specific Discord message. // A Message stores all data related to a specific Discord message.
@ -369,7 +371,7 @@ type MessageApplication struct {
type MessageReference struct { type MessageReference struct {
MessageID string `json:"message_id"` MessageID string `json:"message_id"`
ChannelID string `json:"channel_id"` ChannelID string `json:"channel_id"`
GuildID string `json:"guild_id"` GuildID string `json:"guild_id,omitempty"`
} }
// Reference returns MessageReference of given message // Reference returns MessageReference of given message

View File

@ -1,6 +1,7 @@
package discordgo package discordgo
import ( import (
"math"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -140,20 +141,21 @@ func (b *Bucket) Release(headers http.Header) error {
remaining := headers.Get("X-RateLimit-Remaining") remaining := headers.Get("X-RateLimit-Remaining")
reset := headers.Get("X-RateLimit-Reset") reset := headers.Get("X-RateLimit-Reset")
global := headers.Get("X-RateLimit-Global") global := headers.Get("X-RateLimit-Global")
retryAfter := headers.Get("Retry-After") resetAfter := headers.Get("X-RateLimit-Reset-After")
// Update global and per bucket reset time if the proper headers are available // Update global and per bucket reset time if the proper headers are available
// If global is set, then it will block all buckets until after Retry-After // If global is set, then it will block all buckets until after Retry-After
// If Retry-After without global is provided it will use that for the new reset // If Retry-After without global is provided it will use that for the new reset
// time since it's more accurate than X-RateLimit-Reset. // time since it's more accurate than X-RateLimit-Reset.
// If Retry-After after is not proided, it will update the reset time from X-RateLimit-Reset // If Retry-After after is not proided, it will update the reset time from X-RateLimit-Reset
if retryAfter != "" { if resetAfter != "" {
parsedAfter, err := strconv.ParseInt(retryAfter, 10, 64) parsedAfter, err := strconv.ParseFloat(resetAfter, 64)
if err != nil { if err != nil {
return err return err
} }
resetAt := time.Now().Add(time.Duration(parsedAfter) * time.Millisecond) whole, frac := math.Modf(parsedAfter)
resetAt := time.Now().Add(time.Duration(whole) * time.Second).Add(time.Duration(frac*1000) * time.Millisecond)
// Lock either this single bucket or all buckets // Lock either this single bucket or all buckets
if global != "" { if global != "" {
@ -168,7 +170,7 @@ func (b *Bucket) Release(headers http.Header) error {
return err return err
} }
unix, err := strconv.ParseInt(reset, 10, 64) unix, err := strconv.ParseFloat(reset, 64)
if err != nil { if err != nil {
return err return err
} }
@ -177,7 +179,8 @@ func (b *Bucket) Release(headers http.Header) error {
// some extra time is added because without it i still encountered 429's. // some extra time is added because without it i still encountered 429's.
// The added amount is the lowest amount that gave no 429's // The added amount is the lowest amount that gave no 429's
// in 1k requests // in 1k requests
delta := time.Unix(unix, 0).Sub(discordTime) + time.Millisecond*250 whole, frac := math.Modf(unix)
delta := time.Unix(int64(whole), 0).Add(time.Duration(frac*1000)*time.Millisecond).Sub(discordTime) + time.Millisecond*250
b.reset = time.Now().Add(delta) b.reset = time.Now().Add(delta)
} }

View File

@ -155,7 +155,7 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b
return return
} }
s.log(LogInformational, "Rate Limiting %s, retry in %d", urlStr, rl.RetryAfter) s.log(LogInformational, "Rate Limiting %s, retry in %d", urlStr, rl.RetryAfter)
s.handleEvent(rateLimitEventType, RateLimit{TooManyRequests: &rl, URL: urlStr}) s.handleEvent(rateLimitEventType, &RateLimit{TooManyRequests: &rl, URL: urlStr})
time.Sleep(rl.RetryAfter * time.Millisecond) time.Sleep(rl.RetryAfter * time.Millisecond)
// we can make the above smarter // we can make the above smarter
@ -465,7 +465,7 @@ func (s *Session) UserGuildSettingsEdit(guildID string, settings *UserGuildSetti
// //
// NOTE: This function is now deprecated and will be removed in the future. // NOTE: This function is now deprecated and will be removed in the future.
// Please see the same function inside state.go // Please see the same function inside state.go
func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions int, err error) { func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions int64, err error) {
// Try to just get permissions from state. // Try to just get permissions from state.
apermissions, err = s.State.UserChannelPermissions(userID, channelID) apermissions, err = s.State.UserChannelPermissions(userID, channelID)
if err == nil { if err == nil {
@ -507,7 +507,7 @@ func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions
// Calculates the permissions for a member. // Calculates the permissions for a member.
// https://support.discord.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, userID string, roles []string) (apermissions int) { func memberPermissions(guild *Guild, channel *Channel, userID string, roles []string) (apermissions int64) {
if userID == guild.OwnerID { if userID == guild.OwnerID {
apermissions = PermissionAll apermissions = PermissionAll
return return
@ -542,13 +542,11 @@ func memberPermissions(guild *Guild, channel *Channel, userID string, roles []st
} }
} }
denies := 0 var denies, allows int64
allows := 0
// Member overwrites can override role overrides, so do two passes // Member overwrites can override role overrides, so do two passes
for _, overwrite := range channel.PermissionOverwrites { for _, overwrite := range channel.PermissionOverwrites {
for _, roleID := range roles { for _, roleID := range roles {
if overwrite.Type == "role" && roleID == overwrite.ID { if overwrite.Type == PermissionOverwriteTypeRole && roleID == overwrite.ID {
denies |= overwrite.Deny denies |= overwrite.Deny
allows |= overwrite.Allow allows |= overwrite.Allow
break break
@ -560,7 +558,7 @@ func memberPermissions(guild *Guild, channel *Channel, userID string, roles []st
apermissions |= allows apermissions |= allows
for _, overwrite := range channel.PermissionOverwrites { for _, overwrite := range channel.PermissionOverwrites {
if overwrite.Type == "member" && overwrite.ID == userID { if overwrite.Type == PermissionOverwriteTypeMember && overwrite.ID == userID {
apermissions &= ^overwrite.Deny apermissions &= ^overwrite.Deny
apermissions |= overwrite.Allow apermissions |= overwrite.Allow
break break
@ -693,6 +691,19 @@ func (s *Session) GuildBanCreate(guildID, userID string, days int) (err error) {
return s.GuildBanCreateWithReason(guildID, userID, "", days) return s.GuildBanCreateWithReason(guildID, userID, "", days)
} }
// GuildBan finds ban by given guild and user id and returns GuildBan structure
func (s *Session) GuildBan(guildID, userID string) (st *GuildBan, err error) {
body, err := s.RequestWithBucketID("GET", EndpointGuildBan(guildID, userID), nil, EndpointGuildBan(guildID, userID))
if err != nil {
return
}
err = unmarshal(body, &st)
return
}
// GuildBanCreateWithReason bans the given user from the given guild also providing a reaso. // GuildBanCreateWithReason bans the given user from the given guild also providing a reaso.
// guildID : The ID of a Guild. // guildID : The ID of a Guild.
// userID : The ID of a User // userID : The ID of a User
@ -704,7 +715,7 @@ func (s *Session) GuildBanCreateWithReason(guildID, userID, reason string, days
queryParams := url.Values{} queryParams := url.Values{}
if days > 0 { if days > 0 {
queryParams.Set("delete-message-days", strconv.Itoa(days)) queryParams.Set("delete_message_days", strconv.Itoa(days))
} }
if reason != "" { if reason != "" {
queryParams.Set("reason", reason) queryParams.Set("reason", reason)
@ -1796,11 +1807,11 @@ func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, e
// ChannelPermissionSet creates a Permission Override for the given channel. // ChannelPermissionSet creates a Permission Override for the given channel.
// NOTE: This func name may changed. Using Set instead of Create because // NOTE: This func name may changed. Using Set instead of Create because
// you can both create a new override or update an override with this function. // you can both create a new override or update an override with this function.
func (s *Session) ChannelPermissionSet(channelID, targetID, targetType string, allow, deny int) (err error) { func (s *Session) ChannelPermissionSet(channelID, targetID string, targetType PermissionOverwriteType, allow, deny int) (err error) {
data := struct { data := struct {
ID string `json:"id"` ID string `json:"id"`
Type string `json:"type"` Type PermissionOverwriteType `json:"type"`
Allow int `json:"allow"` Allow int `json:"allow"`
Deny int `json:"deny"` Deny int `json:"deny"`
}{targetID, targetType, allow, deny} }{targetID, targetType, allow, deny}

View File

@ -200,14 +200,10 @@ func (s *State) PresenceAdd(guildID string, presence *Presence) error {
//guild.Presences[i] = presence //guild.Presences[i] = presence
//Update status //Update status
guild.Presences[i].Game = presence.Game guild.Presences[i].Activities = presence.Activities
guild.Presences[i].Roles = presence.Roles
if presence.Status != "" { if presence.Status != "" {
guild.Presences[i].Status = presence.Status guild.Presences[i].Status = presence.Status
} }
if presence.Nick != "" {
guild.Presences[i].Nick = presence.Nick
}
//Update the optionally sent user information //Update the optionally sent user information
//ID Is a mandatory field so you should not need to check if it is empty //ID Is a mandatory field so you should not need to check if it is empty
@ -966,24 +962,12 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
// Member not found; this is a user coming online // Member not found; this is a user coming online
m = &Member{ m = &Member{
GuildID: t.GuildID, GuildID: t.GuildID,
Nick: t.Nick,
User: t.User, User: t.User,
Roles: t.Roles,
} }
} else { } else {
if t.Nick != "" {
m.Nick = t.Nick
}
if t.User.Username != "" { if t.User.Username != "" {
m.User.Username = t.User.Username m.User.Username = t.User.Username
} }
// PresenceUpdates always contain a list of roles, so there's no need to check for an empty list here
m.Roles = t.Roles
} }
err = s.MemberAdd(m) err = s.MemberAdd(m)
@ -997,7 +981,7 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
// UserChannelPermissions returns the permission of a user in a channel. // UserChannelPermissions returns the permission of a user in a channel.
// userID : The ID of the user to calculate permissions for. // userID : The ID of the user to calculate permissions for.
// channelID : The ID of the channel to calculate permission for. // channelID : The ID of the channel to calculate permission for.
func (s *State) UserChannelPermissions(userID, channelID string) (apermissions int, err error) { func (s *State) UserChannelPermissions(userID, channelID string) (apermissions int64, err error) {
if s == nil { if s == nil {
return 0, ErrNilState return 0, ErrNilState
} }
@ -1022,7 +1006,7 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
// MessagePermissions returns the permissions of the author of the message // MessagePermissions returns the permissions of the author of the message
// in the channel in which it was sent. // in the channel in which it was sent.
func (s *State) MessagePermissions(message *Message) (apermissions int, err error) { func (s *State) MessagePermissions(message *Message) (apermissions int64, err error) {
if s == nil { if s == nil {
return 0, ErrNilState return 0, ErrNilState
} }

View File

@ -14,6 +14,7 @@ package discordgo
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"math"
"net/http" "net/http"
"strings" "strings"
"sync" "sync"
@ -322,12 +323,22 @@ type ChannelFollow struct {
WebhookID string `json:"webhook_id"` WebhookID string `json:"webhook_id"`
} }
// PermissionOverwriteType represents the type of resource on which
// a permission overwrite acts.
type PermissionOverwriteType int
// The possible permission overwrite types.
const (
PermissionOverwriteTypeRole PermissionOverwriteType = iota
PermissionOverwriteTypeMember
)
// A PermissionOverwrite holds permission overwrite data for a Channel // A PermissionOverwrite holds permission overwrite data for a Channel
type PermissionOverwrite struct { type PermissionOverwrite struct {
ID string `json:"id"` ID string `json:"id"`
Type string `json:"type"` Type PermissionOverwriteType `json:"type"`
Deny int `json:"deny"` Deny int64 `json:"deny,string"`
Allow int `json:"allow"` Allow int64 `json:"allow,string"`
} }
// Emoji struct holds data related to Emoji's // Emoji struct holds data related to Emoji's
@ -427,9 +438,6 @@ type Guild struct {
// The ID of the AFK voice channel. // The ID of the AFK voice channel.
AfkChannelID string `json:"afk_channel_id"` AfkChannelID string `json:"afk_channel_id"`
// The ID of the embed channel ID, used for embed widgets.
EmbedChannelID string `json:"embed_channel_id"`
// The user ID of the owner of the guild. // The user ID of the owner of the guild.
OwnerID string `json:"owner_id"` OwnerID string `json:"owner_id"`
@ -458,9 +466,6 @@ type Guild struct {
// The verification level required for the guild. // The verification level required for the guild.
VerificationLevel VerificationLevel `json:"verification_level"` VerificationLevel VerificationLevel `json:"verification_level"`
// Whether the guild has embedding enabled.
EmbedEnabled bool `json:"embed_enabled"`
// Whether the guild is considered large. This is // Whether the guild is considered large. This is
// determined by a member threshold in the identify packet, // determined by a member threshold in the identify packet,
// and is currently hard-coded at 250 members in the library. // and is currently hard-coded at 250 members in the library.
@ -564,7 +569,7 @@ type Guild struct {
ApproximatePresenceCount int `json:"approximate_presence_count"` ApproximatePresenceCount int `json:"approximate_presence_count"`
// Permissions of our user // Permissions of our user
Permissions int `json:"permissions"` Permissions int64 `json:"permissions,string"`
} }
// MessageNotifications is the notification level for a guild // MessageNotifications is the notification level for a guild
@ -606,7 +611,7 @@ type UserGuild struct {
Name string `json:"name"` Name string `json:"name"`
Icon string `json:"icon"` Icon string `json:"icon"`
Owner bool `json:"owner"` Owner bool `json:"owner"`
Permissions int `json:"permissions"` Permissions int64 `json:"permissions,string"`
} }
// A GuildParams stores all the data needed to update discord guild settings // A GuildParams stores all the data needed to update discord guild settings
@ -650,7 +655,7 @@ type Role struct {
// The permissions of the role on the guild (doesn't include channel overrides). // The permissions of the role on the guild (doesn't include channel overrides).
// This is a combination of bit masks; the presence of a certain permission can // This is a combination of bit masks; the presence of a certain permission can
// be checked by performing a bitwise AND between this int and the permission. // be checked by performing a bitwise AND between this int and the permission.
Permissions int `json:"permissions"` Permissions int64 `json:"permissions,string"`
} }
// Mention returns a string which mentions the role // Mention returns a string which mentions the role
@ -690,39 +695,10 @@ type VoiceState struct {
type Presence struct { type Presence struct {
User *User `json:"user"` User *User `json:"user"`
Status Status `json:"status"` Status Status `json:"status"`
Game *Game `json:"game"` Activities []*Activity `json:"activities"`
Activities []*Game `json:"activities"`
Nick string `json:"nick"`
Roles []string `json:"roles"`
Since *int `json:"since"` Since *int `json:"since"`
} }
// GameType is the type of "game" (see GameType* consts) in the Game struct
type GameType int
// Valid GameType values
const (
GameTypeGame GameType = iota
GameTypeStreaming
GameTypeListening
GameTypeWatching
GameTypeCustom
)
// A Game struct holds the name of the "playing .." game for a user
type Game struct {
Name string `json:"name"`
Type GameType `json:"type"`
URL string `json:"url,omitempty"`
Details string `json:"details,omitempty"`
State string `json:"state,omitempty"`
TimeStamps TimeStamps `json:"timestamps,omitempty"`
Assets Assets `json:"assets,omitempty"`
ApplicationID string `json:"application_id,omitempty"`
Instance int8 `json:"instance,omitempty"`
// TODO: Party and Secrets (unknown structure)
}
// A TimeStamps struct contains start and end times used in the rich presence "playing .." Game // A TimeStamps struct contains start and end times used in the rich presence "playing .." Game
type TimeStamps struct { type TimeStamps struct {
EndTimestamp int64 `json:"end,omitempty"` EndTimestamp int64 `json:"end,omitempty"`
@ -778,6 +754,9 @@ type Member struct {
// When the user used their Nitro boost on the server // When the user used their Nitro boost on the server
PremiumSince Timestamp `json:"premium_since"` PremiumSince Timestamp `json:"premium_since"`
// Is true while the member hasn't accepted the membership screen.
Pending bool `json:"pending"`
} }
// Mention creates a member mention // Mention creates a member mention
@ -838,6 +817,26 @@ type TooManyRequests struct {
RetryAfter time.Duration `json:"retry_after"` RetryAfter time.Duration `json:"retry_after"`
} }
// UnmarshalJSON helps support translation of a milliseconds-based float
// into a time.Duration on TooManyRequests.
func (t *TooManyRequests) UnmarshalJSON(b []byte) error {
u := struct {
Bucket string `json:"bucket"`
Message string `json:"message"`
RetryAfter float64 `json:"retry_after"`
}{}
err := json.Unmarshal(b, &u)
if err != nil {
return err
}
t.Bucket = u.Bucket
t.Message = u.Message
whole, frac := math.Modf(u.RetryAfter)
t.RetryAfter = time.Duration(whole)*time.Second + time.Duration(frac*1000)*time.Millisecond
return nil
}
// A ReadState stores data on the read state of channels. // A ReadState stores data on the read state of channels.
type ReadState struct { type ReadState struct {
MentionCount int `json:"mention_count"` MentionCount int `json:"mention_count"`
@ -1117,9 +1116,9 @@ type GatewayStatusUpdate struct {
// Activity defines the Activity sent with GatewayStatusUpdate // Activity defines the Activity sent with GatewayStatusUpdate
// https://discord.com/developers/docs/topics/gateway#activity-object // https://discord.com/developers/docs/topics/gateway#activity-object
type Activity struct { type Activity struct {
Name string Name string `json:"name"`
Type ActivityType Type ActivityType `json:"type"`
URL string URL string `json:"url,omitempty"`
} }
// ActivityType is the type of Activity (see ActivityType* consts) in the Activity struct // ActivityType is the type of Activity (see ActivityType* consts) in the Activity struct
@ -1128,7 +1127,7 @@ type ActivityType int
// Valid ActivityType values // Valid ActivityType values
const ( const (
ActivityTypeGame GameType = iota ActivityTypeGame ActivityType = iota
ActivityTypeStreaming ActivityTypeStreaming
ActivityTypeListening ActivityTypeListening
// ActivityTypeWatching // not valid in this use case? // ActivityTypeWatching // not valid in this use case?
@ -1145,7 +1144,7 @@ type Identify struct {
Shard *[2]int `json:"shard,omitempty"` Shard *[2]int `json:"shard,omitempty"`
Presence GatewayStatusUpdate `json:"presence,omitempty"` Presence GatewayStatusUpdate `json:"presence,omitempty"`
GuildSubscriptions bool `json:"guild_subscriptions"` GuildSubscriptions bool `json:"guild_subscriptions"`
Intents *Intent `json:"intents,omitempty"` Intents Intent `json:"intents"`
} }
// IdentifyProperties contains the "properties" portion of an Identify packet // IdentifyProperties contains the "properties" portion of an Identify packet
@ -1253,6 +1252,7 @@ const (
ErrCodeUnknownUser = 10013 ErrCodeUnknownUser = 10013
ErrCodeUnknownEmoji = 10014 ErrCodeUnknownEmoji = 10014
ErrCodeUnknownWebhook = 10015 ErrCodeUnknownWebhook = 10015
ErrCodeUnknownBan = 10026
ErrCodeBotsCannotUseEndpoint = 20001 ErrCodeBotsCannotUseEndpoint = 20001
ErrCodeOnlyBotsCanUseEndpoint = 20002 ErrCodeOnlyBotsCanUseEndpoint = 20002
@ -1331,7 +1331,9 @@ const (
IntentsNone Intent = 0 IntentsNone Intent = 0
) )
// MakeIntent helps convert a gateway intent value for use in the Identify structure. // MakeIntent used to help convert a gateway intent value for use in the Identify structure;
func MakeIntent(intents Intent) *Intent { // this was useful to help support the use of a pointer type when intents were optional.
return &intents // This is now a no-op, and is not necessary to use.
func MakeIntent(intents Intent) Intent {
return intents
} }

View File

@ -323,7 +323,7 @@ func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}
// UpdateStatusData ia provided to UpdateStatusComplex() // UpdateStatusData ia provided to UpdateStatusComplex()
type UpdateStatusData struct { type UpdateStatusData struct {
IdleSince *int `json:"since"` IdleSince *int `json:"since"`
Game *Game `json:"game"` Activities []*Activity `json:"activities"`
AFK bool `json:"afk"` AFK bool `json:"afk"`
Status string `json:"status"` Status string `json:"status"`
} }
@ -333,7 +333,7 @@ type updateStatusOp struct {
Data UpdateStatusData `json:"d"` Data UpdateStatusData `json:"d"`
} }
func newUpdateStatusData(idle int, gameType GameType, game, url string) *UpdateStatusData { func newUpdateStatusData(idle int, activityType ActivityType, name, url string) *UpdateStatusData {
usd := &UpdateStatusData{ usd := &UpdateStatusData{
Status: "online", Status: "online",
} }
@ -342,43 +342,43 @@ func newUpdateStatusData(idle int, gameType GameType, game, url string) *UpdateS
usd.IdleSince = &idle usd.IdleSince = &idle
} }
if game != "" { if name != "" {
usd.Game = &Game{ usd.Activities = []*Activity{{
Name: game, Name: name,
Type: gameType, Type: activityType,
URL: url, URL: url,
} }}
} }
return usd return usd
} }
// UpdateStatus is used to update the user's status. // UpdateGameStatus is used to update the user's status.
// If idle>0 then set status to idle. // If idle>0 then set status to idle.
// If game!="" then set game. // If name!="" then set game.
// if otherwise, set status to active, and no game. // if otherwise, set status to active, and no activity.
func (s *Session) UpdateStatus(idle int, game string) (err error) { func (s *Session) UpdateGameStatus(idle int, name string) (err error) {
return s.UpdateStatusComplex(*newUpdateStatusData(idle, GameTypeGame, game, "")) return s.UpdateStatusComplex(*newUpdateStatusData(idle, ActivityTypeGame, name, ""))
} }
// UpdateStreamingStatus is used to update the user's streaming status. // UpdateStreamingStatus is used to update the user's streaming status.
// If idle>0 then set status to idle. // If idle>0 then set status to idle.
// If game!="" then set game. // If name!="" then set game.
// If game!="" and url!="" then set the status type to streaming with the URL set. // If name!="" and url!="" then set the status type to streaming with the URL set.
// if otherwise, set status to active, and no game. // if otherwise, set status to active, and no game.
func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err error) { func (s *Session) UpdateStreamingStatus(idle int, name string, url string) (err error) {
gameType := GameTypeGame gameType := ActivityTypeGame
if url != "" { if url != "" {
gameType = GameTypeStreaming gameType = ActivityTypeStreaming
} }
return s.UpdateStatusComplex(*newUpdateStatusData(idle, gameType, game, url)) return s.UpdateStatusComplex(*newUpdateStatusData(idle, gameType, name, url))
} }
// UpdateListeningStatus is used to set the user to "Listening to..." // UpdateListeningStatus is used to set the user to "Listening to..."
// If game!="" then set to what user is listening to // If name!="" then set to what user is listening to
// Else, set user to active and no game. // Else, set user to active and no activity.
func (s *Session) UpdateListeningStatus(game string) (err error) { func (s *Session) UpdateListeningStatus(name string) (err error) {
return s.UpdateStatusComplex(*newUpdateStatusData(0, GameTypeListening, game, "")) return s.UpdateStatusComplex(*newUpdateStatusData(0, ActivityTypeListening, name, ""))
} }
// UpdateStatusComplex allows for sending the raw status update data untouched by discordgo. // UpdateStatusComplex allows for sending the raw status update data untouched by discordgo.

View File

@ -1,14 +0,0 @@
{
"DisableAll": true,
"Enable": [
"structcheck",
"vet",
"misspell",
"unconvert",
"interfacer",
"goimports"
],
"Vendor": true,
"Exclude": ["vendor"],
"Deadline": "300s"
}

View File

@ -3,6 +3,7 @@ package slack
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"net/url"
) )
type listEventAuthorizationsResponse struct { type listEventAuthorizationsResponse struct {
@ -41,3 +42,20 @@ func (api *Client) ListEventAuthorizationsContext(ctx context.Context, eventCont
return resp.Authorizations, nil return resp.Authorizations, nil
} }
func (api *Client) UninstallApp(clientID, clientSecret string) error {
values := url.Values{
"token": {api.token},
"client_id": {clientID},
"client_secret": {clientSecret},
}
response := SlackResponse{}
err := api.getMethod(context.Background(), "apps.uninstall", values, &response)
if err != nil {
return err
}
return response.Err()
}

View File

@ -48,11 +48,13 @@ type BlockAction struct {
SelectedConversation string `json:"selected_conversation"` SelectedConversation string `json:"selected_conversation"`
SelectedConversations []string `json:"selected_conversations"` SelectedConversations []string `json:"selected_conversations"`
SelectedDate string `json:"selected_date"` SelectedDate string `json:"selected_date"`
SelectedTime string `json:"selected_time"`
InitialOption OptionBlockObject `json:"initial_option"` InitialOption OptionBlockObject `json:"initial_option"`
InitialUser string `json:"initial_user"` InitialUser string `json:"initial_user"`
InitialChannel string `json:"initial_channel"` InitialChannel string `json:"initial_channel"`
InitialConversation string `json:"initial_conversation"` InitialConversation string `json:"initial_conversation"`
InitialDate string `json:"initial_date"` InitialDate string `json:"initial_date"`
InitialTime string `json:"initial_time"`
} }
// actionType returns the type of the action // actionType returns the type of the action

View File

@ -59,6 +59,8 @@ func (b *Blocks) UnmarshalJSON(data []byte) error {
block = &DividerBlock{} block = &DividerBlock{}
case "file": case "file":
block = &FileBlock{} block = &FileBlock{}
case "header":
block = &HeaderBlock{}
case "image": case "image":
block = &ImageBlock{} block = &ImageBlock{}
case "input": case "input":
@ -105,6 +107,8 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error {
switch s.TypeVal { switch s.TypeVal {
case "datepicker": case "datepicker":
e = &DatePickerBlockElement{} e = &DatePickerBlockElement{}
case "timepicker":
e = &TimePickerBlockElement{}
case "plain_text_input": case "plain_text_input":
e = &PlainTextInputBlockElement{} e = &PlainTextInputBlockElement{}
case "static_select", "external_select", "users_select", "conversations_select", "channels_select": case "static_select", "external_select", "users_select", "conversations_select", "channels_select":
@ -262,6 +266,12 @@ func (a *Accessory) UnmarshalJSON(data []byte) error {
return err return err
} }
a.DatePickerElement = element.(*DatePickerBlockElement) a.DatePickerElement = element.(*DatePickerBlockElement)
case "timepicker":
element, err := unmarshalBlockElement(r, &TimePickerBlockElement{})
if err != nil {
return err
}
a.TimePickerElement = element.(*TimePickerBlockElement)
case "plain_text_input": case "plain_text_input":
element, err := unmarshalBlockElement(r, &PlainTextInputBlockElement{}) element, err := unmarshalBlockElement(r, &PlainTextInputBlockElement{})
if err != nil { if err != nil {
@ -324,6 +334,9 @@ func toBlockElement(element *Accessory) BlockElement {
if element.DatePickerElement != nil { if element.DatePickerElement != nil {
return element.DatePickerElement return element.DatePickerElement
} }
if element.TimePickerElement != nil {
return element.TimePickerElement
}
if element.PlainTextInputElement != nil { if element.PlainTextInputElement != nil {
return element.PlainTextInputElement return element.PlainTextInputElement
} }

View File

@ -8,6 +8,7 @@ const (
METButton MessageElementType = "button" METButton MessageElementType = "button"
METOverflow MessageElementType = "overflow" METOverflow MessageElementType = "overflow"
METDatepicker MessageElementType = "datepicker" METDatepicker MessageElementType = "datepicker"
METTimepicker MessageElementType = "timepicker"
METPlainTextInput MessageElementType = "plain_text_input" METPlainTextInput MessageElementType = "plain_text_input"
METRadioButtons MessageElementType = "radio_buttons" METRadioButtons MessageElementType = "radio_buttons"
@ -44,6 +45,7 @@ type Accessory struct {
ButtonElement *ButtonBlockElement ButtonElement *ButtonBlockElement
OverflowElement *OverflowBlockElement OverflowElement *OverflowBlockElement
DatePickerElement *DatePickerBlockElement DatePickerElement *DatePickerBlockElement
TimePickerElement *TimePickerBlockElement
PlainTextInputElement *PlainTextInputBlockElement PlainTextInputElement *PlainTextInputBlockElement
RadioButtonsElement *RadioButtonsBlockElement RadioButtonsElement *RadioButtonsBlockElement
SelectElement *SelectBlockElement SelectElement *SelectBlockElement
@ -63,6 +65,8 @@ func NewAccessory(element BlockElement) *Accessory {
return &Accessory{OverflowElement: element.(*OverflowBlockElement)} return &Accessory{OverflowElement: element.(*OverflowBlockElement)}
case *DatePickerBlockElement: case *DatePickerBlockElement:
return &Accessory{DatePickerElement: element.(*DatePickerBlockElement)} return &Accessory{DatePickerElement: element.(*DatePickerBlockElement)}
case *TimePickerBlockElement:
return &Accessory{TimePickerElement: element.(*TimePickerBlockElement)}
case *PlainTextInputBlockElement: case *PlainTextInputBlockElement:
return &Accessory{PlainTextInputElement: element.(*PlainTextInputBlockElement)} return &Accessory{PlainTextInputElement: element.(*PlainTextInputBlockElement)}
case *RadioButtonsBlockElement: case *RadioButtonsBlockElement:
@ -127,10 +131,12 @@ func NewImageBlockElement(imageURL, altText string) *ImageBlockElement {
} }
} }
// Style is a style of Button element
// https://api.slack.com/reference/block-kit/block-elements#button__fields
type Style string type Style string
const ( const (
StyleDefault Style = "default" StyleDefault Style = ""
StylePrimary Style = "primary" StylePrimary Style = "primary"
StyleDanger Style = "danger" StyleDanger Style = "danger"
) )
@ -155,7 +161,7 @@ func (s ButtonBlockElement) ElementType() MessageElementType {
return s.Type return s.Type
} }
// WithStyling adds styling to the button object and returns the modified ButtonBlockElement // WithStyle adds styling to the button object and returns the modified ButtonBlockElement
func (s *ButtonBlockElement) WithStyle(style Style) *ButtonBlockElement { func (s *ButtonBlockElement) WithStyle(style Style) *ButtonBlockElement {
s.Style = style s.Style = style
return s return s
@ -350,6 +356,32 @@ func NewDatePickerBlockElement(actionID string) *DatePickerBlockElement {
} }
} }
// TimePickerBlockElement defines an element which lets users easily select a
// time from nice UI. Time picker elements can be used inside of
// section and actions blocks.
//
// More Information: https://api.slack.com/reference/messaging/block-elements#timepicker
type TimePickerBlockElement struct {
Type MessageElementType `json:"type"`
ActionID string `json:"action_id,omitempty"`
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
InitialTime string `json:"initial_time,omitempty"`
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
}
// ElementType returns the type of the Element
func (s TimePickerBlockElement) ElementType() MessageElementType {
return s.Type
}
// NewTimePickerBlockElement returns an instance of a date picker element
func NewTimePickerBlockElement(actionID string) *TimePickerBlockElement {
return &TimePickerBlockElement{
Type: METTimepicker,
ActionID: actionID,
}
}
// PlainTextInputBlockElement creates a field where a user can enter freeform // PlainTextInputBlockElement creates a field where a user can enter freeform
// data. // data.
// Plain-text input elements are currently only available in modals. // Plain-text input elements are currently only available in modals.

View File

@ -171,7 +171,7 @@ func (s ConfirmationBlockObject) validateType() MessageObjectType {
return motConfirmation return motConfirmation
} }
// add styling to confirmation object // WithStyle add styling to confirmation object
func (s *ConfirmationBlockObject) WithStyle(style Style) { func (s *ConfirmationBlockObject) WithStyle(style Style) {
s.Style = style s.Style = style
} }

View File

@ -191,6 +191,22 @@ func (api *Client) UnfurlMessage(channelID, timestamp string, unfurls map[string
return api.SendMessageContext(context.Background(), channelID, MsgOptionUnfurl(timestamp, unfurls), MsgOptionCompose(options...)) return api.SendMessageContext(context.Background(), channelID, MsgOptionUnfurl(timestamp, unfurls), MsgOptionCompose(options...))
} }
// UnfurlMessageWithAuthURL sends an unfurl request containing an
// authentication URL.
// For more details see:
// https://api.slack.com/reference/messaging/link-unfurling#authenticated_unfurls
func (api *Client) UnfurlMessageWithAuthURL(channelID, timestamp string, userAuthURL string, options ...MsgOption) (string, string, string, error) {
return api.UnfurlMessageWithAuthURLContext(context.Background(), channelID, timestamp, userAuthURL, options...)
}
// UnfurlMessageWithAuthURLContext sends an unfurl request containing an
// authentication URL.
// For more details see:
// https://api.slack.com/reference/messaging/link-unfurling#authenticated_unfurls
func (api *Client) UnfurlMessageWithAuthURLContext(ctx context.Context, channelID, timestamp string, userAuthURL string, options ...MsgOption) (string, string, string, error) {
return api.SendMessageContext(ctx, channelID, MsgOptionUnfurlAuthURL(timestamp, userAuthURL), MsgOptionCompose(options...))
}
// SendMessage more flexible method for configuring messages. // SendMessage more flexible method for configuring messages.
func (api *Client) SendMessage(channel string, options ...MsgOption) (string, string, string, error) { func (api *Client) SendMessage(channel string, options ...MsgOption) (string, string, string, error) {
return api.SendMessageContext(context.Background(), channel, options...) return api.SendMessageContext(context.Background(), channel, options...)
@ -413,6 +429,38 @@ func MsgOptionUnfurl(timestamp string, unfurls map[string]Attachment) MsgOption
} }
} }
// MsgOptionUnfurlAuthURL unfurls a message using an auth url based on the timestamp.
func MsgOptionUnfurlAuthURL(timestamp string, userAuthURL string) MsgOption {
return func(config *sendConfig) error {
config.endpoint = config.apiurl + string(chatUnfurl)
config.values.Add("ts", timestamp)
config.values.Add("user_auth_url", userAuthURL)
return nil
}
}
// MsgOptionUnfurlAuthRequired requests that the user installs the
// Slack app for unfurling.
func MsgOptionUnfurlAuthRequired(timestamp string) MsgOption {
return func(config *sendConfig) error {
config.endpoint = config.apiurl + string(chatUnfurl)
config.values.Add("ts", timestamp)
config.values.Add("user_auth_required", "true")
return nil
}
}
// MsgOptionUnfurlAuthMessage attaches a message inviting the user to
// authenticate.
func MsgOptionUnfurlAuthMessage(timestamp string, msg string) MsgOption {
return func(config *sendConfig) error {
config.endpoint = config.apiurl + string(chatUnfurl)
config.values.Add("ts", timestamp)
config.values.Add("user_auth_message", msg)
return nil
}
}
// MsgOptionResponseURL supplies a url to use as the endpoint. // MsgOptionResponseURL supplies a url to use as the endpoint.
func MsgOptionResponseURL(url string, responseType string) MsgOption { func MsgOptionResponseURL(url string, responseType string) MsgOption {
return func(config *sendConfig) error { return func(config *sendConfig) error {

View File

@ -1,4 +1,4 @@
package slack package backoff
import ( import (
"math/rand" "math/rand"
@ -11,7 +11,7 @@ import (
// call to Duration() it is multiplied by Factor. It is capped at // call to Duration() it is multiplied by Factor. It is capped at
// Max. It returns to Min on every call to Reset(). Used in // Max. It returns to Min on every call to Reset(). Used in
// conjunction with the time package. // conjunction with the time package.
type backoff struct { type Backoff struct {
attempts int attempts int
// Initial value to scale out // Initial value to scale out
Initial time.Duration Initial time.Duration
@ -23,7 +23,7 @@ type backoff struct {
// Returns the current value of the counter and then multiplies it // Returns the current value of the counter and then multiplies it
// Factor // Factor
func (b *backoff) Duration() (dur time.Duration) { func (b *Backoff) Duration() (dur time.Duration) {
// Zero-values are nonsensical, so we use // Zero-values are nonsensical, so we use
// them to apply defaults // them to apply defaults
if b.Max == 0 { if b.Max == 0 {
@ -52,6 +52,11 @@ func (b *backoff) Duration() (dur time.Duration) {
} }
//Resets the current value of the counter back to Min //Resets the current value of the counter back to Min
func (b *backoff) Reset() { func (b *Backoff) Reset() {
b.attempts = 0 b.attempts = 0
} }
// Attempts returns the number of attempts that we had done so far
func (b *Backoff) Attempts() int {
return b.attempts
}

28
vendor/github.com/slack-go/slack/internal/misc/misc.go generated vendored Normal file
View File

@ -0,0 +1,28 @@
package misc
import (
"fmt"
"net/http"
)
// StatusCodeError represents an http response error.
// type httpStatusCode interface { HTTPStatusCode() int } to handle it.
type StatusCodeError struct {
Code int
Status string
}
func (t StatusCodeError) Error() string {
return fmt.Sprintf("slack server error: %s", t.Status)
}
func (t StatusCodeError) HTTPStatusCode() int {
return t.Code
}
func (t StatusCodeError) Retryable() bool {
if t.Code >= 500 || t.Code == http.StatusTooManyRequests {
return true
}
return false
}

View File

@ -18,6 +18,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/slack-go/slack/internal/misc"
) )
// SlackResponse handles parsing out errors from the web api. // SlackResponse handles parsing out errors from the web api.
@ -42,28 +44,6 @@ func (t SlackResponse) Err() error {
return errors.New(t.Error) return errors.New(t.Error)
} }
// StatusCodeError represents an http response error.
// type httpStatusCode interface { HTTPStatusCode() int } to handle it.
type statusCodeError struct {
Code int
Status string
}
func (t statusCodeError) Error() string {
return fmt.Sprintf("slack server error: %s", t.Status)
}
func (t statusCodeError) HTTPStatusCode() int {
return t.Code
}
func (t statusCodeError) Retryable() bool {
if t.Code >= 500 || t.Code == http.StatusTooManyRequests {
return true
}
return false
}
// RateLimitedError represents the rate limit respond from slack // RateLimitedError represents the rate limit respond from slack
type RateLimitedError struct { type RateLimitedError struct {
RetryAfter time.Duration RetryAfter time.Duration
@ -312,7 +292,7 @@ func checkStatusCode(resp *http.Response, d Debug) error {
// Slack seems to send an HTML body along with 5xx error codes. Don't parse it. // Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
logResponse(resp, d) logResponse(resp, d)
return statusCodeError{Code: resp.StatusCode, Status: resp.Status} return misc.StatusCodeError{Code: resp.StatusCode, Status: resp.Status}
} }
return nil return nil

34
vendor/github.com/slack-go/slack/socket_mode.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
package slack
import (
"context"
)
// SocketModeConnection contains various details about the SocketMode connection.
// It is returned by an "apps.connections.open" API call.
type SocketModeConnection struct {
URL string `json:"url,omitempty"`
Data map[string]interface{} `json:"-"`
}
type openResponseFull struct {
SlackResponse
SocketModeConnection
}
// StartSocketModeContext calls the "apps.connections.open" endpoint and returns the provided URL and the full Info block with a custom context.
//
// To have a fully managed Socket Mode connection, use `socketmode.New()`, and call `Run()` on it.
func (api *Client) StartSocketModeContext(ctx context.Context) (info *SocketModeConnection, websocketURL string, err error) {
response := &openResponseFull{}
err = postJSON(ctx, api.httpclient, api.endpoint+"apps.connections.open", api.appLevelToken, nil, response, api)
if err != nil {
return nil, "", err
}
if response.Err() == nil {
api.Debugln("Using URL:", response.SocketModeConnection.URL)
}
return &response.SocketModeConnection, response.SocketModeConnection.URL, response.Err()
}

View File

@ -31,6 +31,7 @@ type UserProfile struct {
Image48 string `json:"image_48"` Image48 string `json:"image_48"`
Image72 string `json:"image_72"` Image72 string `json:"image_72"`
Image192 string `json:"image_192"` Image192 string `json:"image_192"`
Image512 string `json:"image_512"`
ImageOriginal string `json:"image_original"` ImageOriginal string `json:"image_original"`
Title string `json:"title"` Title string `json:"title"`
BotID string `json:"bot_id,omitempty"` BotID string `json:"bot_id,omitempty"`

View File

@ -9,6 +9,9 @@ import (
"reflect" "reflect"
"time" "time"
"github.com/slack-go/slack/internal/backoff"
"github.com/slack-go/slack/internal/misc"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/slack-go/slack/internal/errorsx" "github.com/slack-go/slack/internal/errorsx"
"github.com/slack-go/slack/internal/timex" "github.com/slack-go/slack/internal/timex"
@ -92,7 +95,7 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
// used to provide exponential backoff wait time with jitter before trying // used to provide exponential backoff wait time with jitter before trying
// to connect to slack again // to connect to slack again
boff := &backoff{ boff := &backoff.Backoff{
Max: 5 * time.Minute, Max: 5 * time.Minute,
} }
@ -103,7 +106,7 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
// send connecting event // send connecting event
rtm.IncomingEvents <- RTMEvent{"connecting", &ConnectingEvent{ rtm.IncomingEvents <- RTMEvent{"connecting", &ConnectingEvent{
Attempt: boff.attempts + 1, Attempt: boff.Attempts() + 1,
ConnectionCount: connectionCount, ConnectionCount: connectionCount,
}} }}
@ -123,7 +126,7 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
} }
switch actual := err.(type) { switch actual := err.(type) {
case statusCodeError: case misc.StatusCodeError:
if actual.Code == http.StatusNotFound { if actual.Code == http.StatusNotFound {
rtm.Debugf("invalid auth when connecting with RTM: %s", err) rtm.Debugf("invalid auth when connecting with RTM: %s", err)
rtm.IncomingEvents <- RTMEvent{"invalid_auth", &InvalidAuthEvent{}} rtm.IncomingEvents <- RTMEvent{"invalid_auth", &InvalidAuthEvent{}}
@ -138,13 +141,13 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
// any other errors are treated as recoverable and we try again after // any other errors are treated as recoverable and we try again after
// sending the event along the IncomingEvents channel // sending the event along the IncomingEvents channel
rtm.IncomingEvents <- RTMEvent{"connection_error", &ConnectionErrorEvent{ rtm.IncomingEvents <- RTMEvent{"connection_error", &ConnectionErrorEvent{
Attempt: boff.attempts, Attempt: boff.Attempts(),
Backoff: backoff, Backoff: backoff,
ErrorObj: err, ErrorObj: err,
}} }}
// get time we should wait before attempting to connect again // get time we should wait before attempting to connect again
rtm.Debugf("reconnection %d failed: %s reconnecting in %v\n", boff.attempts, err, backoff) rtm.Debugf("reconnection %d failed: %s reconnecting in %v\n", boff.Attempts(), err, backoff)
// wait for one of the following to occur, // wait for one of the following to occur,
// backoff duration has elapsed, killChannel is signalled, or // backoff duration has elapsed, killChannel is signalled, or

View File

@ -13,12 +13,42 @@ const (
compareGreater compareGreater
) )
var (
intType = reflect.TypeOf(int(1))
int8Type = reflect.TypeOf(int8(1))
int16Type = reflect.TypeOf(int16(1))
int32Type = reflect.TypeOf(int32(1))
int64Type = reflect.TypeOf(int64(1))
uintType = reflect.TypeOf(uint(1))
uint8Type = reflect.TypeOf(uint8(1))
uint16Type = reflect.TypeOf(uint16(1))
uint32Type = reflect.TypeOf(uint32(1))
uint64Type = reflect.TypeOf(uint64(1))
float32Type = reflect.TypeOf(float32(1))
float64Type = reflect.TypeOf(float64(1))
stringType = reflect.TypeOf("")
)
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
obj1Value := reflect.ValueOf(obj1)
obj2Value := reflect.ValueOf(obj2)
// throughout this switch we try and avoid calling .Convert() if possible,
// as this has a pretty big performance impact
switch kind { switch kind {
case reflect.Int: case reflect.Int:
{ {
intobj1 := obj1.(int) intobj1, ok := obj1.(int)
intobj2 := obj2.(int) if !ok {
intobj1 = obj1Value.Convert(intType).Interface().(int)
}
intobj2, ok := obj2.(int)
if !ok {
intobj2 = obj2Value.Convert(intType).Interface().(int)
}
if intobj1 > intobj2 { if intobj1 > intobj2 {
return compareGreater, true return compareGreater, true
} }
@ -31,8 +61,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Int8: case reflect.Int8:
{ {
int8obj1 := obj1.(int8) int8obj1, ok := obj1.(int8)
int8obj2 := obj2.(int8) if !ok {
int8obj1 = obj1Value.Convert(int8Type).Interface().(int8)
}
int8obj2, ok := obj2.(int8)
if !ok {
int8obj2 = obj2Value.Convert(int8Type).Interface().(int8)
}
if int8obj1 > int8obj2 { if int8obj1 > int8obj2 {
return compareGreater, true return compareGreater, true
} }
@ -45,8 +81,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Int16: case reflect.Int16:
{ {
int16obj1 := obj1.(int16) int16obj1, ok := obj1.(int16)
int16obj2 := obj2.(int16) if !ok {
int16obj1 = obj1Value.Convert(int16Type).Interface().(int16)
}
int16obj2, ok := obj2.(int16)
if !ok {
int16obj2 = obj2Value.Convert(int16Type).Interface().(int16)
}
if int16obj1 > int16obj2 { if int16obj1 > int16obj2 {
return compareGreater, true return compareGreater, true
} }
@ -59,8 +101,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Int32: case reflect.Int32:
{ {
int32obj1 := obj1.(int32) int32obj1, ok := obj1.(int32)
int32obj2 := obj2.(int32) if !ok {
int32obj1 = obj1Value.Convert(int32Type).Interface().(int32)
}
int32obj2, ok := obj2.(int32)
if !ok {
int32obj2 = obj2Value.Convert(int32Type).Interface().(int32)
}
if int32obj1 > int32obj2 { if int32obj1 > int32obj2 {
return compareGreater, true return compareGreater, true
} }
@ -73,8 +121,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Int64: case reflect.Int64:
{ {
int64obj1 := obj1.(int64) int64obj1, ok := obj1.(int64)
int64obj2 := obj2.(int64) if !ok {
int64obj1 = obj1Value.Convert(int64Type).Interface().(int64)
}
int64obj2, ok := obj2.(int64)
if !ok {
int64obj2 = obj2Value.Convert(int64Type).Interface().(int64)
}
if int64obj1 > int64obj2 { if int64obj1 > int64obj2 {
return compareGreater, true return compareGreater, true
} }
@ -87,8 +141,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Uint: case reflect.Uint:
{ {
uintobj1 := obj1.(uint) uintobj1, ok := obj1.(uint)
uintobj2 := obj2.(uint) if !ok {
uintobj1 = obj1Value.Convert(uintType).Interface().(uint)
}
uintobj2, ok := obj2.(uint)
if !ok {
uintobj2 = obj2Value.Convert(uintType).Interface().(uint)
}
if uintobj1 > uintobj2 { if uintobj1 > uintobj2 {
return compareGreater, true return compareGreater, true
} }
@ -101,8 +161,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Uint8: case reflect.Uint8:
{ {
uint8obj1 := obj1.(uint8) uint8obj1, ok := obj1.(uint8)
uint8obj2 := obj2.(uint8) if !ok {
uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8)
}
uint8obj2, ok := obj2.(uint8)
if !ok {
uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8)
}
if uint8obj1 > uint8obj2 { if uint8obj1 > uint8obj2 {
return compareGreater, true return compareGreater, true
} }
@ -115,8 +181,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Uint16: case reflect.Uint16:
{ {
uint16obj1 := obj1.(uint16) uint16obj1, ok := obj1.(uint16)
uint16obj2 := obj2.(uint16) if !ok {
uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16)
}
uint16obj2, ok := obj2.(uint16)
if !ok {
uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16)
}
if uint16obj1 > uint16obj2 { if uint16obj1 > uint16obj2 {
return compareGreater, true return compareGreater, true
} }
@ -129,8 +201,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Uint32: case reflect.Uint32:
{ {
uint32obj1 := obj1.(uint32) uint32obj1, ok := obj1.(uint32)
uint32obj2 := obj2.(uint32) if !ok {
uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32)
}
uint32obj2, ok := obj2.(uint32)
if !ok {
uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32)
}
if uint32obj1 > uint32obj2 { if uint32obj1 > uint32obj2 {
return compareGreater, true return compareGreater, true
} }
@ -143,8 +221,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Uint64: case reflect.Uint64:
{ {
uint64obj1 := obj1.(uint64) uint64obj1, ok := obj1.(uint64)
uint64obj2 := obj2.(uint64) if !ok {
uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64)
}
uint64obj2, ok := obj2.(uint64)
if !ok {
uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64)
}
if uint64obj1 > uint64obj2 { if uint64obj1 > uint64obj2 {
return compareGreater, true return compareGreater, true
} }
@ -157,8 +241,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Float32: case reflect.Float32:
{ {
float32obj1 := obj1.(float32) float32obj1, ok := obj1.(float32)
float32obj2 := obj2.(float32) if !ok {
float32obj1 = obj1Value.Convert(float32Type).Interface().(float32)
}
float32obj2, ok := obj2.(float32)
if !ok {
float32obj2 = obj2Value.Convert(float32Type).Interface().(float32)
}
if float32obj1 > float32obj2 { if float32obj1 > float32obj2 {
return compareGreater, true return compareGreater, true
} }
@ -171,8 +261,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Float64: case reflect.Float64:
{ {
float64obj1 := obj1.(float64) float64obj1, ok := obj1.(float64)
float64obj2 := obj2.(float64) if !ok {
float64obj1 = obj1Value.Convert(float64Type).Interface().(float64)
}
float64obj2, ok := obj2.(float64)
if !ok {
float64obj2 = obj2Value.Convert(float64Type).Interface().(float64)
}
if float64obj1 > float64obj2 { if float64obj1 > float64obj2 {
return compareGreater, true return compareGreater, true
} }
@ -185,8 +281,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.String: case reflect.String:
{ {
stringobj1 := obj1.(string) stringobj1, ok := obj1.(string)
stringobj2 := obj2.(string) if !ok {
stringobj1 = obj1Value.Convert(stringType).Interface().(string)
}
stringobj2, ok := obj2.(string)
if !ok {
stringobj2 = obj2Value.Convert(stringType).Interface().(string)
}
if stringobj1 > stringobj2 { if stringobj1 > stringobj2 {
return compareGreater, true return compareGreater, true
} }
@ -240,6 +342,24 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
} }
// Positive asserts that the specified element is positive
//
// assert.Positive(t, 1)
// assert.Positive(t, 1.23)
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
zero := reflect.Zero(reflect.TypeOf(e))
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs)
}
// Negative asserts that the specified element is negative
//
// assert.Negative(t, -1)
// assert.Negative(t, -1.23)
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
zero := reflect.Zero(reflect.TypeOf(e))
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs)
}
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok { if h, ok := t.(tHelper); ok {
h.Helper() h.Helper()

View File

@ -114,6 +114,24 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
return Error(t, err, append([]interface{}{msg}, args...)...) return Error(t, err, append([]interface{}{msg}, args...)...)
} }
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...)
}
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...)
}
// Eventuallyf asserts that given condition will be met in waitFor time, // Eventuallyf asserts that given condition will be met in waitFor time,
// periodically checking target function each tick. // periodically checking target function each tick.
// //
@ -321,6 +339,54 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil
return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
} }
// IsDecreasingf asserts that the collection is decreasing
//
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return IsDecreasing(t, object, append([]interface{}{msg}, args...)...)
}
// IsIncreasingf asserts that the collection is increasing
//
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return IsIncreasing(t, object, append([]interface{}{msg}, args...)...)
}
// IsNonDecreasingf asserts that the collection is not decreasing
//
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...)
}
// IsNonIncreasingf asserts that the collection is not increasing
//
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...)
}
// IsTypef asserts that the specified objects are of the same type. // IsTypef asserts that the specified objects are of the same type.
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok { if h, ok := t.(tHelper); ok {
@ -375,6 +441,17 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args .
return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
} }
// Negativef asserts that the specified element is negative
//
// assert.Negativef(t, -1, "error message %s", "formatted")
// assert.Negativef(t, -1.23, "error message %s", "formatted")
func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Negative(t, e, append([]interface{}{msg}, args...)...)
}
// Neverf asserts that the given condition doesn't satisfy in waitFor time, // Neverf asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick. // periodically checking the target function each tick.
// //
@ -476,6 +553,15 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
} }
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...)
}
// NotNilf asserts that the specified object is not nil. // NotNilf asserts that the specified object is not nil.
// //
// assert.NotNilf(t, err, "error message %s", "formatted") // assert.NotNilf(t, err, "error message %s", "formatted")
@ -572,6 +658,17 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
} }
// Positivef asserts that the specified element is positive
//
// assert.Positivef(t, 1, "error message %s", "formatted")
// assert.Positivef(t, 1.23, "error message %s", "formatted")
func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Positive(t, e, append([]interface{}{msg}, args...)...)
}
// Regexpf asserts that a specified regexp matches a string. // Regexpf asserts that a specified regexp matches a string.
// //
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") // assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")

View File

@ -204,6 +204,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
return Error(a.t, err, msgAndArgs...) return Error(a.t, err, msgAndArgs...)
} }
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ErrorAs(a.t, err, target, msgAndArgs...)
}
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ErrorAsf(a.t, err, target, msg, args...)
}
// ErrorIs asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ErrorIs(a.t, err, target, msgAndArgs...)
}
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ErrorIsf(a.t, err, target, msg, args...)
}
// Errorf asserts that a function returned an error (i.e. not `nil`). // Errorf asserts that a function returned an error (i.e. not `nil`).
// //
// actualObj, err := SomeFunction() // actualObj, err := SomeFunction()
@ -631,6 +667,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo
return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) return InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
} }
// IsDecreasing asserts that the collection is decreasing
//
// a.IsDecreasing([]int{2, 1, 0})
// a.IsDecreasing([]float{2, 1})
// a.IsDecreasing([]string{"b", "a"})
func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsDecreasing(a.t, object, msgAndArgs...)
}
// IsDecreasingf asserts that the collection is decreasing
//
// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted")
// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted")
// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted")
func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsDecreasingf(a.t, object, msg, args...)
}
// IsIncreasing asserts that the collection is increasing
//
// a.IsIncreasing([]int{1, 2, 3})
// a.IsIncreasing([]float{1, 2})
// a.IsIncreasing([]string{"a", "b"})
func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsIncreasing(a.t, object, msgAndArgs...)
}
// IsIncreasingf asserts that the collection is increasing
//
// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted")
// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted")
// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted")
func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsIncreasingf(a.t, object, msg, args...)
}
// IsNonDecreasing asserts that the collection is not decreasing
//
// a.IsNonDecreasing([]int{1, 1, 2})
// a.IsNonDecreasing([]float{1, 2})
// a.IsNonDecreasing([]string{"a", "b"})
func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsNonDecreasing(a.t, object, msgAndArgs...)
}
// IsNonDecreasingf asserts that the collection is not decreasing
//
// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted")
// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted")
// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted")
func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsNonDecreasingf(a.t, object, msg, args...)
}
// IsNonIncreasing asserts that the collection is not increasing
//
// a.IsNonIncreasing([]int{2, 1, 1})
// a.IsNonIncreasing([]float{2, 1})
// a.IsNonIncreasing([]string{"b", "a"})
func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsNonIncreasing(a.t, object, msgAndArgs...)
}
// IsNonIncreasingf asserts that the collection is not increasing
//
// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted")
// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted")
// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted")
func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsNonIncreasingf(a.t, object, msg, args...)
}
// IsType asserts that the specified objects are of the same type. // IsType asserts that the specified objects are of the same type.
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok { if h, ok := a.t.(tHelper); ok {
@ -739,6 +871,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i
return Lessf(a.t, e1, e2, msg, args...) return Lessf(a.t, e1, e2, msg, args...)
} }
// Negative asserts that the specified element is negative
//
// a.Negative(-1)
// a.Negative(-1.23)
func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Negative(a.t, e, msgAndArgs...)
}
// Negativef asserts that the specified element is negative
//
// a.Negativef(-1, "error message %s", "formatted")
// a.Negativef(-1.23, "error message %s", "formatted")
func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Negativef(a.t, e, msg, args...)
}
// Never asserts that the given condition doesn't satisfy in waitFor time, // Never asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick. // periodically checking the target function each tick.
// //
@ -941,6 +1095,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
return NotEqualf(a.t, expected, actual, msg, args...) return NotEqualf(a.t, expected, actual, msg, args...)
} }
// NotErrorIs asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotErrorIs(a.t, err, target, msgAndArgs...)
}
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotErrorIsf(a.t, err, target, msg, args...)
}
// NotNil asserts that the specified object is not nil. // NotNil asserts that the specified object is not nil.
// //
// a.NotNil(err) // a.NotNil(err)
@ -1133,6 +1305,28 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b
return Panicsf(a.t, f, msg, args...) return Panicsf(a.t, f, msg, args...)
} }
// Positive asserts that the specified element is positive
//
// a.Positive(1)
// a.Positive(1.23)
func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Positive(a.t, e, msgAndArgs...)
}
// Positivef asserts that the specified element is positive
//
// a.Positivef(1, "error message %s", "formatted")
// a.Positivef(1.23, "error message %s", "formatted")
func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Positivef(a.t, e, msg, args...)
}
// Regexp asserts that a specified regexp matches a string. // Regexp asserts that a specified regexp matches a string.
// //
// a.Regexp(regexp.MustCompile("start"), "it's starting") // a.Regexp(regexp.MustCompile("start"), "it's starting")

View File

@ -0,0 +1,81 @@
package assert
import (
"fmt"
"reflect"
)
// isOrdered checks that collection contains orderable elements.
func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
objKind := reflect.TypeOf(object).Kind()
if objKind != reflect.Slice && objKind != reflect.Array {
return false
}
objValue := reflect.ValueOf(object)
objLen := objValue.Len()
if objLen <= 1 {
return true
}
value := objValue.Index(0)
valueInterface := value.Interface()
firstValueKind := value.Kind()
for i := 1; i < objLen; i++ {
prevValue := value
prevValueInterface := valueInterface
value = objValue.Index(i)
valueInterface = value.Interface()
compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind)
if !isComparable {
return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...)
}
if !containsValue(allowedComparesResults, compareResult) {
return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...)
}
}
return true
}
// IsIncreasing asserts that the collection is increasing
//
// assert.IsIncreasing(t, []int{1, 2, 3})
// assert.IsIncreasing(t, []float{1, 2})
// assert.IsIncreasing(t, []string{"a", "b"})
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
}
// IsNonIncreasing asserts that the collection is not increasing
//
// assert.IsNonIncreasing(t, []int{2, 1, 1})
// assert.IsNonIncreasing(t, []float{2, 1})
// assert.IsNonIncreasing(t, []string{"b", "a"})
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
}
// IsDecreasing asserts that the collection is decreasing
//
// assert.IsDecreasing(t, []int{2, 1, 0})
// assert.IsDecreasing(t, []float{2, 1})
// assert.IsDecreasing(t, []string{"b", "a"})
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
}
// IsNonDecreasing asserts that the collection is not decreasing
//
// assert.IsNonDecreasing(t, []int{1, 1, 2})
// assert.IsNonDecreasing(t, []float{1, 2})
// assert.IsNonDecreasing(t, []string{"a", "b"})
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
}

View File

@ -172,8 +172,8 @@ func isTest(name, prefix string) bool {
if len(name) == len(prefix) { // "Test" is ok if len(name) == len(prefix) { // "Test" is ok
return true return true
} }
rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) r, _ := utf8.DecodeRuneInString(name[len(prefix):])
return !unicode.IsLower(rune) return !unicode.IsLower(r)
} }
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
@ -1622,6 +1622,7 @@ var spewConfig = spew.ConfigState{
DisableCapacities: true, DisableCapacities: true,
SortKeys: true, SortKeys: true,
DisableMethods: true, DisableMethods: true,
MaxDepth: 10,
} }
type tHelper interface { type tHelper interface {
@ -1693,3 +1694,81 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D
} }
} }
} }
// ErrorIs asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if errors.Is(err, target) {
return true
}
var expectedText string
if target != nil {
expectedText = target.Error()
}
chain := buildErrorChainString(err)
return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+
"expected: %q\n"+
"in chain: %s", expectedText, chain,
), msgAndArgs...)
}
// NotErrorIs asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if !errors.Is(err, target) {
return true
}
var expectedText string
if target != nil {
expectedText = target.Error()
}
chain := buildErrorChainString(err)
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
"found: %q\n"+
"in chain: %s", expectedText, chain,
), msgAndArgs...)
}
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if errors.As(err, target) {
return true
}
chain := buildErrorChainString(err)
return Fail(t, fmt.Sprintf("Should be in error chain:\n"+
"expected: %q\n"+
"in chain: %s", target, chain,
), msgAndArgs...)
}
func buildErrorChainString(err error) string {
if err == nil {
return ""
}
e := errors.Unwrap(err)
chain := fmt.Sprintf("%q", err.Error())
for e != nil {
chain += fmt.Sprintf("\n\t%q", e.Error())
e = errors.Unwrap(e)
}
return chain
}

View File

@ -256,6 +256,54 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) {
t.FailNow() t.FailNow()
} }
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.ErrorAs(t, err, target, msgAndArgs...) {
return
}
t.FailNow()
}
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.ErrorAsf(t, err, target, msg, args...) {
return
}
t.FailNow()
}
// ErrorIs asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.ErrorIs(t, err, target, msgAndArgs...) {
return
}
t.FailNow()
}
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.ErrorIsf(t, err, target, msg, args...) {
return
}
t.FailNow()
}
// Errorf asserts that a function returned an error (i.e. not `nil`). // Errorf asserts that a function returned an error (i.e. not `nil`).
// //
// actualObj, err := SomeFunction() // actualObj, err := SomeFunction()
@ -806,6 +854,126 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl
t.FailNow() t.FailNow()
} }
// IsDecreasing asserts that the collection is decreasing
//
// assert.IsDecreasing(t, []int{2, 1, 0})
// assert.IsDecreasing(t, []float{2, 1})
// assert.IsDecreasing(t, []string{"b", "a"})
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsDecreasing(t, object, msgAndArgs...) {
return
}
t.FailNow()
}
// IsDecreasingf asserts that the collection is decreasing
//
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsDecreasingf(t, object, msg, args...) {
return
}
t.FailNow()
}
// IsIncreasing asserts that the collection is increasing
//
// assert.IsIncreasing(t, []int{1, 2, 3})
// assert.IsIncreasing(t, []float{1, 2})
// assert.IsIncreasing(t, []string{"a", "b"})
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsIncreasing(t, object, msgAndArgs...) {
return
}
t.FailNow()
}
// IsIncreasingf asserts that the collection is increasing
//
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsIncreasingf(t, object, msg, args...) {
return
}
t.FailNow()
}
// IsNonDecreasing asserts that the collection is not decreasing
//
// assert.IsNonDecreasing(t, []int{1, 1, 2})
// assert.IsNonDecreasing(t, []float{1, 2})
// assert.IsNonDecreasing(t, []string{"a", "b"})
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsNonDecreasing(t, object, msgAndArgs...) {
return
}
t.FailNow()
}
// IsNonDecreasingf asserts that the collection is not decreasing
//
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsNonDecreasingf(t, object, msg, args...) {
return
}
t.FailNow()
}
// IsNonIncreasing asserts that the collection is not increasing
//
// assert.IsNonIncreasing(t, []int{2, 1, 1})
// assert.IsNonIncreasing(t, []float{2, 1})
// assert.IsNonIncreasing(t, []string{"b", "a"})
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsNonIncreasing(t, object, msgAndArgs...) {
return
}
t.FailNow()
}
// IsNonIncreasingf asserts that the collection is not increasing
//
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsNonIncreasingf(t, object, msg, args...) {
return
}
t.FailNow()
}
// IsType asserts that the specified objects are of the same type. // IsType asserts that the specified objects are of the same type.
func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok { if h, ok := t.(tHelper); ok {
@ -944,6 +1112,34 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter
t.FailNow() t.FailNow()
} }
// Negative asserts that the specified element is negative
//
// assert.Negative(t, -1)
// assert.Negative(t, -1.23)
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.Negative(t, e, msgAndArgs...) {
return
}
t.FailNow()
}
// Negativef asserts that the specified element is negative
//
// assert.Negativef(t, -1, "error message %s", "formatted")
// assert.Negativef(t, -1.23, "error message %s", "formatted")
func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.Negativef(t, e, msg, args...) {
return
}
t.FailNow()
}
// Never asserts that the given condition doesn't satisfy in waitFor time, // Never asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick. // periodically checking the target function each tick.
// //
@ -1200,6 +1396,30 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string,
t.FailNow() t.FailNow()
} }
// NotErrorIs asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.NotErrorIs(t, err, target, msgAndArgs...) {
return
}
t.FailNow()
}
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.NotErrorIsf(t, err, target, msg, args...) {
return
}
t.FailNow()
}
// NotNil asserts that the specified object is not nil. // NotNil asserts that the specified object is not nil.
// //
// assert.NotNil(t, err) // assert.NotNil(t, err)
@ -1446,6 +1666,34 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}
t.FailNow() t.FailNow()
} }
// Positive asserts that the specified element is positive
//
// assert.Positive(t, 1)
// assert.Positive(t, 1.23)
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.Positive(t, e, msgAndArgs...) {
return
}
t.FailNow()
}
// Positivef asserts that the specified element is positive
//
// assert.Positivef(t, 1, "error message %s", "formatted")
// assert.Positivef(t, 1.23, "error message %s", "formatted")
func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.Positivef(t, e, msg, args...) {
return
}
t.FailNow()
}
// Regexp asserts that a specified regexp matches a string. // Regexp asserts that a specified regexp matches a string.
// //
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") // assert.Regexp(t, regexp.MustCompile("start"), "it's starting")

View File

@ -205,6 +205,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) {
Error(a.t, err, msgAndArgs...) Error(a.t, err, msgAndArgs...)
} }
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
ErrorAs(a.t, err, target, msgAndArgs...)
}
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
ErrorAsf(a.t, err, target, msg, args...)
}
// ErrorIs asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
ErrorIs(a.t, err, target, msgAndArgs...)
}
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
ErrorIsf(a.t, err, target, msg, args...)
}
// Errorf asserts that a function returned an error (i.e. not `nil`). // Errorf asserts that a function returned an error (i.e. not `nil`).
// //
// actualObj, err := SomeFunction() // actualObj, err := SomeFunction()
@ -632,6 +668,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo
InEpsilonf(a.t, expected, actual, epsilon, msg, args...) InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
} }
// IsDecreasing asserts that the collection is decreasing
//
// a.IsDecreasing([]int{2, 1, 0})
// a.IsDecreasing([]float{2, 1})
// a.IsDecreasing([]string{"b", "a"})
func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsDecreasing(a.t, object, msgAndArgs...)
}
// IsDecreasingf asserts that the collection is decreasing
//
// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted")
// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted")
// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted")
func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsDecreasingf(a.t, object, msg, args...)
}
// IsIncreasing asserts that the collection is increasing
//
// a.IsIncreasing([]int{1, 2, 3})
// a.IsIncreasing([]float{1, 2})
// a.IsIncreasing([]string{"a", "b"})
func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsIncreasing(a.t, object, msgAndArgs...)
}
// IsIncreasingf asserts that the collection is increasing
//
// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted")
// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted")
// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted")
func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsIncreasingf(a.t, object, msg, args...)
}
// IsNonDecreasing asserts that the collection is not decreasing
//
// a.IsNonDecreasing([]int{1, 1, 2})
// a.IsNonDecreasing([]float{1, 2})
// a.IsNonDecreasing([]string{"a", "b"})
func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsNonDecreasing(a.t, object, msgAndArgs...)
}
// IsNonDecreasingf asserts that the collection is not decreasing
//
// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted")
// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted")
// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted")
func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsNonDecreasingf(a.t, object, msg, args...)
}
// IsNonIncreasing asserts that the collection is not increasing
//
// a.IsNonIncreasing([]int{2, 1, 1})
// a.IsNonIncreasing([]float{2, 1})
// a.IsNonIncreasing([]string{"b", "a"})
func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsNonIncreasing(a.t, object, msgAndArgs...)
}
// IsNonIncreasingf asserts that the collection is not increasing
//
// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted")
// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted")
// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted")
func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsNonIncreasingf(a.t, object, msg, args...)
}
// IsType asserts that the specified objects are of the same type. // IsType asserts that the specified objects are of the same type.
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok { if h, ok := a.t.(tHelper); ok {
@ -740,6 +872,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i
Lessf(a.t, e1, e2, msg, args...) Lessf(a.t, e1, e2, msg, args...)
} }
// Negative asserts that the specified element is negative
//
// a.Negative(-1)
// a.Negative(-1.23)
func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Negative(a.t, e, msgAndArgs...)
}
// Negativef asserts that the specified element is negative
//
// a.Negativef(-1, "error message %s", "formatted")
// a.Negativef(-1.23, "error message %s", "formatted")
func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Negativef(a.t, e, msg, args...)
}
// Never asserts that the given condition doesn't satisfy in waitFor time, // Never asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick. // periodically checking the target function each tick.
// //
@ -942,6 +1096,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
NotEqualf(a.t, expected, actual, msg, args...) NotEqualf(a.t, expected, actual, msg, args...)
} }
// NotErrorIs asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotErrorIs(a.t, err, target, msgAndArgs...)
}
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotErrorIsf(a.t, err, target, msg, args...)
}
// NotNil asserts that the specified object is not nil. // NotNil asserts that the specified object is not nil.
// //
// a.NotNil(err) // a.NotNil(err)
@ -1134,6 +1306,28 @@ func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interfa
Panicsf(a.t, f, msg, args...) Panicsf(a.t, f, msg, args...)
} }
// Positive asserts that the specified element is positive
//
// a.Positive(1)
// a.Positive(1.23)
func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Positive(a.t, e, msgAndArgs...)
}
// Positivef asserts that the specified element is positive
//
// a.Positivef(1, "error message %s", "formatted")
// a.Positivef(1.23, "error message %s", "formatted")
func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Positivef(a.t, e, msg, args...)
}
// Regexp asserts that a specified regexp matches a string. // Regexp asserts that a specified regexp matches a string.
// //
// a.Regexp(regexp.MustCompile("start"), "it's starting") // a.Regexp(regexp.MustCompile("start"), "it's starting")

12
vendor/modules.txt vendored
View File

@ -18,7 +18,7 @@ github.com/Philipp15b/go-steam/protocol/steamlang
github.com/Philipp15b/go-steam/rwu github.com/Philipp15b/go-steam/rwu
github.com/Philipp15b/go-steam/socialcache github.com/Philipp15b/go-steam/socialcache
github.com/Philipp15b/go-steam/steamid github.com/Philipp15b/go-steam/steamid
# github.com/Rhymen/go-whatsapp v0.1.2-0.20201226125722-8029c28f5c5a # github.com/Rhymen/go-whatsapp v0.1.2-0.20210126174449-3c094ebae0ce
## explicit ## explicit
github.com/Rhymen/go-whatsapp github.com/Rhymen/go-whatsapp
github.com/Rhymen/go-whatsapp/binary github.com/Rhymen/go-whatsapp/binary
@ -140,7 +140,7 @@ github.com/matrix-org/gomatrix
github.com/matterbridge/Rocket.Chat.Go.SDK/models github.com/matterbridge/Rocket.Chat.Go.SDK/models
github.com/matterbridge/Rocket.Chat.Go.SDK/realtime github.com/matterbridge/Rocket.Chat.Go.SDK/realtime
github.com/matterbridge/Rocket.Chat.Go.SDK/rest github.com/matterbridge/Rocket.Chat.Go.SDK/rest
# github.com/matterbridge/discordgo v0.22.1 # github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7
## explicit ## explicit
github.com/matterbridge/discordgo github.com/matterbridge/discordgo
# github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible # github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible
@ -243,10 +243,12 @@ github.com/sirupsen/logrus
github.com/skip2/go-qrcode github.com/skip2/go-qrcode
github.com/skip2/go-qrcode/bitset github.com/skip2/go-qrcode/bitset
github.com/skip2/go-qrcode/reedsolomon github.com/skip2/go-qrcode/reedsolomon
# github.com/slack-go/slack v0.7.4 # github.com/slack-go/slack v0.8.0
## explicit ## explicit
github.com/slack-go/slack github.com/slack-go/slack
github.com/slack-go/slack/internal/backoff
github.com/slack-go/slack/internal/errorsx github.com/slack-go/slack/internal/errorsx
github.com/slack-go/slack/internal/misc
github.com/slack-go/slack/internal/timex github.com/slack-go/slack/internal/timex
github.com/slack-go/slack/slackutilsx github.com/slack-go/slack/slackutilsx
# github.com/spf13/afero v1.3.4 # github.com/spf13/afero v1.3.4
@ -264,7 +266,7 @@ github.com/spf13/pflag
# github.com/spf13/viper v1.7.1 # github.com/spf13/viper v1.7.1
## explicit ## explicit
github.com/spf13/viper github.com/spf13/viper
# github.com/stretchr/testify v1.6.1 # github.com/stretchr/testify v1.7.0
## explicit ## explicit
github.com/stretchr/testify/assert github.com/stretchr/testify/assert
github.com/stretchr/testify/require github.com/stretchr/testify/require
@ -352,7 +354,7 @@ golang.org/x/net/http2/h2c
golang.org/x/net/http2/hpack golang.org/x/net/http2/hpack
golang.org/x/net/idna golang.org/x/net/idna
golang.org/x/net/websocket golang.org/x/net/websocket
# golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 # golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c
## explicit ## explicit
golang.org/x/oauth2 golang.org/x/oauth2
golang.org/x/oauth2/clientcredentials golang.org/x/oauth2/clientcredentials