Compare commits

...

16 Commits

Author SHA1 Message Date
Wim
bad1990173 Release v1.16.1 2019-10-27 01:49:41 +02:00
Wim
0bc159341d Update vendor (#932)
* Update vendor

* Fix godiscord api change
2019-10-27 01:45:57 +02:00
Wim
45bf1fd63a Convert slack bold/strike to correct markdown (slack). Fixes #918 (#930) 2019-10-27 01:10:59 +02:00
Wim
ff0de85817 Remove obsolete file upload links (discord). Fixes #908 (#931)
Since v1.16.0 we now can upload files via webhook.
Old way of showing files with webhook only setup can be removed.
2019-10-27 01:10:43 +02:00
Wim
727fa9f929 Add support for uploading application/x and audio/x (matrix). Fixes #925 (#929) 2019-10-27 00:06:44 +02:00
Wim
0b9bc18236 Update vendor matterbridge/gomatrix fork (#928) 2019-10-26 23:31:44 +02:00
Wim
bad3b83d33 Update golang-commonmark/linkify vendor and use upstream again. Fixes #924 (#926) 2019-10-26 22:08:02 +02:00
Wim
00967a98ac Fix panic on WebhookURL only setting (mattermost). Closes #916 (#917) 2019-10-04 01:01:24 +02:00
Qais Patankar
1d708ab351 Suppress unhandled HelloEvent message (slack) (#913) 2019-10-04 00:19:50 +02:00
Qais Patankar
ba6759010b Add UserTypingSupport (discord) (#914)
* Add Discord to UserTypingSupport

* discord: start typing in a channel on EventUserTyping receive

* discord: emit EventUserTyping to gateway
2019-10-04 00:18:56 +02:00
Wim
da3868c104 Try to fix blackfriday go modules mess 2019-09-22 00:34:37 +02:00
Wim
0abf4d5d5d Specify correct GuildID on unknown user query (discord). Fixes #879 (#894) 2019-09-15 20:25:42 +02:00
Michal Suchánek
9b320cd43f Add token support (RocketChat) (#892)
Signed-off-by: Michal Suchanek <msuchanek@suse.de>
2019-09-13 23:41:02 +02:00
Wim
28783a4146 Do configuration validation on start-up. Fixes #888 (#889)
Fail if:
* we don't have any gateways configured
* we have gateways configured but with non-existing bridge configuration
* we have gateways configured without any configuration
2019-09-09 23:48:00 +02:00
Wim
f92927eae5 Fix deprecation in goreleaser 2019-09-07 23:37:49 +02:00
Wim
294139ce7a Bump version and fix changelog 2019-09-07 23:30:17 +02:00
59 changed files with 4549 additions and 1739 deletions

View File

@@ -21,7 +21,11 @@ builds:
ldflags:
- -s -w -X main.githash={{.ShortCommit}}
archive:
archives:
-
id: matterbridge
builds:
- matterbridge
name_template: "{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
format: binary
files:

View File

@@ -140,7 +140,7 @@ See https://github.com/42wim/matterbridge/wiki
### Binaries
- Latest stable release [v1.16.0](https://github.com/42wim/matterbridge/releases/latest)
- Latest stable release [v1.16.1](https://github.com/42wim/matterbridge/releases/latest)
- Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.

View File

@@ -206,6 +206,7 @@ type BridgeValues struct {
}
type Config interface {
Viper() *viper.Viper
BridgeValues() *BridgeValues
GetBool(key string) (bool, bool)
GetInt(key string) (int, bool)
@@ -274,6 +275,10 @@ func (c *config) BridgeValues() *BridgeValues {
return c.cv
}
func (c *config) Viper() *viper.Viper {
return c.v
}
func (c *config) GetBool(key string) (bool, bool) {
c.RLock()
defer c.RUnlock()

View File

@@ -72,6 +72,7 @@ func (b *Bdiscord) Connect() error {
}
b.Log.Info("Connection succeeded")
b.c.AddHandler(b.messageCreate)
b.c.AddHandler(b.messageTyping)
b.c.AddHandler(b.memberUpdate)
b.c.AddHandler(b.messageUpdate)
b.c.AddHandler(b.messageDelete)
@@ -188,6 +189,14 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
return "", fmt.Errorf("Could not find channelID for %v", msg.Channel)
}
if msg.Event == config.EventUserTyping {
if b.GetBool("ShowUserTyping") {
err := b.c.ChannelTyping(channelID)
return "", err
}
return "", nil
}
// Make a action /me of the message
if msg.Event == config.EventUserAction {
msg.Text = "_" + msg.Text + "_"
@@ -225,18 +234,6 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
}
b.Log.Debugf("Broadcasting using Webhook")
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
if fi.Comment != "" {
msg.Text += fi.Comment + ": "
}
if fi.URL != "" {
msg.Text = fi.URL
if fi.Comment != "" {
msg.Text = fi.Comment + ": " + fi.URL
}
}
}
// skip empty messages
if msg.Text == "" && (msg.Extra == nil || len(msg.Extra["file"]) == 0) {

View File

@@ -37,6 +37,19 @@ func (b *Bdiscord) messageDeleteBulk(s *discordgo.Session, m *discordgo.MessageD
}
}
func (b *Bdiscord) messageTyping(s *discordgo.Session, m *discordgo.TypingStart) {
if !b.GetBool("ShowUserTyping") {
return
}
rmsg := config.Message{Account: b.Account, Event: config.EventUserTyping}
rmsg.Channel = b.getChannelName(m.ChannelID)
if b.useChannelID {
rmsg.Channel = "ID:" + m.ChannelID
}
b.Remote <- rmsg
}
func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) { //nolint:unparam
if b.GetBool("EditDisable") {
return
@@ -45,7 +58,10 @@ func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdat
if m.Message.EditedTimestamp != "" {
b.Log.Debugf("Sending edit message")
m.Content += b.GetString("EditSuffix")
b.messageCreate(s, (*discordgo.MessageCreate)(m))
msg := &discordgo.MessageCreate{
Message: m.Message,
}
b.messageCreate(s, msg)
}
}
@@ -89,7 +105,7 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
// set username
if !b.GetBool("UseUserName") {
rmsg.Username = b.getNick(m.Author)
rmsg.Username = b.getNick(m.Author, m.GuildID)
} else {
rmsg.Username = m.Author.Username
if b.GetBool("UseDiscriminator") {

View File

@@ -9,7 +9,7 @@ import (
"github.com/bwmarrin/discordgo"
)
func (b *Bdiscord) getNick(user *discordgo.User) string {
func (b *Bdiscord) getNick(user *discordgo.User, guildID string) string {
b.membersMutex.RLock()
defer b.membersMutex.RUnlock()
@@ -23,9 +23,9 @@ func (b *Bdiscord) getNick(user *discordgo.User) string {
}
// If we didn't find nick, search for it.
member, err := b.c.GuildMember(b.guildID, user.ID)
member, err := b.c.GuildMember(guildID, user.ID)
if err != nil {
b.Log.Warnf("Failed to fetch information for member %#v on guild %#v: %s", user, b.guildID, err)
b.Log.Warnf("Failed to fetch information for member %#v on guild %#v: %s", user, guildID, err)
return user.Username
} else if member == nil {
b.Log.Warnf("Got no information for member %#v", user)

View File

@@ -291,7 +291,8 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *conf
content := bytes.NewReader(*fi.Data)
sp := strings.Split(fi.Name, ".")
mtype := mime.TypeByExtension("." + sp[len(sp)-1])
if !strings.Contains(mtype, "image") && !strings.Contains(mtype, "video") {
if !(strings.Contains(mtype, "image") || strings.Contains(mtype, "video") ||
strings.Contains(mtype, "application") || strings.Contains(mtype, "audio")) {
return
}
if fi.Comment != "" {
@@ -326,6 +327,18 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *conf
if err != nil {
b.Log.Errorf("sendImage failed: %#v", err)
}
case strings.Contains(mtype, "application"):
b.Log.Debugf("sendFile %s", res.ContentURI)
_, err = b.mc.SendFile(channel, fi.Name, res.ContentURI, mtype, uint(len(*fi.Data)))
if err != nil {
b.Log.Errorf("sendFile failed: %#v", err)
}
case strings.Contains(mtype, "audio"):
b.Log.Debugf("sendAudio %s", res.ContentURI)
_, err = b.mc.SendAudio(channel, fi.Name, res.ContentURI, mtype, uint(len(*fi.Data)))
if err != nil {
b.Log.Errorf("sendAudio failed: %#v", err)
}
}
b.Log.Debugf("result: %#v", res)
}

View File

@@ -66,6 +66,11 @@ func (b *Bmattermost) handleMatter() {
} else {
b.Log.Debugf("Choosing login/password based receiving")
}
// if for some reason we only want to sent stuff to mattermost but not receive, return
if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") != "" {
b.Log.Debugf("No WebhookBindAddress specified, only WebhookURL. You will not receive messages from mattermost, only sending is possible.")
return
}
go b.handleMatterClient(messages)
}
var ok bool

View File

@@ -58,6 +58,9 @@ func (b *Brocketchat) doConnectWebhookURL() error {
func (b *Brocketchat) apiLogin() error {
b.Log.Debugf("handling apiLogin()")
credentials := &models.UserCredentials{Email: b.GetString("login"), Password: b.GetString("password")}
if b.GetString("Token") != "" {
credentials = &models.UserCredentials{ID: b.GetString("Login"), Token: b.GetString("Token")}
}
myURL, err := url.Parse(b.GetString("server"))
if err != nil {
return err

View File

@@ -30,6 +30,7 @@ func (b *Bslack) handleSlack() {
message.Text = b.replaceVariable(message.Text)
message.Text = b.replaceChannel(message.Text)
message.Text = b.replaceURL(message.Text)
message.Text = b.replaceb0rkedMarkDown(message.Text)
message.Text = html.UnescapeString(message.Text)
// Add the avatar
@@ -43,7 +44,7 @@ func (b *Bslack) handleSlack() {
func (b *Bslack) handleSlackClient(messages chan *config.Message) {
for msg := range b.rtm.IncomingEvents {
if msg.Type != sUserTyping && msg.Type != sLatencyReport {
if msg.Type != sUserTyping && msg.Type != sHello && msg.Type != sLatencyReport {
b.Log.Debugf("== Receiving event %#v", msg.Data)
}
switch ev := msg.Data.(type) {
@@ -86,7 +87,7 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) {
b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj)
case *slack.MemberJoinedChannelEvent:
b.users.populateUser(ev.User)
case *slack.LatencyReport:
case *slack.HelloEvent, *slack.LatencyReport:
continue
default:
b.Log.Debugf("Unhandled incoming event: %T", ev)

View File

@@ -188,6 +188,36 @@ func (b *Bslack) replaceURL(text string) string {
return text
}
func (b *Bslack) replaceb0rkedMarkDown(text string) string {
// taken from https://github.com/mattermost/mattermost-server/blob/master/app/slackimport.go
//
regexReplaceAllString := []struct {
regex *regexp.Regexp
rpl string
}{
// bold
{
regexp.MustCompile(`(^|[\s.;,])\*(\S[^*\n]+)\*`),
"$1**$2**",
},
// strikethrough
{
regexp.MustCompile(`(^|[\s.;,])\~(\S[^~\n]+)\~`),
"$1~~$2~~",
},
// single paragraph blockquote
// Slack converts > character to &gt;
{
regexp.MustCompile(`(?sm)^&gt;`),
">",
},
}
for _, rule := range regexReplaceAllString {
text = rule.regex.ReplaceAllString(text, rule.rpl)
}
return text
}
func (b *Bslack) replaceCodeFence(text string) string {
return codeFenceRE.ReplaceAllString(text, "```")
}

View File

@@ -12,7 +12,7 @@ import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/42wim/matterbridge/matterhook"
"github.com/hashicorp/golang-lru"
lru "github.com/hashicorp/golang-lru"
"github.com/nlopes/slack"
"github.com/rs/xid"
)
@@ -36,6 +36,7 @@ type Bslack struct {
}
const (
sHello = "hello"
sChannelJoin = "channel_join"
sChannelLeave = "channel_leave"
sChannelJoined = "channel_joined"

View File

@@ -1,3 +1,30 @@
# dev
# v1.16.1
## New features
* rocketchat: add token support #892
* matrix: Add support for uploading application/x and audio/x (matrix). #929
## Enhancements
* general: Do configuration validation on start-up. Fixes #888
* general: updated vendored libraries (discord/whatsapp) #932
* discord: user typing messages #914
* slack: Convert slack bold/strike to correct markdown (slack). Fixes #918
## Bugfix
* discord: fix Failed to fetch information for members message. #894
* discord: remove obsolete file upload links (discord). #931
* slack: suppress unhandled HelloEvent message #913
* mattermost: Fix panic on WebhookURL only setting (mattermost). #917
* matrix: fix corrupted links between slack and matrix #924
This release couldn't exist without the following contributors:
@qaisjp, @hramrach, @42wim
# v1.16.0
## New features
@@ -15,6 +42,9 @@
* xmpp: Fix possible panic at startup of the XMPP bridge #869
* mattermost: Make getChannelIdTeam behave like GetChannelId for groups (mattermost) #873
This release couldn't exist without the following contributors:
@hyperobject, @42wim, @bucko909, @MOZGIII
# v1.15.1
## New features

View File

@@ -41,5 +41,6 @@ var (
UserTypingSupport = map[string]struct{}{
"slack": {},
"discord": {},
}
)

View File

@@ -85,6 +85,7 @@ func (gw *Gateway) FindCanonicalMsgID(protocol string, mID string) string {
func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
br := gw.Router.getBridge(cfg.Account)
if br == nil {
gw.checkConfig(cfg)
br = bridge.New(cfg)
br.Config = gw.Router.Config
br.General = &gw.BridgeValues().General
@@ -104,6 +105,19 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
return nil
}
func (gw *Gateway) checkConfig(cfg *config.Bridge) {
match := false
for _, key := range gw.Router.Config.Viper().AllKeys() {
if strings.HasPrefix(key, cfg.Account) {
match = true
break
}
}
if !match {
gw.logger.Fatalf("Account %s defined in gateway %s but no configuration found, exiting.", cfg.Account, gw.Name)
}
}
// AddConfig associates a new configuration with the gateway object.
func (gw *Gateway) AddConfig(cfg *config.Gateway) error {
gw.Name = cfg.Name

View File

@@ -15,10 +15,15 @@ import (
var testconfig = []byte(`
[irc.freenode]
server=""
[mattermost.test]
server=""
[gitter.42wim]
server=""
[discord.test]
server=""
[slack.test]
server=""
[[gateway]]
name = "bridge1"
@@ -44,10 +49,15 @@ var testconfig = []byte(`
var testconfig2 = []byte(`
[irc.freenode]
server=""
[mattermost.test]
server=""
[gitter.42wim]
server=""
[discord.test]
server=""
[slack.test]
server=""
[[gateway]]
name = "bridge1"
@@ -87,8 +97,11 @@ var testconfig2 = []byte(`
var testconfig3 = []byte(`
[irc.zzz]
server=""
[telegram.zzz]
server=""
[slack.zzz]
server=""
[[gateway]]
name="bridge"
enable=true
@@ -176,7 +189,6 @@ func TestNewRouter(t *testing.T) {
assert.Equal(t, 1, len(r.Gateways))
assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges))
assert.Equal(t, 4, len(r.Gateways["bridge1"].Channels))
r = maketestRouter(testconfig2)
assert.Equal(t, 2, len(r.Gateways))
assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges))

View File

@@ -59,8 +59,14 @@ func NewRouter(rootLogger *logrus.Logger, cfg config.Config, bridgeMap map[strin
// between them.
func (r *Router) Start() error {
m := make(map[string]*bridge.Bridge)
if len(r.Gateways) == 0 {
return fmt.Errorf("no [[gateway]] configured. See https://github.com/42wim/matterbridge/wiki/How-to-create-your-config for more info")
}
for _, gw := range r.Gateways {
r.logger.Infof("Parsing gateway %s", gw.Name)
if len(gw.Bridges) == 0 {
return fmt.Errorf("no bridges configured for gateway %s. See https://github.com/42wim/matterbridge/wiki/How-to-create-your-config for more info", gw.Name)
}
for _, br := range gw.Bridges {
m[br.Account] = br
}

14
go.mod
View File

@@ -5,10 +5,10 @@ require (
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
github.com/Jeffail/gabs v1.1.1 // indirect
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0
github.com/Rhymen/go-whatsapp v0.0.3-0.20190729104911-5c79b2cf277a
github.com/Rhymen/go-whatsapp v0.0.3-0.20191003184814-fc3f792c814c
github.com/bwmarrin/discordgo v0.19.0
// github.com/bwmarrin/discordgo v0.19.0
github.com/d5/tengo v1.24.3
github.com/d5/tengo v1.24.8
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
github.com/fsnotify/fsnotify v1.4.7
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
@@ -26,7 +26,7 @@ require (
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea
github.com/matterbridge/gomatrix v0.0.0-20191026211822-6fc7accd00ca
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61
github.com/mattermost/mattermost-server v5.5.0+incompatible
@@ -55,7 +55,7 @@ require (
github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a // indirect
gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 // indirect
gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82 // indirect
gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f
gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2 // indirect
gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe // indirect
@@ -66,12 +66,10 @@ require (
golang.org/x/text v0.3.2 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/russross/blackfriday.v2 v2.0.1 // indirect
gopkg.in/russross/blackfriday.v2 v2.0.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)
replace github.com/bwmarrin/discordgo v0.19.0 => github.com/matterbridge/discordgo v0.0.0-20190818085008-57c6e0fc2f40
replace gopkg.in/russross/blackfriday.v2 v2.0.1 => github.com/russross/blackfriday/v2 v2.0.1
replace github.com/bwmarrin/discordgo v0.19.0 => github.com/matterbridge/discordgo v0.0.0-20191026232317-01823f4ebba4
go 1.13

24
go.sum
View File

@@ -11,8 +11,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 h1:TO7d4rocnNFng6ZQrPe7U6WqHtK5eHEMrgrnnM/72IQ=
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
github.com/Rhymen/go-whatsapp v0.0.3-0.20190729104911-5c79b2cf277a h1:umvfZW+YE+ynhYwsyheyunB/3xRK68kNFMRNUMQxzJI=
github.com/Rhymen/go-whatsapp v0.0.3-0.20190729104911-5c79b2cf277a/go.mod h1:qf/2PQi82Okxw/igghu/oMGzTeUYuKBq1JNo3tdQyNg=
github.com/Rhymen/go-whatsapp v0.0.3-0.20191003184814-fc3f792c814c h1:+yAllLxP+WjpuVVE1WNm0/Oigbeob9+liYEyk/v4nj8=
github.com/Rhymen/go-whatsapp v0.0.3-0.20191003184814-fc3f792c814c/go.mod h1:qf/2PQi82Okxw/igghu/oMGzTeUYuKBq1JNo3tdQyNg=
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME=
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU=
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw=
@@ -32,8 +32,8 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/d5/tengo v1.24.3 h1:wp44VW7fdfzMzIDT19tT5uNeGnm2UMd6s3TLAahrwSU=
github.com/d5/tengo v1.24.3/go.mod h1:VhLq8Q2QFhCIJO3NhvM934qOThykMqJi9y9Siqd1ocQ=
github.com/d5/tengo v1.24.8 h1:PRJ+NWt7ae/9sSbIfThOBTkPSvNV+dwYoBAvwfNgNJY=
github.com/d5/tengo v1.24.8/go.mod h1:VhLq8Q2QFhCIJO3NhvM934qOThykMqJi9y9Siqd1ocQ=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -121,12 +121,12 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d h1:F+Sr+C0ojSlYQ37BLylQtSFmyQULe3jbAygcyXQ9mVs=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
github.com/matterbridge/discordgo v0.0.0-20190818085008-57c6e0fc2f40 h1:OJmjOa1ry5IZzFowLhAZ8b3bFPWFFNUbqGxs9pNqgEU=
github.com/matterbridge/discordgo v0.0.0-20190818085008-57c6e0fc2f40/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
github.com/matterbridge/discordgo v0.0.0-20191026232317-01823f4ebba4 h1:RvH3lC4bEp+bieca+Yh5xPU8tLJgnk7ridiSwMFHrrw=
github.com/matterbridge/discordgo v0.0.0-20191026232317-01823f4ebba4/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 h1:KzDEcy8eDbTx881giW8a6llsAck3e2bJvMyKvh1IK+k=
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q=
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea h1:kaADGqpK4gGO2BpzEyJrBxq2Jc57Rsar4i2EUxcACUc=
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g=
github.com/matterbridge/gomatrix v0.0.0-20191026211822-6fc7accd00ca h1:3ypqEpFpt6vg5Sv2xxA8/v4WiSOnWMXW7DqxTxpM4XI=
github.com/matterbridge/gomatrix v0.0.0-20191026211822-6fc7accd00ca/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g=
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtWGtfTgZVxHG1lcKjv+re7dRwyyuYFNu69xdho=
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18/go.mod h1:yAjnZ34DuDyPHMPHHjOsTk/FefW4JJjoMMCGt/8uuQA=
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61 h1:R/MgM/eUyRBQx2FiH6JVmXck8PaAuKfe2M1tWIzW7nE=
@@ -191,8 +191,6 @@ github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
@@ -249,8 +247,8 @@ github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2 h1:UQwvu7FjUE
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU=
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a h1:Ax7kdHNICZiIeFpmevmaEWb0Ae3BUj3zCTKhZHZ+zd0=
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a/go.mod h1:JT4uoTz0tfPoyVH88GZoWDNm5NHJI2VbUW+eyPClueI=
gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 h1:rbON2KwBnWuFMlSHM8LELLlwroDRZw6xv0e6il6e5dk=
gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179/go.mod h1:Gn+LZmCrhPECMD3SOKlE+BOHwhOYD9j7WT9NUtkCrC8=
gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82 h1:oYrL81N608MLZhma3ruL8qTM4xcpYECGut8KSxRY59g=
gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82/go.mod h1:Gn+LZmCrhPECMD3SOKlE+BOHwhOYD9j7WT9NUtkCrC8=
gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f h1:jwXy/CsM4xS2aoiF2fHAlukmInWhd2TlWB+HDCyvzKc=
gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f/go.mod h1:SIHlEr9462fpIfTrVWf3GqQDxnA65Vm3BMMsUtuA6W0=
gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2 h1:wD/sPUgx2QJFPTyXZpJnLaROolfeKuruh06U4pRV0WY=
@@ -326,6 +324,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/russross/blackfriday.v2 v2.0.0 h1:+FlnIV8DSQnT7NZ43hcVKcdJdzZoeCmJj4Ql8gq5keA=
gopkg.in/russross/blackfriday.v2 v2.0.0/go.mod h1:6sSBNz/GtOm/pJTuh5UmBK2ZHfmnxGbl2NZg1UliSOI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=

View File

@@ -15,7 +15,7 @@ import (
)
var (
version = "1.16.0"
version = "1.16.1"
githash string
flagConfig = flag.String("conf", "matterbridge.toml", "config file")

View File

@@ -956,6 +956,11 @@ Server="https://yourrocketchatserver.domain.com:443"
#REQUIRED (when not using webhooks)
Login="yourlogin@domain.com"
Password="yourpass"
# When using access token set Login to the User ID associated with your token and Token to your token.
# When Token is set Password is ignored.
# Login="yOurUSerID"
# Token="YoUrUsER_toKEN"
#### Settings for webhook matterbridge.
#USE DEDICATED BOT USER WHEN POSSIBLE! This allows you to use advanced features like message editing/deleting and uploads

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,94 @@
syntax = "proto2";
package proto;
message FingerprintData {
optional string publicKey = 1;
optional string identifier = 2;
message HydratedQuickReplyButton {
optional string displayText = 1;
optional string buttonId = 2;
}
message CombinedFingerprint {
optional uint32 version = 1;
optional FingerprintData localFingerprint = 2;
optional FingerprintData remoteFingerprint = 3;
message HydratedURLButton {
optional string displayText = 1;
optional string url = 2;
}
message MessageKey {
optional string remoteJid = 1;
optional bool fromMe = 2;
optional string id = 3;
optional string participant = 4;
message HydratedCallButton {
optional string displayText = 1;
optional string phoneNumber = 2;
}
message HydratedTemplateButton {
oneof hydratedButton {
HydratedQuickReplyButton quickReplyButton = 1;
HydratedURLButton urlButton = 2;
HydratedCallButton callButton = 3;
}
}
message QuickReplyButton {
optional HighlyStructuredMessage displayText = 1;
optional string buttonId = 2;
}
message URLButton {
optional HighlyStructuredMessage displayText = 1;
optional HighlyStructuredMessage url = 2;
}
message CallButton {
optional HighlyStructuredMessage displayText = 1;
optional HighlyStructuredMessage phoneNumber = 2;
}
message TemplateButton {
oneof button {
QuickReplyButton quickReplyButton = 1;
URLButton urlButton = 2;
CallButton callButton = 3;
}
}
message Location {
optional double degreesLatitude = 1;
optional double degreesLongitude = 2;
optional string name = 3;
}
message Point {
optional double x = 3;
optional double y = 4;
}
message InteractiveAnnotation {
repeated Point polygonVertices = 1;
oneof action {
Location location = 2;
}
}
message AdReplyInfo {
optional string advertiserName = 1;
enum AD_REPLY_INFO_MEDIATYPE {
NONE = 0;
IMAGE = 1;
VIDEO = 2;
}
optional AD_REPLY_INFO_MEDIATYPE mediaType = 2;
optional bytes jpegThumbnail = 16;
optional string caption = 17;
}
message ContextInfo {
optional string stanzaId = 1;
optional string participant = 2;
optional Message quotedMessage = 3;
optional string remoteJid = 4;
repeated string mentionedJid = 15;
optional string conversionSource = 18;
optional bytes conversionData = 19;
optional uint32 conversionDelaySeconds = 20;
optional uint32 forwardingScore = 21;
optional bool isForwarded = 22;
optional AdReplyInfo quotedAd = 23;
}
message SenderKeyDistributionMessage {
@@ -36,10 +108,12 @@ message ImageMessage {
optional bytes fileEncSha256 = 9;
repeated InteractiveAnnotation interactiveAnnotations = 10;
optional string directPath = 11;
optional int64 mediaKeyTimestamp = 12;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
optional bytes firstScanSidecar = 18;
optional uint32 firstScanLength = 19;
optional uint32 experimentGroupId = 20;
}
message ContactMessage {
@@ -66,7 +140,7 @@ message ExtendedTextMessage {
optional string title = 6;
optional fixed32 textArgb = 7;
optional fixed32 backgroundArgb = 8;
enum FONTTYPE {
enum EXTENDED_TEXT_MESSAGE_FONTTYPE {
SANS_SERIF = 0;
SERIF = 1;
NORICAN_REGULAR = 2;
@@ -74,7 +148,12 @@ message ExtendedTextMessage {
BEBASNEUE_REGULAR = 4;
OSWALD_HEAVY = 5;
}
optional FONTTYPE font = 9;
optional EXTENDED_TEXT_MESSAGE_FONTTYPE font = 9;
enum EXTENDED_TEXT_MESSAGE_PREVIEWTYPE {
NONE = 0;
VIDEO = 1;
}
optional EXTENDED_TEXT_MESSAGE_PREVIEWTYPE previewType = 10;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
@@ -90,6 +169,7 @@ message DocumentMessage {
optional string fileName = 8;
optional bytes fileEncSha256 = 9;
optional string directPath = 10;
optional int64 mediaKeyTimestamp = 11;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
@@ -104,6 +184,7 @@ message AudioMessage {
optional bytes mediaKey = 7;
optional bytes fileEncSha256 = 8;
optional string directPath = 9;
optional int64 mediaKeyTimestamp = 10;
optional ContextInfo contextInfo = 17;
optional bytes streamingSidecar = 18;
}
@@ -122,15 +203,16 @@ message VideoMessage {
optional bytes fileEncSha256 = 11;
repeated InteractiveAnnotation interactiveAnnotations = 12;
optional string directPath = 13;
optional int64 mediaKeyTimestamp = 14;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
optional bytes streamingSidecar = 18;
enum ATTRIBUTION {
enum VIDEO_MESSAGE_ATTRIBUTION {
NONE = 0;
GIPHY = 1;
TENOR = 2;
}
optional ATTRIBUTION gifAttribution = 19;
optional VIDEO_MESSAGE_ATTRIBUTION gifAttribution = 19;
}
message Call {
@@ -144,10 +226,10 @@ message Chat {
message ProtocolMessage {
optional MessageKey key = 1;
enum TYPE {
enum PROTOCOL_MESSAGE_TYPE {
REVOKE = 0;
}
optional TYPE type = 2;
optional PROTOCOL_MESSAGE_TYPE type = 2;
}
message ContactsArrayMessage {
@@ -162,7 +244,7 @@ message HSMCurrency {
}
message HSMDateTimeComponent {
enum DAYOFWEEKTYPE {
enum HSM_DATE_TIME_COMPONENT_DAYOFWEEKTYPE {
MONDAY = 1;
TUESDAY = 2;
WEDNESDAY = 3;
@@ -171,17 +253,17 @@ message HSMDateTimeComponent {
SATURDAY = 6;
SUNDAY = 7;
}
optional DAYOFWEEKTYPE dayOfWeek = 1;
optional HSM_DATE_TIME_COMPONENT_DAYOFWEEKTYPE dayOfWeek = 1;
optional uint32 year = 2;
optional uint32 month = 3;
optional uint32 dayOfMonth = 4;
optional uint32 hour = 5;
optional uint32 minute = 6;
enum CALENDARTYPE {
enum HSM_DATE_TIME_COMPONENT_CALENDARTYPE {
GREGORIAN = 1;
SOLAR_HIJRI = 2;
}
optional CALENDARTYPE calendar = 7;
optional HSM_DATE_TIME_COMPONENT_CALENDARTYPE calendar = 7;
}
message HSMDateTimeUnixEpoch {
@@ -210,17 +292,29 @@ message HighlyStructuredMessage {
optional string fallbackLg = 4;
optional string fallbackLc = 5;
repeated HSMLocalizableParameter localizableParams = 6;
optional string deterministicLg = 7;
optional string deterministicLc = 8;
}
message SendPaymentMessage {
optional Message noteMessage = 2;
optional MessageKey requestMessageKey = 3;
}
message RequestPaymentMessage {
optional Message noteMessage = 4;
optional string currencyCodeIso4217 = 1;
optional uint64 amount1000 = 2;
optional string requestFrom = 3;
optional Message noteMessage = 4;
optional int64 expiryTimestamp = 5;
}
message DeclinePaymentRequestMessage {
optional MessageKey key = 1;
}
message CancelPaymentRequestMessage {
optional MessageKey key = 1;
}
message LiveLocationMessage {
@@ -231,6 +325,7 @@ message LiveLocationMessage {
optional uint32 degreesClockwiseFromMagneticNorth = 5;
optional string caption = 6;
optional int64 sequenceNumber = 7;
optional uint32 timeOffset = 8;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
@@ -245,10 +340,77 @@ message StickerMessage {
optional uint32 width = 7;
optional string directPath = 8;
optional uint64 fileLength = 9;
optional int64 mediaKeyTimestamp = 10;
optional bytes pngThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
message FourRowTemplate {
optional HighlyStructuredMessage content = 6;
optional HighlyStructuredMessage footer = 7;
repeated TemplateButton buttons = 8;
oneof title {
DocumentMessage documentMessage = 1;
HighlyStructuredMessage highlyStructuredMessage = 2;
ImageMessage imageMessage = 3;
VideoMessage videoMessage = 4;
LocationMessage locationMessage = 5;
}
}
message HydratedFourRowTemplate {
optional string hydratedContentText = 6;
optional string hydratedFooterText = 7;
repeated HydratedTemplateButton hydratedButtons = 9;
oneof title {
DocumentMessage documentMessage = 1;
string hydratedTitleText = 2;
ImageMessage imageMessage = 3;
VideoMessage videoMessage = 4;
LocationMessage locationMessage = 5;
}
}
message TemplateMessage {
oneof format {
FourRowTemplate fourRowTemplate = 1;
HydratedFourRowTemplate hydratedFourRowTemplate = 2;
}
}
message TemplateButtonReplyMessage {
optional string selectedButtonId = 1;
repeated string selectedButtonDisplayText = 2;
optional ContextInfo contextInfo = 3;
}
message ProductSnapshot {
optional ImageMessage productImage = 1;
optional string productId = 2;
optional string title = 3;
optional string description = 4;
optional string currencyCode = 5;
optional int64 priceAmount1000 = 6;
optional string retailerId = 7;
optional string url = 8;
optional uint32 productImageCount = 9;
}
message ProductMessage {
optional ProductSnapshot product = 1;
optional string businessOwnerJid = 2;
optional ContextInfo contextInfo = 17;
}
message GroupInviteMessage {
optional string groupJid = 1;
optional string inviteCode = 2;
optional int64 inviteExpiration = 3;
optional string groupName = 4;
optional bytes jpegThumbnail = 5;
optional string caption = 6;
}
message Message {
optional string conversation = 1;
optional SenderKeyDistributionMessage senderKeyDistributionMessage = 2;
@@ -266,47 +428,104 @@ message Message {
optional HighlyStructuredMessage highlyStructuredMessage = 14;
optional SenderKeyDistributionMessage fastRatchetKeySenderKeyDistributionMessage = 15;
optional SendPaymentMessage sendPaymentMessage = 16;
optional RequestPaymentMessage requestPaymentMessage = 17;
optional LiveLocationMessage liveLocationMessage = 18;
optional StickerMessage stickerMessage = 20;
optional RequestPaymentMessage requestPaymentMessage = 22;
optional DeclinePaymentRequestMessage declinePaymentRequestMessage = 23;
optional CancelPaymentRequestMessage cancelPaymentRequestMessage = 24;
optional TemplateMessage templateMessage = 25;
optional StickerMessage stickerMessage = 26;
optional ProductMessage productMessage = 27;
optional GroupInviteMessage groupInviteMessage = 28;
}
message ContextInfo {
optional string stanzaId = 1;
optional string participant = 2;
repeated Message quotedMessage = 3;
optional string remoteJid = 4;
repeated string mentionedJid = 15;
optional string conversionSource = 18;
optional bytes conversionData = 19;
optional uint32 conversionDelaySeconds = 20;
optional bool isForwarded = 22;
reserved 16, 17;
message MessageKey {
optional string remoteJid = 1;
optional bool fromMe = 2;
optional string id = 3;
optional string participant = 4;
}
message InteractiveAnnotation {
repeated Point polygonVertices = 1;
oneof action {
Location location = 2;
message WebFeatures {
enum WEB_FEATURES_FLAG {
NOT_IMPLEMENTED = 0;
IMPLEMENTED = 1;
OPTIONAL = 2;
}
optional WEB_FEATURES_FLAG labelsDisplay = 1;
optional WEB_FEATURES_FLAG voipIndividualOutgoing = 2;
optional WEB_FEATURES_FLAG groupsV3 = 3;
optional WEB_FEATURES_FLAG groupsV3Create = 4;
optional WEB_FEATURES_FLAG changeNumberV2 = 5;
optional WEB_FEATURES_FLAG queryStatusV3Thumbnail = 6;
optional WEB_FEATURES_FLAG liveLocations = 7;
optional WEB_FEATURES_FLAG queryVname = 8;
optional WEB_FEATURES_FLAG voipIndividualIncoming = 9;
optional WEB_FEATURES_FLAG quickRepliesQuery = 10;
optional WEB_FEATURES_FLAG payments = 11;
optional WEB_FEATURES_FLAG stickerPackQuery = 12;
optional WEB_FEATURES_FLAG liveLocationsFinal = 13;
optional WEB_FEATURES_FLAG labelsEdit = 14;
optional WEB_FEATURES_FLAG mediaUpload = 15;
optional WEB_FEATURES_FLAG mediaUploadRichQuickReplies = 18;
optional WEB_FEATURES_FLAG vnameV2 = 19;
optional WEB_FEATURES_FLAG videoPlaybackUrl = 20;
optional WEB_FEATURES_FLAG statusRanking = 21;
optional WEB_FEATURES_FLAG voipIndividualVideo = 22;
optional WEB_FEATURES_FLAG thirdPartyStickers = 23;
optional WEB_FEATURES_FLAG frequentlyForwardedSetting = 24;
}
message Point {
optional double x = 3;
optional double y = 4;
message TabletNotificationsInfo {
optional uint64 timestamp = 2;
optional uint32 unreadChats = 3;
optional uint32 notifyMessageCount = 4;
repeated NotificationMessageInfo notifyMessage = 5;
}
message Location {
optional double degreesLatitude = 1;
optional double degreesLongitude = 2;
optional string name = 3;
message NotificationMessageInfo {
optional MessageKey key = 1;
optional Message message = 2;
optional uint64 messageTimestamp = 3;
optional string participant = 4;
}
message WebNotificationsInfo {
optional uint64 timestamp = 2;
optional uint32 unreadChats = 3;
optional uint32 notifyMessageCount = 4;
repeated WebMessageInfo notifyMessages = 5;
}
message PaymentInfo {
optional uint64 amount1000 = 2;
optional string receiverJid = 3;
enum PAYMENT_INFO_STATUS {
UNKNOWN_STATUS = 0;
PROCESSING = 1;
SENT = 2;
NEED_TO_ACCEPT = 3;
COMPLETE = 4;
COULD_NOT_COMPLETE = 5;
REFUNDED = 6;
EXPIRED = 7;
REJECTED = 8;
CANCELLED = 9;
WAITING_FOR_PAYER = 10;
WAITING = 11;
}
optional PAYMENT_INFO_STATUS status = 4;
optional uint64 transactionTimestamp = 5;
optional MessageKey requestMessageKey = 6;
optional uint64 expiryTimestamp = 7;
optional bool futureproofed = 8;
optional string currency = 9;
}
message WebMessageInfo {
required MessageKey key = 1;
optional Message message = 2;
optional uint64 messageTimestamp = 3;
enum STATUS {
enum WEB_MESSAGE_INFO_STATUS {
ERROR = 0;
PENDING = 1;
SERVER_ACK = 2;
@@ -314,7 +533,7 @@ message WebMessageInfo {
READ = 4;
PLAYED = 5;
}
optional STATUS status = 4 [default=PENDING];
optional WEB_MESSAGE_INFO_STATUS status = 4;
optional string participant = 5;
optional bool ignore = 16;
optional bool starred = 17;
@@ -324,7 +543,7 @@ message WebMessageInfo {
optional bool multicast = 21;
optional bool urlText = 22;
optional bool urlNumber = 23;
enum STUBTYPE {
enum WEB_MESSAGE_INFO_STUBTYPE {
UNKNOWN = 0;
REVOKE = 1;
CIPHERTEXT = 2;
@@ -369,49 +588,39 @@ message WebMessageInfo {
CALL_MISSED_VIDEO = 41;
INDIVIDUAL_CHANGE_NUMBER = 42;
GROUP_DELETE = 43;
GROUP_ANNOUNCE_MODE_MESSAGE_BOUNCE = 44;
CALL_MISSED_GROUP_VOICE = 45;
CALL_MISSED_GROUP_VIDEO = 46;
PAYMENT_CIPHERTEXT = 47;
PAYMENT_FUTUREPROOF = 48;
PAYMENT_TRANSACTION_STATUS_UPDATE_FAILED = 49;
PAYMENT_TRANSACTION_STATUS_UPDATE_REFUNDED = 50;
PAYMENT_TRANSACTION_STATUS_UPDATE_REFUND_FAILED = 51;
PAYMENT_TRANSACTION_STATUS_RECEIVER_PENDING_SETUP = 52;
PAYMENT_TRANSACTION_STATUS_RECEIVER_SUCCESS_AFTER_HICCUP = 53;
PAYMENT_ACTION_ACCOUNT_SETUP_REMINDER = 54;
PAYMENT_ACTION_SEND_PAYMENT_REMINDER = 55;
PAYMENT_ACTION_SEND_PAYMENT_INVITATION = 56;
PAYMENT_ACTION_REQUEST_DECLINED = 57;
PAYMENT_ACTION_REQUEST_EXPIRED = 58;
PAYMENT_ACTION_REQUEST_CANCELLED = 59;
BIZ_VERIFIED_TRANSITION_TOP_TO_BOTTOM = 60;
BIZ_VERIFIED_TRANSITION_BOTTOM_TO_TOP = 61;
BIZ_INTRO_TOP = 62;
BIZ_INTRO_BOTTOM = 63;
BIZ_NAME_CHANGE = 64;
BIZ_MOVE_TO_CONSUMER_APP = 65;
BIZ_TWO_TIER_MIGRATION_TOP = 66;
BIZ_TWO_TIER_MIGRATION_BOTTOM = 67;
OVERSIZED = 68;
GROUP_CHANGE_NO_FREQUENTLY_FORWARDED = 69;
}
optional STUBTYPE messageStubType = 24;
optional WEB_MESSAGE_INFO_STUBTYPE messageStubType = 24;
optional bool clearMedia = 25;
repeated string messageStubParameters = 26;
optional uint32 duration = 27;
repeated string labels = 28;
}
message WebNotificationsInfo {
optional uint64 timestamp = 2;
optional uint32 unreadChats = 3;
optional uint32 notifyMessageCount = 4;
repeated Message notifyMessages = 5;
}
message NotificationMessageInfo {
optional MessageKey key = 1;
optional Message message = 2;
optional uint64 messageTimestamp = 3;
optional string participant = 4;
}
message TabletNotificationsInfo {
optional uint64 timestamp = 2;
optional uint32 unreadChats = 3;
optional uint32 notifyMessageCount = 4;
repeated Message notifyMessage = 5;
}
message WebFeatures {
enum FLAG {
NOT_IMPLEMENTED = 0;
IMPLEMENTED = 1;
OPTIONAL = 2;
}
optional FLAG labelsDisplay = 1;
optional FLAG voipIndividualOutgoing = 2;
optional FLAG groupsV3 = 3;
optional FLAG groupsV3Create = 4;
optional FLAG changeNumberV2 = 5;
optional FLAG queryStatusV3Thumbnail = 6;
optional FLAG liveLocations = 7;
optional FLAG queryVname = 8;
optional FLAG voipIndividualIncoming = 9;
optional FLAG quickRepliesQuery = 10;
optional PaymentInfo paymentInfo = 29;
optional LiveLocationMessage finalLiveLocation = 30;
optional PaymentInfo quotedPaymentInfo = 31;
}

View File

@@ -4,6 +4,7 @@ package whatsapp
import (
"math/rand"
"net/http"
"net/url"
"sync"
"time"
@@ -91,6 +92,7 @@ type Conn struct {
shortClientName string
loginSessionLock sync.RWMutex
Proxy func(*http.Request) (*url.URL, error)
}
type websocketWrapper struct {
@@ -121,6 +123,21 @@ func NewConn(timeout time.Duration) (*Conn, error) {
return wac, wac.connect()
}
// NewConnWithProxy Create a new connect with a given timeout and a http proxy.
func NewConnWithProxy(timeout time.Duration, proxy func(*http.Request) (*url.URL, error)) (*Conn, error) {
wac := &Conn{
handler: make([]Handler, 0),
msgCount: 0,
msgTimeout: timeout,
Store: newStore(),
longClientName: "github.com/rhymen/go-whatsapp",
shortClientName: "go-whatsapp",
Proxy: proxy,
}
return wac, wac.connect()
}
// connect should be guarded with wsWriteMutex
func (wac *Conn) connect() (err error) {
if wac.connected {
@@ -137,6 +154,7 @@ func (wac *Conn) connect() (err error) {
ReadBufferSize: 25 * 1024 * 1024,
WriteBufferSize: 10 * 1024 * 1024,
HandshakeTimeout: wac.msgTimeout,
Proxy: wac.Proxy,
}
headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}}

View File

@@ -51,6 +51,10 @@ func (wac *Conn) LoadMessagesAfter(jid, messageId string, count int) (*binary.No
return wac.query("message", jid, messageId, "after", "true", "", count, 0)
}
func (wac *Conn) LoadMediaInfo(jid, messageId, owner string) (*binary.Node, error) {
return wac.query("media", jid, messageId, "", owner, "", 0, 0)
}
func (wac *Conn) Presence(jid string, presence Presence) (<-chan string, error) {
ts := time.Now().Unix()
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
@@ -163,7 +167,12 @@ func (wac *Conn) query(t, jid, messageId, kind, owner, search string, count, pag
n.Attributes["page"] = strconv.Itoa(page)
}
ch, err := wac.writeBinary(n, group, ignore, tag)
metric := group
if t == "media" {
metric = queryMedia
}
ch, err := wac.writeBinary(n, metric, ignore, tag)
if err != nil {
return nil, err
}

View File

@@ -18,6 +18,8 @@ var (
ErrInvalidHmac = errors.New("invalid hmac")
ErrInvalidServerResponse = errors.New("invalid response received from server")
ErrServerRespondedWith404 = errors.New("server responded with status 404")
ErrMediaDownloadFailedWith404 = errors.New("download failed with status code 404")
ErrMediaDownloadFailedWith410 = errors.New("download failed with status code 410")
)
type ErrConnectionFailed struct {

View File

@@ -81,6 +81,14 @@ type LocationMessageHandler interface {
HandleLocationMessage(message LocationMessage)
}
/*
The StickerMessageHandler interface needs to be implemented to receive location messages dispatched by the dispatcher.
*/
type StickerMessageHandler interface {
Handler
HandleStickerMessage(message StickerMessage)
}
/*
The JsonMessageHandler interface needs to be implemented to receive json messages dispatched by the dispatcher.
These json messages contain status updates of every kind sent by WhatsAppWeb servers. WhatsAppWeb uses these messages
@@ -247,6 +255,18 @@ func (wac *Conn) handleWithCustomHandlers(message interface{}, handlers []Handle
}
}
}
case StickerMessage:
for _, h := range handlers {
if x, ok := h.(StickerMessageHandler); ok {
if wac.shouldCallSynchronously(h) {
x.HandleStickerMessage(m)
} else {
go x.HandleStickerMessage(m)
}
}
}
case *proto.WebMessageInfo:
for _, h := range handlers {
if x, ok := h.(RawMessageHandler); ok {

View File

@@ -74,6 +74,12 @@ func downloadMedia(url string) (file []byte, mac []byte, err error) {
return nil, nil, err
}
if resp.StatusCode != 200 {
if resp.StatusCode == 404 {
return nil, nil, ErrMediaDownloadFailedWith404
}
if resp.StatusCode == 410 {
return nil, nil, ErrMediaDownloadFailedWith410
}
return nil, nil, fmt.Errorf("download failed with status code %d", resp.StatusCode)
}
defer resp.Body.Close()

View File

@@ -4,13 +4,14 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/Rhymen/go-whatsapp/binary"
"github.com/Rhymen/go-whatsapp/binary/proto"
"io"
"math/rand"
"strconv"
"strings"
"time"
"github.com/Rhymen/go-whatsapp/binary"
"github.com/Rhymen/go-whatsapp/binary/proto"
)
type MediaType string
@@ -131,6 +132,7 @@ type MessageInfo struct {
PushName string
Status MessageStatus
QuotedMessageID string
QuotedMessage proto.Message
Source *proto.WebMessageInfo
}
@@ -170,7 +172,7 @@ func getInfoProto(info *MessageInfo) *proto.WebMessageInfo {
}
info.FromMe = true
status := proto.WebMessageInfo_STATUS(info.Status)
status := proto.WebMessageInfo_WEB_MESSAGE_INFO_STATUS(info.Status)
return &proto.WebMessageInfo{
Key: &proto.MessageKey{
@@ -183,6 +185,22 @@ func getInfoProto(info *MessageInfo) *proto.WebMessageInfo {
}
}
func getContextInfoProto(info *MessageInfo) *proto.ContextInfo {
if len(info.QuotedMessageID) > 0 {
contextInfo := &proto.ContextInfo{
StanzaId: &info.QuotedMessageID,
}
if &info.QuotedMessage != nil {
contextInfo.QuotedMessage = &info.QuotedMessage
}
return contextInfo
}
return nil
}
/*
TextMessage represents a text message.
*/
@@ -204,9 +222,21 @@ func getTextMessage(msg *proto.WebMessageInfo) TextMessage {
func getTextProto(msg TextMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
if contextInfo == nil {
p.Message = &proto.Message{
Conversation: &msg.Text,
}
} else {
p.Message = &proto.Message{
ExtendedTextMessage: &proto.ExtendedTextMessage{
Text: &msg.Text,
ContextInfo: contextInfo,
},
}
}
return p
}
@@ -229,7 +259,8 @@ type ImageMessage struct {
func getImageMessage(msg *proto.WebMessageInfo) ImageMessage {
image := msg.GetMessage().GetImageMessage()
return ImageMessage{
imageMessage := ImageMessage{
Info: getMessageInfo(msg),
Caption: image.GetCaption(),
Thumbnail: image.GetJpegThumbnail(),
@@ -240,10 +271,18 @@ func getImageMessage(msg *proto.WebMessageInfo) ImageMessage {
fileSha256: image.GetFileSha256(),
fileLength: image.GetFileLength(),
}
if contextInfo := image.GetContextInfo(); contextInfo != nil {
imageMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return imageMessage
}
func getImageProto(msg ImageMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
ImageMessage: &proto.ImageMessage{
Caption: &msg.Caption,
@@ -254,6 +293,7 @@ func getImageProto(msg ImageMessage) *proto.WebMessageInfo {
FileEncSha256: msg.fileEncSha256,
FileSha256: msg.fileSha256,
FileLength: &msg.fileLength,
ContextInfo: contextInfo,
},
}
return p
@@ -287,7 +327,8 @@ type VideoMessage struct {
func getVideoMessage(msg *proto.WebMessageInfo) VideoMessage {
vid := msg.GetMessage().GetVideoMessage()
return VideoMessage{
videoMessage := VideoMessage{
Info: getMessageInfo(msg),
Caption: vid.GetCaption(),
Thumbnail: vid.GetJpegThumbnail(),
@@ -300,10 +341,18 @@ func getVideoMessage(msg *proto.WebMessageInfo) VideoMessage {
fileSha256: vid.GetFileSha256(),
fileLength: vid.GetFileLength(),
}
if contextInfo := vid.GetContextInfo(); contextInfo != nil {
videoMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return videoMessage
}
func getVideoProto(msg VideoMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
VideoMessage: &proto.VideoMessage{
Caption: &msg.Caption,
@@ -316,6 +365,7 @@ func getVideoProto(msg VideoMessage) *proto.WebMessageInfo {
FileSha256: msg.fileSha256,
FileLength: &msg.fileLength,
Mimetype: &msg.Type,
ContextInfo: contextInfo,
},
}
return p
@@ -337,6 +387,7 @@ type AudioMessage struct {
Length uint32
Type string
Content io.Reader
Ptt bool
url string
mediaKey []byte
fileEncSha256 []byte
@@ -346,7 +397,8 @@ type AudioMessage struct {
func getAudioMessage(msg *proto.WebMessageInfo) AudioMessage {
aud := msg.GetMessage().GetAudioMessage()
return AudioMessage{
audioMessage := AudioMessage{
Info: getMessageInfo(msg),
url: aud.GetUrl(),
mediaKey: aud.GetMediaKey(),
@@ -356,10 +408,17 @@ func getAudioMessage(msg *proto.WebMessageInfo) AudioMessage {
fileSha256: aud.GetFileSha256(),
fileLength: aud.GetFileLength(),
}
if contextInfo := aud.GetContextInfo(); contextInfo != nil {
audioMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return audioMessage
}
func getAudioProto(msg AudioMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
AudioMessage: &proto.AudioMessage{
Url: &msg.url,
@@ -369,6 +428,8 @@ func getAudioProto(msg AudioMessage) *proto.WebMessageInfo {
FileSha256: msg.fileSha256,
FileLength: &msg.fileLength,
Mimetype: &msg.Type,
ContextInfo: contextInfo,
Ptt: &msg.Ptt,
},
}
return p
@@ -402,7 +463,8 @@ type DocumentMessage struct {
func getDocumentMessage(msg *proto.WebMessageInfo) DocumentMessage {
doc := msg.GetMessage().GetDocumentMessage()
return DocumentMessage{
documentMessage := DocumentMessage{
Info: getMessageInfo(msg),
Title: doc.GetTitle(),
PageCount: doc.GetPageCount(),
@@ -415,10 +477,17 @@ func getDocumentMessage(msg *proto.WebMessageInfo) DocumentMessage {
fileSha256: doc.GetFileSha256(),
fileLength: doc.GetFileLength(),
}
if contextInfo := doc.GetContextInfo(); contextInfo != nil {
documentMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return documentMessage
}
func getDocumentProto(msg DocumentMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
DocumentMessage: &proto.DocumentMessage{
JpegThumbnail: msg.Thumbnail,
@@ -430,6 +499,7 @@ func getDocumentProto(msg DocumentMessage) *proto.WebMessageInfo {
PageCount: &msg.PageCount,
Title: &msg.Title,
Mimetype: &msg.Type,
ContextInfo: contextInfo,
},
}
return p
@@ -457,7 +527,8 @@ type LocationMessage struct {
func GetLocationMessage(msg *proto.WebMessageInfo) LocationMessage {
loc := msg.GetMessage().GetLocationMessage()
return LocationMessage{
locationMessage := LocationMessage{
Info: getMessageInfo(msg),
DegreesLatitude: loc.GetDegreesLatitude(),
DegreesLongitude: loc.GetDegreesLongitude(),
@@ -466,10 +537,18 @@ func GetLocationMessage(msg *proto.WebMessageInfo) LocationMessage {
Url: loc.GetUrl(),
JpegThumbnail: loc.GetJpegThumbnail(),
}
if contextInfo := loc.GetContextInfo(); contextInfo != nil {
locationMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return locationMessage
}
func GetLocationProto(msg LocationMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
LocationMessage: &proto.LocationMessage{
DegreesLatitude: &msg.DegreesLatitude,
@@ -478,6 +557,7 @@ func GetLocationProto(msg LocationMessage) *proto.WebMessageInfo {
Address: &msg.Address,
Url: &msg.Url,
JpegThumbnail: msg.JpegThumbnail,
ContextInfo: contextInfo,
},
}
return p
@@ -500,7 +580,8 @@ type LiveLocationMessage struct {
func GetLiveLocationMessage(msg *proto.WebMessageInfo) LiveLocationMessage {
loc := msg.GetMessage().GetLiveLocationMessage()
return LiveLocationMessage{
liveLocationMessage := LiveLocationMessage{
Info: getMessageInfo(msg),
DegreesLatitude: loc.GetDegreesLatitude(),
DegreesLongitude: loc.GetDegreesLongitude(),
@@ -511,10 +592,17 @@ func GetLiveLocationMessage(msg *proto.WebMessageInfo) LiveLocationMessage {
SequenceNumber: loc.GetSequenceNumber(),
JpegThumbnail: loc.GetJpegThumbnail(),
}
if contextInfo := loc.GetContextInfo(); contextInfo != nil {
liveLocationMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return liveLocationMessage
}
func GetLiveLocationProto(msg LiveLocationMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
LiveLocationMessage: &proto.LiveLocationMessage{
DegreesLatitude: &msg.DegreesLatitude,
@@ -525,11 +613,49 @@ func GetLiveLocationProto(msg LiveLocationMessage) *proto.WebMessageInfo {
Caption: &msg.Caption,
SequenceNumber: &msg.SequenceNumber,
JpegThumbnail: msg.JpegThumbnail,
ContextInfo: contextInfo,
},
}
return p
}
/*
StickerMessage represents a sticker message.
*/
type StickerMessage struct {
Info MessageInfo
Thumbnail []byte
Type string
Content io.Reader
url string
mediaKey []byte
fileEncSha256 []byte
fileSha256 []byte
fileLength uint64
}
func getStickerMessage(msg *proto.WebMessageInfo) StickerMessage {
sticker := msg.GetMessage().GetStickerMessage()
StickerMessage := StickerMessage{
Info: getMessageInfo(msg),
Thumbnail: sticker.GetPngThumbnail(),
url: sticker.GetUrl(),
mediaKey: sticker.GetMediaKey(),
Type: sticker.GetMimetype(),
fileEncSha256: sticker.GetFileEncSha256(),
fileSha256: sticker.GetFileSha256(),
fileLength: sticker.GetFileLength(),
}
if contextInfo := sticker.GetContextInfo(); contextInfo != nil {
StickerMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return StickerMessage
}
func ParseProtoMessage(msg *proto.WebMessageInfo) interface{} {
switch {
@@ -557,6 +683,9 @@ func ParseProtoMessage(msg *proto.WebMessageInfo) interface{} {
case msg.GetMessage().GetLiveLocationMessage() != nil:
return GetLiveLocationMessage(msg)
case msg.GetMessage().GetStickerMessage() != nil:
return getStickerMessage(msg)
default:
//cannot match message
}

View File

@@ -7,6 +7,8 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"strconv"
"strings"
"sync/atomic"
"time"
@@ -88,6 +90,53 @@ func newInfoFromReq(info map[string]interface{}) *Info {
return ret
}
/*
CheckCurrentServerVersion is based on the login method logic in order to establish the websocket connection and get
the current version from the server with the `admin init` command. This can be very useful for automations in which
you need to quickly perceive new versions (mostly patches) and update your application so it suddenly stops working.
*/
func CheckCurrentServerVersion() ([]int, error) {
wac, err := NewConn(5 * time.Second)
if err != nil {
return nil, fmt.Errorf("fail to create connection")
}
clientId := make([]byte, 16)
if _, err = rand.Read(clientId); err != nil {
return nil, fmt.Errorf("error creating random ClientId: %v", err)
}
b64ClientId := base64.StdEncoding.EncodeToString(clientId)
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, b64ClientId, true}
loginChan, err := wac.writeJson(login)
if err != nil {
return nil, fmt.Errorf("error writing login", err)
}
// Retrieve an answer from the websocket
var r string
select {
case r = <-loginChan:
case <-time.After(wac.msgTimeout):
return nil, fmt.Errorf("login connection timed out")
}
var resp map[string]interface{}
if err = json.Unmarshal([]byte(r), &resp); err != nil {
return nil, fmt.Errorf("error decoding login", err)
}
// Take the curr property as X.Y.Z and split it into as int slice
curr := resp["curr"].(string)
currArray := strings.Split(curr, ".")
version := make([]int, len(currArray))
for i := range version {
version[i], _ = strconv.Atoi(currArray[i])
}
return version, nil
}
/*
SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the
WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible.
@@ -108,6 +157,11 @@ func (wac *Conn) SetClientVersion(major int, minor int, patch int) {
waVersion = []int{major, minor, patch}
}
// GetClientVersion returns WhatsApp client version
func (wac *Conn) GetClientVersion() []int {
return waVersion
}
/*
Login is the function that creates a new whatsapp session and logs you in. If you do not want to scan the qr code
every time, you should save the returned session and use RestoreWithSession the next time. Login takes a writable channel

View File

@@ -1,6 +1,6 @@
# DiscordGo
[![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) [![Go report](http://goreportcard.com/badge/bwmarrin/discordgo)](http://goreportcard.com/report/bwmarrin/discordgo) [![Build Status](https://travis-ci.org/bwmarrin/discordgo.svg?branch=master)](https://travis-ci.org/bwmarrin/discordgo) [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)](https://discord.gg/0f1SbxBZjYoCtNPP) [![Discord API](https://img.shields.io/badge/Discord%20API-%23go_discordgo-blue.svg)](https://discord.gg/0SBTUU1wZTWT6sqd)
[![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) [![Go report](http://goreportcard.com/badge/bwmarrin/discordgo)](http://goreportcard.com/report/bwmarrin/discordgo) [![Build Status](https://travis-ci.org/bwmarrin/discordgo.svg?branch=master)](https://travis-ci.org/bwmarrin/discordgo) [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)](https://discord.gg/0f1SbxBZjYoCtNPP) [![Discord API](https://img.shields.io/badge/Discord%20API-%23go_discordgo-blue.svg)](https://discordapp.com/invite/discord-api)
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">

View File

@@ -21,7 +21,7 @@ import (
)
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
const VERSION = "0.19.0"
const VERSION = "0.20.1"
// ErrMFA will be risen by New when the user has 2FA.
var ErrMFA = errors.New("account has 2FA enabled")
@@ -58,6 +58,7 @@ func New(args ...interface{}) (s *Session, err error) {
ShardCount: 1,
MaxRestRetries: 3,
Client: &http.Client{Timeout: (20 * time.Second)},
UserAgent: "DiscordBot (https://github.com/bwmarrin/discordgo, v" + VERSION + ")",
sequence: new(int64),
LastHeartbeatAck: time.Now().UTC(),
}

View File

@@ -38,6 +38,7 @@ var (
EndpointCDNIcons = EndpointCDN + "icons/"
EndpointCDNSplashes = EndpointCDN + "splashes/"
EndpointCDNChannelIcons = EndpointCDN + "channel-icons/"
EndpointCDNBanners = EndpointCDN + "banners/"
EndpointAuth = EndpointAPI + "auth/"
EndpointLogin = EndpointAuth + "login"
@@ -92,11 +93,13 @@ var (
EndpointGuildEmbed = func(gID string) string { return EndpointGuilds + gID + "/embed" }
EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" }
EndpointGuildIcon = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" }
EndpointGuildIconAnimated = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".gif" }
EndpointGuildSplash = func(gID, hash string) string { return EndpointCDNSplashes + gID + "/" + hash + ".png" }
EndpointGuildWebhooks = func(gID string) string { return EndpointGuilds + gID + "/webhooks" }
EndpointGuildAuditLogs = func(gID string) string { return EndpointGuilds + gID + "/audit-logs" }
EndpointGuildEmojis = func(gID string) string { return EndpointGuilds + gID + "/emojis" }
EndpointGuildEmoji = func(gID, eID string) string { return EndpointGuilds + gID + "/emojis/" + eID }
EndpointGuildBanner = func(gID, hash string) string { return EndpointCDNBanners + gID + "/" + hash + ".png" }
EndpointChannel = func(cID string) string { return EndpointChannels + cID }
EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" }
@@ -143,4 +146,5 @@ var (
EndpointApplications = EndpointOauth2 + "applications"
EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID }
EndpointApplicationsBot = func(aID string) string { return EndpointApplications + "/" + aID + "/bot" }
EndpointApplicationAssets = func(aID string) string { return EndpointApplications + "/" + aID + "/assets" }
)

View File

@@ -10,15 +10,15 @@ import (
//go:generate go run tools/cmd/eventhandlers/main.go
// Connect is the data for a Connect event.
// This is a sythetic event and is not dispatched by Discord.
// This is a synthetic event and is not dispatched by Discord.
type Connect struct{}
// Disconnect is the data for a Disconnect event.
// This is a sythetic event and is not dispatched by Discord.
// This is a synthetic event and is not dispatched by Discord.
type Disconnect struct{}
// RateLimit is the data for a RateLimit event.
// This is a sythetic event and is not dispatched by Discord.
// This is a synthetic event and is not dispatched by Discord.
type RateLimit struct {
*TooManyRequests
URL string
@@ -162,6 +162,8 @@ type MessageCreate struct {
// MessageUpdate is the data for a MessageUpdate event.
type MessageUpdate struct {
*Message
// BeforeUpdate will be nil if the Message was not previously cached in the state cache.
BeforeUpdate *Message `json:"-"`
}
// MessageDelete is the data for a MessageDelete event.

View File

@@ -28,6 +28,11 @@ const (
MessageTypeChannelIconChange
MessageTypeChannelPinnedMessage
MessageTypeGuildMemberJoin
MessageTypeUserPremiumGuildSubscription
MessageTypeUserPremiumGuildSubscriptionTierOne
MessageTypeUserPremiumGuildSubscriptionTierTwo
MessageTypeUserPremiumGuildSubscriptionTierThree
MessageTypeChannelFollowAdd
)
// A Message stores all data related to a specific Discord message.
@@ -80,11 +85,39 @@ type Message struct {
// A list of reactions to the message.
Reactions []*MessageReactions `json:"reactions"`
// Whether the message is pinned or not.
Pinned bool `json:"pinned"`
// The type of the message.
Type MessageType `json:"type"`
// The webhook ID of the message, if it was generated by a webhook
WebhookID string `json:"webhook_id"`
// Member properties for this message's author,
// contains only partial information
Member *Member `json:"member"`
// Channels specifically mentioned in this message
// Not all channel mentions in a message will appear in mention_channels.
// Only textual channels that are visible to everyone in a lurkable guild will ever be included.
// Only crossposted messages (via Channel Following) currently include mention_channels at all.
// If no mentions in the message meet these requirements, this field will not be sent.
MentionChannels []*Channel `json:"mention_channels"`
// Is sent with Rich Presence-related chat embeds
Activity *MessageActivity `json:"activity"`
// Is sent with Rich Presence-related chat embeds
Application *MessageApplication `json:"application"`
// MessageReference contains reference data sent with crossposted messages
MessageReference *MessageReference `json:"message_reference"`
// The flags of the message, which describe extra features of a message.
// 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 flag.
Flags int `json:"flags"`
}
// File stores info about files you e.g. send in messages.
@@ -225,6 +258,52 @@ type MessageReactions struct {
Emoji *Emoji `json:"emoji"`
}
// MessageActivity is sent with Rich Presence-related chat embeds
type MessageActivity struct {
Type MessageActivityType `json:"type"`
PartyID string `json:"party_id"`
}
// MessageActivityType is the type of message activity
type MessageActivityType int
// Constants for the different types of Message Activity
const (
MessageActivityTypeJoin = iota + 1
MessageActivityTypeSpectate
MessageActivityTypeListen
MessageActivityTypeJoinRequest
)
// MessageFlag describes an extra feature of the message
type MessageFlag int
// Constants for the different bit offsets of Message Flags
const (
// This message has been published to subscribed channels (via Channel Following)
MessageFlagCrossposted = 1 << iota
// This message originated from a message in another channel (via Channel Following)
MessageFlagIsCrosspost
// Do not include any embeds when serializing this message
MessageFlagSuppressEmbeds
)
// MessageApplication is sent with Rich Presence-related chat embeds
type MessageApplication struct {
ID string `json:"id"`
CoverImage string `json:"cover_image"`
Description string `json:"description"`
Icon string `json:"icon"`
Name string `json:"name"`
}
// MessageReference contains reference data sent with crossposted messages
type MessageReference struct {
MessageID string `json:"message_id"`
ChannelID string `json:"channel_id"`
GuildID string `json:"guild_id"`
}
// ContentWithMentionsReplaced will replace all @<id> mentions with the
// username of the mention.
func (m *Message) ContentWithMentionsReplaced() (content string) {

View File

@@ -105,6 +105,25 @@ func (s *Session) ApplicationDelete(appID string) (err error) {
return
}
// Asset struct stores values for an asset of an application
type Asset struct {
Type int `json:"type"`
ID string `json:"id"`
Name string `json:"name"`
}
// ApplicationAssets returns an application's assets
func (s *Session) ApplicationAssets(appID string) (ass []*Asset, err error) {
body, err := s.RequestWithBucketID("GET", EndpointApplicationAssets(appID), nil, EndpointApplicationAssets(""))
if err != nil {
return
}
err = unmarshal(body, &ass)
return
}
// ------------------------------------------------------------------------------------------------
// Code specific to Discord OAuth2 Application Bots
// ------------------------------------------------------------------------------------------------

View File

@@ -90,7 +90,7 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b
req.Header.Set("Content-Type", contentType)
// TODO: Make a configurable static variable.
req.Header.Set("User-Agent", "DiscordBot (https://github.com/bwmarrin/discordgo, v"+VERSION+")")
req.Header.Set("User-Agent", s.UserAgent)
if s.Debug {
for k, v := range req.Header {
@@ -617,10 +617,10 @@ func (s *Session) GuildCreate(name string) (st *Guild, err error) {
// g : A GuildParams struct with the values Name, Region and VerificationLevel defined.
func (s *Session) GuildEdit(guildID string, g GuildParams) (st *Guild, err error) {
// Bounds checking for VerificationLevel, interval: [0, 3]
// Bounds checking for VerificationLevel, interval: [0, 4]
if g.VerificationLevel != nil {
val := *g.VerificationLevel
if val < 0 || val > 3 {
if val < 0 || val > 4 {
err = ErrVerificationLevelBounds
return
}
@@ -2067,7 +2067,7 @@ func (s *Session) WebhookDeleteWithToken(webhookID, token string) (st *Webhook,
// WebhookExecute executes a webhook.
// webhookID: The ID of a webhook.
// token : The auth token for the webhook
// wait : Wait for server to confirm the message arrival
// wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise)
//
// If `wait` is `false`, the returned *Message is always empty, because server
// does not provide the response data.
@@ -2150,6 +2150,8 @@ func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *Webho
// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier.
func (s *Session) MessageReactionAdd(channelID, messageID, emojiID string) error {
// emoji such as #⃣ need to have # escaped
emojiID = strings.Replace(emojiID, "#", "%23", -1)
_, err := s.RequestWithBucketID("PUT", EndpointMessageReaction(channelID, messageID, emojiID, "@me"), nil, EndpointMessageReaction(channelID, "", "", ""))
return err
@@ -2162,6 +2164,8 @@ func (s *Session) MessageReactionAdd(channelID, messageID, emojiID string) error
// userID : @me or ID of the user to delete the reaction for.
func (s *Session) MessageReactionRemove(channelID, messageID, emojiID, userID string) error {
// emoji such as #⃣ need to have # escaped
emojiID = strings.Replace(emojiID, "#", "%23", -1)
_, err := s.RequestWithBucketID("DELETE", EndpointMessageReaction(channelID, messageID, emojiID, userID), nil, EndpointMessageReaction(channelID, "", "", ""))
return err
@@ -2183,6 +2187,8 @@ func (s *Session) MessageReactionsRemoveAll(channelID, messageID string) error {
// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier.
// limit : max number of users to return (max 100)
func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit int) (st []*User, err error) {
// emoji such as #⃣ need to have # escaped
emojiID = strings.Replace(emojiID, "#", "%23", -1)
uri := EndpointMessageReactions(channelID, messageID, emojiID)
v := url.Values{}

View File

@@ -882,6 +882,13 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
}
case *MessageUpdate:
if s.MaxMessageCount != 0 {
var old *Message
old, err = s.Message(t.ChannelID, t.ID)
if err == nil {
oldCopy := *old
t.BeforeUpdate = &oldCopy
}
err = s.MessageAdd(t.Message)
}
case *MessageDelete:

View File

@@ -15,6 +15,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"
"sync"
"time"
@@ -82,6 +83,9 @@ type Session struct {
// The http client used for REST requests
Client *http.Client
// The user agent used for REST APIs
UserAgent string
// Stores the last HeartbeatAck that was recieved (in UTC)
LastHeartbeatAck time.Time
@@ -196,6 +200,8 @@ const (
ChannelTypeGuildVoice
ChannelTypeGroupDM
ChannelTypeGuildCategory
ChannelTypeGuildNews
ChannelTypeGuildStore
)
// A Channel holds all data related to an individual Discord channel.
@@ -220,6 +226,10 @@ type Channel struct {
// guaranteed to be an ID of a valid message.
LastMessageID string `json:"last_message_id"`
// The timestamp of the last pinned message in the channel.
// Empty if the channel has no pinned messages.
LastPinTimestamp Timestamp `json:"last_pin_timestamp"`
// Whether the channel is marked as NSFW.
NSFW bool `json:"nsfw"`
@@ -247,6 +257,10 @@ type Channel struct {
// The ID of the parent channel, if the channel is under a category
ParentID string `json:"parent_id"`
// Amount of seconds a user has to wait before sending another message (0-21600)
// bots, as well as users with the permission manage_messages or manage_channel, are unaffected
RateLimitPerUser int `json:"rate_limit_per_user"`
}
// Mention returns a string which mentions the channel
@@ -283,6 +297,7 @@ type Emoji struct {
Managed bool `json:"managed"`
RequireColons bool `json:"require_colons"`
Animated bool `json:"animated"`
Available bool `json:"available"`
}
// MessageFormat returns a correctly formatted Emoji for use in Message content and embeds
@@ -312,12 +327,13 @@ func (e *Emoji) APIName() string {
// VerificationLevel type definition
type VerificationLevel int
// Constants for VerificationLevel levels from 0 to 3 inclusive
// Constants for VerificationLevel levels from 0 to 4 inclusive
const (
VerificationLevelNone VerificationLevel = iota
VerificationLevelLow
VerificationLevelMedium
VerificationLevelHigh
VerificationLevelVeryHigh
)
// ExplicitContentFilterLevel type definition
@@ -339,6 +355,17 @@ const (
MfaLevelElevated
)
// PremiumTier type definition
type PremiumTier int
// Constants for PremiumTier levels from 0 to 3 inclusive
const (
PremiumTierNone PremiumTier = iota
PremiumTier1
PremiumTier2
PremiumTier3
)
// A Guild holds all data related to a specific Discord Guild. Guilds are also
// sometimes referred to as Servers in the Discord client.
type Guild struct {
@@ -443,6 +470,34 @@ type Guild struct {
// The Channel ID to which system messages are sent (eg join and leave messages)
SystemChannelID string `json:"system_channel_id"`
// the vanity url code for the guild
VanityURLCode string `json:"vanity_url_code"`
// the description for the guild
Description string `json:"description"`
// The hash of the guild's banner
Banner string `json:"banner"`
// The premium tier of the guild
PremiumTier PremiumTier `json:"premium_tier"`
// The total number of users currently boosting this server
PremiumSubscriptionCount int `json:"premium_subscription_count"`
}
// IconURL returns a URL to the guild's icon.
func (g *Guild) IconURL() string {
if g.Icon == "" {
return ""
}
if strings.HasPrefix(g.Icon, "a_") {
return EndpointGuildIconAnimated(g.ID, g.Icon)
}
return EndpointGuildIcon(g.ID, g.Icon)
}
// A UserGuild holds a brief version of a Guild
@@ -617,6 +672,9 @@ type Member struct {
// A list of IDs of the roles which are possessed by the member.
Roles []string `json:"roles"`
// When the user used their Nitro boost on the server
PremiumSince Timestamp `json:"premium_since"`
}
// Mention creates a member mention
@@ -872,6 +930,7 @@ const (
PermissionVoiceDeafenMembers
PermissionVoiceMoveMembers
PermissionVoiceUseVAD
PermissionVoicePrioritySpeaker = 1 << (iota + 2)
)
// Constants for general management.
@@ -907,7 +966,8 @@ const (
PermissionVoiceMuteMembers |
PermissionVoiceDeafenMembers |
PermissionVoiceMoveMembers |
PermissionVoiceUseVAD
PermissionVoiceUseVAD |
PermissionVoicePrioritySpeaker
PermissionAllChannel = PermissionAllText |
PermissionAllVoice |
PermissionCreateInstantInvite |
@@ -956,7 +1016,7 @@ const (
ErrCodeMissingAccess = 50001
ErrCodeInvalidAccountType = 50002
ErrCodeCannotExecuteActionOnDMChannel = 50003
ErrCodeEmbedCisabled = 50004
ErrCodeEmbedDisabled = 50004
ErrCodeCannotEditFromAnotherUser = 50005
ErrCodeCannotSendEmptyMessage = 50006
ErrCodeCannotSendMessagesToThisUser = 50007

17
vendor/github.com/bwmarrin/discordgo/util.go generated vendored Normal file
View File

@@ -0,0 +1,17 @@
package discordgo
import (
"strconv"
"time"
)
// SnowflakeTimestamp returns the creation time of a Snowflake ID relative to the creation of Discord.
func SnowflakeTimestamp(ID string) (t time.Time, err error) {
i, err := strconv.ParseInt(ID, 10, 64)
if err != nil {
return
}
timestamp := (i >> 22) + 1420070400000
t = time.Unix(timestamp/1000, 0)
return
}

View File

@@ -243,6 +243,7 @@ type voiceOP2 struct {
Port int `json:"port"`
Modes []string `json:"modes"`
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
IP string `json:"ip"`
}
// WaitUntilConnected waits for the Voice Connection to
@@ -542,7 +543,7 @@ func (v *VoiceConnection) udpOpen() (err error) {
return fmt.Errorf("empty endpoint")
}
host := strings.TrimSuffix(v.endpoint, ":80") + ":" + strconv.Itoa(v.op2.Port)
host := v.op2.IP + ":" + strconv.Itoa(v.op2.Port)
addr, err := net.ResolveUDPAddr("udp", host)
if err != nil {
v.log(LogWarning, "error resolving udp host %s, %s", host, err)
@@ -593,7 +594,7 @@ func (v *VoiceConnection) udpOpen() (err error) {
}
// Grab port from position 68 and 69
port := binary.LittleEndian.Uint16(rb[68:70])
port := binary.BigEndian.Uint16(rb[68:70])
// Take the data from above and send it back to Discord to finalize
// the UDP connection handshake.

View File

@@ -261,7 +261,6 @@ type heartbeatOp struct {
type helloOp struct {
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
Trace []string `json:"_trace"`
}
// FailedHeartbeatAcks is the Number of heartbeat intervals to wait until forcing a connection restart.
@@ -615,11 +614,7 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *Voi
voice.session = s
voice.Unlock()
// Send the request to Discord that we want to join the voice channel
data := voiceChannelJoinOp{4, voiceChannelJoinData{&gID, &cID, mute, deaf}}
s.wsMutex.Lock()
err = s.wsConn.WriteJSON(data)
s.wsMutex.Unlock()
err = s.ChannelVoiceJoinManual(gID, cID, mute, deaf)
if err != nil {
return
}
@@ -640,22 +635,25 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *Voi
// This should only be used when the VoiceServerUpdate will be intercepted and used elsewhere.
//
// gID : Guild ID of the channel to join.
// cID : Channel ID of the channel to join.
// cID : Channel ID of the channel to join, leave empty to disconnect.
// mute : If true, you will be set to muted upon joining.
// deaf : If true, you will be set to deafened upon joining.
func (s *Session) ChannelVoiceJoinManual(gID, cID string, mute, deaf bool) (err error) {
s.log(LogInformational, "called")
var channelID *string
if cID == "" {
channelID = nil
} else {
channelID = &cID
}
// Send the request to Discord that we want to join the voice channel
data := voiceChannelJoinOp{4, voiceChannelJoinData{&gID, &cID, mute, deaf}}
data := voiceChannelJoinOp{4, voiceChannelJoinData{&gID, channelID, mute, deaf}}
s.wsMutex.Lock()
err = s.wsConn.WriteJSON(data)
s.wsMutex.Unlock()
if err != nil {
return
}
return
}

View File

@@ -1,3 +1,8 @@
env:
- GO111MODULE=on
before:
hooks:
- go mod tidy
builds:
- env:
- CGO_ENABLED=0
@@ -9,6 +14,7 @@ builds:
- env:
- CGO_ENABLED=0
main: ./cmd/tengomin/main.go
id: tengomin
binary: tengomin
goos:
- darwin

0
vendor/github.com/d5/tengo/go.sum generated vendored Normal file
View File

20
vendor/github.com/d5/tengo/stdlib/base64.go generated vendored Normal file
View File

@@ -0,0 +1,20 @@
package stdlib
import (
"encoding/base64"
"github.com/d5/tengo/objects"
)
var base64Module = map[string]objects.Object{
"encode": &objects.UserFunction{Value: FuncAYRS(base64.StdEncoding.EncodeToString)},
"decode": &objects.UserFunction{Value: FuncASRYE(base64.StdEncoding.DecodeString)},
"raw_encode": &objects.UserFunction{Value: FuncAYRS(base64.RawStdEncoding.EncodeToString)},
"raw_decode": &objects.UserFunction{Value: FuncASRYE(base64.RawStdEncoding.DecodeString)},
"url_encode": &objects.UserFunction{Value: FuncAYRS(base64.URLEncoding.EncodeToString)},
"url_decode": &objects.UserFunction{Value: FuncASRYE(base64.URLEncoding.DecodeString)},
"raw_url_encode": &objects.UserFunction{Value: FuncAYRS(base64.RawURLEncoding.EncodeToString)},
"raw_url_decode": &objects.UserFunction{Value: FuncASRYE(base64.RawURLEncoding.DecodeString)},
}

View File

@@ -11,4 +11,6 @@ var BuiltinModules = map[string]map[string]objects.Object{
"rand": randModule,
"fmt": fmtModule,
"json": jsonModule,
"base64": base64Module,
"hex": hexModule,
}

View File

@@ -1036,6 +1036,29 @@ func FuncAYRIE(fn func([]byte) (int, error)) objects.CallableFunc {
}
}
// FuncAYRS transform a function of 'func([]byte) string' signature
// into CallableFunc type.
func FuncAYRS(fn func([]byte) string) objects.CallableFunc {
return func(args ...objects.Object) (ret objects.Object, err error) {
if len(args) != 1 {
return nil, objects.ErrWrongNumArguments
}
y1, ok := objects.ToByteSlice(args[0])
if !ok {
return nil, objects.ErrInvalidArgumentType{
Name: "first",
Expected: "bytes(compatible)",
Found: args[0].TypeName(),
}
}
res := fn(y1)
return &objects.String{Value: res}, nil
}
}
// FuncASRIE transform a function of 'func(string) (int, error)' signature
// into CallableFunc type.
func FuncASRIE(fn func(string) (int, error)) objects.CallableFunc {
@@ -1062,6 +1085,36 @@ func FuncASRIE(fn func(string) (int, error)) objects.CallableFunc {
}
}
// FuncASRYE transform a function of 'func(string) ([]byte, error)' signature
// into CallableFunc type.
func FuncASRYE(fn func(string) ([]byte, error)) objects.CallableFunc {
return func(args ...objects.Object) (ret objects.Object, err error) {
if len(args) != 1 {
return nil, objects.ErrWrongNumArguments
}
s1, ok := objects.ToString(args[0])
if !ok {
return nil, objects.ErrInvalidArgumentType{
Name: "first",
Expected: "string(compatible)",
Found: args[0].TypeName(),
}
}
res, err := fn(s1)
if err != nil {
return wrapError(err), nil
}
if len(res) > tengo.MaxBytesLen {
return nil, objects.ErrBytesLimit
}
return &objects.Bytes{Value: res}, nil
}
}
// FuncAIRSsE transform a function of 'func(int) ([]string, error)' signature
// into CallableFunc type.
func FuncAIRSsE(fn func(int) ([]string, error)) objects.CallableFunc {

11
vendor/github.com/d5/tengo/stdlib/hex.go generated vendored Normal file
View File

@@ -0,0 +1,11 @@
package stdlib
import (
"encoding/hex"
"github.com/d5/tengo/objects"
)
var hexModule = map[string]objects.Object{
"encode": &objects.UserFunction{Value: FuncAYRS(hex.EncodeToString)},
"decode": &objects.UserFunction{Value: FuncASRYE(hex.DecodeString)},
}

View File

@@ -1,6 +1,6 @@
language: go
go:
- 1.8
- 1.10.x
install:
- go get github.com/golang/lint/golint
- go get github.com/fzipp/gocyclo

View File

@@ -13,6 +13,7 @@ import (
"net/url"
"path"
"strconv"
"strings"
"sync"
"time"
)
@@ -38,6 +39,7 @@ type Client struct {
// HTTPError An HTTP Error response, which may wrap an underlying native Go Error.
type HTTPError struct {
Contents []byte
WrappedError error
Message string
Code int
@@ -48,7 +50,7 @@ func (e HTTPError) Error() string {
if e.WrappedError != nil {
wrappedErrMsg = e.WrappedError.Error()
}
return fmt.Sprintf("msg=%s code=%d wrapped=%s", e.Message, e.Code, wrappedErrMsg)
return fmt.Sprintf("contents=%v msg=%s code=%d wrapped=%s", e.Contents, e.Message, e.Code, wrappedErrMsg)
}
// BuildURL builds a URL with the Client's homserver/prefix/access_token set already.
@@ -68,6 +70,10 @@ func (cli *Client) BuildBaseURL(urlPath ...string) string {
parts := []string{hsURL.Path}
parts = append(parts, urlPath...)
hsURL.Path = path.Join(parts...)
// Manually add the trailing slash back to the end of the path if it's explicitly needed
if strings.HasSuffix(urlPath[len(urlPath)-1], "/") {
hsURL.Path = hsURL.Path + "/"
}
query := hsURL.Query()
if cli.AccessToken != "" {
query.Set("access_token", cli.AccessToken)
@@ -178,27 +184,27 @@ func (cli *Client) StopSync() {
}
// MakeRequest makes a JSON HTTP request to the given URL.
// If "resBody" is not nil, the response body will be json.Unmarshalled into it.
// The response body will be stream decoded into an interface. This will automatically stop if the response
// body is nil.
//
// Returns the HTTP body as bytes on 2xx with a nil error. Returns an error if the response is not 2xx along
// with the HTTP body bytes if it got that far. This error is an HTTPError which includes the returned
// HTTP status code and possibly a RespError as the WrappedError, if the HTTP body could be decoded as a RespError.
func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) {
// Returns an error if the response is not 2xx along with the HTTP body bytes if it got that far. This error is
// an HTTPError which includes the returned HTTP status code, byte contents of the response body and possibly a
// RespError as the WrappedError, if the HTTP body could be decoded as a RespError.
func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) error {
var req *http.Request
var err error
if reqBody != nil {
var jsonStr []byte
jsonStr, err = json.Marshal(reqBody)
if err != nil {
return nil, err
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(reqBody); err != nil {
return err
}
req, err = http.NewRequest(method, httpURL, bytes.NewBuffer(jsonStr))
req, err = http.NewRequest(method, httpURL, buf)
} else {
req, err = http.NewRequest(method, httpURL, nil)
}
if err != nil {
return nil, err
return err
}
req.Header.Set("Content-Type", "application/json")
res, err := cli.Client.Do(req)
@@ -206,10 +212,14 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{
defer res.Body.Close()
}
if err != nil {
return nil, err
return err
}
contents, err := ioutil.ReadAll(res.Body)
if res.StatusCode/100 != 2 { // not 2xx
contents, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
var wrap error
var respErr RespError
if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" {
@@ -223,29 +233,25 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{
msg = msg + ": " + string(contents)
}
return contents, HTTPError{
return HTTPError{
Contents: contents,
Code: res.StatusCode,
Message: msg,
WrappedError: wrap,
}
}
if err != nil {
return nil, err
if resBody != nil && res.Body != nil {
return json.NewDecoder(res.Body).Decode(&resBody)
}
if resBody != nil {
if err = json.Unmarshal(contents, &resBody); err != nil {
return nil, err
}
}
return contents, nil
return nil
}
// CreateFilter makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter
func (cli *Client) CreateFilter(filter json.RawMessage) (resp *RespCreateFilter, err error) {
urlPath := cli.BuildURL("user", cli.UserID, "filter")
_, err = cli.MakeRequest("POST", urlPath, &filter, &resp)
err = cli.MakeRequest("POST", urlPath, &filter, &resp)
return
}
@@ -267,13 +273,12 @@ func (cli *Client) SyncRequest(timeout int, since, filterID string, fullState bo
query["full_state"] = "true"
}
urlPath := cli.BuildURLWithQuery([]string{"sync"}, query)
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
func (cli *Client) register(u string, req *ReqRegister) (resp *RespRegister, uiaResp *RespUserInteractive, err error) {
var bodyBytes []byte
bodyBytes, err = cli.MakeRequest("POST", u, req, nil)
err = cli.MakeRequest("POST", u, req, &resp)
if err != nil {
httpErr, ok := err.(HTTPError)
if !ok { // network error
@@ -281,13 +286,10 @@ func (cli *Client) register(u string, req *ReqRegister) (resp *RespRegister, uia
}
if httpErr.Code == 401 {
// body should be RespUserInteractive, if it isn't, fail with the error
err = json.Unmarshal(bodyBytes, &uiaResp)
err = json.Unmarshal(httpErr.Contents, &uiaResp)
return
}
return
}
// body should be RespRegister
err = json.Unmarshal(bodyBytes, &resp)
return
}
@@ -351,7 +353,7 @@ func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) {
// This does not set credentials on this client instance. See SetCredentials() instead.
func (cli *Client) Login(req *ReqLogin) (resp *RespLogin, err error) {
urlPath := cli.BuildURL("login")
_, err = cli.MakeRequest("POST", urlPath, req, &resp)
err = cli.MakeRequest("POST", urlPath, req, &resp)
return
}
@@ -359,14 +361,14 @@ func (cli *Client) Login(req *ReqLogin) (resp *RespLogin, err error) {
// This does not clear the credentials from the client instance. See ClearCredentials() instead.
func (cli *Client) Logout() (resp *RespLogout, err error) {
urlPath := cli.BuildURL("logout")
_, err = cli.MakeRequest("POST", urlPath, nil, &resp)
err = cli.MakeRequest("POST", urlPath, nil, &resp)
return
}
// Versions returns the list of supported Matrix versions on this homeserver. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions
func (cli *Client) Versions() (resp *RespVersions, err error) {
urlPath := cli.BuildBaseURL("_matrix", "client", "versions")
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
@@ -383,21 +385,21 @@ func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{
} else {
urlPath = cli.BuildURL("join", roomIDorAlias)
}
_, err = cli.MakeRequest("POST", urlPath, content, &resp)
err = cli.MakeRequest("POST", urlPath, content, &resp)
return
}
// GetDisplayName returns the display name of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
func (cli *Client) GetDisplayName(mxid string) (resp *RespUserDisplayName, err error) {
urlPath := cli.BuildURL("profile", mxid, "displayname")
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
// GetOwnDisplayName returns the user's display name. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
func (cli *Client) GetOwnDisplayName() (resp *RespUserDisplayName, err error) {
urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
@@ -407,18 +409,18 @@ func (cli *Client) SetDisplayName(displayName string) (err error) {
s := struct {
DisplayName string `json:"displayname"`
}{displayName}
_, err = cli.MakeRequest("PUT", urlPath, &s, nil)
err = cli.MakeRequest("PUT", urlPath, &s, nil)
return
}
// GetAvatarURL gets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-avatar-url
func (cli *Client) GetAvatarURL() (url string, err error) {
func (cli *Client) GetAvatarURL() (string, error) {
urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
s := struct {
AvatarURL string `json:"avatar_url"`
}{}
_, err = cli.MakeRequest("GET", urlPath, nil, &s)
err := cli.MakeRequest("GET", urlPath, nil, &s)
if err != nil {
return "", err
}
@@ -427,12 +429,12 @@ func (cli *Client) GetAvatarURL() (url string, err error) {
}
// SetAvatarURL sets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-avatar-url
func (cli *Client) SetAvatarURL(url string) (err error) {
func (cli *Client) SetAvatarURL(url string) error {
urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
s := struct {
AvatarURL string `json:"avatar_url"`
}{url}
_, err = cli.MakeRequest("PUT", urlPath, &s, nil)
err := cli.MakeRequest("PUT", urlPath, &s, nil)
if err != nil {
return err
}
@@ -445,7 +447,7 @@ func (cli *Client) SetAvatarURL(url string) (err error) {
func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) {
txnID := txnID()
urlPath := cli.BuildURL("rooms", roomID, "send", eventType, txnID)
_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
return
}
@@ -453,7 +455,7 @@ func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) {
urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
return
}
@@ -491,6 +493,36 @@ func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) {
})
}
// SendAudio sends an m.room.message event into the given room with a msgtype of m.audio
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio
func (cli *Client) SendAudio(roomID, body, url, mimetype string, size uint) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message",
AudioMessage{
MsgType: "m.audio",
Body: body,
URL: url,
Info: AudioInfo{
Size: size,
Mimetype: mimetype,
},
})
}
// SendFile sends an m.room.message event into the given room with a msgtype of m.file
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-file
func (cli *Client) SendFile(roomID, body, url, mimetype string, size uint) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message",
FileMessage{
MsgType: "m.file",
Body: body,
URL: url,
Info: FileInfo{
Size: size,
Mimetype: mimetype,
},
})
}
// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice
func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) {
@@ -507,7 +539,7 @@ func (cli *Client) SendNoticeHTML(roomID, textclear, text string) (*RespSendEven
func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) {
txnID := txnID()
urlPath := cli.BuildURL("rooms", roomID, "redact", eventID, txnID)
_, err = cli.MakeRequest("PUT", urlPath, req, &resp)
err = cli.MakeRequest("PUT", urlPath, req, &resp)
return
}
@@ -518,56 +550,56 @@ func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *Re
// fmt.Println("Room:", resp.RoomID)
func (cli *Client) CreateRoom(req *ReqCreateRoom) (resp *RespCreateRoom, err error) {
urlPath := cli.BuildURL("createRoom")
_, err = cli.MakeRequest("POST", urlPath, req, &resp)
err = cli.MakeRequest("POST", urlPath, req, &resp)
return
}
// LeaveRoom leaves the given room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
func (cli *Client) LeaveRoom(roomID string) (resp *RespLeaveRoom, err error) {
u := cli.BuildURL("rooms", roomID, "leave")
_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
err = cli.MakeRequest("POST", u, struct{}{}, &resp)
return
}
// ForgetRoom forgets a room entirely. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
func (cli *Client) ForgetRoom(roomID string) (resp *RespForgetRoom, err error) {
u := cli.BuildURL("rooms", roomID, "forget")
_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
err = cli.MakeRequest("POST", u, struct{}{}, &resp)
return
}
// InviteUser invites a user to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
func (cli *Client) InviteUser(roomID string, req *ReqInviteUser) (resp *RespInviteUser, err error) {
u := cli.BuildURL("rooms", roomID, "invite")
_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
err = cli.MakeRequest("POST", u, req, &resp)
return
}
// InviteUserByThirdParty invites a third-party identifier to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#invite-by-third-party-id-endpoint
func (cli *Client) InviteUserByThirdParty(roomID string, req *ReqInvite3PID) (resp *RespInviteUser, err error) {
u := cli.BuildURL("rooms", roomID, "invite")
_, err = cli.MakeRequest("POST", u, req, &resp)
err = cli.MakeRequest("POST", u, req, &resp)
return
}
// KickUser kicks a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
func (cli *Client) KickUser(roomID string, req *ReqKickUser) (resp *RespKickUser, err error) {
u := cli.BuildURL("rooms", roomID, "kick")
_, err = cli.MakeRequest("POST", u, req, &resp)
err = cli.MakeRequest("POST", u, req, &resp)
return
}
// BanUser bans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
func (cli *Client) BanUser(roomID string, req *ReqBanUser) (resp *RespBanUser, err error) {
u := cli.BuildURL("rooms", roomID, "ban")
_, err = cli.MakeRequest("POST", u, req, &resp)
err = cli.MakeRequest("POST", u, req, &resp)
return
}
// UnbanUser unbans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanUser, err error) {
u := cli.BuildURL("rooms", roomID, "unban")
_, err = cli.MakeRequest("POST", u, req, &resp)
err = cli.MakeRequest("POST", u, req, &resp)
return
}
@@ -575,7 +607,7 @@ func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanU
func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *RespTyping, err error) {
req := ReqTyping{Typing: typing, Timeout: timeout}
u := cli.BuildURL("rooms", roomID, "typing", cli.UserID)
_, err = cli.MakeRequest("PUT", u, req, &resp)
err = cli.MakeRequest("PUT", u, req, &resp)
return
}
@@ -584,7 +616,7 @@ func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey
func (cli *Client) StateEvent(roomID, eventType, stateKey string, outContent interface{}) (err error) {
u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
_, err = cli.MakeRequest("GET", u, nil, outContent)
err = cli.MakeRequest("GET", u, nil, outContent)
return
}
@@ -625,6 +657,7 @@ func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, co
}
}
return nil, HTTPError{
Contents: contents,
Message: "Upload request failed: " + string(contents),
Code: res.StatusCode,
}
@@ -642,7 +675,7 @@ func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, co
// This API is primarily designed for application services which may want to efficiently look up joined members in a room.
func (cli *Client) JoinedMembers(roomID string) (resp *RespJoinedMembers, err error) {
u := cli.BuildURL("rooms", roomID, "joined_members")
_, err = cli.MakeRequest("GET", u, nil, &resp)
err = cli.MakeRequest("GET", u, nil, &resp)
return
}
@@ -652,7 +685,7 @@ func (cli *Client) JoinedMembers(roomID string) (resp *RespJoinedMembers, err er
// This API is primarily designed for application services which may want to efficiently look up joined rooms.
func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) {
u := cli.BuildURL("joined_rooms")
_, err = cli.MakeRequest("GET", u, nil, &resp)
err = cli.MakeRequest("GET", u, nil, &resp)
return
}
@@ -672,7 +705,7 @@ func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp
}
urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "messages"}, query)
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
@@ -680,7 +713,7 @@ func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver
func (cli *Client) TurnServer() (resp *RespTurnServer, err error) {
urlPath := cli.BuildURL("voip", "turnServer")
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}

View File

@@ -15,6 +15,7 @@ type Event struct {
RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence)
Content map[string]interface{} `json:"content"` // The JSON content of the event.
Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event
Unsigned map[string]interface{} `json:"unsigned"` // The unsigned portions of the event, such as age and prev_content
}
// Body returns the value of the "body" key in the event content if it is
@@ -47,18 +48,28 @@ type TextMessage struct {
FormattedBody string `json:"formatted_body,omitempty"`
}
// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
type ImageInfo struct {
// ThumbnailInfo contains info about an thumbnail image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
type ThumbnailInfo struct {
Height uint `json:"h,omitempty"`
Width uint `json:"w,omitempty"`
Mimetype string `json:"mimetype,omitempty"`
Size uint `json:"size,omitempty"`
}
// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
type ImageInfo struct {
Height uint `json:"h,omitempty"`
Width uint `json:"w,omitempty"`
Mimetype string `json:"mimetype,omitempty"`
Size uint `json:"size,omitempty"`
ThumbnailInfo ThumbnailInfo `json:"thumbnail_info,omitempty"`
ThumbnailURL string `json:"thumbnail_url,omitempty"`
}
// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
type VideoInfo struct {
Mimetype string `json:"mimetype,omitempty"`
ThumbnailInfo ImageInfo `json:"thumbnail_info"`
ThumbnailInfo ThumbnailInfo `json:"thumbnail_info"`
ThumbnailURL string `json:"thumbnail_url,omitempty"`
Height uint `json:"h,omitempty"`
Width uint `json:"w,omitempty"`
@@ -66,6 +77,21 @@ type VideoInfo struct {
Size uint `json:"size,omitempty"`
}
// AudioInfo contains info about a file - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio
type AudioInfo struct {
Mimetype string `json:"mimetype,omitempty"`
Size uint `json:"size,omitempty"`
Duration uint `json:"duration,omitempty"`
}
// FileInfo contains info about a file - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-file
type FileInfo struct {
Mimetype string `json:"mimetype,omitempty"`
ThumbnailInfo ImageInfo `json:"thumbnail_info"`
ThumbnailURL string `json:"thumbnail_url,omitempty"`
Size uint `json:"size,omitempty"`
}
// VideoMessage is an m.video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
type VideoMessage struct {
MsgType string `json:"msgtype"`
@@ -82,6 +108,22 @@ type ImageMessage struct {
Info ImageInfo `json:"info"`
}
// AudioMessage is an m.audio event
type AudioMessage struct {
MsgType string `json:"msgtype"`
Body string `json:"body"`
URL string `json:"url"`
Info AudioInfo `json:"info"`
}
// FileMessage is a m.file event
type FileMessage struct {
MsgType string `json:"msgtype"`
Body string `json:"body"`
URL string `json:"url"`
Info FileInfo `json:"info"`
}
// An HTMLMessage is the contents of a Matrix HTML formated message event.
type HTMLMessage struct {
Body string `json:"body"`

View File

@@ -14,6 +14,8 @@
package gomatrix
import "errors"
//Filter is used by clients to specify how the server should filter responses to e.g. sync requests
//Specified by: https://matrix.org/docs/spec/client_server/r0.2.0.html#filtering
type Filter struct {
@@ -21,7 +23,11 @@ type Filter struct {
EventFields []string `json:"event_fields,omitempty"`
EventFormat string `json:"event_format,omitempty"`
Presence FilterPart `json:"presence,omitempty"`
Room struct {
Room RoomFilter `json:"room,omitempty"`
}
// RoomFilter is used to define filtering rules for room events
type RoomFilter struct {
AccountData FilterPart `json:"account_data,omitempty"`
Ephemeral FilterPart `json:"ephemeral,omitempty"`
IncludeLeave bool `json:"include_leave,omitempty"`
@@ -29,15 +35,56 @@ type Filter struct {
Rooms []string `json:"rooms,omitempty"`
State FilterPart `json:"state,omitempty"`
Timeline FilterPart `json:"timeline,omitempty"`
} `json:"room,omitempty"`
}
// FilterPart is used to define filtering rules for specific categories of events
type FilterPart struct {
NotRooms []string `json:"not_rooms,omitempty"`
Rooms []string `json:"rooms,omitempty"`
Limit *int `json:"limit,omitempty"`
Limit int `json:"limit,omitempty"`
NotSenders []string `json:"not_senders,omitempty"`
NotTypes []string `json:"not_types,omitempty"`
Senders []string `json:"senders,omitempty"`
Types []string `json:"types,omitempty"`
ContainsURL *bool `json:"contains_url,omitempty"`
}
// Validate checks if the filter contains valid property values
func (filter *Filter) Validate() error {
if filter.EventFormat != "client" && filter.EventFormat != "federation" {
return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]")
}
return nil
}
// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in the request
func DefaultFilter() Filter {
return Filter{
AccountData: DefaultFilterPart(),
EventFields: nil,
EventFormat: "client",
Presence: DefaultFilterPart(),
Room: RoomFilter{
AccountData: DefaultFilterPart(),
Ephemeral: DefaultFilterPart(),
IncludeLeave: false,
NotRooms: nil,
Rooms: nil,
State: DefaultFilterPart(),
Timeline: DefaultFilterPart(),
},
}
}
// DefaultFilterPart returns the default filter part used by the Matrix server if no filter is provided in the request
func DefaultFilterPart() FilterPart {
return FilterPart{
NotRooms: nil,
Rooms: nil,
Limit: 20,
NotSenders: nil,
NotTypes: nil,
Senders: nil,
Types: nil,
}
}

View File

@@ -84,7 +84,7 @@ type RespUserInteractive struct {
Stages []string `json:"stages"`
} `json:"flows"`
Params map[string]interface{} `json:"params"`
Session string `json:"string"`
Session string `json:"session"`
Completed []string `json:"completed"`
ErrCode string `json:"errcode"`
Error string `json:"error"`
@@ -168,6 +168,7 @@ type RespSync struct {
} `json:"rooms"`
}
// RespTurnServer is the JSON response from a Turn Server
type RespTurnServer struct {
Username string `json:"username"`
Password string `json:"password"`

View File

@@ -1,4 +1,4 @@
image: golang:1.11
image: golang:1.13
stages:
- build

View File

@@ -88,7 +88,7 @@ loop:
case isUnreserved(r):
if r == '-' {
nHyphen++
if nHyphen > 1 {
if nHyphen > 2 {
break loop
}
} else {

10
vendor/modules.txt vendored
View File

@@ -15,7 +15,7 @@ github.com/Philipp15b/go-steam/protocol/steamlang
github.com/Philipp15b/go-steam/rwu
github.com/Philipp15b/go-steam/socialcache
github.com/Philipp15b/go-steam/steamid
# github.com/Rhymen/go-whatsapp v0.0.3-0.20190729104911-5c79b2cf277a
# github.com/Rhymen/go-whatsapp v0.0.3-0.20191003184814-fc3f792c814c
github.com/Rhymen/go-whatsapp
github.com/Rhymen/go-whatsapp/binary
github.com/Rhymen/go-whatsapp/binary/proto
@@ -23,9 +23,9 @@ github.com/Rhymen/go-whatsapp/binary/token
github.com/Rhymen/go-whatsapp/crypto/cbc
github.com/Rhymen/go-whatsapp/crypto/curve25519
github.com/Rhymen/go-whatsapp/crypto/hkdf
# github.com/bwmarrin/discordgo v0.19.0 => github.com/matterbridge/discordgo v0.0.0-20190818085008-57c6e0fc2f40
# github.com/bwmarrin/discordgo v0.19.0 => github.com/matterbridge/discordgo v0.0.0-20191026232317-01823f4ebba4
github.com/bwmarrin/discordgo
# github.com/d5/tengo v1.24.3
# github.com/d5/tengo v1.24.8
github.com/d5/tengo
github.com/d5/tengo/compiler
github.com/d5/tengo/compiler/ast
@@ -101,7 +101,7 @@ github.com/matterbridge/Rocket.Chat.Go.SDK/realtime
github.com/matterbridge/Rocket.Chat.Go.SDK/rest
# github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91
github.com/matterbridge/go-xmpp
# github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea
# github.com/matterbridge/gomatrix v0.0.0-20191026211822-6fc7accd00ca
github.com/matterbridge/gomatrix
# github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18
github.com/matterbridge/gozulipbot
@@ -191,7 +191,7 @@ github.com/valyala/fasttemplate
github.com/zfjagann/golang-ring
# gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a
gitlab.com/golang-commonmark/html
# gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179
# gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82
gitlab.com/golang-commonmark/linkify
# gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f
gitlab.com/golang-commonmark/markdown