forked from MirrorHub/mautrix-whatsapp
Merge branch 'break'
This commit is contained in:
commit
86e5ecbbfe
45 changed files with 803 additions and 2004 deletions
56
commands.go
56
commands.go
|
@ -17,6 +17,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
@ -35,7 +36,6 @@ import (
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/database"
|
"maunium.net/go/mautrix-whatsapp/database"
|
||||||
"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandHandler struct {
|
type CommandHandler struct {
|
||||||
|
@ -255,7 +255,7 @@ func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
|
||||||
handler.log.Debugln("%s successfully joined group %s", ce.User.MXID, jid)
|
handler.log.Debugln("%s successfully joined group %s", ce.User.MXID, jid)
|
||||||
portal := handler.bridge.GetPortalByJID(database.GroupPortalKey(jid))
|
portal := handler.bridge.GetPortalByJID(database.GroupPortalKey(jid))
|
||||||
if len(portal.MXID) > 0 {
|
if len(portal.MXID) > 0 {
|
||||||
portal.Sync(ce.User, whatsapp.Contact{Jid: portal.Key.JID})
|
portal.Sync(ce.User, whatsapp.Contact{JID: portal.Key.JID})
|
||||||
ce.Reply("Successfully joined group \"%s\" and synced portal room: [%s](https://matrix.to/#/%s)", portal.Name, portal.Name, portal.MXID)
|
ce.Reply("Successfully joined group \"%s\" and synced portal room: [%s](https://matrix.to/#/%s)", portal.Name, portal.Name, portal.MXID)
|
||||||
} else {
|
} else {
|
||||||
err = portal.CreateMatrixRoom(ce.User)
|
err = portal.CreateMatrixRoom(ce.User)
|
||||||
|
@ -412,11 +412,11 @@ func (handler *CommandHandler) CommandLogout(ce *CommandEvent) {
|
||||||
ce.Reply("Unknown error while logging out: %v", err)
|
ce.Reply("Unknown error while logging out: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ce.User.Disconnect()
|
|
||||||
ce.User.removeFromJIDMap()
|
ce.User.removeFromJIDMap()
|
||||||
// TODO this causes a foreign key violation, which should be fixed
|
// TODO this causes a foreign key violation, which should be fixed
|
||||||
//ce.User.JID = ""
|
//ce.User.JID = ""
|
||||||
ce.User.SetSession(nil)
|
ce.User.SetSession(nil)
|
||||||
|
ce.User.DeleteConnection()
|
||||||
ce.Reply("Logged out successfully.")
|
ce.Reply("Logged out successfully.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,9 +470,10 @@ func (handler *CommandHandler) CommandDeleteSession(ce *CommandEvent) {
|
||||||
ce.Reply("Nothing to purge: no session information stored and no active connection.")
|
ce.Reply("Nothing to purge: no session information stored and no active connection.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ce.User.Disconnect()
|
//ce.User.JID = ""
|
||||||
ce.User.removeFromJIDMap()
|
ce.User.removeFromJIDMap()
|
||||||
ce.User.SetSession(nil)
|
ce.User.SetSession(nil)
|
||||||
|
ce.User.DeleteConnection()
|
||||||
ce.Reply("Session information purged")
|
ce.Reply("Session information purged")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,24 +491,21 @@ func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wasConnected := true
|
wasConnected := true
|
||||||
sess, err := ce.User.Conn.Disconnect()
|
err := ce.User.Conn.Disconnect()
|
||||||
if err == whatsapp.ErrNotConnected {
|
if err == whatsapp.ErrNotConnected {
|
||||||
wasConnected = false
|
wasConnected = false
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
ce.User.log.Warnln("Error while disconnecting:", err)
|
ce.User.log.Warnln("Error while disconnecting:", err)
|
||||||
} else {
|
|
||||||
ce.User.SetSession(&sess)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ce.User.Conn.Restore(true)
|
ctx := context.Background()
|
||||||
|
|
||||||
|
err = ce.User.Conn.Restore(true, ctx)
|
||||||
if err == whatsapp.ErrInvalidSession {
|
if err == whatsapp.ErrInvalidSession {
|
||||||
if ce.User.Session != nil {
|
if ce.User.Session != nil {
|
||||||
ce.User.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...")
|
ce.User.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...")
|
||||||
var sess whatsapp.Session
|
ce.User.Conn.SetSession(*ce.User.Session)
|
||||||
sess, err = ce.User.Conn.RestoreWithSession(*ce.User.Session)
|
err = ce.User.Conn.Restore(true, ctx)
|
||||||
if err == nil {
|
|
||||||
ce.User.SetSession(&sess)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ce.Reply("You are not logged in.")
|
ce.Reply("You are not logged in.")
|
||||||
return
|
return
|
||||||
|
@ -521,17 +519,11 @@ func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) {
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ce.User.log.Warnln("Error while reconnecting:", err)
|
ce.User.log.Warnln("Error while reconnecting:", err)
|
||||||
if errors.Is(err, whatsapp.ErrRestoreSessionTimeout) {
|
|
||||||
ce.Reply("Reconnection timed out. Is WhatsApp on your phone reachable?")
|
|
||||||
} else {
|
|
||||||
ce.Reply("Unknown error while reconnecting: %v", err)
|
ce.Reply("Unknown error while reconnecting: %v", err)
|
||||||
}
|
|
||||||
ce.User.log.Debugln("Disconnecting due to failed session restore in reconnect command...")
|
ce.User.log.Debugln("Disconnecting due to failed session restore in reconnect command...")
|
||||||
sess, err = ce.User.Conn.Disconnect()
|
err = ce.User.Conn.Disconnect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ce.User.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err)
|
ce.User.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err)
|
||||||
} else {
|
|
||||||
ce.User.SetSession(&sess)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -554,7 +546,7 @@ func (handler *CommandHandler) CommandDeleteConnection(ce *CommandEvent) {
|
||||||
ce.Reply("You don't have a WhatsApp connection.")
|
ce.Reply("You don't have a WhatsApp connection.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ce.User.Disconnect()
|
ce.User.DeleteConnection()
|
||||||
ce.Reply("Successfully disconnected. Use the `reconnect` command to reconnect.")
|
ce.Reply("Successfully disconnected. Use the `reconnect` command to reconnect.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,7 +557,7 @@ func (handler *CommandHandler) CommandDisconnect(ce *CommandEvent) {
|
||||||
ce.Reply("You don't have a WhatsApp connection.")
|
ce.Reply("You don't have a WhatsApp connection.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sess, err := ce.User.Conn.Disconnect()
|
err := ce.User.Conn.Disconnect()
|
||||||
if err == whatsapp.ErrNotConnected {
|
if err == whatsapp.ErrNotConnected {
|
||||||
ce.Reply("You were not connected.")
|
ce.Reply("You were not connected.")
|
||||||
return
|
return
|
||||||
|
@ -573,8 +565,6 @@ func (handler *CommandHandler) CommandDisconnect(ce *CommandEvent) {
|
||||||
ce.User.log.Warnln("Error while disconnecting:", err)
|
ce.User.log.Warnln("Error while disconnecting:", err)
|
||||||
ce.Reply("Unknown error while disconnecting: %v", err)
|
ce.Reply("Unknown error while disconnecting: %v", err)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
ce.User.SetSession(&sess)
|
|
||||||
}
|
}
|
||||||
ce.User.bridge.Metrics.TrackConnectionState(ce.User.JID, false)
|
ce.User.bridge.Metrics.TrackConnectionState(ce.User.JID, false)
|
||||||
ce.Reply("Successfully disconnected. Use the `reconnect` command to reconnect.")
|
ce.Reply("Successfully disconnected. Use the `reconnect` command to reconnect.")
|
||||||
|
@ -737,14 +727,14 @@ const cmdListHelp = `list <contacts|groups> [page] [items per page] - Get a list
|
||||||
|
|
||||||
func formatContacts(contacts bool, input map[string]whatsapp.Contact) (result []string) {
|
func formatContacts(contacts bool, input map[string]whatsapp.Contact) (result []string) {
|
||||||
for jid, contact := range input {
|
for jid, contact := range input {
|
||||||
if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) != contacts {
|
if strings.HasSuffix(jid, whatsapp.NewUserSuffix) != contacts {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if contacts {
|
if contacts {
|
||||||
result = append(result, fmt.Sprintf("* %s / %s - `%s`", contact.Name, contact.Notify, contact.Jid[:len(contact.Jid)-len(whatsappExt.NewUserSuffix)]))
|
result = append(result, fmt.Sprintf("* %s / %s - `%s`", contact.Name, contact.Notify, contact.JID[:len(contact.JID)-len(whatsapp.NewUserSuffix)]))
|
||||||
} else {
|
} else {
|
||||||
result = append(result, fmt.Sprintf("* %s - `%s`", contact.Name, contact.Jid))
|
result = append(result, fmt.Sprintf("* %s - `%s`", contact.Name, contact.JID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Sort(sort.StringSlice(result))
|
sort.Sort(sort.StringSlice(result))
|
||||||
|
@ -818,8 +808,8 @@ func (handler *CommandHandler) CommandOpen(ce *CommandEvent) {
|
||||||
user := ce.User
|
user := ce.User
|
||||||
jid := ce.Args[0]
|
jid := ce.Args[0]
|
||||||
|
|
||||||
if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) {
|
if strings.HasSuffix(jid, whatsapp.NewUserSuffix) {
|
||||||
ce.Reply("That looks like a user JID. Did you mean `pm %s`?", jid[:len(jid)-len(whatsappExt.NewUserSuffix)])
|
ce.Reply("That looks like a user JID. Did you mean `pm %s`?", jid[:len(jid)-len(whatsapp.NewUserSuffix)])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,7 +855,7 @@ func (handler *CommandHandler) CommandPM(ce *CommandEvent) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jid := number + whatsappExt.NewUserSuffix
|
jid := number + whatsapp.NewUserSuffix
|
||||||
|
|
||||||
handler.log.Debugln("Importing", jid, "for", user)
|
handler.log.Debugln("Importing", jid, "for", user)
|
||||||
|
|
||||||
|
@ -876,11 +866,11 @@ func (handler *CommandHandler) CommandPM(ce *CommandEvent) {
|
||||||
"To create a portal anyway, use `pm --force <number>`.")
|
"To create a portal anyway, use `pm --force <number>`.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
contact = whatsapp.Contact{Jid: jid}
|
contact = whatsapp.Contact{JID: jid}
|
||||||
}
|
}
|
||||||
puppet := user.bridge.GetPuppetByJID(contact.Jid)
|
puppet := user.bridge.GetPuppetByJID(contact.JID)
|
||||||
puppet.Sync(user, contact)
|
puppet.Sync(user, contact)
|
||||||
portal := user.bridge.GetPortalByJID(database.NewPortalKey(contact.Jid, user.JID))
|
portal := user.bridge.GetPortalByJID(database.NewPortalKey(contact.JID, user.JID))
|
||||||
if len(portal.MXID) > 0 {
|
if len(portal.MXID) > 0 {
|
||||||
err := portal.MainIntent().EnsureInvited(portal.MXID, user.MXID)
|
err := portal.MainIntent().EnsureInvited(portal.MXID, user.MXID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -26,8 +26,6 @@ import (
|
||||||
|
|
||||||
"maunium.net/go/mautrix/event"
|
"maunium.net/go/mautrix/event"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BridgeConfig struct {
|
type BridgeConfig struct {
|
||||||
|
@ -167,8 +165,8 @@ type UsernameTemplateArgs struct {
|
||||||
|
|
||||||
func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8) {
|
func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if index := strings.IndexRune(contact.Jid, '@'); index > 0 {
|
if index := strings.IndexRune(contact.JID, '@'); index > 0 {
|
||||||
contact.Jid = "+" + contact.Jid[:index]
|
contact.JID = "+" + contact.JID[:index]
|
||||||
}
|
}
|
||||||
bc.displaynameTemplate.Execute(&buf, contact)
|
bc.displaynameTemplate.Execute(&buf, contact)
|
||||||
var quality int8
|
var quality int8
|
||||||
|
@ -177,7 +175,7 @@ func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8
|
||||||
quality = 3
|
quality = 3
|
||||||
case len(contact.Name) > 0 || len(contact.Short) > 0:
|
case len(contact.Name) > 0 || len(contact.Short) > 0:
|
||||||
quality = 2
|
quality = 2
|
||||||
case len(contact.Jid) > 0:
|
case len(contact.JID) > 0:
|
||||||
quality = 1
|
quality = 1
|
||||||
default:
|
default:
|
||||||
quality = 0
|
quality = 0
|
||||||
|
@ -185,7 +183,7 @@ func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8
|
||||||
return buf.String(), quality
|
return buf.String(), quality
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc BridgeConfig) FormatUsername(userID types.WhatsAppID) string {
|
func (bc BridgeConfig) FormatUsername(userID whatsapp.JID) string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
bc.usernameTemplate.Execute(&buf, userID)
|
bc.usernameTemplate.Execute(&buf, userID)
|
||||||
return buf.String()
|
return buf.String()
|
||||||
|
|
|
@ -164,7 +164,7 @@ func (puppet *Puppet) ProcessResponse(resp *mautrix.RespSync, _ string) error {
|
||||||
}
|
}
|
||||||
for roomID, events := range resp.Rooms.Join {
|
for roomID, events := range resp.Rooms.Join {
|
||||||
portal := puppet.bridge.GetPortalByMXID(roomID)
|
portal := puppet.bridge.GetPortalByMXID(roomID)
|
||||||
if portal == nil {
|
if portal == nil || portal.IsBroadcastList() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, evt := range events.Ephemeral.Events {
|
for _, evt := range events.Ephemeral.Events {
|
||||||
|
|
|
@ -22,11 +22,11 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
waProto "github.com/Rhymen/go-whatsapp/binary/proto"
|
waProto "github.com/Rhymen/go-whatsapp/binary/proto"
|
||||||
|
|
||||||
log "maunium.net/go/maulogger/v2"
|
log "maunium.net/go/maulogger/v2"
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ func (mq *MessageQuery) New() *Message {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mq *MessageQuery) GetAll(chat PortalKey) (messages []*Message) {
|
func (mq *MessageQuery) GetAll(chat PortalKey) (messages []*Message) {
|
||||||
rows, err := mq.db.Query("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, content FROM message WHERE chat_jid=$1 AND chat_receiver=$2", chat.JID, chat.Receiver)
|
rows, err := mq.db.Query("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, sent, content FROM message WHERE chat_jid=$1 AND chat_receiver=$2", chat.JID, chat.Receiver)
|
||||||
if err != nil || rows == nil {
|
if err != nil || rows == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -54,19 +54,20 @@ func (mq *MessageQuery) GetAll(chat PortalKey) (messages []*Message) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mq *MessageQuery) GetByJID(chat PortalKey, jid types.WhatsAppMessageID) *Message {
|
func (mq *MessageQuery) GetByJID(chat PortalKey, jid whatsapp.MessageID) *Message {
|
||||||
return mq.get("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, content " +
|
return mq.get("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, sent, content "+
|
||||||
"FROM message WHERE chat_jid=$1 AND chat_receiver=$2 AND jid=$3", chat.JID, chat.Receiver, jid)
|
"FROM message WHERE chat_jid=$1 AND chat_receiver=$2 AND jid=$3", chat.JID, chat.Receiver, jid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mq *MessageQuery) GetByMXID(mxid id.EventID) *Message {
|
func (mq *MessageQuery) GetByMXID(mxid id.EventID) *Message {
|
||||||
return mq.get("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, content " +
|
return mq.get("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, sent, content "+
|
||||||
"FROM message WHERE mxid=$1", mxid)
|
"FROM message WHERE mxid=$1", mxid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mq *MessageQuery) GetLastInChat(chat PortalKey) *Message {
|
func (mq *MessageQuery) GetLastInChat(chat PortalKey) *Message {
|
||||||
msg := mq.get("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, content " +
|
msg := mq.get("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, sent, content "+
|
||||||
"FROM message WHERE chat_jid=$1 AND chat_receiver=$2 ORDER BY timestamp DESC LIMIT 1", chat.JID, chat.Receiver)
|
"FROM message WHERE chat_jid=$1 AND chat_receiver=$2 AND sent=true ORDER BY timestamp DESC LIMIT 1",
|
||||||
|
chat.JID, chat.Receiver)
|
||||||
if msg == nil || msg.Timestamp == 0 {
|
if msg == nil || msg.Timestamp == 0 {
|
||||||
// Old db, we don't know what the last message is.
|
// Old db, we don't know what the last message is.
|
||||||
return nil
|
return nil
|
||||||
|
@ -87,10 +88,11 @@ type Message struct {
|
||||||
log log.Logger
|
log log.Logger
|
||||||
|
|
||||||
Chat PortalKey
|
Chat PortalKey
|
||||||
JID types.WhatsAppMessageID
|
JID whatsapp.MessageID
|
||||||
MXID id.EventID
|
MXID id.EventID
|
||||||
Sender types.WhatsAppID
|
Sender whatsapp.JID
|
||||||
Timestamp uint64
|
Timestamp uint64
|
||||||
|
Sent bool
|
||||||
Content *waProto.Message
|
Content *waProto.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +102,7 @@ func (msg *Message) IsFakeMXID() bool {
|
||||||
|
|
||||||
func (msg *Message) Scan(row Scannable) *Message {
|
func (msg *Message) Scan(row Scannable) *Message {
|
||||||
var content []byte
|
var content []byte
|
||||||
err := row.Scan(&msg.Chat.JID, &msg.Chat.Receiver, &msg.JID, &msg.MXID, &msg.Sender, &msg.Timestamp, &content)
|
err := row.Scan(&msg.Chat.JID, &msg.Chat.Receiver, &msg.JID, &msg.MXID, &msg.Sender, &msg.Timestamp, &msg.Sent, &content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != sql.ErrNoRows {
|
if err != sql.ErrNoRows {
|
||||||
msg.log.Errorln("Database scan failed:", err)
|
msg.log.Errorln("Database scan failed:", err)
|
||||||
|
@ -134,14 +136,23 @@ func (msg *Message) encodeBinaryContent() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *Message) Insert() {
|
func (msg *Message) Insert() {
|
||||||
_, err := msg.db.Exec("INSERT INTO message (chat_jid, chat_receiver, jid, mxid, sender, timestamp, content) " +
|
_, err := msg.db.Exec(`INSERT INTO message
|
||||||
"VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
(chat_jid, chat_receiver, jid, mxid, sender, timestamp, sent, content)
|
||||||
msg.Chat.JID, msg.Chat.Receiver, msg.JID, msg.MXID, msg.Sender, msg.Timestamp, msg.encodeBinaryContent())
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
|
||||||
|
msg.Chat.JID, msg.Chat.Receiver, msg.JID, msg.MXID, msg.Sender, msg.Timestamp, msg.Sent, msg.encodeBinaryContent())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg.log.Warnfln("Failed to insert %s@%s: %v", msg.Chat, msg.JID, err)
|
msg.log.Warnfln("Failed to insert %s@%s: %v", msg.Chat, msg.JID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *Message) MarkSent() {
|
||||||
|
msg.Sent = true
|
||||||
|
_, err := msg.db.Exec("UPDATE message SET sent=true WHERE chat_jid=$1 AND chat_receiver=$2 AND jid=$3", msg.Chat.JID, msg.Chat.Receiver, msg.JID)
|
||||||
|
if err != nil {
|
||||||
|
msg.log.Warnfln("Failed to update %s@%s: %v", msg.Chat, msg.JID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (msg *Message) Delete() {
|
func (msg *Message) Delete() {
|
||||||
_, err := msg.db.Exec("DELETE FROM message WHERE chat_jid=$1 AND chat_receiver=$2 AND jid=$3", msg.Chat.JID, msg.Chat.Receiver, msg.JID)
|
_, err := msg.db.Exec("DELETE FROM message WHERE chat_jid=$1 AND chat_receiver=$2 AND jid=$3", msg.Chat.JID, msg.Chat.Receiver, msg.JID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -22,25 +22,25 @@ import (
|
||||||
|
|
||||||
log "maunium.net/go/maulogger/v2"
|
log "maunium.net/go/maulogger/v2"
|
||||||
|
|
||||||
"maunium.net/go/mautrix/id"
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PortalKey struct {
|
type PortalKey struct {
|
||||||
JID types.WhatsAppID
|
JID whatsapp.JID
|
||||||
Receiver types.WhatsAppID
|
Receiver whatsapp.JID
|
||||||
}
|
}
|
||||||
|
|
||||||
func GroupPortalKey(jid types.WhatsAppID) PortalKey {
|
func GroupPortalKey(jid whatsapp.JID) PortalKey {
|
||||||
return PortalKey{
|
return PortalKey{
|
||||||
JID: jid,
|
JID: jid,
|
||||||
Receiver: jid,
|
Receiver: jid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPortalKey(jid, receiver types.WhatsAppID) PortalKey {
|
func NewPortalKey(jid, receiver whatsapp.JID) PortalKey {
|
||||||
if strings.HasSuffix(jid, "@g.us") {
|
if strings.HasSuffix(jid, whatsapp.GroupSuffix) {
|
||||||
receiver = jid
|
receiver = jid
|
||||||
}
|
}
|
||||||
return PortalKey{
|
return PortalKey{
|
||||||
|
@ -80,11 +80,11 @@ func (pq *PortalQuery) GetByMXID(mxid id.RoomID) *Portal {
|
||||||
return pq.get("SELECT * FROM portal WHERE mxid=$1", mxid)
|
return pq.get("SELECT * FROM portal WHERE mxid=$1", mxid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pq *PortalQuery) GetAllByJID(jid types.WhatsAppID) []*Portal {
|
func (pq *PortalQuery) GetAllByJID(jid whatsapp.JID) []*Portal {
|
||||||
return pq.getAll("SELECT * FROM portal WHERE jid=$1", jid)
|
return pq.getAll("SELECT * FROM portal WHERE jid=$1", jid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pq *PortalQuery) FindPrivateChats(receiver types.WhatsAppID) []*Portal {
|
func (pq *PortalQuery) FindPrivateChats(receiver whatsapp.JID) []*Portal {
|
||||||
return pq.getAll("SELECT * FROM portal WHERE receiver=$1 AND jid LIKE '%@s.whatsapp.net'", receiver)
|
return pq.getAll("SELECT * FROM portal WHERE receiver=$1 AND jid LIKE '%@s.whatsapp.net'", receiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,9 @@ import (
|
||||||
|
|
||||||
log "maunium.net/go/maulogger/v2"
|
log "maunium.net/go/maulogger/v2"
|
||||||
|
|
||||||
"maunium.net/go/mautrix/id"
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PuppetQuery struct {
|
type PuppetQuery struct {
|
||||||
|
@ -53,7 +53,7 @@ func (pq *PuppetQuery) GetAll() (puppets []*Puppet) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pq *PuppetQuery) Get(jid types.WhatsAppID) *Puppet {
|
func (pq *PuppetQuery) Get(jid whatsapp.JID) *Puppet {
|
||||||
row := pq.db.QueryRow("SELECT jid, avatar, avatar_url, displayname, name_quality, custom_mxid, access_token, next_batch, enable_presence, enable_receipts FROM puppet WHERE jid=$1", jid)
|
row := pq.db.QueryRow("SELECT jid, avatar, avatar_url, displayname, name_quality, custom_mxid, access_token, next_batch, enable_presence, enable_receipts FROM puppet WHERE jid=$1", jid)
|
||||||
if row == nil {
|
if row == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -85,7 +85,7 @@ type Puppet struct {
|
||||||
db *Database
|
db *Database
|
||||||
log log.Logger
|
log log.Logger
|
||||||
|
|
||||||
JID types.WhatsAppID
|
JID whatsapp.JID
|
||||||
Avatar string
|
Avatar string
|
||||||
AvatarURL id.ContentURI
|
AvatarURL id.ContentURI
|
||||||
Displayname string
|
Displayname string
|
||||||
|
|
12
database/upgrades/2021-02-17-message-sent-status.go
Normal file
12
database/upgrades/2021-02-17-message-sent-status.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package upgrades
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
upgrades[20] = upgrade{"Add sent column for messages", func(tx *sql.Tx, ctx context) error {
|
||||||
|
_, err := tx.Exec(`ALTER TABLE message ADD COLUMN sent BOOLEAN NOT NULL DEFAULT true`)
|
||||||
|
return err
|
||||||
|
}}
|
||||||
|
}
|
|
@ -39,7 +39,7 @@ type upgrade struct {
|
||||||
fn upgradeFunc
|
fn upgradeFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
const NumberOfUpgrades = 20
|
const NumberOfUpgrades = 21
|
||||||
|
|
||||||
var upgrades [NumberOfUpgrades]upgrade
|
var upgrades [NumberOfUpgrades]upgrade
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,6 @@ import (
|
||||||
|
|
||||||
log "maunium.net/go/maulogger/v2"
|
log "maunium.net/go/maulogger/v2"
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
|
||||||
"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,7 +61,7 @@ func (uq *UserQuery) GetByMXID(userID id.UserID) *User {
|
||||||
return uq.New().Scan(row)
|
return uq.New().Scan(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uq *UserQuery) GetByJID(userID types.WhatsAppID) *User {
|
func (uq *UserQuery) GetByJID(userID whatsapp.JID) *User {
|
||||||
row := uq.db.QueryRow(`SELECT mxid, jid, management_room, last_connection, client_id, client_token, server_token, enc_key, mac_key FROM "user" WHERE jid=$1`, stripSuffix(userID))
|
row := uq.db.QueryRow(`SELECT mxid, jid, management_room, last_connection, client_id, client_token, server_token, enc_key, mac_key FROM "user" WHERE jid=$1`, stripSuffix(userID))
|
||||||
if row == nil {
|
if row == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -76,7 +74,7 @@ type User struct {
|
||||||
log log.Logger
|
log log.Logger
|
||||||
|
|
||||||
MXID id.UserID
|
MXID id.UserID
|
||||||
JID types.WhatsAppID
|
JID whatsapp.JID
|
||||||
ManagementRoom id.RoomID
|
ManagementRoom id.RoomID
|
||||||
Session *whatsapp.Session
|
Session *whatsapp.Session
|
||||||
LastConnection uint64
|
LastConnection uint64
|
||||||
|
@ -93,14 +91,14 @@ func (user *User) Scan(row Scannable) *User {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if len(jid.String) > 0 && len(clientID.String) > 0 {
|
if len(jid.String) > 0 && len(clientID.String) > 0 {
|
||||||
user.JID = jid.String + whatsappExt.NewUserSuffix
|
user.JID = jid.String + whatsapp.NewUserSuffix
|
||||||
user.Session = &whatsapp.Session{
|
user.Session = &whatsapp.Session{
|
||||||
ClientId: clientID.String,
|
ClientID: clientID.String,
|
||||||
ClientToken: clientToken.String,
|
ClientToken: clientToken.String,
|
||||||
ServerToken: serverToken.String,
|
ServerToken: serverToken.String,
|
||||||
EncKey: encKey,
|
EncKey: encKey,
|
||||||
MacKey: macKey,
|
MacKey: macKey,
|
||||||
Wid: jid.String + whatsappExt.OldUserSuffix,
|
Wid: jid.String + whatsapp.OldUserSuffix,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
user.Session = nil
|
user.Session = nil
|
||||||
|
@ -108,7 +106,7 @@ func (user *User) Scan(row Scannable) *User {
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
func stripSuffix(jid types.WhatsAppID) string {
|
func stripSuffix(jid whatsapp.JID) string {
|
||||||
if len(jid) == 0 {
|
if len(jid) == 0 {
|
||||||
return jid
|
return jid
|
||||||
}
|
}
|
||||||
|
@ -141,7 +139,7 @@ func (user *User) Insert() {
|
||||||
_, err := user.db.Exec(`INSERT INTO "user" (mxid, jid, management_room, last_connection, client_id, client_token, server_token, enc_key, mac_key) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
|
_, err := user.db.Exec(`INSERT INTO "user" (mxid, jid, management_room, last_connection, client_id, client_token, server_token, enc_key, mac_key) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
|
||||||
user.MXID, user.jidPtr(),
|
user.MXID, user.jidPtr(),
|
||||||
user.ManagementRoom, user.LastConnection,
|
user.ManagementRoom, user.LastConnection,
|
||||||
sess.ClientId, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey)
|
sess.ClientID, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user.log.Warnfln("Failed to insert %s: %v", user.MXID, err)
|
user.log.Warnfln("Failed to insert %s: %v", user.MXID, err)
|
||||||
}
|
}
|
||||||
|
@ -160,7 +158,7 @@ func (user *User) Update() {
|
||||||
sess := user.sessionUnptr()
|
sess := user.sessionUnptr()
|
||||||
_, err := user.db.Exec(`UPDATE "user" SET jid=$1, management_room=$2, last_connection=$3, client_id=$4, client_token=$5, server_token=$6, enc_key=$7, mac_key=$8 WHERE mxid=$9`,
|
_, err := user.db.Exec(`UPDATE "user" SET jid=$1, management_room=$2, last_connection=$3, client_id=$4, client_token=$5, server_token=$6, enc_key=$7, mac_key=$8 WHERE mxid=$9`,
|
||||||
user.jidPtr(), user.ManagementRoom, user.LastConnection,
|
user.jidPtr(), user.ManagementRoom, user.LastConnection,
|
||||||
sess.ClientId, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey,
|
sess.ClientID, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey,
|
||||||
user.MXID)
|
user.MXID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user.log.Warnfln("Failed to update %s: %v", user.MXID, err)
|
user.log.Warnfln("Failed to update %s: %v", user.MXID, err)
|
||||||
|
|
|
@ -22,12 +22,11 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
|
||||||
"maunium.net/go/mautrix/event"
|
"maunium.net/go/mautrix/event"
|
||||||
"maunium.net/go/mautrix/format"
|
"maunium.net/go/mautrix/format"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
|
||||||
"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var italicRegex = regexp.MustCompile("([\\s>~*]|^)_(.+?)_([^a-zA-Z\\d]|$)")
|
var italicRegex = regexp.MustCompile("([\\s>~*]|^)_(.+?)_([^a-zA-Z\\d]|$)")
|
||||||
|
@ -58,9 +57,9 @@ func NewFormatter(bridge *Bridge) *Formatter {
|
||||||
if mxid[0] == '@' {
|
if mxid[0] == '@' {
|
||||||
puppet := bridge.GetPuppetByMXID(id.UserID(mxid))
|
puppet := bridge.GetPuppetByMXID(id.UserID(mxid))
|
||||||
if puppet != nil {
|
if puppet != nil {
|
||||||
jids, ok := ctx[mentionedJIDsContextKey].([]types.WhatsAppID)
|
jids, ok := ctx[mentionedJIDsContextKey].([]whatsapp.JID)
|
||||||
if !ok {
|
if !ok {
|
||||||
ctx[mentionedJIDsContextKey] = []types.WhatsAppID{puppet.JID}
|
ctx[mentionedJIDsContextKey] = []whatsapp.JID{puppet.JID}
|
||||||
} else {
|
} else {
|
||||||
ctx[mentionedJIDsContextKey] = append(jids, puppet.JID)
|
ctx[mentionedJIDsContextKey] = append(jids, puppet.JID)
|
||||||
}
|
}
|
||||||
|
@ -105,7 +104,7 @@ func NewFormatter(bridge *Bridge) *Formatter {
|
||||||
return formatter
|
return formatter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (formatter *Formatter) getMatrixInfoByJID(jid types.WhatsAppID) (mxid id.UserID, displayname string) {
|
func (formatter *Formatter) getMatrixInfoByJID(jid whatsapp.JID) (mxid id.UserID, displayname string) {
|
||||||
if user := formatter.bridge.GetUserByJID(jid); user != nil {
|
if user := formatter.bridge.GetUserByJID(jid); user != nil {
|
||||||
mxid = user.MXID
|
mxid = user.MXID
|
||||||
displayname = string(user.MXID)
|
displayname = string(user.MXID)
|
||||||
|
@ -116,7 +115,7 @@ func (formatter *Formatter) getMatrixInfoByJID(jid types.WhatsAppID) (mxid id.Us
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (formatter *Formatter) ParseWhatsApp(content *event.MessageEventContent, mentionedJIDs []types.WhatsAppID) {
|
func (formatter *Formatter) ParseWhatsApp(content *event.MessageEventContent, mentionedJIDs []whatsapp.JID) {
|
||||||
output := html.EscapeString(content.Body)
|
output := html.EscapeString(content.Body)
|
||||||
for regex, replacement := range formatter.waReplString {
|
for regex, replacement := range formatter.waReplString {
|
||||||
output = regex.ReplaceAllString(output, replacement)
|
output = regex.ReplaceAllString(output, replacement)
|
||||||
|
@ -126,7 +125,7 @@ func (formatter *Formatter) ParseWhatsApp(content *event.MessageEventContent, me
|
||||||
}
|
}
|
||||||
for _, jid := range mentionedJIDs {
|
for _, jid := range mentionedJIDs {
|
||||||
mxid, displayname := formatter.getMatrixInfoByJID(jid)
|
mxid, displayname := formatter.getMatrixInfoByJID(jid)
|
||||||
number := "@" + strings.Replace(jid, whatsappExt.NewUserSuffix, "", 1)
|
number := "@" + strings.Replace(jid, whatsapp.NewUserSuffix, "", 1)
|
||||||
output = strings.Replace(output, number, fmt.Sprintf(`<a href="https://matrix.to/#/%s">%s</a>`, mxid, displayname), -1)
|
output = strings.Replace(output, number, fmt.Sprintf(`<a href="https://matrix.to/#/%s">%s</a>`, mxid, displayname), -1)
|
||||||
content.Body = strings.Replace(content.Body, number, displayname, -1)
|
content.Body = strings.Replace(content.Body, number, displayname, -1)
|
||||||
}
|
}
|
||||||
|
@ -140,9 +139,9 @@ func (formatter *Formatter) ParseWhatsApp(content *event.MessageEventContent, me
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (formatter *Formatter) ParseMatrix(html string) (string, []types.WhatsAppID) {
|
func (formatter *Formatter) ParseMatrix(html string) (string, []whatsapp.JID) {
|
||||||
ctx := make(format.Context)
|
ctx := make(format.Context)
|
||||||
result := formatter.matrixHTMLParser.Parse(html, ctx)
|
result := formatter.matrixHTMLParser.Parse(html, ctx)
|
||||||
mentionedJIDs, _ := ctx[mentionedJIDsContextKey].([]types.WhatsAppID)
|
mentionedJIDs, _ := ctx[mentionedJIDsContextKey].([]whatsapp.JID)
|
||||||
return result, mentionedJIDs
|
return result, mentionedJIDs
|
||||||
}
|
}
|
||||||
|
|
12
go.mod
12
go.mod
|
@ -5,15 +5,15 @@ go 1.14
|
||||||
require (
|
require (
|
||||||
github.com/Rhymen/go-whatsapp v0.1.0
|
github.com/Rhymen/go-whatsapp v0.1.0
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/lib/pq v1.7.0
|
github.com/lib/pq v1.9.0
|
||||||
github.com/mattn/go-sqlite3 v1.14.0
|
github.com/mattn/go-sqlite3 v1.14.6
|
||||||
github.com/prometheus/client_golang v1.7.0
|
github.com/prometheus/client_golang v1.9.0
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
gopkg.in/yaml.v2 v2.3.0
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
maunium.net/go/mauflag v1.0.0
|
maunium.net/go/mauflag v1.0.0
|
||||||
maunium.net/go/maulogger/v2 v2.1.1
|
maunium.net/go/maulogger/v2 v2.2.2
|
||||||
maunium.net/go/mautrix v0.8.2
|
maunium.net/go/mautrix v0.8.3
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.21
|
replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.4.0-rc.1
|
||||||
|
|
344
go.sum
344
go.sum
|
@ -1,16 +1,31 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||||
|
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||||
|
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||||
|
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||||
|
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
|
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
|
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||||
|
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||||
|
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||||
|
@ -21,23 +36,52 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa
|
||||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||||
|
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||||
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||||
|
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||||
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||||
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
|
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||||
|
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
@ -48,161 +92,332 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||||
|
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY=
|
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
|
||||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
|
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||||
|
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||||
|
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||||
|
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||||
|
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
||||||
|
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
|
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
|
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||||
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
|
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||||
|
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||||
|
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
|
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||||
|
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||||
|
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||||
|
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||||
|
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||||
|
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||||
|
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||||
|
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
github.com/prometheus/client_golang v1.7.0 h1:wCi7urQOGBsYcQROHqpUUX4ct84xp40t9R9JX0FuA/U=
|
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||||
github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
|
github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU=
|
||||||
|
github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
|
github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM=
|
||||||
|
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
||||||
|
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
|
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||||
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
|
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||||
|
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||||
|
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc=
|
github.com/tidwall/gjson v1.6.8 h1:CTmXMClGYPAmln7652e69B7OLXfTi5ABcPPwjIWUv7w=
|
||||||
github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
|
github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
|
||||||
github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
|
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
|
||||||
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
|
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
|
||||||
github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8=
|
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/sjson v1.1.5 h1:wsUceI/XDyZk3J1FUvuuYlK62zJv2HO2Pzb8A5EWdUE=
|
||||||
github.com/tidwall/sjson v1.1.1 h1:7h1vk049Jnd5EH9NyzNiEuwYW4b5qgreBbqRC19AS3U=
|
github.com/tidwall/sjson v1.1.5/go.mod h1:VuJzsZnTowhSxWdOgsAnb886i4AjEyTkk7tNtsL7EYE=
|
||||||
github.com/tidwall/sjson v1.1.1/go.mod h1:yvVuSnpEQv5cYIrO+AT6kw4QVfd5SDZoGIS7/5+fZFs=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tulir/go-whatsapp v0.3.13 h1:RPc/GdZ7KlhlGiZp2Zk7B/OP9v0l7ywOt5I2kKAZ+xU=
|
github.com/tulir/go-whatsapp v0.4.0-rc.1 h1:pYaw/V1gzXmchO3wcjI3+LJvNAxsiw2EysG5dUzufCI=
|
||||||
github.com/tulir/go-whatsapp v0.3.13/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o=
|
github.com/tulir/go-whatsapp v0.4.0-rc.1/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E=
|
||||||
github.com/tulir/go-whatsapp v0.3.14 h1:VKXBMw6GvGKRQTceEQ9Dcg6wR+jRy5/G8SDPbisWZqs=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/tulir/go-whatsapp v0.3.14/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/tulir/go-whatsapp v0.3.15 h1:Ogu+f5hvB6Fbdjl6BG7nUb5wuJGCzUa9Z1FraS23M50=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/tulir/go-whatsapp v0.3.15/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o=
|
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
github.com/tulir/go-whatsapp v0.3.16 h1:NfcXC2DQXwls3qkAjbFqSeoMX+rUbbpBBGGvCXI3RUw=
|
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||||
github.com/tulir/go-whatsapp v0.3.16/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o=
|
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||||
github.com/tulir/go-whatsapp v0.3.17 h1:HMRT6HzP1seUt5P0waD8CxThB2bfBgKX2uVjOoXCaf8=
|
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||||
github.com/tulir/go-whatsapp v0.3.17/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
github.com/tulir/go-whatsapp v0.3.18 h1:45pkdjEnAp6yV4RTSWCZn2Eenep+MN7Kndf/rRXQtyY=
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
github.com/tulir/go-whatsapp v0.3.18/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o=
|
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
github.com/tulir/go-whatsapp v0.3.19 h1:76VtmcjKGX8MbfJN9NNi1f0IVmigTLUcxqE1VRcovcQ=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
github.com/tulir/go-whatsapp v0.3.19/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o=
|
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||||
github.com/tulir/go-whatsapp v0.3.20 h1:nK92MgruqXwk+QlaAS39xhzHNbFvJIEgUIOUrN3i8Yc=
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||||
github.com/tulir/go-whatsapp v0.3.20/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
github.com/tulir/go-whatsapp v0.3.21 h1:2m7gUw4oHX4kIpMmP9VwCR7KEUK/PHhXLygPFGF9XfI=
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
github.com/tulir/go-whatsapp v0.3.21/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o=
|
|
||||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201026091529-146b70c837a4 h1:awiuzyrRjJDb+OXi9ceHO3SDxVoN3JER57mhtqkdQBs=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201026091529-146b70c837a4/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d h1:1aflnvSoWWLI2k/dMUAl5lvU1YO4Mb4hz0gh+1rjcxU=
|
||||||
|
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs=
|
||||||
|
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
@ -216,9 +431,16 @@ google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEG
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||||
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
@ -226,19 +448,15 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
||||||
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
||||||
maunium.net/go/maulogger/v2 v2.1.1 h1:NAZNc6XUFJzgzfewCzVoGkxNAsblLCSSEdtDuIjP0XA=
|
maunium.net/go/maulogger/v2 v2.2.2 h1:NCw+7Be1GQFm8xXJ4M2C0Q8yLBTx3c5s7UZ4y1anZMU=
|
||||||
maunium.net/go/maulogger/v2 v2.1.1/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
|
maunium.net/go/maulogger/v2 v2.2.2/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
|
||||||
maunium.net/go/mautrix v0.8.0-rc.3 h1:bb18oNxHUmeiJ0V63YTRVGMjgoeLwu+G40l4n42Z5GI=
|
maunium.net/go/mautrix v0.8.3 h1:nKGdARVCf2w7thEN5GEbAjYrlLjKLX44jOdB1h+BV7U=
|
||||||
maunium.net/go/mautrix v0.8.0-rc.3/go.mod h1:TtVePxoEaw6+RZDKVajw66Yaj1lqLjH8l4FF3krsqWY=
|
maunium.net/go/mautrix v0.8.3/go.mod h1:LPbb/DeAmtOPKnGbJazL9g11cO3mMAaEbLE8udd98BU=
|
||||||
maunium.net/go/mautrix v0.8.0-rc.4 h1:3JXoL2JJPE5nh/YSw9sv9dQA9ulma9yHTMOBMBY1xdo=
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
maunium.net/go/mautrix v0.8.0-rc.4/go.mod h1:TtVePxoEaw6+RZDKVajw66Yaj1lqLjH8l4FF3krsqWY=
|
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||||
maunium.net/go/mautrix v0.8.0 h1:G1jlVslNUTWEqaxuatHAMmzTWnGyoCIc4tAF5GpQJd8=
|
|
||||||
maunium.net/go/mautrix v0.8.0/go.mod h1:TtVePxoEaw6+RZDKVajw66Yaj1lqLjH8l4FF3krsqWY=
|
|
||||||
maunium.net/go/mautrix v0.8.1 h1:YvJGy7euB+x6Mz74jJ+G4NiyrLiX9pzmXpnQB9vFONg=
|
|
||||||
maunium.net/go/mautrix v0.8.1/go.mod h1:KiViCshKBUZwrVRvTOXsJBFfstvR/btxckHUbOPdu54=
|
|
||||||
maunium.net/go/mautrix v0.8.2 h1:E3NudQ/QolmE/yhHau8iCkbmcq6gCLvoEvukdqPFJu4=
|
|
||||||
maunium.net/go/mautrix v0.8.2/go.mod h1:KiViCshKBUZwrVRvTOXsJBFfstvR/btxckHUbOPdu54=
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[*.{yaml,yml}]
|
|
||||||
indent_size = 2
|
|
2
helm/mautrix-whatsapp/.gitignore
vendored
2
helm/mautrix-whatsapp/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
charts/*
|
|
||||||
!*.yaml
|
|
|
@ -1,22 +0,0 @@
|
||||||
# Patterns to ignore when building packages.
|
|
||||||
# This supports shell glob matching, relative path matching, and
|
|
||||||
# negation (prefixed with !). Only one pattern per line.
|
|
||||||
.DS_Store
|
|
||||||
# Common VCS dirs
|
|
||||||
.git/
|
|
||||||
.gitignore
|
|
||||||
.bzr/
|
|
||||||
.bzrignore
|
|
||||||
.hg/
|
|
||||||
.hgignore
|
|
||||||
.svn/
|
|
||||||
# Common backup files
|
|
||||||
*.swp
|
|
||||||
*.bak
|
|
||||||
*.tmp
|
|
||||||
*~
|
|
||||||
# Various IDEs
|
|
||||||
.project
|
|
||||||
.idea/
|
|
||||||
*.tmproj
|
|
||||||
.vscode/
|
|
|
@ -1,14 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
name: mautrix-whatsapp
|
|
||||||
version: 0.1.0
|
|
||||||
appVersion: "0.1.0"
|
|
||||||
description: A Matrix-Whatsapp puppeting bridge.
|
|
||||||
keywords:
|
|
||||||
- matrix
|
|
||||||
- bridge
|
|
||||||
- whatsapp
|
|
||||||
maintainers:
|
|
||||||
- name: Tulir Asokan
|
|
||||||
email: tulir@maunium.net
|
|
||||||
sources:
|
|
||||||
- https://github.com/tulir/mautrix-whatsapp
|
|
|
@ -1,6 +0,0 @@
|
||||||
dependencies:
|
|
||||||
- name: postgresql
|
|
||||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
|
||||||
version: 6.5.0
|
|
||||||
digest: sha256:85139e9d4207e49c11c5f84d7920d0135cffd3d427f3f3638d4e51258990de2a
|
|
||||||
generated: "2019-10-23T22:11:37.005827507+03:00"
|
|
|
@ -1,5 +0,0 @@
|
||||||
dependencies:
|
|
||||||
- name: postgresql
|
|
||||||
version: 6.5.0
|
|
||||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
|
||||||
condition: postgresql.enabled
|
|
|
@ -1,12 +0,0 @@
|
||||||
Your registration file is below. Save it into a YAML file and give the path to that file to synapse:
|
|
||||||
|
|
||||||
id: {{ .Values.appservice.id }}
|
|
||||||
as_token: {{ .Values.appservice.asToken }}
|
|
||||||
hs_token: {{ .Values.appservice.hsToken }}
|
|
||||||
namespaces:
|
|
||||||
users:
|
|
||||||
- exclusive: true
|
|
||||||
regex: "@{{ .Values.bridge.username_template | replace "{{.}}" ".+"}}:{{ .Values.homeserver.domain }}"
|
|
||||||
url: {{ .Values.appservice.address }}
|
|
||||||
sender_localpart: {{ .Values.appservice.botUsername }}
|
|
||||||
rate_limited: false
|
|
|
@ -1,55 +0,0 @@
|
||||||
{{/*
|
|
||||||
Expand the name of the chart.
|
|
||||||
*/}}
|
|
||||||
{{- define "mautrix-whatsapp.name" -}}
|
|
||||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
|
||||||
{{- end -}}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Create a default fully qualified app name.
|
|
||||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
|
||||||
If release name contains chart name it will be used as a full name.
|
|
||||||
*/}}
|
|
||||||
{{- define "mautrix-whatsapp.fullname" -}}
|
|
||||||
{{- if .Values.fullnameOverride -}}
|
|
||||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
|
|
||||||
{{- else -}}
|
|
||||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
|
||||||
{{- if contains $name .Release.Name -}}
|
|
||||||
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
|
|
||||||
{{- else -}}
|
|
||||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
|
||||||
{{- end -}}
|
|
||||||
{{- end -}}
|
|
||||||
{{- end -}}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Create chart name and version as used by the chart label.
|
|
||||||
*/}}
|
|
||||||
{{- define "mautrix-whatsapp.chart" -}}
|
|
||||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
|
|
||||||
{{- end -}}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Common labels
|
|
||||||
*/}}
|
|
||||||
{{- define "mautrix-whatsapp.labels" -}}
|
|
||||||
app.kubernetes.io/name: {{ include "mautrix-whatsapp.name" . }}
|
|
||||||
helm.sh/chart: {{ include "mautrix-whatsapp.chart" . }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
{{- if .Chart.AppVersion }}
|
|
||||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
|
||||||
{{- end }}
|
|
||||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
||||||
{{- end -}}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Create the name of the service account to use
|
|
||||||
*/}}
|
|
||||||
{{- define "mautrix-whatsapp.serviceAccountName" -}}
|
|
||||||
{{- if .Values.serviceAccount.create -}}
|
|
||||||
{{ default (include "mautrix-whatsapp.fullname" .) .Values.serviceAccount.name }}
|
|
||||||
{{- else -}}
|
|
||||||
{{ default "default" .Values.serviceAccount.name }}
|
|
||||||
{{- end -}}
|
|
||||||
{{- end -}}
|
|
|
@ -1,45 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: {{ template "mautrix-whatsapp.fullname" . }}
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
|
||||||
app.kubernetes.io/name: {{ template "mautrix-whatsapp.name" . }}
|
|
||||||
data:
|
|
||||||
config.yaml: |
|
|
||||||
homeserver:
|
|
||||||
address: {{ .Values.homeserver.address }}
|
|
||||||
domain: {{ .Values.homeserver.domain }}
|
|
||||||
|
|
||||||
appservice:
|
|
||||||
address: http://{{ include "mautrix-whatsapp.fullname" . }}:{{ .Values.service.port }}
|
|
||||||
|
|
||||||
hostname: 0.0.0.0
|
|
||||||
port: {{ .Values.service.port }}
|
|
||||||
|
|
||||||
{{- if .Values.postgresql.enabled }}
|
|
||||||
database:
|
|
||||||
type: postgres
|
|
||||||
uri: "postgres://postgres:{{ .Values.postgresql.postgresqlPassword }}@{{ .Release.Name }}-postgresql/{{ .Values.postgresql.postgresqlDatabase }}?sslmode=disable"
|
|
||||||
{{- else }}
|
|
||||||
database:
|
|
||||||
{{- toYaml .Values.appservice.database | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
id: {{ .Values.appservice.id }}
|
|
||||||
bot:
|
|
||||||
username: {{ .Values.appservice.botUsername }}
|
|
||||||
displayname: {{ .Values.appservice.botDisplayname }}
|
|
||||||
avatar: {{ .Values.appservice.botAvatar }}
|
|
||||||
|
|
||||||
as_token: {{ .Values.appservice.asToken }}
|
|
||||||
hs_token: {{ .Values.appservice.hsToken }}
|
|
||||||
|
|
||||||
bridge:
|
|
||||||
{{- toYaml .Values.bridge | nindent 6 }}
|
|
||||||
|
|
||||||
logging:
|
|
||||||
{{- toYaml .Values.logging | nindent 6 }}
|
|
||||||
registration.yaml: ""
|
|
|
@ -1,69 +0,0 @@
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: {{ include "mautrix-whatsapp.fullname" . }}
|
|
||||||
labels:
|
|
||||||
{{- include "mautrix-whatsapp.labels" . | nindent 4 }}
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: {{ include "mautrix-whatsapp.name" . }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
template:
|
|
||||||
{{- if .Values.podAnnotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml .Values.podAnnotations | nindent 6 }}
|
|
||||||
{{- end }}
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: {{ include "mautrix-whatsapp.name" . }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
spec:
|
|
||||||
serviceAccountName: {{ template "mautrix-whatsapp.serviceAccountName" . }}
|
|
||||||
containers:
|
|
||||||
- name: {{ .Chart.Name }}
|
|
||||||
securityContext:
|
|
||||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
|
||||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
|
||||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: /data
|
|
||||||
name: config-volume
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: {{ .Values.service.port }}
|
|
||||||
protocol: TCP
|
|
||||||
# livenessProbe:
|
|
||||||
# httpGet:
|
|
||||||
# path: /_matrix/mau/live
|
|
||||||
# port: http
|
|
||||||
# initialDelaySeconds: 60
|
|
||||||
# periodSeconds: 5
|
|
||||||
# readinessProbe:
|
|
||||||
# httpGet:
|
|
||||||
# path: /_matrix/mau/ready
|
|
||||||
# port: http
|
|
||||||
# initialDelaySeconds: 60
|
|
||||||
# periodSeconds: 5
|
|
||||||
resources:
|
|
||||||
{{- toYaml .Values.resources | nindent 12 }}
|
|
||||||
volumes:
|
|
||||||
- name: config-volume
|
|
||||||
configMap:
|
|
||||||
name: {{ template "mautrix-whatsapp.fullname" . }}
|
|
||||||
|
|
||||||
securityContext:
|
|
||||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
|
||||||
{{- with .Values.nodeSelector }}
|
|
||||||
nodeSelector:
|
|
||||||
{{- toYaml . | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- with .Values.affinity }}
|
|
||||||
affinity:
|
|
||||||
{{- toYaml . | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- with .Values.tolerations }}
|
|
||||||
tolerations:
|
|
||||||
{{- toYaml . | nindent 8 }}
|
|
||||||
{{- end }}
|
|
|
@ -1,16 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ include "mautrix-whatsapp.fullname" . }}
|
|
||||||
labels:
|
|
||||||
{{ include "mautrix-whatsapp.labels" . | indent 4 }}
|
|
||||||
spec:
|
|
||||||
type: {{ .Values.service.type }}
|
|
||||||
ports:
|
|
||||||
- port: {{ .Values.service.port }}
|
|
||||||
targetPort: http
|
|
||||||
protocol: TCP
|
|
||||||
name: http
|
|
||||||
selector:
|
|
||||||
app.kubernetes.io/name: {{ include "mautrix-whatsapp.name" . }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
|
@ -1,8 +0,0 @@
|
||||||
{{- if .Values.serviceAccount.create -}}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: {{ template "mautrix-whatsapp.serviceAccountName" . }}
|
|
||||||
labels:
|
|
||||||
{{ include "mautrix-whatsapp.labels" . | indent 4 }}
|
|
||||||
{{- end -}}
|
|
|
@ -1,137 +0,0 @@
|
||||||
image:
|
|
||||||
repository: dock.mau.dev/tulir/mautrix-whatsapp
|
|
||||||
tag: latest
|
|
||||||
pullPolicy: IfNotPresent
|
|
||||||
|
|
||||||
nameOverride: ""
|
|
||||||
fullnameOverride: ""
|
|
||||||
|
|
||||||
serviceAccount:
|
|
||||||
# Specifies whether a service account should be created
|
|
||||||
create: true
|
|
||||||
# The name of the service account to use.
|
|
||||||
# If not set and create is true, a name is generated using the fullname template
|
|
||||||
name:
|
|
||||||
|
|
||||||
service:
|
|
||||||
type: ClusterIP
|
|
||||||
port: 29318
|
|
||||||
|
|
||||||
resources: {}
|
|
||||||
# limits:
|
|
||||||
# cpu: 100m
|
|
||||||
# memory: 128Mi
|
|
||||||
# requests:
|
|
||||||
# cpu: 100m
|
|
||||||
# memory: 128Mi
|
|
||||||
|
|
||||||
nodeSelector: {}
|
|
||||||
|
|
||||||
tolerations: []
|
|
||||||
|
|
||||||
affinity: {}
|
|
||||||
|
|
||||||
# Postgres pod configs
|
|
||||||
postgresql:
|
|
||||||
enabled: true
|
|
||||||
postgresqlDatabase: mxwa
|
|
||||||
postgresqlPassword: SET TO RANDOM STRING
|
|
||||||
persistence:
|
|
||||||
size: 2Gi
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: 256Mi
|
|
||||||
cpu: 100m
|
|
||||||
|
|
||||||
# Homeserver details
|
|
||||||
homeserver:
|
|
||||||
# The address that this appservice can use to connect to the homeserver.
|
|
||||||
address: https://example.com
|
|
||||||
# The domain of the homeserver (for MXIDs, etc).
|
|
||||||
domain: example.com
|
|
||||||
|
|
||||||
# Application service host/registration related details
|
|
||||||
# Changing these values requires regeneration of the registration.
|
|
||||||
appservice:
|
|
||||||
id: whatsapp
|
|
||||||
botUsername: whatsappbot
|
|
||||||
# Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty
|
|
||||||
# to leave display name/avatar as-is.
|
|
||||||
botDisplayname: WhatsApp bridge bot
|
|
||||||
botAvatar: mxc://maunium.net/NeXNQarUbrlYBiPCpprYsRqr
|
|
||||||
|
|
||||||
# Authentication tokens for AS <-> HS communication.
|
|
||||||
asToken: SET TO RANDOM STRING
|
|
||||||
hsToken: SET TO RANDOM STRING
|
|
||||||
|
|
||||||
# The keys below can be used to override the configs in the base config:
|
|
||||||
# https://github.com/tulir/mautrix-whatsapp/blob/master/example-config.yaml
|
|
||||||
# Note that the "appservice" and "homeserver" sections are above and slightly different than the base.
|
|
||||||
|
|
||||||
# Bridge config
|
|
||||||
bridge:
|
|
||||||
# Localpart template of MXIDs for WhatsApp users.
|
|
||||||
# {{.}} is replaced with the phone number of the WhatsApp user.
|
|
||||||
username_template: whatsapp_{{.}}
|
|
||||||
|
|
||||||
# Number of chats to sync for new users.
|
|
||||||
initial_chat_sync_count: 10
|
|
||||||
# Number of old messages to fill when creating new portal rooms.
|
|
||||||
initial_history_fill_count: 20
|
|
||||||
# Maximum number of chats to sync when recovering from downtime.
|
|
||||||
# Set to -1 to sync all new chats during downtime.
|
|
||||||
recovery_chat_sync_limit: -1
|
|
||||||
# Whether or not to sync history when recovering from downtime.
|
|
||||||
recovery_history_backfill: true
|
|
||||||
# Maximum number of seconds since last message in chat to skip
|
|
||||||
# syncing the chat in any case. This setting will take priority
|
|
||||||
# over both recovery_chat_sync_limit and initial_chat_sync_count.
|
|
||||||
# Default is 3 days = 259200 seconds
|
|
||||||
sync_max_chat_age: 259200
|
|
||||||
|
|
||||||
# Whether or not to explicitly set the avatar and room name for private
|
|
||||||
# chat portal rooms. This can be useful if the previous field works fine,
|
|
||||||
# but causes room avatar/name bugs.
|
|
||||||
private_chat_portal_meta: true
|
|
||||||
|
|
||||||
# Allow invite permission for user. User can invite any bots to room with whatsapp
|
|
||||||
# users (private chat and groups)
|
|
||||||
allow_user_invite: true
|
|
||||||
|
|
||||||
# Permissions for using the bridge.
|
|
||||||
# Permitted values:
|
|
||||||
# relaybot - Talk through the relaybot (if enabled), no access otherwise
|
|
||||||
# user - Access to use the bridge to chat with a WhatsApp account.
|
|
||||||
# admin - User level and some additional administration tools
|
|
||||||
# Permitted keys:
|
|
||||||
# * - All Matrix users
|
|
||||||
# domain - All users on that homeserver
|
|
||||||
# mxid - Specific user
|
|
||||||
permissions:
|
|
||||||
"*": relaybot
|
|
||||||
"example.com": user
|
|
||||||
"@admin:example.com": admin
|
|
||||||
|
|
||||||
relaybot:
|
|
||||||
# Whether or not relaybot support is enabled.
|
|
||||||
enabled: false
|
|
||||||
# The management room for the bot. This is where all status notifications are posted and
|
|
||||||
# in this room, you can use `!wa <command>` instead of `!wa relaybot <command>`. Omitting
|
|
||||||
# the command prefix completely like in user management rooms is not possible.
|
|
||||||
management: !foo:example.com
|
|
||||||
# List of users to invite to all created rooms that include the relaybot.
|
|
||||||
invites: []
|
|
||||||
# The formats to use when sending messages to WhatsApp via the relaybot.
|
|
||||||
message_formats:
|
|
||||||
m.text: "<b>{{ .Sender.Displayname }}</b>: {{ .Message }}"
|
|
||||||
m.notice: "<b>{{ .Sender.Displayname }}</b>: {{ .Message }}"
|
|
||||||
m.emote: "* <b>{{ .Sender.Displayname }}</b> {{ .Message }}"
|
|
||||||
m.file: "<b>{{ .Sender.Displayname }}</b> sent a file"
|
|
||||||
m.image: "<b>{{ .Sender.Displayname }}</b> sent an image"
|
|
||||||
m.audio: "<b>{{ .Sender.Displayname }}</b> sent an audio file"
|
|
||||||
m.video: "<b>{{ .Sender.Displayname }}</b> sent a video"
|
|
||||||
m.location: "<b>{{ .Sender.Displayname }}</b> sent a location"
|
|
||||||
|
|
||||||
logging:
|
|
||||||
timestamp_format: Jan _2, 2006 15:04:05
|
|
||||||
print_level: debug
|
|
15
main.go
15
main.go
|
@ -25,6 +25,8 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
|
||||||
flag "maunium.net/go/mauflag"
|
flag "maunium.net/go/mauflag"
|
||||||
log "maunium.net/go/maulogger/v2"
|
log "maunium.net/go/maulogger/v2"
|
||||||
|
|
||||||
|
@ -36,7 +38,6 @@ import (
|
||||||
"maunium.net/go/mautrix-whatsapp/config"
|
"maunium.net/go/mautrix-whatsapp/config"
|
||||||
"maunium.net/go/mautrix-whatsapp/database"
|
"maunium.net/go/mautrix-whatsapp/database"
|
||||||
"maunium.net/go/mautrix-whatsapp/database/upgrades"
|
"maunium.net/go/mautrix-whatsapp/database/upgrades"
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -137,14 +138,14 @@ type Bridge struct {
|
||||||
Metrics *MetricsHandler
|
Metrics *MetricsHandler
|
||||||
|
|
||||||
usersByMXID map[id.UserID]*User
|
usersByMXID map[id.UserID]*User
|
||||||
usersByJID map[types.WhatsAppID]*User
|
usersByJID map[whatsapp.JID]*User
|
||||||
usersLock sync.Mutex
|
usersLock sync.Mutex
|
||||||
managementRooms map[id.RoomID]*User
|
managementRooms map[id.RoomID]*User
|
||||||
managementRoomsLock sync.Mutex
|
managementRoomsLock sync.Mutex
|
||||||
portalsByMXID map[id.RoomID]*Portal
|
portalsByMXID map[id.RoomID]*Portal
|
||||||
portalsByJID map[database.PortalKey]*Portal
|
portalsByJID map[database.PortalKey]*Portal
|
||||||
portalsLock sync.Mutex
|
portalsLock sync.Mutex
|
||||||
puppets map[types.WhatsAppID]*Puppet
|
puppets map[whatsapp.JID]*Puppet
|
||||||
puppetsByCustomMXID map[id.UserID]*Puppet
|
puppetsByCustomMXID map[id.UserID]*Puppet
|
||||||
puppetsLock sync.Mutex
|
puppetsLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
@ -163,11 +164,11 @@ type Crypto interface {
|
||||||
func NewBridge() *Bridge {
|
func NewBridge() *Bridge {
|
||||||
bridge := &Bridge{
|
bridge := &Bridge{
|
||||||
usersByMXID: make(map[id.UserID]*User),
|
usersByMXID: make(map[id.UserID]*User),
|
||||||
usersByJID: make(map[types.WhatsAppID]*User),
|
usersByJID: make(map[whatsapp.JID]*User),
|
||||||
managementRooms: make(map[id.RoomID]*User),
|
managementRooms: make(map[id.RoomID]*User),
|
||||||
portalsByMXID: make(map[id.RoomID]*Portal),
|
portalsByMXID: make(map[id.RoomID]*Portal),
|
||||||
portalsByJID: make(map[database.PortalKey]*Portal),
|
portalsByJID: make(map[database.PortalKey]*Portal),
|
||||||
puppets: make(map[types.WhatsAppID]*Puppet),
|
puppets: make(map[whatsapp.JID]*Puppet),
|
||||||
puppetsByCustomMXID: make(map[id.UserID]*Puppet),
|
puppetsByCustomMXID: make(map[id.UserID]*Puppet),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,11 +384,9 @@ func (bridge *Bridge) Stop() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bridge.Log.Debugln("Disconnecting", user.MXID)
|
bridge.Log.Debugln("Disconnecting", user.MXID)
|
||||||
sess, err := user.Conn.Disconnect()
|
err := user.Conn.Disconnect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bridge.Log.Errorfln("Error while disconnecting %s: %v", user.MXID, err)
|
bridge.Log.Errorfln("Error while disconnecting %s: %v", user.MXID, err)
|
||||||
} else {
|
|
||||||
user.SetSession(&sess)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,6 +254,10 @@ func (mx *MatrixHandler) HandleMembership(evt *event.Event) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mx.shouldIgnoreEvent(evt) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
user := mx.bridge.GetUserByMXID(evt.Sender)
|
user := mx.bridge.GetUserByMXID(evt.Sender)
|
||||||
if user == nil || !user.Whitelisted || !user.IsConnected() {
|
if user == nil || !user.Whitelisted || !user.IsConnected() {
|
||||||
return
|
return
|
||||||
|
|
21
metrics.go
21
metrics.go
|
@ -27,11 +27,12 @@ import (
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
log "maunium.net/go/maulogger/v2"
|
log "maunium.net/go/maulogger/v2"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
|
||||||
"maunium.net/go/mautrix/event"
|
"maunium.net/go/mautrix/event"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/database"
|
"maunium.net/go/mautrix-whatsapp/database"
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MetricsHandler struct {
|
type MetricsHandler struct {
|
||||||
|
@ -56,11 +57,11 @@ type MetricsHandler struct {
|
||||||
unencryptedPrivateCount prometheus.Gauge
|
unencryptedPrivateCount prometheus.Gauge
|
||||||
|
|
||||||
connected prometheus.Gauge
|
connected prometheus.Gauge
|
||||||
connectedState map[types.WhatsAppID]bool
|
connectedState map[whatsapp.JID]bool
|
||||||
loggedIn prometheus.Gauge
|
loggedIn prometheus.Gauge
|
||||||
loggedInState map[types.WhatsAppID]bool
|
loggedInState map[whatsapp.JID]bool
|
||||||
syncLocked prometheus.Gauge
|
syncLocked prometheus.Gauge
|
||||||
syncLockedState map[types.WhatsAppID]bool
|
syncLockedState map[whatsapp.JID]bool
|
||||||
bufferLength *prometheus.GaugeVec
|
bufferLength *prometheus.GaugeVec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,17 +110,17 @@ func NewMetricsHandler(address string, log log.Logger, db *database.Database) *M
|
||||||
Name: "bridge_logged_in",
|
Name: "bridge_logged_in",
|
||||||
Help: "Users logged into the bridge",
|
Help: "Users logged into the bridge",
|
||||||
}),
|
}),
|
||||||
loggedInState: make(map[types.WhatsAppID]bool),
|
loggedInState: make(map[whatsapp.JID]bool),
|
||||||
connected: promauto.NewGauge(prometheus.GaugeOpts{
|
connected: promauto.NewGauge(prometheus.GaugeOpts{
|
||||||
Name: "bridge_connected",
|
Name: "bridge_connected",
|
||||||
Help: "Bridge users connected to WhatsApp",
|
Help: "Bridge users connected to WhatsApp",
|
||||||
}),
|
}),
|
||||||
connectedState: make(map[types.WhatsAppID]bool),
|
connectedState: make(map[whatsapp.JID]bool),
|
||||||
syncLocked: promauto.NewGauge(prometheus.GaugeOpts{
|
syncLocked: promauto.NewGauge(prometheus.GaugeOpts{
|
||||||
Name: "bridge_sync_locked",
|
Name: "bridge_sync_locked",
|
||||||
Help: "Bridge users locked in post-login sync",
|
Help: "Bridge users locked in post-login sync",
|
||||||
}),
|
}),
|
||||||
syncLockedState: make(map[types.WhatsAppID]bool),
|
syncLockedState: make(map[whatsapp.JID]bool),
|
||||||
bufferLength: promauto.NewGaugeVec(prometheus.GaugeOpts{
|
bufferLength: promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
Name: "bridge_buffer_size",
|
Name: "bridge_buffer_size",
|
||||||
Help: "Number of messages in buffer",
|
Help: "Number of messages in buffer",
|
||||||
|
@ -149,7 +150,7 @@ func (mh *MetricsHandler) TrackDisconnection(userID id.UserID) {
|
||||||
mh.disconnections.With(prometheus.Labels{"user_id": string(userID)}).Inc()
|
mh.disconnections.With(prometheus.Labels{"user_id": string(userID)}).Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mh *MetricsHandler) TrackLoginState(jid types.WhatsAppID, loggedIn bool) {
|
func (mh *MetricsHandler) TrackLoginState(jid whatsapp.JID, loggedIn bool) {
|
||||||
if !mh.running {
|
if !mh.running {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -164,7 +165,7 @@ func (mh *MetricsHandler) TrackLoginState(jid types.WhatsAppID, loggedIn bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mh *MetricsHandler) TrackConnectionState(jid types.WhatsAppID, connected bool) {
|
func (mh *MetricsHandler) TrackConnectionState(jid whatsapp.JID, connected bool) {
|
||||||
if !mh.running {
|
if !mh.running {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -179,7 +180,7 @@ func (mh *MetricsHandler) TrackConnectionState(jid types.WhatsAppID, connected b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mh *MetricsHandler) TrackSyncLock(jid types.WhatsAppID, locked bool) {
|
func (mh *MetricsHandler) TrackSyncLock(jid whatsapp.JID, locked bool) {
|
||||||
if !mh.running {
|
if !mh.running {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
206
portal.go
206
portal.go
|
@ -40,25 +40,26 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "maunium.net/go/maulogger/v2"
|
|
||||||
|
|
||||||
"maunium.net/go/mautrix/crypto/attachment"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
"github.com/Rhymen/go-whatsapp"
|
||||||
waProto "github.com/Rhymen/go-whatsapp/binary/proto"
|
waProto "github.com/Rhymen/go-whatsapp/binary/proto"
|
||||||
|
|
||||||
|
log "maunium.net/go/maulogger/v2"
|
||||||
"maunium.net/go/mautrix"
|
"maunium.net/go/mautrix"
|
||||||
"maunium.net/go/mautrix/appservice"
|
"maunium.net/go/mautrix/appservice"
|
||||||
|
"maunium.net/go/mautrix/crypto/attachment"
|
||||||
"maunium.net/go/mautrix/event"
|
"maunium.net/go/mautrix/event"
|
||||||
"maunium.net/go/mautrix/format"
|
"maunium.net/go/mautrix/format"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
"maunium.net/go/mautrix/pushrules"
|
"maunium.net/go/mautrix/pushrules"
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/database"
|
"maunium.net/go/mautrix-whatsapp/database"
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
|
||||||
"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const StatusBroadcastTopic = "WhatsApp status updates from your contacts"
|
||||||
|
const StatusBroadcastName = "WhatsApp Status Broadcast"
|
||||||
|
const BroadcastTopic = "WhatsApp broadcast list"
|
||||||
|
const UnnamedBroadcastName = "Unnamed broadcast list"
|
||||||
|
|
||||||
func (bridge *Bridge) GetPortalByMXID(mxid id.RoomID) *Portal {
|
func (bridge *Bridge) GetPortalByMXID(mxid id.RoomID) *Portal {
|
||||||
bridge.portalsLock.Lock()
|
bridge.portalsLock.Lock()
|
||||||
defer bridge.portalsLock.Unlock()
|
defer bridge.portalsLock.Unlock()
|
||||||
|
@ -83,7 +84,7 @@ func (bridge *Bridge) GetAllPortals() []*Portal {
|
||||||
return bridge.dbPortalsToPortals(bridge.DB.Portal.GetAll())
|
return bridge.dbPortalsToPortals(bridge.DB.Portal.GetAll())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) GetAllPortalsByJID(jid types.WhatsAppID) []*Portal {
|
func (bridge *Bridge) GetAllPortalsByJID(jid whatsapp.JID) []*Portal {
|
||||||
return bridge.dbPortalsToPortals(bridge.DB.Portal.GetAllByJID(jid))
|
return bridge.dbPortalsToPortals(bridge.DB.Portal.GetAllByJID(jid))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +132,7 @@ func (bridge *Bridge) NewManualPortal(key database.PortalKey) *Portal {
|
||||||
bridge: bridge,
|
bridge: bridge,
|
||||||
log: bridge.Log.Sub(fmt.Sprintf("Portal/%s", key)),
|
log: bridge.Log.Sub(fmt.Sprintf("Portal/%s", key)),
|
||||||
|
|
||||||
recentlyHandled: [recentlyHandledLength]types.WhatsAppMessageID{},
|
recentlyHandled: [recentlyHandledLength]whatsapp.MessageID{},
|
||||||
|
|
||||||
messages: make(chan PortalMessage, bridge.Config.Bridge.PortalMessageBuffer),
|
messages: make(chan PortalMessage, bridge.Config.Bridge.PortalMessageBuffer),
|
||||||
}
|
}
|
||||||
|
@ -146,7 +147,7 @@ func (bridge *Bridge) NewPortal(dbPortal *database.Portal) *Portal {
|
||||||
bridge: bridge,
|
bridge: bridge,
|
||||||
log: bridge.Log.Sub(fmt.Sprintf("Portal/%s", dbPortal.Key)),
|
log: bridge.Log.Sub(fmt.Sprintf("Portal/%s", dbPortal.Key)),
|
||||||
|
|
||||||
recentlyHandled: [recentlyHandledLength]types.WhatsAppMessageID{},
|
recentlyHandled: [recentlyHandledLength]whatsapp.MessageID{},
|
||||||
|
|
||||||
messages: make(chan PortalMessage, bridge.Config.Bridge.PortalMessageBuffer),
|
messages: make(chan PortalMessage, bridge.Config.Bridge.PortalMessageBuffer),
|
||||||
}
|
}
|
||||||
|
@ -171,7 +172,7 @@ type Portal struct {
|
||||||
|
|
||||||
roomCreateLock sync.Mutex
|
roomCreateLock sync.Mutex
|
||||||
|
|
||||||
recentlyHandled [recentlyHandledLength]types.WhatsAppMessageID
|
recentlyHandled [recentlyHandledLength]whatsapp.MessageID
|
||||||
recentlyHandledLock sync.Mutex
|
recentlyHandledLock sync.Mutex
|
||||||
recentlyHandledIndex uint8
|
recentlyHandledIndex uint8
|
||||||
|
|
||||||
|
@ -184,6 +185,7 @@ type Portal struct {
|
||||||
messages chan PortalMessage
|
messages chan PortalMessage
|
||||||
|
|
||||||
isPrivate *bool
|
isPrivate *bool
|
||||||
|
isBroadcast *bool
|
||||||
hasRelaybot *bool
|
hasRelaybot *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +258,7 @@ func (portal *Portal) handleMessage(msg PortalMessage, isBackfill bool) {
|
||||||
portal.HandleLocationMessage(msg.source, data)
|
portal.HandleLocationMessage(msg.source, data)
|
||||||
case whatsapp.StubMessage:
|
case whatsapp.StubMessage:
|
||||||
portal.HandleStubMessage(msg.source, data, isBackfill)
|
portal.HandleStubMessage(msg.source, data, isBackfill)
|
||||||
case whatsappExt.MessageRevocation:
|
case whatsapp.MessageRevocation:
|
||||||
portal.HandleMessageRevoke(msg.source, data)
|
portal.HandleMessageRevoke(msg.source, data)
|
||||||
case FakeMessage:
|
case FakeMessage:
|
||||||
portal.HandleFakeMessage(msg.source, data)
|
portal.HandleFakeMessage(msg.source, data)
|
||||||
|
@ -265,7 +267,7 @@ func (portal *Portal) handleMessage(msg PortalMessage, isBackfill bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) isRecentlyHandled(id types.WhatsAppMessageID) bool {
|
func (portal *Portal) isRecentlyHandled(id whatsapp.MessageID) bool {
|
||||||
start := portal.recentlyHandledIndex
|
start := portal.recentlyHandledIndex
|
||||||
for i := start; i != start; i = (i - 1) % recentlyHandledLength {
|
for i := start; i != start; i = (i - 1) % recentlyHandledLength {
|
||||||
if portal.recentlyHandled[i] == id {
|
if portal.recentlyHandled[i] == id {
|
||||||
|
@ -275,7 +277,7 @@ func (portal *Portal) isRecentlyHandled(id types.WhatsAppMessageID) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) isDuplicate(id types.WhatsAppMessageID) bool {
|
func (portal *Portal) isDuplicate(id whatsapp.MessageID) bool {
|
||||||
msg := portal.bridge.DB.Message.GetByJID(portal.Key, id)
|
msg := portal.bridge.DB.Message.GetByJID(portal.Key, id)
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
return true
|
return true
|
||||||
|
@ -287,7 +289,7 @@ func init() {
|
||||||
gob.Register(&waProto.Message{})
|
gob.Register(&waProto.Message{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) markHandled(source *User, message *waProto.WebMessageInfo, mxid id.EventID) {
|
func (portal *Portal) markHandled(source *User, message *waProto.WebMessageInfo, mxid id.EventID, isSent bool) *database.Message {
|
||||||
msg := portal.bridge.DB.Message.New()
|
msg := portal.bridge.DB.Message.New()
|
||||||
msg.Chat = portal.Key
|
msg.Chat = portal.Key
|
||||||
msg.JID = message.GetKey().GetId()
|
msg.JID = message.GetKey().GetId()
|
||||||
|
@ -304,6 +306,7 @@ func (portal *Portal) markHandled(source *User, message *waProto.WebMessageInfo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msg.Content = message.Message
|
msg.Content = message.Message
|
||||||
|
msg.Sent = isSent
|
||||||
msg.Insert()
|
msg.Insert()
|
||||||
|
|
||||||
portal.recentlyHandledLock.Lock()
|
portal.recentlyHandledLock.Lock()
|
||||||
|
@ -311,6 +314,7 @@ func (portal *Portal) markHandled(source *User, message *waProto.WebMessageInfo,
|
||||||
portal.recentlyHandledIndex = (portal.recentlyHandledIndex + 1) % recentlyHandledLength
|
portal.recentlyHandledIndex = (portal.recentlyHandledIndex + 1) % recentlyHandledLength
|
||||||
portal.recentlyHandledLock.Unlock()
|
portal.recentlyHandledLock.Unlock()
|
||||||
portal.recentlyHandled[index] = msg.JID
|
portal.recentlyHandled[index] = msg.JID
|
||||||
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) getMessageIntent(user *User, info whatsapp.MessageInfo) *appservice.IntentAPI {
|
func (portal *Portal) getMessageIntent(user *User, info whatsapp.MessageInfo) *appservice.IntentAPI {
|
||||||
|
@ -350,19 +354,56 @@ func (portal *Portal) startHandling(source *User, info whatsapp.MessageInfo) *ap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) finishHandling(source *User, message *waProto.WebMessageInfo, mxid id.EventID) {
|
func (portal *Portal) finishHandling(source *User, message *waProto.WebMessageInfo, mxid id.EventID) {
|
||||||
portal.markHandled(source, message, mxid)
|
portal.markHandled(source, message, mxid, true)
|
||||||
portal.sendDeliveryReceipt(mxid)
|
portal.sendDeliveryReceipt(mxid)
|
||||||
portal.log.Debugln("Handled message", message.GetKey().GetId(), "->", mxid)
|
portal.log.Debugln("Handled message", message.GetKey().GetId(), "->", mxid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) SyncParticipants(metadata *whatsappExt.GroupInfo) {
|
func (portal *Portal) kickExtraUsers(participantMap map[whatsapp.JID]bool) {
|
||||||
|
members, err := portal.MainIntent().JoinedMembers(portal.MXID)
|
||||||
|
if err != nil {
|
||||||
|
portal.log.Warnln("Failed to get member list:", err)
|
||||||
|
} else {
|
||||||
|
for member := range members.Joined {
|
||||||
|
jid, ok := portal.bridge.ParsePuppetMXID(member)
|
||||||
|
if ok {
|
||||||
|
_, shouldBePresent := participantMap[jid]
|
||||||
|
if !shouldBePresent {
|
||||||
|
_, err = portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{
|
||||||
|
UserID: member,
|
||||||
|
Reason: "User had left this WhatsApp chat",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
portal.log.Warnfln("Failed to kick user %s who had left: %v", member, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (portal *Portal) SyncBroadcastRecipients(metadata *whatsapp.BroadcastListInfo) {
|
||||||
|
participantMap := make(map[whatsapp.JID]bool)
|
||||||
|
for _, recipient := range metadata.Recipients {
|
||||||
|
participantMap[recipient.JID] = true
|
||||||
|
|
||||||
|
puppet := portal.bridge.GetPuppetByJID(recipient.JID)
|
||||||
|
err := puppet.DefaultIntent().EnsureJoined(portal.MXID)
|
||||||
|
if err != nil {
|
||||||
|
portal.log.Warnfln("Failed to make puppet of %s join %s: %v", recipient.JID, portal.MXID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portal.kickExtraUsers(participantMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (portal *Portal) SyncParticipants(metadata *whatsapp.GroupInfo) {
|
||||||
changed := false
|
changed := false
|
||||||
levels, err := portal.MainIntent().PowerLevels(portal.MXID)
|
levels, err := portal.MainIntent().PowerLevels(portal.MXID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
levels = portal.GetBasePowerLevels()
|
levels = portal.GetBasePowerLevels()
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
participantMap := make(map[string]bool)
|
participantMap := make(map[whatsapp.JID]bool)
|
||||||
for _, participant := range metadata.Participants {
|
for _, participant := range metadata.Participants {
|
||||||
participantMap[participant.JID] = true
|
participantMap[participant.JID] = true
|
||||||
user := portal.bridge.GetUserByJID(participant.JID)
|
user := portal.bridge.GetUserByJID(participant.JID)
|
||||||
|
@ -391,29 +432,10 @@ func (portal *Portal) SyncParticipants(metadata *whatsappExt.GroupInfo) {
|
||||||
portal.log.Errorln("Failed to change power levels:", err)
|
portal.log.Errorln("Failed to change power levels:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
members, err := portal.MainIntent().JoinedMembers(portal.MXID)
|
portal.kickExtraUsers(participantMap)
|
||||||
if err != nil {
|
|
||||||
portal.log.Warnln("Failed to get member list:", err)
|
|
||||||
} else {
|
|
||||||
for member := range members.Joined {
|
|
||||||
jid, ok := portal.bridge.ParsePuppetMXID(member)
|
|
||||||
if ok {
|
|
||||||
_, shouldBePresent := participantMap[jid]
|
|
||||||
if !shouldBePresent {
|
|
||||||
_, err := portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{
|
|
||||||
UserID: member,
|
|
||||||
Reason: "User had left this WhatsApp chat",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
portal.log.Warnfln("Failed to kick user %s who had left: %v", member, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) UpdateAvatar(user *User, avatar *whatsappExt.ProfilePicInfo, updateInfo bool) bool {
|
func (portal *Portal) UpdateAvatar(user *User, avatar *whatsapp.ProfilePicInfo, updateInfo bool) bool {
|
||||||
if avatar == nil || (avatar.Status == 0 && avatar.Tag != "remove" && len(avatar.URL) == 0) {
|
if avatar == nil || (avatar.Status == 0 && avatar.Tag != "remove" && len(avatar.URL) == 0) {
|
||||||
var err error
|
var err error
|
||||||
avatar, err = user.Conn.GetProfilePicThumb(portal.Key.JID)
|
avatar, err = user.Conn.GetProfilePicThumb(portal.Key.JID)
|
||||||
|
@ -464,7 +486,10 @@ func (portal *Portal) UpdateAvatar(user *User, avatar *whatsappExt.ProfilePicInf
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) UpdateName(name string, setBy types.WhatsAppID, intent *appservice.IntentAPI, updateInfo bool) bool {
|
func (portal *Portal) UpdateName(name string, setBy whatsapp.JID, intent *appservice.IntentAPI, updateInfo bool) bool {
|
||||||
|
if name == "" && portal.IsBroadcastList() {
|
||||||
|
name = UnnamedBroadcastName
|
||||||
|
}
|
||||||
if portal.Name != name {
|
if portal.Name != name {
|
||||||
portal.log.Debugfln("Updating name %s -> %s", portal.Name, name)
|
portal.log.Debugfln("Updating name %s -> %s", portal.Name, name)
|
||||||
portal.Name = name
|
portal.Name = name
|
||||||
|
@ -488,7 +513,7 @@ func (portal *Portal) UpdateName(name string, setBy types.WhatsAppID, intent *ap
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) UpdateTopic(topic string, setBy types.WhatsAppID, intent *appservice.IntentAPI, updateInfo bool) bool {
|
func (portal *Portal) UpdateTopic(topic string, setBy whatsapp.JID, intent *appservice.IntentAPI, updateInfo bool) bool {
|
||||||
if portal.Topic != topic {
|
if portal.Topic != topic {
|
||||||
portal.log.Debugfln("Updating topic %s -> %s", portal.Topic, topic)
|
portal.log.Debugfln("Updating topic %s -> %s", portal.Topic, topic)
|
||||||
portal.Topic = topic
|
portal.Topic = topic
|
||||||
|
@ -515,10 +540,22 @@ func (portal *Portal) UpdateTopic(topic string, setBy types.WhatsAppID, intent *
|
||||||
func (portal *Portal) UpdateMetadata(user *User) bool {
|
func (portal *Portal) UpdateMetadata(user *User) bool {
|
||||||
if portal.IsPrivateChat() {
|
if portal.IsPrivateChat() {
|
||||||
return false
|
return false
|
||||||
} else if portal.IsStatusBroadcastRoom() {
|
} else if portal.IsStatusBroadcastList() {
|
||||||
update := false
|
update := false
|
||||||
update = portal.UpdateName("WhatsApp Status Broadcast", "", nil, false) || update
|
update = portal.UpdateName(StatusBroadcastName, "", nil, false) || update
|
||||||
update = portal.UpdateTopic("WhatsApp status updates from your contacts", "", nil, false) || update
|
update = portal.UpdateTopic(StatusBroadcastTopic, "", nil, false) || update
|
||||||
|
return update
|
||||||
|
} else if portal.IsBroadcastList() {
|
||||||
|
update := false
|
||||||
|
broadcastMetadata, err := user.Conn.GetBroadcastMetadata(portal.Key.JID)
|
||||||
|
if err == nil && broadcastMetadata.Status == 200 {
|
||||||
|
portal.SyncBroadcastRecipients(broadcastMetadata)
|
||||||
|
update = portal.UpdateName(broadcastMetadata.Name, "", nil, false) || update
|
||||||
|
} else {
|
||||||
|
contact, _ := user.Conn.Store.Contacts[portal.Key.JID]
|
||||||
|
update = portal.UpdateName(contact.Name, "", nil, false) || update
|
||||||
|
}
|
||||||
|
update = portal.UpdateTopic(BroadcastTopic, "", nil, false) || update
|
||||||
return update
|
return update
|
||||||
}
|
}
|
||||||
metadata, err := user.Conn.GetGroupMetaData(portal.Key.JID)
|
metadata, err := user.Conn.GetGroupMetaData(portal.Key.JID)
|
||||||
|
@ -597,13 +634,9 @@ func (portal *Portal) Sync(user *User, contact whatsapp.Contact) {
|
||||||
portal.ensureUserInvited(user)
|
portal.ensureUserInvited(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
if portal.IsPrivateChat() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
update := false
|
update := false
|
||||||
update = portal.UpdateMetadata(user) || update
|
update = portal.UpdateMetadata(user) || update
|
||||||
if !portal.IsStatusBroadcastRoom() && portal.Avatar == "" {
|
if !portal.IsPrivateChat() && !portal.IsBroadcastList() && portal.Avatar == "" {
|
||||||
update = portal.UpdateAvatar(user, nil, false) || update
|
update = portal.UpdateAvatar(user, nil, false) || update
|
||||||
}
|
}
|
||||||
if update {
|
if update {
|
||||||
|
@ -739,6 +772,10 @@ func (portal *Portal) BackfillHistory(user *User, lastMessageTime uint64) error
|
||||||
for len(lastMessageID) > 0 {
|
for len(lastMessageID) > 0 {
|
||||||
portal.log.Debugln("Fetching 50 messages of history after", lastMessageID)
|
portal.log.Debugln("Fetching 50 messages of history after", lastMessageID)
|
||||||
resp, err := user.Conn.LoadMessagesAfter(portal.Key.JID, lastMessageID, lastMessageFromMe, 50)
|
resp, err := user.Conn.LoadMessagesAfter(portal.Key.JID, lastMessageID, lastMessageFromMe, 50)
|
||||||
|
if err == whatsapp.ErrServerRespondedWith404 {
|
||||||
|
portal.log.Warnln("Got 404 response trying to fetch messages to backfill. Fetching latest messages as fallback.")
|
||||||
|
resp, err = user.Conn.LoadMessagesBefore(portal.Key.JID, "", true, 50)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -974,7 +1011,8 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
|
||||||
|
|
||||||
portal.log.Infoln("Creating Matrix room. Info source:", user.MXID)
|
portal.log.Infoln("Creating Matrix room. Info source:", user.MXID)
|
||||||
|
|
||||||
var metadata *whatsappExt.GroupInfo
|
var metadata *whatsapp.GroupInfo
|
||||||
|
var broadcastMetadata *whatsapp.BroadcastListInfo
|
||||||
if portal.IsPrivateChat() {
|
if portal.IsPrivateChat() {
|
||||||
puppet := portal.bridge.GetPuppetByJID(portal.Key.JID)
|
puppet := portal.bridge.GetPuppetByJID(portal.Key.JID)
|
||||||
if portal.bridge.Config.Bridge.PrivateChatPortalMeta {
|
if portal.bridge.Config.Bridge.PrivateChatPortalMeta {
|
||||||
|
@ -985,9 +1023,22 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
|
||||||
portal.Name = ""
|
portal.Name = ""
|
||||||
}
|
}
|
||||||
portal.Topic = "WhatsApp private chat"
|
portal.Topic = "WhatsApp private chat"
|
||||||
} else if portal.IsStatusBroadcastRoom() {
|
} else if portal.IsStatusBroadcastList() {
|
||||||
portal.Name = "WhatsApp Status Broadcast"
|
portal.Name = "WhatsApp Status Broadcast"
|
||||||
portal.Topic = "WhatsApp status updates from your contacts"
|
portal.Topic = "WhatsApp status updates from your contacts"
|
||||||
|
} else if portal.IsBroadcastList() {
|
||||||
|
var err error
|
||||||
|
broadcastMetadata, err = user.Conn.GetBroadcastMetadata(portal.Key.JID)
|
||||||
|
if err == nil && broadcastMetadata.Status == 200 {
|
||||||
|
portal.Name = broadcastMetadata.Name
|
||||||
|
} else {
|
||||||
|
contact, _ := user.Conn.Store.Contacts[portal.Key.JID]
|
||||||
|
portal.Name = contact.Name
|
||||||
|
}
|
||||||
|
if len(portal.Name) == 0 {
|
||||||
|
portal.Name = UnnamedBroadcastName
|
||||||
|
}
|
||||||
|
portal.Topic = BroadcastTopic
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
metadata, err = user.Conn.GetGroupMetaData(portal.Key.JID)
|
metadata, err = user.Conn.GetGroupMetaData(portal.Key.JID)
|
||||||
|
@ -1076,6 +1127,9 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
|
||||||
_ = customPuppet.CustomIntent().EnsureJoined(portal.MXID)
|
_ = customPuppet.CustomIntent().EnsureJoined(portal.MXID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if broadcastMetadata != nil {
|
||||||
|
portal.SyncBroadcastRecipients(broadcastMetadata)
|
||||||
|
}
|
||||||
user.addPortalToCommunity(portal)
|
user.addPortalToCommunity(portal)
|
||||||
if portal.IsPrivateChat() {
|
if portal.IsPrivateChat() {
|
||||||
puppet := user.bridge.GetPuppetByJID(portal.Key.JID)
|
puppet := user.bridge.GetPuppetByJID(portal.Key.JID)
|
||||||
|
@ -1099,12 +1153,24 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
|
||||||
|
|
||||||
func (portal *Portal) IsPrivateChat() bool {
|
func (portal *Portal) IsPrivateChat() bool {
|
||||||
if portal.isPrivate == nil {
|
if portal.isPrivate == nil {
|
||||||
val := strings.HasSuffix(portal.Key.JID, whatsappExt.NewUserSuffix)
|
val := strings.HasSuffix(portal.Key.JID, whatsapp.NewUserSuffix)
|
||||||
portal.isPrivate = &val
|
portal.isPrivate = &val
|
||||||
}
|
}
|
||||||
return *portal.isPrivate
|
return *portal.isPrivate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (portal *Portal) IsBroadcastList() bool {
|
||||||
|
if portal.isBroadcast == nil {
|
||||||
|
val := strings.HasSuffix(portal.Key.JID, whatsapp.BroadcastSuffix)
|
||||||
|
portal.isBroadcast = &val
|
||||||
|
}
|
||||||
|
return *portal.isBroadcast
|
||||||
|
}
|
||||||
|
|
||||||
|
func (portal *Portal) IsStatusBroadcastList() bool {
|
||||||
|
return portal.Key.JID == "status@broadcast"
|
||||||
|
}
|
||||||
|
|
||||||
func (portal *Portal) HasRelaybot() bool {
|
func (portal *Portal) HasRelaybot() bool {
|
||||||
if portal.bridge.Relaybot == nil {
|
if portal.bridge.Relaybot == nil {
|
||||||
return false
|
return false
|
||||||
|
@ -1115,10 +1181,6 @@ func (portal *Portal) HasRelaybot() bool {
|
||||||
return *portal.hasRelaybot
|
return *portal.hasRelaybot
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) IsStatusBroadcastRoom() bool {
|
|
||||||
return portal.Key.JID == "status@broadcast"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (portal *Portal) MainIntent() *appservice.IntentAPI {
|
func (portal *Portal) MainIntent() *appservice.IntentAPI {
|
||||||
if portal.IsPrivateChat() {
|
if portal.IsPrivateChat() {
|
||||||
return portal.bridge.GetPuppetByJID(portal.Key.JID).DefaultIntent()
|
return portal.bridge.GetPuppetByJID(portal.Key.JID).DefaultIntent()
|
||||||
|
@ -1152,7 +1214,7 @@ func (portal *Portal) SetReply(content *event.MessageEventContent, info whatsapp
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) HandleMessageRevoke(user *User, message whatsappExt.MessageRevocation) {
|
func (portal *Portal) HandleMessageRevoke(user *User, message whatsapp.MessageRevocation) {
|
||||||
msg := portal.bridge.DB.Message.GetByJID(portal.Key, message.Id)
|
msg := portal.bridge.DB.Message.GetByJID(portal.Key, message.Id)
|
||||||
if msg == nil || msg.IsFakeMXID() {
|
if msg == nil || msg.IsFakeMXID() {
|
||||||
return
|
return
|
||||||
|
@ -1281,8 +1343,9 @@ func (portal *Portal) HandleTextMessage(source *User, message whatsapp.TextMessa
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) HandleStubMessage(source *User, message whatsapp.StubMessage, isBackfill bool) {
|
func (portal *Portal) HandleStubMessage(source *User, message whatsapp.StubMessage, isBackfill bool) {
|
||||||
if portal.bridge.Config.Bridge.ChatMetaSync {
|
if portal.bridge.Config.Bridge.ChatMetaSync && (!portal.IsBroadcastList() || isBackfill) {
|
||||||
// Chat meta sync is enabled, so we use chat update commands and full-syncs instead of message history
|
// Chat meta sync is enabled, so we use chat update commands and full-syncs instead of message history
|
||||||
|
// However, broadcast lists don't have update commands, so we handle these if it's not a backfill
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
intent := portal.startHandling(source, message.Info)
|
intent := portal.startHandling(source, message.Info)
|
||||||
|
@ -1312,9 +1375,9 @@ func (portal *Portal) HandleStubMessage(source *User, message whatsapp.StubMessa
|
||||||
eventID = portal.RestrictMessageSending(message.FirstParam == "on")
|
eventID = portal.RestrictMessageSending(message.FirstParam == "on")
|
||||||
case waProto.WebMessageInfo_GROUP_CHANGE_RESTRICT:
|
case waProto.WebMessageInfo_GROUP_CHANGE_RESTRICT:
|
||||||
eventID = portal.RestrictMetadataChanges(message.FirstParam == "on")
|
eventID = portal.RestrictMetadataChanges(message.FirstParam == "on")
|
||||||
case waProto.WebMessageInfo_GROUP_PARTICIPANT_ADD, waProto.WebMessageInfo_GROUP_PARTICIPANT_INVITE:
|
case waProto.WebMessageInfo_GROUP_PARTICIPANT_ADD, waProto.WebMessageInfo_GROUP_PARTICIPANT_INVITE, waProto.WebMessageInfo_BROADCAST_ADD:
|
||||||
portal.HandleWhatsAppInvite(senderJID, intent, message.Params)
|
portal.HandleWhatsAppInvite(senderJID, intent, message.Params)
|
||||||
case waProto.WebMessageInfo_GROUP_PARTICIPANT_REMOVE, waProto.WebMessageInfo_GROUP_PARTICIPANT_LEAVE:
|
case waProto.WebMessageInfo_GROUP_PARTICIPANT_REMOVE, waProto.WebMessageInfo_GROUP_PARTICIPANT_LEAVE, waProto.WebMessageInfo_BROADCAST_REMOVE:
|
||||||
portal.HandleWhatsAppKick(source, senderJID, message.Params)
|
portal.HandleWhatsAppKick(source, senderJID, message.Params)
|
||||||
case waProto.WebMessageInfo_GROUP_PARTICIPANT_PROMOTE:
|
case waProto.WebMessageInfo_GROUP_PARTICIPANT_PROMOTE:
|
||||||
eventID = portal.ChangeAdminStatus(message.Params, true)
|
eventID = portal.ChangeAdminStatus(message.Params, true)
|
||||||
|
@ -1326,7 +1389,7 @@ func (portal *Portal) HandleStubMessage(source *User, message whatsapp.StubMessa
|
||||||
if len(eventID) == 0 {
|
if len(eventID) == 0 {
|
||||||
eventID = id.EventID(fmt.Sprintf("net.maunium.whatsapp.fake::%s", message.Info.Id))
|
eventID = id.EventID(fmt.Sprintf("net.maunium.whatsapp.fake::%s", message.Info.Id))
|
||||||
}
|
}
|
||||||
portal.markHandled(source, message.Info.Source, eventID)
|
portal.markHandled(source, message.Info.Source, eventID, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) HandleLocationMessage(source *User, message whatsapp.LocationMessage) {
|
func (portal *Portal) HandleLocationMessage(source *User, message whatsapp.LocationMessage) {
|
||||||
|
@ -1496,6 +1559,7 @@ func (portal *Portal) HandleWhatsAppKick(source *User, senderJID string, jids []
|
||||||
puppet := portal.bridge.GetPuppetByJID(jid)
|
puppet := portal.bridge.GetPuppetByJID(jid)
|
||||||
portal.removeUser(puppet.JID == sender.JID, senderIntent, puppet.MXID, puppet.DefaultIntent())
|
portal.removeUser(puppet.JID == sender.JID, senderIntent, puppet.MXID, puppet.DefaultIntent())
|
||||||
|
|
||||||
|
if !portal.IsBroadcastList() {
|
||||||
user := portal.bridge.GetUserByJID(jid)
|
user := portal.bridge.GetUserByJID(jid)
|
||||||
if user != nil {
|
if user != nil {
|
||||||
var customIntent *appservice.IntentAPI
|
var customIntent *appservice.IntentAPI
|
||||||
|
@ -1505,6 +1569,7 @@ func (portal *Portal) HandleWhatsAppKick(source *User, senderJID string, jids []
|
||||||
portal.removeUser(puppet.JID == sender.JID, senderIntent, user.MXID, customIntent)
|
portal.removeUser(puppet.JID == sender.JID, senderIntent, user.MXID, customIntent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) HandleWhatsAppInvite(senderJID string, intent *appservice.IntentAPI, jids []string) {
|
func (portal *Portal) HandleWhatsAppInvite(senderJID string, intent *appservice.IntentAPI, jids []string) {
|
||||||
|
@ -1769,7 +1834,7 @@ func (portal *Portal) convertGifToVideo(gif []byte) ([]byte, error) {
|
||||||
"-pix_fmt", "yuv420p", "-c:v", "libx264", "-movflags", "+faststart",
|
"-pix_fmt", "yuv420p", "-c:v", "libx264", "-movflags", "+faststart",
|
||||||
"-filter:v", "crop='floor(in_w/2)*2:floor(in_h/2)*2'",
|
"-filter:v", "crop='floor(in_w/2)*2:floor(in_h/2)*2'",
|
||||||
outputFileName)
|
outputFileName)
|
||||||
vcLog := portal.log.Sub("VideoConverter").WithDefaultLevel(log.LevelWarn)
|
vcLog := portal.log.Sub("VideoConverter").Writer(log.LevelWarn)
|
||||||
cmd.Stdout = vcLog
|
cmd.Stdout = vcLog
|
||||||
cmd.Stderr = vcLog
|
cmd.Stderr = vcLog
|
||||||
|
|
||||||
|
@ -1794,7 +1859,7 @@ func (portal *Portal) convertGifToVideo(gif []byte) ([]byte, error) {
|
||||||
|
|
||||||
func (portal *Portal) preprocessMatrixMedia(sender *User, relaybotFormatted bool, content *event.MessageEventContent, eventID id.EventID, mediaType whatsapp.MediaType) *MediaUpload {
|
func (portal *Portal) preprocessMatrixMedia(sender *User, relaybotFormatted bool, content *event.MessageEventContent, eventID id.EventID, mediaType whatsapp.MediaType) *MediaUpload {
|
||||||
var caption string
|
var caption string
|
||||||
var mentionedJIDs []types.WhatsAppID
|
var mentionedJIDs []whatsapp.JID
|
||||||
if relaybotFormatted {
|
if relaybotFormatted {
|
||||||
caption, mentionedJIDs = portal.bridge.Formatter.ParseMatrix(content.FormattedBody)
|
caption, mentionedJIDs = portal.bridge.Formatter.ParseMatrix(content.FormattedBody)
|
||||||
}
|
}
|
||||||
|
@ -1851,7 +1916,7 @@ func (portal *Portal) preprocessMatrixMedia(sender *User, relaybotFormatted bool
|
||||||
|
|
||||||
type MediaUpload struct {
|
type MediaUpload struct {
|
||||||
Caption string
|
Caption string
|
||||||
MentionedJIDs []types.WhatsAppID
|
MentionedJIDs []whatsapp.JID
|
||||||
URL string
|
URL string
|
||||||
MediaKey []byte
|
MediaKey []byte
|
||||||
FileEncSHA256 []byte
|
FileEncSHA256 []byte
|
||||||
|
@ -2091,12 +2156,12 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
|
||||||
if info == nil {
|
if info == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
portal.markHandled(sender, info, evt.ID)
|
dbMsg := portal.markHandled(sender, info, evt.ID, false)
|
||||||
portal.log.Debugln("Sending event", evt.ID, "to WhatsApp", info.Key.GetId())
|
portal.log.Debugln("Sending event", evt.ID, "to WhatsApp", info.Key.GetId())
|
||||||
portal.sendRaw(sender, evt, info)
|
portal.sendRaw(sender, evt, info, dbMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) sendRaw(sender *User, evt *event.Event, info *waProto.WebMessageInfo) {
|
func (portal *Portal) sendRaw(sender *User, evt *event.Event, info *waProto.WebMessageInfo, dbMsg *database.Message) {
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
go sender.Conn.SendRaw(info, errChan)
|
go sender.Conn.SendRaw(info, errChan)
|
||||||
|
|
||||||
|
@ -2116,16 +2181,11 @@ func (portal *Portal) sendRaw(sender *User, evt *event.Event, info *waProto.WebM
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
portal.log.Errorfln("Error handling Matrix event %s: %v", evt.ID, err)
|
portal.log.Errorfln("Error handling Matrix event %s: %v", evt.ID, err)
|
||||||
var statusResp whatsapp.StatusResponse
|
|
||||||
if errors.As(err, &statusResp) && statusResp.Status == 599 {
|
|
||||||
portal.log.Debugfln("599 status response extra data: %+v", statusResp.Extra)
|
|
||||||
portal.sendErrorMessage(fmt.Sprintf("%v. Please try again after a few minutes", err))
|
|
||||||
} else {
|
|
||||||
portal.sendErrorMessage(err.Error())
|
portal.sendErrorMessage(err.Error())
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
portal.log.Debugfln("Handled Matrix event %s", evt.ID)
|
portal.log.Debugfln("Handled Matrix event %s", evt.ID)
|
||||||
portal.sendDeliveryReceipt(evt.ID)
|
portal.sendDeliveryReceipt(evt.ID)
|
||||||
|
dbMsg.MarkSent()
|
||||||
}
|
}
|
||||||
if errorEventID != "" {
|
if errorEventID != "" {
|
||||||
_, err = portal.MainIntent().RedactEvent(portal.MXID, errorEventID)
|
_, err = portal.MainIntent().RedactEvent(portal.MXID, errorEventID)
|
||||||
|
@ -2322,7 +2382,7 @@ func (portal *Portal) HandleMatrixMeta(sender *User, evt *event.Event) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
portal.Topic = content.Topic
|
portal.Topic = content.Topic
|
||||||
resp, err = sender.Conn.UpdateGroupDescription(portal.Key.JID, content.Topic)
|
resp, err = sender.Conn.UpdateGroupDescription(sender.JID, portal.Key.JID, content.Topic)
|
||||||
case *event.RoomAvatarEventContent:
|
case *event.RoomAvatarEventContent:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,11 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
log "maunium.net/go/maulogger/v2"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
"github.com/Rhymen/go-whatsapp"
|
||||||
"maunium.net/go/mautrix/id"
|
|
||||||
|
|
||||||
whatsappExt "maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
log "maunium.net/go/maulogger/v2"
|
||||||
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProvisioningAPI struct {
|
type ProvisioningAPI struct {
|
||||||
|
@ -126,7 +125,7 @@ func (prov *ProvisioningAPI) DeleteSession(w http.ResponseWriter, r *http.Reques
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user.Disconnect()
|
user.DeleteConnection()
|
||||||
user.SetSession(nil)
|
user.SetSession(nil)
|
||||||
jsonResponse(w, http.StatusOK, Response{true, "Session information purged"})
|
jsonResponse(w, http.StatusOK, Response{true, "Session information purged"})
|
||||||
}
|
}
|
||||||
|
@ -140,7 +139,7 @@ func (prov *ProvisioningAPI) DeleteConnection(w http.ResponseWriter, r *http.Req
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user.Disconnect()
|
user.DeleteConnection()
|
||||||
jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp and connection deleted"})
|
jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp and connection deleted"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +152,7 @@ func (prov *ProvisioningAPI) Disconnect(w http.ResponseWriter, r *http.Request)
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sess, err := user.Conn.Disconnect()
|
err := user.Conn.Disconnect()
|
||||||
if err == whatsapp.ErrNotConnected {
|
if err == whatsapp.ErrNotConnected {
|
||||||
jsonResponse(w, http.StatusNotFound, Error{
|
jsonResponse(w, http.StatusNotFound, Error{
|
||||||
Error: "You were not connected",
|
Error: "You were not connected",
|
||||||
|
@ -167,8 +166,6 @@ func (prov *ProvisioningAPI) Disconnect(w http.ResponseWriter, r *http.Request)
|
||||||
ErrCode: err.Error(),
|
ErrCode: err.Error(),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
user.SetSession(&sess)
|
|
||||||
}
|
}
|
||||||
user.bridge.Metrics.TrackConnectionState(user.JID, false)
|
user.bridge.Metrics.TrackConnectionState(user.JID, false)
|
||||||
jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp"})
|
jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp"})
|
||||||
|
@ -191,25 +188,21 @@ func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
user.log.Debugln("Received /reconnect request, disconnecting")
|
user.log.Debugln("Received /reconnect request, disconnecting")
|
||||||
wasConnected := true
|
wasConnected := true
|
||||||
sess, err := user.Conn.Disconnect()
|
err := user.Conn.Disconnect()
|
||||||
if err == whatsapp.ErrNotConnected {
|
if err == whatsapp.ErrNotConnected {
|
||||||
wasConnected = false
|
wasConnected = false
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
user.log.Warnln("Error while disconnecting:", err)
|
user.log.Warnln("Error while disconnecting:", err)
|
||||||
} else {
|
|
||||||
user.SetSession(&sess)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user.log.Debugln("Restoring session for /reconnect")
|
user.log.Debugln("Restoring session for /reconnect")
|
||||||
err = user.Conn.Restore(true)
|
err = user.Conn.Restore(true, r.Context())
|
||||||
user.log.Debugfln("Restore session for /reconnect responded with %v", err)
|
user.log.Debugfln("Restore session for /reconnect responded with %v", err)
|
||||||
if err == whatsapp.ErrInvalidSession {
|
if err == whatsapp.ErrInvalidSession {
|
||||||
if user.Session != nil {
|
if user.Session != nil {
|
||||||
user.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...")
|
user.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...")
|
||||||
sess, err = user.Conn.RestoreWithSession(*user.Session)
|
user.Conn.SetSession(*user.Session)
|
||||||
if err == nil {
|
err = user.Conn.Restore(true, r.Context())
|
||||||
user.SetSession(&sess)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
jsonResponse(w, http.StatusForbidden, Error{
|
jsonResponse(w, http.StatusForbidden, Error{
|
||||||
Error: "You're not logged in",
|
Error: "You're not logged in",
|
||||||
|
@ -217,7 +210,8 @@ func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if err == whatsapp.ErrLoginInProgress {
|
}
|
||||||
|
if err == whatsapp.ErrLoginInProgress {
|
||||||
jsonResponse(w, http.StatusConflict, Error{
|
jsonResponse(w, http.StatusConflict, Error{
|
||||||
Error: "A login or reconnection is already in progress.",
|
Error: "A login or reconnection is already in progress.",
|
||||||
ErrCode: "login in progress",
|
ErrCode: "login in progress",
|
||||||
|
@ -232,23 +226,14 @@ func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user.log.Warnln("Error while reconnecting:", err)
|
user.log.Warnln("Error while reconnecting:", err)
|
||||||
if errors.Is(err, whatsapp.ErrRestoreSessionTimeout) {
|
jsonResponse(w, http.StatusInternalServerError, Error{
|
||||||
jsonResponse(w, http.StatusForbidden, Error{
|
|
||||||
Error: "Reconnection timed out. Is WhatsApp on your phone reachable?",
|
|
||||||
ErrCode: err.Error(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
jsonResponse(w, http.StatusForbidden, Error{
|
|
||||||
Error: fmt.Sprintf("Unknown error while reconnecting: %v", err),
|
Error: fmt.Sprintf("Unknown error while reconnecting: %v", err),
|
||||||
ErrCode: err.Error(),
|
ErrCode: err.Error(),
|
||||||
})
|
})
|
||||||
}
|
|
||||||
user.log.Debugln("Disconnecting due to failed session restore in reconnect command...")
|
user.log.Debugln("Disconnecting due to failed session restore in reconnect command...")
|
||||||
sess, err := user.Conn.Disconnect()
|
err = user.Conn.Disconnect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err)
|
user.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err)
|
||||||
} else {
|
|
||||||
user.SetSession(&sess)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -340,7 +325,7 @@ func (prov *ProvisioningAPI) Logout(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
user.Disconnect()
|
user.DeleteConnection()
|
||||||
}
|
}
|
||||||
|
|
||||||
user.bridge.Metrics.TrackConnectionState(user.JID, false)
|
user.bridge.Metrics.TrackConnectionState(user.JID, false)
|
||||||
|
@ -408,7 +393,7 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
|
||||||
})
|
})
|
||||||
|
|
||||||
user.log.Debugln("Starting login via provisioning API")
|
user.log.Debugln("Starting login via provisioning API")
|
||||||
session, err := user.Conn.LoginWithRetry(qrChan, ctx, user.bridge.Config.Bridge.LoginQRRegenCount)
|
session, jid, err := user.Conn.Login(qrChan, ctx, user.bridge.Config.Bridge.LoginQRRegenCount)
|
||||||
qrChan <- "stop"
|
qrChan <- "stop"
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var msg string
|
var msg string
|
||||||
|
@ -420,7 +405,7 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
|
||||||
msg = "QR code scan timed out. Please try again."
|
msg = "QR code scan timed out. Please try again."
|
||||||
} else if errors.Is(err, whatsapp.ErrInvalidWebsocket) {
|
} else if errors.Is(err, whatsapp.ErrInvalidWebsocket) {
|
||||||
msg = "WhatsApp connection error. Please try again."
|
msg = "WhatsApp connection error. Please try again."
|
||||||
user.Disconnect()
|
// TODO might need to make sure it reconnects?
|
||||||
} else {
|
} else {
|
||||||
msg = fmt.Sprintf("Unknown error while logging in: %v", err)
|
msg = fmt.Sprintf("Unknown error while logging in: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -431,9 +416,9 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user.log.Debugln("Successful login via provisioning API")
|
user.log.Debugln("Successful login as", jid, "via provisioning API")
|
||||||
user.ConnectionErrors = 0
|
user.ConnectionErrors = 0
|
||||||
user.JID = strings.Replace(user.Conn.Info.Wid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, 1)
|
user.JID = strings.Replace(jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1)
|
||||||
user.addToJIDMap()
|
user.addToJIDMap()
|
||||||
user.SetSession(&session)
|
user.SetSession(&session)
|
||||||
_ = c.WriteJSON(map[string]interface{}{
|
_ = c.WriteJSON(map[string]interface{}{
|
||||||
|
|
23
puppet.go
23
puppet.go
|
@ -22,21 +22,18 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "maunium.net/go/maulogger/v2"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
|
||||||
|
log "maunium.net/go/maulogger/v2"
|
||||||
"maunium.net/go/mautrix/appservice"
|
"maunium.net/go/mautrix/appservice"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/database"
|
"maunium.net/go/mautrix-whatsapp/database"
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
|
||||||
"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var userIDRegex *regexp.Regexp
|
var userIDRegex *regexp.Regexp
|
||||||
|
|
||||||
func (bridge *Bridge) ParsePuppetMXID(mxid id.UserID) (types.WhatsAppID, bool) {
|
func (bridge *Bridge) ParsePuppetMXID(mxid id.UserID) (whatsapp.JID, bool) {
|
||||||
if userIDRegex == nil {
|
if userIDRegex == nil {
|
||||||
userIDRegex = regexp.MustCompile(fmt.Sprintf("^@%s:%s$",
|
userIDRegex = regexp.MustCompile(fmt.Sprintf("^@%s:%s$",
|
||||||
bridge.Config.Bridge.FormatUsername("([0-9]+)"),
|
bridge.Config.Bridge.FormatUsername("([0-9]+)"),
|
||||||
|
@ -47,7 +44,7 @@ func (bridge *Bridge) ParsePuppetMXID(mxid id.UserID) (types.WhatsAppID, bool) {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
jid := types.WhatsAppID(match[1] + whatsappExt.NewUserSuffix)
|
jid := whatsapp.JID(match[1] + whatsapp.NewUserSuffix)
|
||||||
return jid, true
|
return jid, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +57,7 @@ func (bridge *Bridge) GetPuppetByMXID(mxid id.UserID) *Puppet {
|
||||||
return bridge.GetPuppetByJID(jid)
|
return bridge.GetPuppetByJID(jid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) GetPuppetByJID(jid types.WhatsAppID) *Puppet {
|
func (bridge *Bridge) GetPuppetByJID(jid whatsapp.JID) *Puppet {
|
||||||
bridge.puppetsLock.Lock()
|
bridge.puppetsLock.Lock()
|
||||||
defer bridge.puppetsLock.Unlock()
|
defer bridge.puppetsLock.Unlock()
|
||||||
puppet, ok := bridge.puppets[jid]
|
puppet, ok := bridge.puppets[jid]
|
||||||
|
@ -125,12 +122,12 @@ func (bridge *Bridge) dbPuppetsToPuppets(dbPuppets []*database.Puppet) []*Puppet
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) FormatPuppetMXID(jid types.WhatsAppID) id.UserID {
|
func (bridge *Bridge) FormatPuppetMXID(jid whatsapp.JID) id.UserID {
|
||||||
return id.NewUserID(
|
return id.NewUserID(
|
||||||
bridge.Config.Bridge.FormatUsername(
|
bridge.Config.Bridge.FormatUsername(
|
||||||
strings.Replace(
|
strings.Replace(
|
||||||
jid,
|
jid,
|
||||||
whatsappExt.NewUserSuffix, "", 1)),
|
whatsapp.NewUserSuffix, "", 1)),
|
||||||
bridge.Config.Homeserver.Domain)
|
bridge.Config.Homeserver.Domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +158,7 @@ type Puppet struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (puppet *Puppet) PhoneNumber() string {
|
func (puppet *Puppet) PhoneNumber() string {
|
||||||
return strings.Replace(puppet.JID, whatsappExt.NewUserSuffix, "", 1)
|
return strings.Replace(puppet.JID, whatsapp.NewUserSuffix, "", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (puppet *Puppet) IntentFor(portal *Portal) *appservice.IntentAPI {
|
func (puppet *Puppet) IntentFor(portal *Portal) *appservice.IntentAPI {
|
||||||
|
@ -181,7 +178,7 @@ func (puppet *Puppet) DefaultIntent() *appservice.IntentAPI {
|
||||||
return puppet.bridge.AS.Intent(puppet.MXID)
|
return puppet.bridge.AS.Intent(puppet.MXID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (puppet *Puppet) UpdateAvatar(source *User, avatar *whatsappExt.ProfilePicInfo) bool {
|
func (puppet *Puppet) UpdateAvatar(source *User, avatar *whatsapp.ProfilePicInfo) bool {
|
||||||
if avatar == nil {
|
if avatar == nil {
|
||||||
var err error
|
var err error
|
||||||
avatar, err = source.Conn.GetProfilePicThumb(puppet.JID)
|
avatar, err = source.Conn.GetProfilePicThumb(puppet.JID)
|
||||||
|
@ -294,8 +291,8 @@ func (puppet *Puppet) Sync(source *User, contact whatsapp.Contact) {
|
||||||
puppet.log.Errorln("Failed to ensure registered:", err)
|
puppet.log.Errorln("Failed to ensure registered:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if contact.Jid == source.JID {
|
if contact.JID == source.JID {
|
||||||
contact.Notify = source.Conn.Info.Pushname
|
contact.Notify = source.pushName
|
||||||
}
|
}
|
||||||
|
|
||||||
update := false
|
update := false
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package types
|
|
||||||
|
|
||||||
// WhatsAppID is a WhatsApp JID.
|
|
||||||
type WhatsAppID = string
|
|
||||||
|
|
||||||
// WhatsAppMessageID is the internal ID of a WhatsApp message.
|
|
||||||
type WhatsAppMessageID = string
|
|
374
user.go
374
user.go
|
@ -17,6 +17,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -40,13 +41,11 @@ import (
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/database"
|
"maunium.net/go/mautrix-whatsapp/database"
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
|
||||||
"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
*database.User
|
*database.User
|
||||||
Conn *whatsappExt.ExtendedConn
|
Conn *whatsapp.Conn
|
||||||
|
|
||||||
bridge *Bridge
|
bridge *Bridge
|
||||||
log log.Logger
|
log log.Logger
|
||||||
|
@ -63,6 +62,7 @@ type User struct {
|
||||||
cleanDisconnection bool
|
cleanDisconnection bool
|
||||||
batteryWarningsSent int
|
batteryWarningsSent int
|
||||||
lastReconnection int64
|
lastReconnection int64
|
||||||
|
pushName string
|
||||||
|
|
||||||
chatListReceived chan struct{}
|
chatListReceived chan struct{}
|
||||||
syncPortalsDone chan struct{}
|
syncPortalsDone chan struct{}
|
||||||
|
@ -75,6 +75,7 @@ type User struct {
|
||||||
|
|
||||||
mgmtCreateLock sync.Mutex
|
mgmtCreateLock sync.Mutex
|
||||||
connLock sync.Mutex
|
connLock sync.Mutex
|
||||||
|
cancelReconnect func()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) GetUserByMXID(userID id.UserID) *User {
|
func (bridge *Bridge) GetUserByMXID(userID id.UserID) *User {
|
||||||
|
@ -91,7 +92,7 @@ func (bridge *Bridge) GetUserByMXID(userID id.UserID) *User {
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) GetUserByJID(userID types.WhatsAppID) *User {
|
func (bridge *Bridge) GetUserByJID(userID whatsapp.JID) *User {
|
||||||
bridge.usersLock.Lock()
|
bridge.usersLock.Lock()
|
||||||
defer bridge.usersLock.Unlock()
|
defer bridge.usersLock.Unlock()
|
||||||
user, ok := bridge.usersByJID[userID]
|
user, ok := bridge.usersByJID[userID]
|
||||||
|
@ -236,55 +237,56 @@ func (user *User) SetSession(session *whatsapp.Session) {
|
||||||
|
|
||||||
func (user *User) Connect(evenIfNoSession bool) bool {
|
func (user *User) Connect(evenIfNoSession bool) bool {
|
||||||
user.connLock.Lock()
|
user.connLock.Lock()
|
||||||
if user.Conn != nil && user.Conn.IsConnected() {
|
if user.Conn != nil {
|
||||||
user.connLock.Unlock()
|
user.connLock.Unlock()
|
||||||
|
if user.Conn.IsConnected() {
|
||||||
return true
|
return true
|
||||||
|
} else {
|
||||||
|
return user.RestoreSession()
|
||||||
|
}
|
||||||
} else if !evenIfNoSession && user.Session == nil {
|
} else if !evenIfNoSession && user.Session == nil {
|
||||||
user.connLock.Unlock()
|
user.connLock.Unlock()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if user.Conn != nil {
|
|
||||||
user.Disconnect()
|
|
||||||
}
|
|
||||||
user.log.Debugln("Connecting to WhatsApp")
|
user.log.Debugln("Connecting to WhatsApp")
|
||||||
timeout := time.Duration(user.bridge.Config.Bridge.ConnectionTimeout)
|
timeout := time.Duration(user.bridge.Config.Bridge.ConnectionTimeout)
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
timeout = 20
|
timeout = 20
|
||||||
}
|
}
|
||||||
conn, err := whatsapp.NewConnWithOptions(&whatsapp.Options{
|
user.Conn = whatsapp.NewConn(&whatsapp.Options{
|
||||||
Timeout: timeout * time.Second,
|
Timeout: timeout * time.Second,
|
||||||
LongClientName: user.bridge.Config.WhatsApp.OSName,
|
LongClientName: user.bridge.Config.WhatsApp.OSName,
|
||||||
ShortClientName: user.bridge.Config.WhatsApp.BrowserName,
|
ShortClientName: user.bridge.Config.WhatsApp.BrowserName,
|
||||||
ClientVersion: WAVersion,
|
ClientVersion: WAVersion,
|
||||||
|
Log: user.log.Sub("Conn"),
|
||||||
|
Handler: []whatsapp.Handler{user},
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
user.log.Errorln("Failed to connect to WhatsApp:", err)
|
|
||||||
user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp server. " +
|
|
||||||
"This indicates a network problem on the bridge server. See bridge logs for more info.")
|
|
||||||
user.connLock.Unlock()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
user.Conn = whatsappExt.ExtendConn(conn)
|
|
||||||
user.log.Debugln("WhatsApp connection successful")
|
|
||||||
user.Conn.AddHandler(user)
|
|
||||||
user.connLock.Unlock()
|
user.connLock.Unlock()
|
||||||
return user.RestoreSession()
|
return user.RestoreSession()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) Disconnect() {
|
func (user *User) DeleteConnection() {
|
||||||
sess, err := user.Conn.Disconnect()
|
user.connLock.Lock()
|
||||||
|
if user.Conn == nil {
|
||||||
|
user.connLock.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := user.Conn.Disconnect()
|
||||||
if err != nil && err != whatsapp.ErrNotConnected {
|
if err != nil && err != whatsapp.ErrNotConnected {
|
||||||
user.log.Warnln("Error disconnecting: %v", err)
|
user.log.Warnln("Error disconnecting: %v", err)
|
||||||
}
|
}
|
||||||
user.SetSession(&sess)
|
|
||||||
user.Conn.RemoveHandlers()
|
user.Conn.RemoveHandlers()
|
||||||
user.Conn = nil
|
user.Conn = nil
|
||||||
user.bridge.Metrics.TrackConnectionState(user.JID, false)
|
user.bridge.Metrics.TrackConnectionState(user.JID, false)
|
||||||
|
user.connLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) RestoreSession() bool {
|
func (user *User) RestoreSession() bool {
|
||||||
if user.Session != nil {
|
if user.Session != nil {
|
||||||
sess, err := user.Conn.RestoreWithSession(*user.Session)
|
user.Conn.SetSession(*user.Session)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
err := user.Conn.Restore(true, ctx)
|
||||||
if err == whatsapp.ErrAlreadyLoggedIn {
|
if err == whatsapp.ErrAlreadyLoggedIn {
|
||||||
return true
|
return true
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -292,24 +294,23 @@ func (user *User) RestoreSession() bool {
|
||||||
if errors.Is(err, whatsapp.ErrUnpaired) {
|
if errors.Is(err, whatsapp.ErrUnpaired) {
|
||||||
user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp: unpaired from phone. " +
|
user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp: unpaired from phone. " +
|
||||||
"To re-pair your phone, log in again.")
|
"To re-pair your phone, log in again.")
|
||||||
user.Disconnect()
|
|
||||||
user.removeFromJIDMap()
|
user.removeFromJIDMap()
|
||||||
//user.JID = ""
|
//user.JID = ""
|
||||||
user.SetSession(nil)
|
user.SetSession(nil)
|
||||||
|
user.DeleteConnection()
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp. Make sure WhatsApp " +
|
user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp. Make sure WhatsApp " +
|
||||||
"on your phone is reachable and use `reconnect` to try connecting again.")
|
"on your phone is reachable and use `reconnect` to try connecting again.")
|
||||||
}
|
}
|
||||||
user.log.Debugln("Disconnecting due to failed session restore...")
|
user.log.Debugln("Disconnecting due to failed session restore...")
|
||||||
_, err := user.Conn.Disconnect()
|
err = user.Conn.Disconnect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user.log.Errorln("Failed to disconnect after failed session restore:", err)
|
user.log.Errorln("Failed to disconnect after failed session restore:", err)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
user.ConnectionErrors = 0
|
user.ConnectionErrors = 0
|
||||||
user.SetSession(&sess)
|
|
||||||
user.log.Debugln("Session restored successfully")
|
user.log.Debugln("Session restored successfully")
|
||||||
user.PostLogin()
|
user.PostLogin()
|
||||||
}
|
}
|
||||||
|
@ -384,7 +385,7 @@ func (user *User) Login(ce *CommandEvent) {
|
||||||
qrChan := make(chan string, 3)
|
qrChan := make(chan string, 3)
|
||||||
eventIDChan := make(chan id.EventID, 1)
|
eventIDChan := make(chan id.EventID, 1)
|
||||||
go user.loginQrChannel(ce, qrChan, eventIDChan)
|
go user.loginQrChannel(ce, qrChan, eventIDChan)
|
||||||
session, err := user.Conn.LoginWithRetry(qrChan, nil, user.bridge.Config.Bridge.LoginQRRegenCount)
|
session, jid, err := user.Conn.Login(qrChan, nil, user.bridge.Config.Bridge.LoginQRRegenCount)
|
||||||
qrChan <- "stop"
|
qrChan <- "stop"
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var eventID id.EventID
|
var eventID id.EventID
|
||||||
|
@ -418,8 +419,9 @@ func (user *User) Login(ce *CommandEvent) {
|
||||||
}
|
}
|
||||||
// TODO there's a bit of duplication between this and the provisioning API login method
|
// TODO there's a bit of duplication between this and the provisioning API login method
|
||||||
// Also between the two logout methods (commands.go and provisioning.go)
|
// Also between the two logout methods (commands.go and provisioning.go)
|
||||||
|
user.log.Debugln("Successful login as", jid, "via command")
|
||||||
user.ConnectionErrors = 0
|
user.ConnectionErrors = 0
|
||||||
user.JID = strings.Replace(user.Conn.Info.Wid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, 1)
|
user.JID = strings.Replace(jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1)
|
||||||
user.addToJIDMap()
|
user.addToJIDMap()
|
||||||
user.SetSession(&session)
|
user.SetSession(&session)
|
||||||
ce.Reply("Successfully logged in, synchronizing chats...")
|
ce.Reply("Successfully logged in, synchronizing chats...")
|
||||||
|
@ -501,20 +503,24 @@ func (user *User) sendMarkdownBridgeAlert(formatString string, args ...interface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) postConnPing(conn *whatsappExt.ExtendedConn) bool {
|
func (user *User) postConnPing() bool {
|
||||||
if user.Conn != conn {
|
|
||||||
user.log.Warnln("Connection changed before scheduled post-connection ping, canceling ping")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
user.log.Debugln("Making post-connection ping")
|
user.log.Debugln("Making post-connection ping")
|
||||||
err := conn.AdminTest()
|
var err error
|
||||||
if err != nil {
|
for i := 0; ; i++ {
|
||||||
|
err = user.Conn.AdminTest()
|
||||||
|
if err == nil {
|
||||||
|
user.log.Debugln("Post-connection ping OK")
|
||||||
|
return true
|
||||||
|
} else if errors.Is(err, whatsapp.ErrConnectionTimeout) && i < 5 {
|
||||||
|
user.log.Warnfln("Post-connection ping timed out, sending new one")
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
user.log.Errorfln("Post-connection ping failed: %v. Disconnecting and then reconnecting after a second", err)
|
user.log.Errorfln("Post-connection ping failed: %v. Disconnecting and then reconnecting after a second", err)
|
||||||
sess, disconnectErr := conn.Disconnect()
|
disconnectErr := user.Conn.Disconnect()
|
||||||
if disconnectErr != nil {
|
if disconnectErr != nil {
|
||||||
user.log.Warnln("Error while disconnecting after failed post-connection ping:", disconnectErr)
|
user.log.Warnln("Error while disconnecting after failed post-connection ping:", disconnectErr)
|
||||||
} else {
|
|
||||||
user.Session = &sess
|
|
||||||
}
|
}
|
||||||
user.bridge.Metrics.TrackDisconnection(user.MXID)
|
user.bridge.Metrics.TrackDisconnection(user.MXID)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -522,13 +528,9 @@ func (user *User) postConnPing(conn *whatsappExt.ExtendedConn) bool {
|
||||||
user.tryReconnect(fmt.Sprintf("Post-connection ping failed: %v", err))
|
user.tryReconnect(fmt.Sprintf("Post-connection ping failed: %v", err))
|
||||||
}()
|
}()
|
||||||
return false
|
return false
|
||||||
} else {
|
|
||||||
user.log.Debugln("Post-connection ping OK")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) intPostLogin(conn *whatsappExt.ExtendedConn) {
|
func (user *User) intPostLogin(conn *whatsapp.Conn) {
|
||||||
defer user.syncWait.Done()
|
defer user.syncWait.Done()
|
||||||
user.lastReconnection = time.Now().Unix()
|
user.lastReconnection = time.Now().Unix()
|
||||||
user.createCommunity()
|
user.createCommunity()
|
||||||
|
@ -540,11 +542,11 @@ func (user *User) intPostLogin(conn *whatsappExt.ExtendedConn) {
|
||||||
user.log.Debugln("Chat list receive confirmation received in PostLogin")
|
user.log.Debugln("Chat list receive confirmation received in PostLogin")
|
||||||
case <-time.After(time.Duration(user.bridge.Config.Bridge.ChatListWait) * time.Second):
|
case <-time.After(time.Duration(user.bridge.Config.Bridge.ChatListWait) * time.Second):
|
||||||
user.log.Warnln("Timed out waiting for chat list to arrive!")
|
user.log.Warnln("Timed out waiting for chat list to arrive!")
|
||||||
user.postConnPing(conn)
|
user.postConnPing()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.postConnPing(conn) {
|
if !user.postConnPing() {
|
||||||
user.log.Debugln("Post-connection ping failed, unlocking processing of incoming messages.")
|
user.log.Debugln("Post-connection ping failed, unlocking processing of incoming messages.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -559,16 +561,68 @@ func (user *User) intPostLogin(conn *whatsappExt.ExtendedConn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleStreamEvent(evt whatsappExt.StreamEvent) {
|
type NormalMessage interface {
|
||||||
if evt.Type == whatsappExt.StreamSleep {
|
GetInfo() whatsapp.MessageInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) HandleEvent(event interface{}) {
|
||||||
|
switch v := event.(type) {
|
||||||
|
case NormalMessage:
|
||||||
|
info := v.GetInfo()
|
||||||
|
user.messageInput <- PortalMessage{info.RemoteJid, user, v, info.Timestamp}
|
||||||
|
case whatsapp.MessageRevocation:
|
||||||
|
user.messageInput <- PortalMessage{v.RemoteJid, user, v, 0}
|
||||||
|
case whatsapp.StreamEvent:
|
||||||
|
user.HandleStreamEvent(v)
|
||||||
|
case []whatsapp.Chat:
|
||||||
|
user.HandleChatList(v)
|
||||||
|
case []whatsapp.Contact:
|
||||||
|
user.HandleContactList(v)
|
||||||
|
case error:
|
||||||
|
user.HandleError(v)
|
||||||
|
case whatsapp.Contact:
|
||||||
|
go user.HandleNewContact(v)
|
||||||
|
case whatsapp.BatteryMessage:
|
||||||
|
user.HandleBatteryMessage(v)
|
||||||
|
case whatsapp.CallInfo:
|
||||||
|
user.HandleCallInfo(v)
|
||||||
|
case whatsapp.PresenceEvent:
|
||||||
|
go user.HandlePresence(v)
|
||||||
|
case whatsapp.JSONMsgInfo:
|
||||||
|
go user.HandleMsgInfo(v)
|
||||||
|
case whatsapp.ReceivedMessage:
|
||||||
|
user.HandleReceivedMessage(v)
|
||||||
|
case whatsapp.ReadMessage:
|
||||||
|
user.HandleReadMessage(v)
|
||||||
|
case whatsapp.JSONCommand:
|
||||||
|
user.HandleCommand(v)
|
||||||
|
case whatsapp.ChatUpdate:
|
||||||
|
user.HandleChatUpdate(v)
|
||||||
|
case whatsapp.ConnInfo:
|
||||||
|
user.HandleConnInfo(v)
|
||||||
|
case json.RawMessage:
|
||||||
|
user.HandleJSONMessage(v)
|
||||||
|
case *waProto.WebMessageInfo:
|
||||||
|
user.updateLastConnectionIfNecessary()
|
||||||
|
// TODO trace log
|
||||||
|
//user.log.Debugfln("WebMessageInfo: %+v", v)
|
||||||
|
case *waBinary.Node:
|
||||||
|
user.log.Debugfln("Unknown binary message: %+v", v)
|
||||||
|
default:
|
||||||
|
user.log.Debugfln("Unknown type of event in HandleEvent: %T", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) HandleStreamEvent(evt whatsapp.StreamEvent) {
|
||||||
|
if evt.Type == whatsapp.StreamSleep {
|
||||||
if user.lastReconnection+60 > time.Now().Unix() {
|
if user.lastReconnection+60 > time.Now().Unix() {
|
||||||
user.lastReconnection = 0
|
user.lastReconnection = 0
|
||||||
user.log.Infoln("Stream went to sleep soon after reconnection, making new post-connection ping in 20 seconds")
|
user.log.Infoln("Stream went to sleep soon after reconnection, making new post-connection ping in 20 seconds")
|
||||||
conn := user.Conn
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(20 * time.Second)
|
time.Sleep(20 * time.Second)
|
||||||
// TODO if this happens during the post-login sync, it can get stuck forever
|
// TODO if this happens during the post-login sync, it can get stuck forever
|
||||||
user.postConnPing(conn)
|
// TODO check if the above is still true
|
||||||
|
user.postConnPing()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -580,10 +634,10 @@ func (user *User) HandleChatList(chats []whatsapp.Chat) {
|
||||||
user.log.Infoln("Chat list received")
|
user.log.Infoln("Chat list received")
|
||||||
chatMap := make(map[string]whatsapp.Chat)
|
chatMap := make(map[string]whatsapp.Chat)
|
||||||
for _, chat := range user.Conn.Store.Chats {
|
for _, chat := range user.Conn.Store.Chats {
|
||||||
chatMap[chat.Jid] = chat
|
chatMap[chat.JID] = chat
|
||||||
}
|
}
|
||||||
for _, chat := range chats {
|
for _, chat := range chats {
|
||||||
chatMap[chat.Jid] = chat
|
chatMap[chat.JID] = chat
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case user.chatListReceived <- struct{}{}:
|
case user.chatListReceived <- struct{}{}:
|
||||||
|
@ -605,14 +659,14 @@ func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool)
|
||||||
for _, chat := range chatMap {
|
for _, chat := range chatMap {
|
||||||
ts, err := strconv.ParseUint(chat.LastMessageTime, 10, 64)
|
ts, err := strconv.ParseUint(chat.LastMessageTime, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user.log.Warnfln("Non-integer last message time in %s: %s", chat.Jid, chat.LastMessageTime)
|
user.log.Warnfln("Non-integer last message time in %s: %s", chat.JID, chat.LastMessageTime)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
portal := user.GetPortalByJID(chat.Jid)
|
portal := user.GetPortalByJID(chat.JID)
|
||||||
|
|
||||||
chats = append(chats, Chat{
|
chats = append(chats, Chat{
|
||||||
Portal: portal,
|
Portal: portal,
|
||||||
Contact: user.Conn.Store.Contacts[chat.Jid],
|
Contact: user.Conn.Store.Contacts[chat.JID],
|
||||||
LastMessageTime: ts,
|
LastMessageTime: ts,
|
||||||
})
|
})
|
||||||
var inCommunity, ok bool
|
var inCommunity, ok bool
|
||||||
|
@ -725,22 +779,35 @@ func (user *User) UpdateDirectChats(chats map[id.UserID][]id.RoomID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleContactList(contacts []whatsapp.Contact) {
|
func (user *User) HandleContactList(contacts []whatsapp.Contact) {
|
||||||
contactMap := make(map[string]whatsapp.Contact)
|
contactMap := make(map[whatsapp.JID]whatsapp.Contact)
|
||||||
for _, contact := range contacts {
|
for _, contact := range contacts {
|
||||||
contactMap[contact.Jid] = contact
|
contactMap[contact.JID] = contact
|
||||||
}
|
}
|
||||||
go user.syncPuppets(contactMap)
|
go user.syncPuppets(contactMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) syncPuppets(contacts map[string]whatsapp.Contact) {
|
func (user *User) syncPuppets(contacts map[whatsapp.JID]whatsapp.Contact) {
|
||||||
if contacts == nil {
|
if contacts == nil {
|
||||||
contacts = user.Conn.Store.Contacts
|
contacts = user.Conn.Store.Contacts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, hasSelf := contacts[user.JID]
|
||||||
|
if !hasSelf {
|
||||||
|
contacts[user.JID] = whatsapp.Contact{
|
||||||
|
Name: user.pushName,
|
||||||
|
Notify: user.pushName,
|
||||||
|
JID: user.JID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
user.log.Infoln("Syncing puppet info from contacts")
|
user.log.Infoln("Syncing puppet info from contacts")
|
||||||
for jid, contact := range contacts {
|
for jid, contact := range contacts {
|
||||||
if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) {
|
if strings.HasSuffix(jid, whatsapp.NewUserSuffix) {
|
||||||
puppet := user.bridge.GetPuppetByJID(contact.Jid)
|
puppet := user.bridge.GetPuppetByJID(contact.JID)
|
||||||
puppet.Sync(user, contact)
|
puppet.Sync(user, contact)
|
||||||
|
} else if strings.HasSuffix(jid, whatsapp.BroadcastSuffix) {
|
||||||
|
portal := user.GetPortalByJID(contact.JID)
|
||||||
|
portal.Sync(user, contact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
user.log.Infoln("Finished syncing puppet info from contacts")
|
user.log.Infoln("Finished syncing puppet info from contacts")
|
||||||
|
@ -796,14 +863,18 @@ func (user *User) tryReconnect(msg string) {
|
||||||
baseDelay = -baseDelay + 1
|
baseDelay = -baseDelay + 1
|
||||||
}
|
}
|
||||||
delay := baseDelay
|
delay := baseDelay
|
||||||
conn := user.Conn
|
|
||||||
takeover := false
|
takeover := false
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
user.cancelReconnect = cancel
|
||||||
for user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
|
for user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
|
||||||
if user.Conn != conn {
|
select {
|
||||||
user.log.Debugln("Connection was recreated, aborting reconnection attempts")
|
case <-ctx.Done():
|
||||||
|
user.log.Debugln("tryReconnect context cancelled, aborting reconnection attempts")
|
||||||
return
|
return
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
err := conn.Restore(takeover)
|
err := user.Conn.Restore(takeover, ctx)
|
||||||
takeover = true
|
takeover = true
|
||||||
if err == nil {
|
if err == nil {
|
||||||
user.ConnectionErrors = 0
|
user.ConnectionErrors = 0
|
||||||
|
@ -813,14 +884,23 @@ func (user *User) tryReconnect(msg string) {
|
||||||
user.PostLogin()
|
user.PostLogin()
|
||||||
return
|
return
|
||||||
} else if errors.Is(err, whatsapp.ErrBadRequest) {
|
} else if errors.Is(err, whatsapp.ErrBadRequest) {
|
||||||
user.log.Infoln("Got init 400 error when trying to reconnect, resetting connection...")
|
user.log.Warnln("Got init 400 error when trying to reconnect, resetting connection...")
|
||||||
sess, err := conn.Disconnect()
|
err = user.Conn.Disconnect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user.log.Debugln("Error while disconnecting for connection reset:", err)
|
user.log.Debugln("Error while disconnecting for connection reset:", err)
|
||||||
}
|
}
|
||||||
user.SetSession(&sess)
|
} else if errors.Is(err, whatsapp.ErrUnpaired) {
|
||||||
}
|
user.log.Errorln("Got init 401 (unpaired) error when trying to reconnect, not retrying")
|
||||||
|
user.removeFromJIDMap()
|
||||||
|
//user.JID = ""
|
||||||
|
user.SetSession(nil)
|
||||||
|
user.DeleteConnection()
|
||||||
|
user.sendMarkdownBridgeAlert("\u26a0 Failed to reconnect to WhatsApp: unpaired from phone. " +
|
||||||
|
"To re-pair your phone, log in again.")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
user.log.Errorln("Error while trying to reconnect after disconnection:", err)
|
user.log.Errorln("Error while trying to reconnect after disconnection:", err)
|
||||||
|
}
|
||||||
tries++
|
tries++
|
||||||
user.ConnectionErrors++
|
user.ConnectionErrors++
|
||||||
if user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
|
if user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
|
||||||
|
@ -841,19 +921,11 @@ func (user *User) tryReconnect(msg string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) ShouldCallSynchronously() bool {
|
func (user *User) PortalKey(jid whatsapp.JID) database.PortalKey {
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) HandleJSONParseError(err error) {
|
|
||||||
user.log.Errorln("WhatsApp JSON parse error:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) PortalKey(jid types.WhatsAppID) database.PortalKey {
|
|
||||||
return database.NewPortalKey(jid, user.JID)
|
return database.NewPortalKey(jid, user.JID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) GetPortalByJID(jid types.WhatsAppID) *Portal {
|
func (user *User) GetPortalByJID(jid whatsapp.JID) *Portal {
|
||||||
return user.bridge.GetPortalByJID(user.PortalKey(jid))
|
return user.bridge.GetPortalByJID(user.PortalKey(jid))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -888,13 +960,16 @@ func (user *User) handleMessageLoop() {
|
||||||
|
|
||||||
func (user *User) HandleNewContact(contact whatsapp.Contact) {
|
func (user *User) HandleNewContact(contact whatsapp.Contact) {
|
||||||
user.log.Debugfln("Contact message: %+v", contact)
|
user.log.Debugfln("Contact message: %+v", contact)
|
||||||
go func() {
|
if strings.HasSuffix(contact.JID, whatsapp.OldUserSuffix) {
|
||||||
if strings.HasSuffix(contact.Jid, whatsappExt.OldUserSuffix) {
|
contact.JID = strings.Replace(contact.JID, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, -1)
|
||||||
contact.Jid = strings.Replace(contact.Jid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, -1)
|
|
||||||
}
|
}
|
||||||
puppet := user.bridge.GetPuppetByJID(contact.Jid)
|
if strings.HasSuffix(contact.JID, whatsapp.NewUserSuffix) {
|
||||||
|
puppet := user.bridge.GetPuppetByJID(contact.JID)
|
||||||
puppet.UpdateName(user, contact)
|
puppet.UpdateName(user, contact)
|
||||||
}()
|
} else if strings.HasSuffix(contact.JID, whatsapp.BroadcastSuffix) {
|
||||||
|
portal := user.GetPortalByJID(contact.JID)
|
||||||
|
portal.UpdateName(contact.Name, "", nil, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleBatteryMessage(battery whatsapp.BatteryMessage) {
|
func (user *User) HandleBatteryMessage(battery whatsapp.BatteryMessage) {
|
||||||
|
@ -914,53 +989,13 @@ func (user *User) HandleBatteryMessage(battery whatsapp.BatteryMessage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleTextMessage(message whatsapp.TextMessage) {
|
|
||||||
user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) HandleImageMessage(message whatsapp.ImageMessage) {
|
|
||||||
user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) HandleStickerMessage(message whatsapp.StickerMessage) {
|
|
||||||
user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) HandleVideoMessage(message whatsapp.VideoMessage) {
|
|
||||||
user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) HandleAudioMessage(message whatsapp.AudioMessage) {
|
|
||||||
user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) HandleDocumentMessage(message whatsapp.DocumentMessage) {
|
|
||||||
user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) HandleContactMessage(message whatsapp.ContactMessage) {
|
|
||||||
user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) HandleStubMessage(message whatsapp.StubMessage) {
|
|
||||||
user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) HandleLocationMessage(message whatsapp.LocationMessage) {
|
|
||||||
user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) HandleMessageRevoke(message whatsappExt.MessageRevocation) {
|
|
||||||
user.messageInput <- PortalMessage{message.RemoteJid, user, message, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
type FakeMessage struct {
|
type FakeMessage struct {
|
||||||
Text string
|
Text string
|
||||||
ID string
|
ID string
|
||||||
Alert bool
|
Alert bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleCallInfo(info whatsappExt.CallInfo) {
|
func (user *User) HandleCallInfo(info whatsapp.CallInfo) {
|
||||||
if info.Data != nil {
|
if info.Data != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -968,19 +1003,19 @@ func (user *User) HandleCallInfo(info whatsappExt.CallInfo) {
|
||||||
ID: info.ID,
|
ID: info.ID,
|
||||||
}
|
}
|
||||||
switch info.Type {
|
switch info.Type {
|
||||||
case whatsappExt.CallOffer:
|
case whatsapp.CallOffer:
|
||||||
if !user.bridge.Config.Bridge.CallNotices.Start {
|
if !user.bridge.Config.Bridge.CallNotices.Start {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data.Text = "Incoming call"
|
data.Text = "Incoming call"
|
||||||
data.Alert = true
|
data.Alert = true
|
||||||
case whatsappExt.CallOfferVideo:
|
case whatsapp.CallOfferVideo:
|
||||||
if !user.bridge.Config.Bridge.CallNotices.Start {
|
if !user.bridge.Config.Bridge.CallNotices.Start {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data.Text = "Incoming video call"
|
data.Text = "Incoming video call"
|
||||||
data.Alert = true
|
data.Alert = true
|
||||||
case whatsappExt.CallTerminate:
|
case whatsapp.CallTerminate:
|
||||||
if !user.bridge.Config.Bridge.CallNotices.End {
|
if !user.bridge.Config.Bridge.CallNotices.End {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -995,7 +1030,7 @@ func (user *User) HandleCallInfo(info whatsappExt.CallInfo) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandlePresence(info whatsappExt.Presence) {
|
func (user *User) HandlePresence(info whatsapp.PresenceEvent) {
|
||||||
puppet := user.bridge.GetPuppetByJID(info.SenderJID)
|
puppet := user.bridge.GetPuppetByJID(info.SenderJID)
|
||||||
switch info.Status {
|
switch info.Status {
|
||||||
case whatsapp.PresenceUnavailable:
|
case whatsapp.PresenceUnavailable:
|
||||||
|
@ -1023,14 +1058,13 @@ func (user *User) HandlePresence(info whatsappExt.Presence) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleMsgInfo(info whatsappExt.MsgInfo) {
|
func (user *User) HandleMsgInfo(info whatsapp.JSONMsgInfo) {
|
||||||
if (info.Command == whatsappExt.MsgInfoCommandAck || info.Command == whatsappExt.MsgInfoCommandAcks) && info.Acknowledgement == whatsappExt.AckMessageRead {
|
if (info.Command == whatsapp.MsgInfoCommandAck || info.Command == whatsapp.MsgInfoCommandAcks) && info.Acknowledgement == whatsapp.AckMessageRead {
|
||||||
portal := user.GetPortalByJID(info.ToJID)
|
portal := user.GetPortalByJID(info.ToJID)
|
||||||
if len(portal.MXID) == 0 {
|
if len(portal.MXID) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
|
||||||
intent := user.bridge.GetPuppetByJID(info.SenderJID).IntentFor(portal)
|
intent := user.bridge.GetPuppetByJID(info.SenderJID).IntentFor(portal)
|
||||||
for _, msgID := range info.IDs {
|
for _, msgID := range info.IDs {
|
||||||
msg := user.bridge.DB.Message.GetByJID(portal.Key, msgID)
|
msg := user.bridge.DB.Message.GetByJID(portal.Key, msgID)
|
||||||
|
@ -1043,13 +1077,12 @@ func (user *User) HandleMsgInfo(info whatsappExt.MsgInfo) {
|
||||||
user.log.Warnln("Failed to mark message %s as read by %s: %v", msg.MXID, info.SenderJID, err)
|
user.log.Warnln("Failed to mark message %s as read by %s: %v", msg.MXID, info.SenderJID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleReceivedMessage(received whatsapp.ReceivedMessage) {
|
func (user *User) HandleReceivedMessage(received whatsapp.ReceivedMessage) {
|
||||||
if received.Type == "read" {
|
if received.Type == "read" {
|
||||||
user.markSelfRead(received.Jid, received.Index)
|
go user.markSelfRead(received.Jid, received.Index)
|
||||||
} else {
|
} else {
|
||||||
user.log.Debugfln("Unknown received message type: %+v", received)
|
user.log.Debugfln("Unknown received message type: %+v", received)
|
||||||
}
|
}
|
||||||
|
@ -1057,12 +1090,12 @@ func (user *User) HandleReceivedMessage(received whatsapp.ReceivedMessage) {
|
||||||
|
|
||||||
func (user *User) HandleReadMessage(read whatsapp.ReadMessage) {
|
func (user *User) HandleReadMessage(read whatsapp.ReadMessage) {
|
||||||
user.log.Debugfln("Received chat read message: %+v", read)
|
user.log.Debugfln("Received chat read message: %+v", read)
|
||||||
user.markSelfRead(read.Jid, "")
|
go user.markSelfRead(read.Jid, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) markSelfRead(jid, messageID string) {
|
func (user *User) markSelfRead(jid, messageID string) {
|
||||||
if strings.HasSuffix(jid, whatsappExt.OldUserSuffix) {
|
if strings.HasSuffix(jid, whatsapp.OldUserSuffix) {
|
||||||
jid = strings.Replace(jid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, -1)
|
jid = strings.Replace(jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, -1)
|
||||||
}
|
}
|
||||||
puppet := user.bridge.GetPuppetByJID(user.JID)
|
puppet := user.bridge.GetPuppetByJID(user.JID)
|
||||||
if puppet == nil {
|
if puppet == nil {
|
||||||
|
@ -1096,17 +1129,17 @@ func (user *User) markSelfRead(jid, messageID string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleCommand(cmd whatsappExt.Command) {
|
func (user *User) HandleCommand(cmd whatsapp.JSONCommand) {
|
||||||
switch cmd.Type {
|
switch cmd.Type {
|
||||||
case whatsappExt.CommandPicture:
|
case whatsapp.CommandPicture:
|
||||||
if strings.HasSuffix(cmd.JID, whatsappExt.NewUserSuffix) {
|
if strings.HasSuffix(cmd.JID, whatsapp.NewUserSuffix) {
|
||||||
puppet := user.bridge.GetPuppetByJID(cmd.JID)
|
puppet := user.bridge.GetPuppetByJID(cmd.JID)
|
||||||
go puppet.UpdateAvatar(user, cmd.ProfilePicInfo)
|
go puppet.UpdateAvatar(user, cmd.ProfilePicInfo)
|
||||||
} else if user.bridge.Config.Bridge.ChatMetaSync {
|
} else if user.bridge.Config.Bridge.ChatMetaSync {
|
||||||
portal := user.GetPortalByJID(cmd.JID)
|
portal := user.GetPortalByJID(cmd.JID)
|
||||||
go portal.UpdateAvatar(user, cmd.ProfilePicInfo, true)
|
go portal.UpdateAvatar(user, cmd.ProfilePicInfo, true)
|
||||||
}
|
}
|
||||||
case whatsappExt.CommandDisconnect:
|
case whatsapp.CommandDisconnect:
|
||||||
if cmd.Kind == "replaced" {
|
if cmd.Kind == "replaced" {
|
||||||
user.cleanDisconnection = true
|
user.cleanDisconnection = true
|
||||||
go user.sendMarkdownBridgeAlert("\u26a0 Your WhatsApp connection was closed by the server because you opened another WhatsApp Web client.\n\n" +
|
go user.sendMarkdownBridgeAlert("\u26a0 Your WhatsApp connection was closed by the server because you opened another WhatsApp Web client.\n\n" +
|
||||||
|
@ -1119,14 +1152,14 @@ func (user *User) HandleCommand(cmd whatsappExt.Command) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
|
func (user *User) HandleChatUpdate(cmd whatsapp.ChatUpdate) {
|
||||||
if cmd.Command != whatsappExt.ChatUpdateCommandAction {
|
if cmd.Command != whatsapp.ChatUpdateCommandAction {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
portal := user.GetPortalByJID(cmd.JID)
|
portal := user.GetPortalByJID(cmd.JID)
|
||||||
if len(portal.MXID) == 0 {
|
if len(portal.MXID) == 0 {
|
||||||
if cmd.Data.Action == whatsappExt.ChatActionIntroduce || cmd.Data.Action == whatsappExt.ChatActionCreate {
|
if cmd.Data.Action == whatsapp.ChatActionIntroduce || cmd.Data.Action == whatsapp.ChatActionCreate {
|
||||||
go func() {
|
go func() {
|
||||||
err := portal.CreateMatrixRoom(user)
|
err := portal.CreateMatrixRoom(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1139,11 +1172,11 @@ func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
|
||||||
|
|
||||||
// These don't come down the message history :(
|
// These don't come down the message history :(
|
||||||
switch cmd.Data.Action {
|
switch cmd.Data.Action {
|
||||||
case whatsappExt.ChatActionAddTopic:
|
case whatsapp.ChatActionAddTopic:
|
||||||
go portal.UpdateTopic(cmd.Data.AddTopic.Topic, cmd.Data.SenderJID, nil,true)
|
go portal.UpdateTopic(cmd.Data.AddTopic.Topic, cmd.Data.SenderJID, nil, true)
|
||||||
case whatsappExt.ChatActionRemoveTopic:
|
case whatsapp.ChatActionRemoveTopic:
|
||||||
go portal.UpdateTopic("", cmd.Data.SenderJID, nil,true)
|
go portal.UpdateTopic("", cmd.Data.SenderJID, nil, true)
|
||||||
case whatsappExt.ChatActionRemove:
|
case whatsapp.ChatActionRemove:
|
||||||
// We ignore leaving groups in the message history to avoid accidentally leaving rejoined groups,
|
// We ignore leaving groups in the message history to avoid accidentally leaving rejoined groups,
|
||||||
// but if we get a real-time command that says we left, it should be safe to bridge it.
|
// but if we get a real-time command that says we left, it should be safe to bridge it.
|
||||||
if !user.bridge.Config.Bridge.ChatMetaSync {
|
if !user.bridge.Config.Bridge.ChatMetaSync {
|
||||||
|
@ -1162,45 +1195,48 @@ func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch cmd.Data.Action {
|
switch cmd.Data.Action {
|
||||||
case whatsappExt.ChatActionNameChange:
|
case whatsapp.ChatActionNameChange:
|
||||||
go portal.UpdateName(cmd.Data.NameChange.Name, cmd.Data.SenderJID, nil, true)
|
go portal.UpdateName(cmd.Data.NameChange.Name, cmd.Data.SenderJID, nil, true)
|
||||||
case whatsappExt.ChatActionPromote:
|
case whatsapp.ChatActionPromote:
|
||||||
go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, true)
|
go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, true)
|
||||||
case whatsappExt.ChatActionDemote:
|
case whatsapp.ChatActionDemote:
|
||||||
go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, false)
|
go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, false)
|
||||||
case whatsappExt.ChatActionAnnounce:
|
case whatsapp.ChatActionAnnounce:
|
||||||
go portal.RestrictMessageSending(cmd.Data.Announce)
|
go portal.RestrictMessageSending(cmd.Data.Announce)
|
||||||
case whatsappExt.ChatActionRestrict:
|
case whatsapp.ChatActionRestrict:
|
||||||
go portal.RestrictMetadataChanges(cmd.Data.Restrict)
|
go portal.RestrictMetadataChanges(cmd.Data.Restrict)
|
||||||
case whatsappExt.ChatActionRemove:
|
case whatsapp.ChatActionRemove:
|
||||||
go portal.HandleWhatsAppKick(nil, cmd.Data.SenderJID, cmd.Data.UserChange.JIDs)
|
go portal.HandleWhatsAppKick(nil, cmd.Data.SenderJID, cmd.Data.UserChange.JIDs)
|
||||||
case whatsappExt.ChatActionAdd:
|
case whatsapp.ChatActionAdd:
|
||||||
go portal.HandleWhatsAppInvite(cmd.Data.SenderJID, nil, cmd.Data.UserChange.JIDs)
|
go portal.HandleWhatsAppInvite(cmd.Data.SenderJID, nil, cmd.Data.UserChange.JIDs)
|
||||||
case whatsappExt.ChatActionIntroduce:
|
case whatsapp.ChatActionIntroduce:
|
||||||
if cmd.Data.SenderJID != "unknown" {
|
if cmd.Data.SenderJID != "unknown" {
|
||||||
go portal.Sync(user, whatsapp.Contact{Jid: portal.Key.JID})
|
go portal.Sync(user, whatsapp.Contact{JID: portal.Key.JID})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleJsonMessage(message string) {
|
func (user *User) HandleConnInfo(info whatsapp.ConnInfo) {
|
||||||
var msg json.RawMessage
|
if user.Session != nil && info.Connected && len(info.ClientToken) > 0 {
|
||||||
err := json.Unmarshal([]byte(message), &msg)
|
user.log.Debugln("Received new tokens")
|
||||||
if err != nil {
|
user.Session.ClientToken = info.ClientToken
|
||||||
|
user.Session.ServerToken = info.ServerToken
|
||||||
|
user.Session.Wid = info.WID
|
||||||
|
user.Update()
|
||||||
|
}
|
||||||
|
if len(info.PushName) > 0 {
|
||||||
|
user.pushName = info.PushName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) HandleJSONMessage(message json.RawMessage) {
|
||||||
|
if !json.Valid(message) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user.log.Debugln("JSON message:", message)
|
user.log.Debugfln("JSON message: %s", message)
|
||||||
user.updateLastConnectionIfNecessary()
|
user.updateLastConnectionIfNecessary()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleRawMessage(message *waProto.WebMessageInfo) {
|
|
||||||
user.updateLastConnectionIfNecessary()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) HandleUnknownBinaryNode(node *waBinary.Node) {
|
|
||||||
user.log.Debugfln("Unknown binary message: %+v", node)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) NeedsRelaybot(portal *Portal) bool {
|
func (user *User) NeedsRelaybot(portal *Portal) bool {
|
||||||
return !user.HasSession() || !user.IsInPortal(portal.Key)
|
return !user.HasSession() || !user.IsInPortal(portal.Key)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CallInfoType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
CallOffer CallInfoType = "offer"
|
|
||||||
CallOfferVideo CallInfoType = "offer_video"
|
|
||||||
CallTransport CallInfoType = "transport"
|
|
||||||
CallRelayLatency CallInfoType = "relaylatency"
|
|
||||||
CallTerminate CallInfoType = "terminate"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CallInfo struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Type CallInfoType `json:"type"`
|
|
||||||
From string `json:"from"`
|
|
||||||
|
|
||||||
Platform string `json:"platform"`
|
|
||||||
Version []int `json:"version"`
|
|
||||||
|
|
||||||
Data [][]interface{} `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CallInfoHandler interface {
|
|
||||||
whatsapp.Handler
|
|
||||||
HandleCallInfo(CallInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) handleMessageCall(message []byte) {
|
|
||||||
var event CallInfo
|
|
||||||
err := json.Unmarshal(message, &event)
|
|
||||||
if err != nil {
|
|
||||||
ext.jsonParseError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
event.From = strings.Replace(event.From, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
for _, handler := range ext.handlers {
|
|
||||||
callInfoHandler, ok := handler.(CallInfoHandler)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext.shouldCallSynchronously(callInfoHandler) {
|
|
||||||
callInfoHandler.HandleCallInfo(event)
|
|
||||||
} else {
|
|
||||||
go callInfoHandler.HandleCallInfo(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,183 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ChatUpdateCommand string
|
|
||||||
|
|
||||||
const (
|
|
||||||
ChatUpdateCommandAction ChatUpdateCommand = "action"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ChatUpdate struct {
|
|
||||||
JID string `json:"id"`
|
|
||||||
Command ChatUpdateCommand `json:"cmd"`
|
|
||||||
Data ChatUpdateData `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChatActionType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
ChatActionNameChange ChatActionType = "subject"
|
|
||||||
ChatActionAddTopic ChatActionType = "desc_add"
|
|
||||||
ChatActionRemoveTopic ChatActionType = "desc_remove"
|
|
||||||
ChatActionRestrict ChatActionType = "restrict"
|
|
||||||
ChatActionAnnounce ChatActionType = "announce"
|
|
||||||
ChatActionPromote ChatActionType = "promote"
|
|
||||||
ChatActionDemote ChatActionType = "demote"
|
|
||||||
ChatActionIntroduce ChatActionType = "introduce"
|
|
||||||
ChatActionCreate ChatActionType = "create"
|
|
||||||
ChatActionRemove ChatActionType = "remove"
|
|
||||||
ChatActionAdd ChatActionType = "add"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ChatUpdateData struct {
|
|
||||||
Action ChatActionType
|
|
||||||
SenderJID string
|
|
||||||
|
|
||||||
NameChange struct {
|
|
||||||
Name string `json:"subject"`
|
|
||||||
SetAt int64 `json:"s_t"`
|
|
||||||
SetBy string `json:"s_o"`
|
|
||||||
}
|
|
||||||
|
|
||||||
AddTopic struct {
|
|
||||||
Topic string `json:"desc"`
|
|
||||||
ID string `json:"descId"`
|
|
||||||
SetAt int64 `json:"descTime"`
|
|
||||||
SetBy string `json:"descOwner"`
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoveTopic struct {
|
|
||||||
ID string `json:"descId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
Introduce struct {
|
|
||||||
CreationTime int64 `json:"creation"`
|
|
||||||
Admins []string `json:"admins"`
|
|
||||||
SuperAdmins []string `json:"superadmins"`
|
|
||||||
Regulars []string `json:"regulars"`
|
|
||||||
}
|
|
||||||
|
|
||||||
Restrict bool
|
|
||||||
|
|
||||||
Announce bool
|
|
||||||
|
|
||||||
UserChange struct {
|
|
||||||
JIDs []string `json:"participants"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cud *ChatUpdateData) UnmarshalJSON(data []byte) error {
|
|
||||||
var arr []json.RawMessage
|
|
||||||
err := json.Unmarshal(data, &arr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if len(arr) < 3 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(arr[0], &cud.Action)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(arr[1], &cud.SenderJID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cud.SenderJID = strings.Replace(cud.SenderJID, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
|
|
||||||
var unmarshalTo interface{}
|
|
||||||
switch cud.Action {
|
|
||||||
case ChatActionIntroduce, ChatActionCreate:
|
|
||||||
err = json.Unmarshal(arr[2], &cud.NameChange)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(arr[2], &cud.AddTopic)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
unmarshalTo = &cud.Introduce
|
|
||||||
case ChatActionNameChange:
|
|
||||||
unmarshalTo = &cud.NameChange
|
|
||||||
case ChatActionAddTopic:
|
|
||||||
unmarshalTo = &cud.AddTopic
|
|
||||||
case ChatActionRemoveTopic:
|
|
||||||
unmarshalTo = &cud.RemoveTopic
|
|
||||||
case ChatActionRestrict:
|
|
||||||
unmarshalTo = &cud.Restrict
|
|
||||||
case ChatActionAnnounce:
|
|
||||||
unmarshalTo = &cud.Announce
|
|
||||||
case ChatActionPromote, ChatActionDemote, ChatActionRemove, ChatActionAdd:
|
|
||||||
unmarshalTo = &cud.UserChange
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(arr[2], unmarshalTo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cud.NameChange.SetBy = strings.Replace(cud.NameChange.SetBy, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
for index, jid := range cud.UserChange.JIDs {
|
|
||||||
cud.UserChange.JIDs[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
}
|
|
||||||
for index, jid := range cud.Introduce.SuperAdmins {
|
|
||||||
cud.Introduce.SuperAdmins[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
}
|
|
||||||
for index, jid := range cud.Introduce.Admins {
|
|
||||||
cud.Introduce.Admins[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
}
|
|
||||||
for index, jid := range cud.Introduce.Regulars {
|
|
||||||
cud.Introduce.Regulars[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChatUpdateHandler interface {
|
|
||||||
whatsapp.Handler
|
|
||||||
HandleChatUpdate(ChatUpdate)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) handleMessageChatUpdate(message []byte) {
|
|
||||||
var event ChatUpdate
|
|
||||||
err := json.Unmarshal(message, &event)
|
|
||||||
if err != nil {
|
|
||||||
ext.jsonParseError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
event.JID = strings.Replace(event.JID, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
for _, handler := range ext.handlers {
|
|
||||||
chatUpdateHandler, ok := handler.(ChatUpdateHandler)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext.shouldCallSynchronously(chatUpdateHandler) {
|
|
||||||
chatUpdateHandler.HandleChatUpdate(event)
|
|
||||||
} else {
|
|
||||||
go chatUpdateHandler.HandleChatUpdate(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CommandType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
CommandPicture CommandType = "picture"
|
|
||||||
CommandDisconnect CommandType = "disconnect"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Command struct {
|
|
||||||
Type CommandType `json:"type"`
|
|
||||||
JID string `json:"jid"`
|
|
||||||
|
|
||||||
*ProfilePicInfo
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
|
|
||||||
Raw json.RawMessage `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommandHandler interface {
|
|
||||||
whatsapp.Handler
|
|
||||||
HandleCommand(Command)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) handleMessageCommand(message []byte) {
|
|
||||||
var event Command
|
|
||||||
err := json.Unmarshal(message, &event)
|
|
||||||
if err != nil {
|
|
||||||
ext.jsonParseError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
event.Raw = message
|
|
||||||
event.JID = strings.Replace(event.JID, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
for _, handler := range ext.handlers {
|
|
||||||
commandHandler, ok := handler.(CommandHandler)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext.shouldCallSynchronously(commandHandler) {
|
|
||||||
commandHandler.HandleCommand(event)
|
|
||||||
} else {
|
|
||||||
go commandHandler.HandleCommand(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConnInfo struct {
|
|
||||||
ProtocolVersion []int `json:"protoVersion"`
|
|
||||||
BinaryVersion int `json:"binVersion"`
|
|
||||||
Phone struct {
|
|
||||||
WhatsAppVersion string `json:"wa_version"`
|
|
||||||
MCC string `json:"mcc"`
|
|
||||||
MNC string `json:"mnc"`
|
|
||||||
OSVersion string `json:"os_version"`
|
|
||||||
DeviceManufacturer string `json:"device_manufacturer"`
|
|
||||||
DeviceModel string `json:"device_model"`
|
|
||||||
OSBuildNumber string `json:"os_build_number"`
|
|
||||||
} `json:"phone"`
|
|
||||||
Features map[string]interface{} `json:"features"`
|
|
||||||
PushName string `json:"pushname"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConnInfoHandler interface {
|
|
||||||
whatsapp.Handler
|
|
||||||
HandleConnInfo(ConnInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) handleMessageConn(message []byte) {
|
|
||||||
var event ConnInfo
|
|
||||||
err := json.Unmarshal(message, &event)
|
|
||||||
if err != nil {
|
|
||||||
ext.jsonParseError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, handler := range ext.handlers {
|
|
||||||
connInfoHandler, ok := handler.(ConnInfoHandler)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext.shouldCallSynchronously(connInfoHandler) {
|
|
||||||
connInfoHandler.HandleConnInfo(event)
|
|
||||||
} else {
|
|
||||||
go connInfoHandler.HandleConnInfo(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"maunium.net/go/mautrix-whatsapp/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CreateGroupResponse struct {
|
|
||||||
Status int `json:"status"`
|
|
||||||
GroupID types.WhatsAppID `json:"gid"`
|
|
||||||
Participants map[types.WhatsAppID]struct {
|
|
||||||
Code string `json:"code"`
|
|
||||||
} `json:"participants"`
|
|
||||||
|
|
||||||
Source string `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type actualCreateGroupResponse struct {
|
|
||||||
Status int `json:"status"`
|
|
||||||
GroupID types.WhatsAppID `json:"gid"`
|
|
||||||
Participants []map[types.WhatsAppID]struct {
|
|
||||||
Code string `json:"code"`
|
|
||||||
} `json:"participants"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) CreateGroup(subject string, participants []types.WhatsAppID) (*CreateGroupResponse, error) {
|
|
||||||
respChan, err := ext.Conn.CreateGroup(subject, participants)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var resp CreateGroupResponse
|
|
||||||
var actualResp actualCreateGroupResponse
|
|
||||||
resp.Source = <-respChan
|
|
||||||
fmt.Println(">>>>>>", resp.Source)
|
|
||||||
err = json.Unmarshal([]byte(resp.Source), &actualResp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
resp.Status = actualResp.Status
|
|
||||||
resp.GroupID = actualResp.GroupID
|
|
||||||
resp.Participants = make(map[types.WhatsAppID]struct {
|
|
||||||
Code string `json:"code"`
|
|
||||||
})
|
|
||||||
for _, participantMap := range actualResp.Participants {
|
|
||||||
for jid, status := range participantMap {
|
|
||||||
resp.Participants[jid] = status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &resp, nil
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JSONMessage []json.RawMessage
|
|
||||||
|
|
||||||
type JSONMessageType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
MessageMsgInfo JSONMessageType = "MsgInfo"
|
|
||||||
MessageMsg JSONMessageType = "Msg"
|
|
||||||
MessagePresence JSONMessageType = "Presence"
|
|
||||||
MessageStream JSONMessageType = "Stream"
|
|
||||||
MessageConn JSONMessageType = "Conn"
|
|
||||||
MessageProps JSONMessageType = "Props"
|
|
||||||
MessageCmd JSONMessageType = "Cmd"
|
|
||||||
MessageChat JSONMessageType = "Chat"
|
|
||||||
MessageCall JSONMessageType = "Call"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) HandleError(error) {}
|
|
||||||
|
|
||||||
type UnhandledJSONMessageHandler interface {
|
|
||||||
whatsapp.Handler
|
|
||||||
HandleUnhandledJSONMessage(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
type JSONParseErrorHandler interface {
|
|
||||||
whatsapp.Handler
|
|
||||||
HandleJSONParseError(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) jsonParseError(err error) {
|
|
||||||
for _, handler := range ext.handlers {
|
|
||||||
errorHandler, ok := handler.(JSONParseErrorHandler)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
errorHandler.HandleJSONParseError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) HandleJsonMessage(message string) {
|
|
||||||
msg := JSONMessage{}
|
|
||||||
err := json.Unmarshal([]byte(message), &msg)
|
|
||||||
if err != nil || len(msg) < 2 {
|
|
||||||
ext.jsonParseError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var msgType JSONMessageType
|
|
||||||
json.Unmarshal(msg[0], &msgType)
|
|
||||||
|
|
||||||
switch msgType {
|
|
||||||
case MessagePresence:
|
|
||||||
ext.handleMessagePresence(msg[1])
|
|
||||||
case MessageStream:
|
|
||||||
ext.handleMessageStream(msg[1:])
|
|
||||||
case MessageConn:
|
|
||||||
ext.handleMessageConn(msg[1])
|
|
||||||
case MessageProps:
|
|
||||||
ext.handleMessageProps(msg[1])
|
|
||||||
case MessageMsgInfo, MessageMsg:
|
|
||||||
ext.handleMessageMsgInfo(msgType, msg[1])
|
|
||||||
case MessageCmd:
|
|
||||||
ext.handleMessageCommand(msg[1])
|
|
||||||
case MessageChat:
|
|
||||||
ext.handleMessageChatUpdate(msg[1])
|
|
||||||
case MessageCall:
|
|
||||||
ext.handleMessageCall(msg[1])
|
|
||||||
default:
|
|
||||||
for _, handler := range ext.handlers {
|
|
||||||
ujmHandler, ok := handler.(UnhandledJSONMessageHandler)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext.shouldCallSynchronously(ujmHandler) {
|
|
||||||
ujmHandler.HandleUnhandledJSONMessage(message)
|
|
||||||
} else {
|
|
||||||
go ujmHandler.HandleUnhandledJSONMessage(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MsgInfoCommand string
|
|
||||||
|
|
||||||
const (
|
|
||||||
MsgInfoCommandAck MsgInfoCommand = "ack"
|
|
||||||
MsgInfoCommandAcks MsgInfoCommand = "acks"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Acknowledgement int
|
|
||||||
|
|
||||||
const (
|
|
||||||
AckMessageSent Acknowledgement = 1
|
|
||||||
AckMessageDelivered Acknowledgement = 2
|
|
||||||
AckMessageRead Acknowledgement = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
type JSONStringOrArray []string
|
|
||||||
|
|
||||||
func (jsoa *JSONStringOrArray) UnmarshalJSON(data []byte) error {
|
|
||||||
var str string
|
|
||||||
if json.Unmarshal(data, &str) == nil {
|
|
||||||
*jsoa = []string{str}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var strs []string
|
|
||||||
json.Unmarshal(data, &strs)
|
|
||||||
*jsoa = strs
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type MsgInfo struct {
|
|
||||||
Command MsgInfoCommand `json:"cmd"`
|
|
||||||
IDs JSONStringOrArray `json:"id"`
|
|
||||||
Acknowledgement Acknowledgement `json:"ack"`
|
|
||||||
MessageFromJID string `json:"from"`
|
|
||||||
SenderJID string `json:"participant"`
|
|
||||||
ToJID string `json:"to"`
|
|
||||||
Timestamp int64 `json:"t"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MsgInfoHandler interface {
|
|
||||||
whatsapp.Handler
|
|
||||||
HandleMsgInfo(MsgInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) handleMessageMsgInfo(msgType JSONMessageType, message []byte) {
|
|
||||||
var event MsgInfo
|
|
||||||
err := json.Unmarshal(message, &event)
|
|
||||||
if err != nil {
|
|
||||||
ext.jsonParseError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
event.MessageFromJID = strings.Replace(event.MessageFromJID, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
event.SenderJID = strings.Replace(event.SenderJID, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
event.ToJID = strings.Replace(event.ToJID, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
if msgType == MessageMsg {
|
|
||||||
event.SenderJID = event.ToJID
|
|
||||||
}
|
|
||||||
for _, handler := range ext.handlers {
|
|
||||||
msgInfoHandler, ok := handler.(MsgInfoHandler)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext.shouldCallSynchronously(msgInfoHandler) {
|
|
||||||
msgInfoHandler.HandleMsgInfo(event)
|
|
||||||
} else {
|
|
||||||
go msgInfoHandler.HandleMsgInfo(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Presence struct {
|
|
||||||
JID string `json:"id"`
|
|
||||||
SenderJID string `json:"participant"`
|
|
||||||
Status whatsapp.Presence `json:"type"`
|
|
||||||
Timestamp int64 `json:"t"`
|
|
||||||
Deny bool `json:"deny"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PresenceHandler interface {
|
|
||||||
whatsapp.Handler
|
|
||||||
HandlePresence(Presence)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) handleMessagePresence(message []byte) {
|
|
||||||
var event Presence
|
|
||||||
err := json.Unmarshal(message, &event)
|
|
||||||
if err != nil {
|
|
||||||
ext.jsonParseError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
event.JID = strings.Replace(event.JID, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
if len(event.SenderJID) == 0 {
|
|
||||||
event.SenderJID = event.JID
|
|
||||||
} else {
|
|
||||||
event.SenderJID = strings.Replace(event.SenderJID, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
}
|
|
||||||
for _, handler := range ext.handlers {
|
|
||||||
presenceHandler, ok := handler.(PresenceHandler)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext.shouldCallSynchronously(presenceHandler) {
|
|
||||||
presenceHandler.HandlePresence(event)
|
|
||||||
} else {
|
|
||||||
go presenceHandler.HandlePresence(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ProtocolProps struct {
|
|
||||||
WebPresence bool `json:"webPresence"`
|
|
||||||
NotificationQuery bool `json:"notificationQuery"`
|
|
||||||
FacebookCrashLog bool `json:"fbCrashlog"`
|
|
||||||
Bucket string `json:"bucket"`
|
|
||||||
GIFSearch string `json:"gifSearch"`
|
|
||||||
Spam bool `json:"SPAM"`
|
|
||||||
SetBlock bool `json:"SET_BLOCK"`
|
|
||||||
MessageInfo bool `json:"MESSAGE_INFO"`
|
|
||||||
MaxFileSize int `json:"maxFileSize"`
|
|
||||||
Media int `json:"media"`
|
|
||||||
GroupNameLength int `json:"maxSubject"`
|
|
||||||
GroupDescriptionLength int `json:"groupDescLength"`
|
|
||||||
MaxParticipants int `json:"maxParticipants"`
|
|
||||||
VideoMaxEdge int `json:"videoMaxEdge"`
|
|
||||||
ImageMaxEdge int `json:"imageMaxEdge"`
|
|
||||||
ImageMaxKilobytes int `json:"imageMaxKBytes"`
|
|
||||||
Edit int `json:"edit"`
|
|
||||||
FwdUIStartTimestamp int `json:"fwdUiStartTs"`
|
|
||||||
GroupsV3 int `json:"groupsV3"`
|
|
||||||
RestrictGroups int `json:"restrictGroups"`
|
|
||||||
AnnounceGroups int `json:"announceGroups"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProtocolPropsHandler interface {
|
|
||||||
whatsapp.Handler
|
|
||||||
HandleProtocolProps(ProtocolProps)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) handleMessageProps(message []byte) {
|
|
||||||
var event ProtocolProps
|
|
||||||
err := json.Unmarshal(message, &event)
|
|
||||||
if err != nil {
|
|
||||||
ext.jsonParseError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, handler := range ext.handlers {
|
|
||||||
protocolPropsHandler, ok := handler.(ProtocolPropsHandler)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext.shouldCallSynchronously(protocolPropsHandler) {
|
|
||||||
protocolPropsHandler.HandleProtocolProps(event)
|
|
||||||
} else {
|
|
||||||
go protocolPropsHandler.HandleProtocolProps(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MessageRevokeHandler interface {
|
|
||||||
whatsapp.Handler
|
|
||||||
HandleMessageRevoke(key MessageRevocation)
|
|
||||||
}
|
|
||||||
|
|
||||||
type MessageRevocation struct {
|
|
||||||
Id string
|
|
||||||
RemoteJid string
|
|
||||||
FromMe bool
|
|
||||||
Participant string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) HandleRawMessage(message *proto.WebMessageInfo) {
|
|
||||||
protoMsg := message.GetMessage().GetProtocolMessage()
|
|
||||||
if protoMsg != nil && protoMsg.GetType() == proto.ProtocolMessage_REVOKE {
|
|
||||||
key := protoMsg.GetKey()
|
|
||||||
deletedMessage := MessageRevocation{
|
|
||||||
Id: key.GetId(),
|
|
||||||
RemoteJid: key.GetRemoteJid(),
|
|
||||||
FromMe: key.GetFromMe(),
|
|
||||||
Participant: key.GetParticipant(),
|
|
||||||
}
|
|
||||||
for _, handler := range ext.handlers {
|
|
||||||
mrHandler, ok := handler.(MessageRevokeHandler)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext.shouldCallSynchronously(mrHandler) {
|
|
||||||
mrHandler.HandleMessageRevoke(deletedMessage)
|
|
||||||
} else {
|
|
||||||
go mrHandler.HandleMessageRevoke(deletedMessage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StreamType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
StreamUpdate = "update"
|
|
||||||
StreamSleep = "asleep"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StreamEvent struct {
|
|
||||||
Type StreamType
|
|
||||||
|
|
||||||
IsOutdated bool
|
|
||||||
Version string
|
|
||||||
|
|
||||||
Extra []json.RawMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
type StreamEventHandler interface {
|
|
||||||
whatsapp.Handler
|
|
||||||
HandleStreamEvent(StreamEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) handleMessageStream(message []json.RawMessage) {
|
|
||||||
var event StreamEvent
|
|
||||||
err := json.Unmarshal(message[0], &event.Type)
|
|
||||||
if err != nil {
|
|
||||||
ext.jsonParseError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.Type == StreamUpdate && len(message) >= 3 {
|
|
||||||
_ = json.Unmarshal(message[1], &event.IsOutdated)
|
|
||||||
_ = json.Unmarshal(message[2], &event.Version)
|
|
||||||
if len(message) >= 4 {
|
|
||||||
event.Extra = message[3:]
|
|
||||||
}
|
|
||||||
} else if len(message) >= 2 {
|
|
||||||
event.Extra = message[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, handler := range ext.handlers {
|
|
||||||
streamHandler, ok := handler.(StreamEventHandler)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext.shouldCallSynchronously(streamHandler) {
|
|
||||||
streamHandler.HandleStreamEvent(event)
|
|
||||||
} else {
|
|
||||||
go streamHandler.HandleStreamEvent(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,164 +0,0 @@
|
||||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
||||||
// Copyright (C) 2019 Tulir Asokan
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package whatsappExt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
OldUserSuffix = "@c.us"
|
|
||||||
NewUserSuffix = "@s.whatsapp.net"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ExtendedConn struct {
|
|
||||||
*whatsapp.Conn
|
|
||||||
|
|
||||||
handlers []whatsapp.Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExtendConn(conn *whatsapp.Conn) *ExtendedConn {
|
|
||||||
ext := &ExtendedConn{
|
|
||||||
Conn: conn,
|
|
||||||
}
|
|
||||||
ext.Conn.AddHandler(ext)
|
|
||||||
return ext
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) AddHandler(handler whatsapp.Handler) {
|
|
||||||
ext.Conn.AddHandler(handler)
|
|
||||||
ext.handlers = append(ext.handlers, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) RemoveHandler(handler whatsapp.Handler) bool {
|
|
||||||
ext.Conn.RemoveHandler(handler)
|
|
||||||
for i, v := range ext.handlers {
|
|
||||||
if v == handler {
|
|
||||||
ext.handlers = append(ext.handlers[:i], ext.handlers[i+1:]...)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) RemoveHandlers() {
|
|
||||||
ext.Conn.RemoveHandlers()
|
|
||||||
ext.handlers = make([]whatsapp.Handler, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) shouldCallSynchronously(handler whatsapp.Handler) bool {
|
|
||||||
sh, ok := handler.(whatsapp.SyncHandler)
|
|
||||||
return ok && sh.ShouldCallSynchronously()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) ShouldCallSynchronously() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
type GroupInfo struct {
|
|
||||||
JID string `json:"jid"`
|
|
||||||
OwnerJID string `json:"owner"`
|
|
||||||
|
|
||||||
Name string `json:"subject"`
|
|
||||||
NameSetTime int64 `json:"subjectTime"`
|
|
||||||
NameSetBy string `json:"subjectOwner"`
|
|
||||||
|
|
||||||
Announce bool `json:"announce"` // Can only admins send messages?
|
|
||||||
|
|
||||||
Topic string `json:"desc"`
|
|
||||||
TopicID string `json:"descId"`
|
|
||||||
TopicSetAt int64 `json:"descTime"`
|
|
||||||
TopicSetBy string `json:"descOwner"`
|
|
||||||
|
|
||||||
GroupCreated int64 `json:"creation"`
|
|
||||||
|
|
||||||
Status int16 `json:"status"`
|
|
||||||
|
|
||||||
Participants []struct {
|
|
||||||
JID string `json:"id"`
|
|
||||||
IsAdmin bool `json:"isAdmin"`
|
|
||||||
IsSuperAdmin bool `json:"isSuperAdmin"`
|
|
||||||
} `json:"participants"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) GetGroupMetaData(jid string) (*GroupInfo, error) {
|
|
||||||
data, err := ext.Conn.GetGroupMetaData(jid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get group metadata: %v", err)
|
|
||||||
}
|
|
||||||
content := <-data
|
|
||||||
|
|
||||||
info := &GroupInfo{}
|
|
||||||
err = json.Unmarshal([]byte(content), info)
|
|
||||||
if err != nil {
|
|
||||||
return info, fmt.Errorf("failed to unmarshal group metadata: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for index, participant := range info.Participants {
|
|
||||||
info.Participants[index].JID = strings.Replace(participant.JID, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
}
|
|
||||||
info.NameSetBy = strings.Replace(info.NameSetBy, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
info.TopicSetBy = strings.Replace(info.TopicSetBy, OldUserSuffix, NewUserSuffix, 1)
|
|
||||||
|
|
||||||
return info, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProfilePicInfo struct {
|
|
||||||
URL string `json:"eurl"`
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
|
|
||||||
Status int `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ppi *ProfilePicInfo) Download() (io.ReadCloser, error) {
|
|
||||||
resp, err := http.Get(ppi.URL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp.Body, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ppi *ProfilePicInfo) DownloadBytes() ([]byte, error) {
|
|
||||||
body, err := ppi.Download()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer body.Close()
|
|
||||||
data, err := ioutil.ReadAll(body)
|
|
||||||
return data, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ext *ExtendedConn) GetProfilePicThumb(jid string) (*ProfilePicInfo, error) {
|
|
||||||
data, err := ext.Conn.GetProfilePicThumb(jid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get avatar: %v", err)
|
|
||||||
}
|
|
||||||
content := <-data
|
|
||||||
info := &ProfilePicInfo{}
|
|
||||||
err = json.Unmarshal([]byte(content), info)
|
|
||||||
if err != nil {
|
|
||||||
return info, fmt.Errorf("failed to unmarshal avatar info: %v", err)
|
|
||||||
}
|
|
||||||
return info, nil
|
|
||||||
}
|
|
Loading…
Reference in a new issue