From 69dd7f803ae865d48f5b998891407a84f44f74aa Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 17 Feb 2021 01:21:30 +0200 Subject: [PATCH 01/10] Move whatsapp-ext to go-whatsapp --- commands.go | 11 +- config/bridge.go | 4 +- database/message.go | 16 +- database/portal.go | 16 +- database/puppet.go | 8 +- database/user.go | 12 +- formatting.go | 19 +- helm/mautrix-whatsapp/.editorconfig | 2 - helm/mautrix-whatsapp/.gitignore | 2 - helm/mautrix-whatsapp/.helmignore | 22 -- helm/mautrix-whatsapp/Chart.yaml | 14 -- helm/mautrix-whatsapp/requirements.lock | 6 - helm/mautrix-whatsapp/requirements.yaml | 5 - helm/mautrix-whatsapp/templates/NOTES.txt | 12 - helm/mautrix-whatsapp/templates/_helpers.tpl | 55 ---- .../mautrix-whatsapp/templates/configmap.yaml | 45 ---- .../templates/deployment.yaml | 69 ----- helm/mautrix-whatsapp/templates/service.yaml | 16 -- .../templates/serviceaccount.yaml | 8 - helm/mautrix-whatsapp/values.yaml | 137 ---------- main.go | 11 +- matrix.go | 4 + metrics.go | 21 +- portal.go | 40 ++- provisioning.go | 7 +- puppet.go | 19 +- types/types.go | 23 -- user.go | 238 +++++++++--------- whatsapp-ext/call.go | 72 ------ whatsapp-ext/chat.go | 183 -------------- whatsapp-ext/cmd.go | 69 ----- whatsapp-ext/conn.go | 65 ----- whatsapp-ext/group.go | 68 ----- whatsapp-ext/jsonmessage.go | 105 -------- whatsapp-ext/msginfo.go | 95 ------- whatsapp-ext/presence.go | 64 ----- whatsapp-ext/props.go | 73 ------ whatsapp-ext/protomessage.go | 59 ----- whatsapp-ext/stream.go | 76 ------ whatsapp-ext/whatsapp.go | 164 ------------ 40 files changed, 203 insertions(+), 1732 deletions(-) delete mode 100644 helm/mautrix-whatsapp/.editorconfig delete mode 100644 helm/mautrix-whatsapp/.gitignore delete mode 100644 helm/mautrix-whatsapp/.helmignore delete mode 100644 helm/mautrix-whatsapp/Chart.yaml delete mode 100644 helm/mautrix-whatsapp/requirements.lock delete mode 100644 helm/mautrix-whatsapp/requirements.yaml delete mode 100644 helm/mautrix-whatsapp/templates/NOTES.txt delete mode 100644 helm/mautrix-whatsapp/templates/_helpers.tpl delete mode 100644 helm/mautrix-whatsapp/templates/configmap.yaml delete mode 100644 helm/mautrix-whatsapp/templates/deployment.yaml delete mode 100644 helm/mautrix-whatsapp/templates/service.yaml delete mode 100644 helm/mautrix-whatsapp/templates/serviceaccount.yaml delete mode 100644 helm/mautrix-whatsapp/values.yaml delete mode 100644 types/types.go delete mode 100644 whatsapp-ext/call.go delete mode 100644 whatsapp-ext/chat.go delete mode 100644 whatsapp-ext/cmd.go delete mode 100644 whatsapp-ext/conn.go delete mode 100644 whatsapp-ext/group.go delete mode 100644 whatsapp-ext/jsonmessage.go delete mode 100644 whatsapp-ext/msginfo.go delete mode 100644 whatsapp-ext/presence.go delete mode 100644 whatsapp-ext/props.go delete mode 100644 whatsapp-ext/protomessage.go delete mode 100644 whatsapp-ext/stream.go delete mode 100644 whatsapp-ext/whatsapp.go diff --git a/commands.go b/commands.go index fff0808..4174639 100644 --- a/commands.go +++ b/commands.go @@ -35,7 +35,6 @@ import ( "maunium.net/go/mautrix/id" "maunium.net/go/mautrix-whatsapp/database" - "maunium.net/go/mautrix-whatsapp/whatsapp-ext" ) type CommandHandler struct { @@ -737,12 +736,12 @@ const cmdListHelp = `list [page] [items per page] - Get a list func formatContacts(contacts bool, input map[string]whatsapp.Contact) (result []string) { for jid, contact := range input { - if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) != contacts { + if strings.HasSuffix(jid, whatsapp.NewUserSuffix) != contacts { continue } 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 { result = append(result, fmt.Sprintf("* %s - `%s`", contact.Name, contact.Jid)) } @@ -818,8 +817,8 @@ func (handler *CommandHandler) CommandOpen(ce *CommandEvent) { user := ce.User jid := ce.Args[0] - if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) { - ce.Reply("That looks like a user JID. Did you mean `pm %s`?", jid[:len(jid)-len(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(whatsapp.NewUserSuffix)]) return } @@ -865,7 +864,7 @@ func (handler *CommandHandler) CommandPM(ce *CommandEvent) { return } } - jid := number + whatsappExt.NewUserSuffix + jid := number + whatsapp.NewUserSuffix handler.log.Debugln("Importing", jid, "for", user) diff --git a/config/bridge.go b/config/bridge.go index dda00bd..ea5e104 100644 --- a/config/bridge.go +++ b/config/bridge.go @@ -26,8 +26,6 @@ import ( "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" - - "maunium.net/go/mautrix-whatsapp/types" ) type BridgeConfig struct { @@ -185,7 +183,7 @@ func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8 return buf.String(), quality } -func (bc BridgeConfig) FormatUsername(userID types.WhatsAppID) string { +func (bc BridgeConfig) FormatUsername(userID whatsapp.JID) string { var buf bytes.Buffer bc.usernameTemplate.Execute(&buf, userID) return buf.String() diff --git a/database/message.go b/database/message.go index cc776f0..254c9d3 100644 --- a/database/message.go +++ b/database/message.go @@ -22,11 +22,11 @@ import ( "encoding/json" "strings" + "github.com/Rhymen/go-whatsapp" waProto "github.com/Rhymen/go-whatsapp/binary/proto" log "maunium.net/go/maulogger/v2" - "maunium.net/go/mautrix-whatsapp/types" "maunium.net/go/mautrix/id" ) @@ -54,18 +54,18 @@ func (mq *MessageQuery) GetAll(chat PortalKey) (messages []*Message) { return } -func (mq *MessageQuery) GetByJID(chat PortalKey, jid types.WhatsAppMessageID) *Message { - return mq.get("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, content " + +func (mq *MessageQuery) GetByJID(chat PortalKey, jid whatsapp.MessageID) *Message { + return mq.get("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, content "+ "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 { - 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, content "+ "FROM message WHERE mxid=$1", mxid) } 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, content "+ "FROM message WHERE chat_jid=$1 AND chat_receiver=$2 ORDER BY timestamp DESC LIMIT 1", chat.JID, chat.Receiver) if msg == nil || msg.Timestamp == 0 { // Old db, we don't know what the last message is. @@ -87,9 +87,9 @@ type Message struct { log log.Logger Chat PortalKey - JID types.WhatsAppMessageID + JID whatsapp.MessageID MXID id.EventID - Sender types.WhatsAppID + Sender whatsapp.JID Timestamp uint64 Content *waProto.Message } @@ -134,7 +134,7 @@ func (msg *Message) encodeBinaryContent() []byte { } 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 (chat_jid, chat_receiver, jid, mxid, sender, timestamp, content) "+ "VALUES ($1, $2, $3, $4, $5, $6, $7)", msg.Chat.JID, msg.Chat.Receiver, msg.JID, msg.MXID, msg.Sender, msg.Timestamp, msg.encodeBinaryContent()) if err != nil { diff --git a/database/portal.go b/database/portal.go index fc235a3..9db62a9 100644 --- a/database/portal.go +++ b/database/portal.go @@ -22,24 +22,24 @@ import ( 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 { - JID types.WhatsAppID - Receiver types.WhatsAppID + JID whatsapp.JID + Receiver whatsapp.JID } -func GroupPortalKey(jid types.WhatsAppID) PortalKey { +func GroupPortalKey(jid whatsapp.JID) PortalKey { return PortalKey{ JID: jid, Receiver: jid, } } -func NewPortalKey(jid, receiver types.WhatsAppID) PortalKey { +func NewPortalKey(jid, receiver whatsapp.JID) PortalKey { if strings.HasSuffix(jid, "@g.us") { receiver = jid } @@ -80,11 +80,11 @@ func (pq *PortalQuery) GetByMXID(mxid id.RoomID) *Portal { 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) } -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) } diff --git a/database/puppet.go b/database/puppet.go index ef35dd4..b9ba2fc 100644 --- a/database/puppet.go +++ b/database/puppet.go @@ -21,9 +21,9 @@ import ( 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 { @@ -53,7 +53,7 @@ func (pq *PuppetQuery) GetAll() (puppets []*Puppet) { 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) if row == nil { return nil @@ -85,7 +85,7 @@ type Puppet struct { db *Database log log.Logger - JID types.WhatsAppID + JID whatsapp.JID Avatar string AvatarURL id.ContentURI Displayname string diff --git a/database/user.go b/database/user.go index 2e820ff..88367e0 100644 --- a/database/user.go +++ b/database/user.go @@ -26,8 +26,6 @@ import ( log "maunium.net/go/maulogger/v2" - "maunium.net/go/mautrix-whatsapp/types" - "maunium.net/go/mautrix-whatsapp/whatsapp-ext" "maunium.net/go/mautrix/id" ) @@ -63,7 +61,7 @@ func (uq *UserQuery) GetByMXID(userID id.UserID) *User { 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)) if row == nil { return nil @@ -76,7 +74,7 @@ type User struct { log log.Logger MXID id.UserID - JID types.WhatsAppID + JID whatsapp.JID ManagementRoom id.RoomID Session *whatsapp.Session LastConnection uint64 @@ -93,14 +91,14 @@ func (user *User) Scan(row Scannable) *User { return nil } 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{ ClientId: clientID.String, ClientToken: clientToken.String, ServerToken: serverToken.String, EncKey: encKey, MacKey: macKey, - Wid: jid.String + whatsappExt.OldUserSuffix, + Wid: jid.String + whatsapp.OldUserSuffix, } } else { user.Session = nil @@ -108,7 +106,7 @@ func (user *User) Scan(row Scannable) *User { return user } -func stripSuffix(jid types.WhatsAppID) string { +func stripSuffix(jid whatsapp.JID) string { if len(jid) == 0 { return jid } diff --git a/formatting.go b/formatting.go index e150b59..a12a283 100644 --- a/formatting.go +++ b/formatting.go @@ -22,12 +22,11 @@ import ( "regexp" "strings" + "github.com/Rhymen/go-whatsapp" + "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/format" "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]|$)") @@ -58,9 +57,9 @@ func NewFormatter(bridge *Bridge) *Formatter { if mxid[0] == '@' { puppet := bridge.GetPuppetByMXID(id.UserID(mxid)) if puppet != nil { - jids, ok := ctx[mentionedJIDsContextKey].([]types.WhatsAppID) + jids, ok := ctx[mentionedJIDsContextKey].([]whatsapp.JID) if !ok { - ctx[mentionedJIDsContextKey] = []types.WhatsAppID{puppet.JID} + ctx[mentionedJIDsContextKey] = []whatsapp.JID{puppet.JID} } else { ctx[mentionedJIDsContextKey] = append(jids, puppet.JID) } @@ -105,7 +104,7 @@ func NewFormatter(bridge *Bridge) *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 { mxid = user.MXID displayname = string(user.MXID) @@ -116,7 +115,7 @@ func (formatter *Formatter) getMatrixInfoByJID(jid types.WhatsAppID) (mxid id.Us 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) for regex, replacement := range formatter.waReplString { output = regex.ReplaceAllString(output, replacement) @@ -126,7 +125,7 @@ func (formatter *Formatter) ParseWhatsApp(content *event.MessageEventContent, me } for _, jid := range mentionedJIDs { 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(`%s`, mxid, 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) result := formatter.matrixHTMLParser.Parse(html, ctx) - mentionedJIDs, _ := ctx[mentionedJIDsContextKey].([]types.WhatsAppID) + mentionedJIDs, _ := ctx[mentionedJIDsContextKey].([]whatsapp.JID) return result, mentionedJIDs } diff --git a/helm/mautrix-whatsapp/.editorconfig b/helm/mautrix-whatsapp/.editorconfig deleted file mode 100644 index d2097f2..0000000 --- a/helm/mautrix-whatsapp/.editorconfig +++ /dev/null @@ -1,2 +0,0 @@ -[*.{yaml,yml}] -indent_size = 2 diff --git a/helm/mautrix-whatsapp/.gitignore b/helm/mautrix-whatsapp/.gitignore deleted file mode 100644 index 3685de0..0000000 --- a/helm/mautrix-whatsapp/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -charts/* -!*.yaml diff --git a/helm/mautrix-whatsapp/.helmignore b/helm/mautrix-whatsapp/.helmignore deleted file mode 100644 index 50af031..0000000 --- a/helm/mautrix-whatsapp/.helmignore +++ /dev/null @@ -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/ diff --git a/helm/mautrix-whatsapp/Chart.yaml b/helm/mautrix-whatsapp/Chart.yaml deleted file mode 100644 index 2cf274c..0000000 --- a/helm/mautrix-whatsapp/Chart.yaml +++ /dev/null @@ -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 diff --git a/helm/mautrix-whatsapp/requirements.lock b/helm/mautrix-whatsapp/requirements.lock deleted file mode 100644 index c8aa53a..0000000 --- a/helm/mautrix-whatsapp/requirements.lock +++ /dev/null @@ -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" diff --git a/helm/mautrix-whatsapp/requirements.yaml b/helm/mautrix-whatsapp/requirements.yaml deleted file mode 100644 index 1545fe2..0000000 --- a/helm/mautrix-whatsapp/requirements.yaml +++ /dev/null @@ -1,5 +0,0 @@ -dependencies: - - name: postgresql - version: 6.5.0 - repository: https://kubernetes-charts.storage.googleapis.com/ - condition: postgresql.enabled diff --git a/helm/mautrix-whatsapp/templates/NOTES.txt b/helm/mautrix-whatsapp/templates/NOTES.txt deleted file mode 100644 index 58b07ce..0000000 --- a/helm/mautrix-whatsapp/templates/NOTES.txt +++ /dev/null @@ -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 diff --git a/helm/mautrix-whatsapp/templates/_helpers.tpl b/helm/mautrix-whatsapp/templates/_helpers.tpl deleted file mode 100644 index 1255560..0000000 --- a/helm/mautrix-whatsapp/templates/_helpers.tpl +++ /dev/null @@ -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 -}} diff --git a/helm/mautrix-whatsapp/templates/configmap.yaml b/helm/mautrix-whatsapp/templates/configmap.yaml deleted file mode 100644 index 9abd656..0000000 --- a/helm/mautrix-whatsapp/templates/configmap.yaml +++ /dev/null @@ -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: "" diff --git a/helm/mautrix-whatsapp/templates/deployment.yaml b/helm/mautrix-whatsapp/templates/deployment.yaml deleted file mode 100644 index 0bd5581..0000000 --- a/helm/mautrix-whatsapp/templates/deployment.yaml +++ /dev/null @@ -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 }} diff --git a/helm/mautrix-whatsapp/templates/service.yaml b/helm/mautrix-whatsapp/templates/service.yaml deleted file mode 100644 index 94145e7..0000000 --- a/helm/mautrix-whatsapp/templates/service.yaml +++ /dev/null @@ -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 }} diff --git a/helm/mautrix-whatsapp/templates/serviceaccount.yaml b/helm/mautrix-whatsapp/templates/serviceaccount.yaml deleted file mode 100644 index 57903c5..0000000 --- a/helm/mautrix-whatsapp/templates/serviceaccount.yaml +++ /dev/null @@ -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 -}} diff --git a/helm/mautrix-whatsapp/values.yaml b/helm/mautrix-whatsapp/values.yaml deleted file mode 100644 index 989cc9f..0000000 --- a/helm/mautrix-whatsapp/values.yaml +++ /dev/null @@ -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 ` instead of `!wa relaybot `. 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: "{{ .Sender.Displayname }}: {{ .Message }}" - m.notice: "{{ .Sender.Displayname }}: {{ .Message }}" - m.emote: "* {{ .Sender.Displayname }} {{ .Message }}" - m.file: "{{ .Sender.Displayname }} sent a file" - m.image: "{{ .Sender.Displayname }} sent an image" - m.audio: "{{ .Sender.Displayname }} sent an audio file" - m.video: "{{ .Sender.Displayname }} sent a video" - m.location: "{{ .Sender.Displayname }} sent a location" - -logging: - timestamp_format: Jan _2, 2006 15:04:05 - print_level: debug diff --git a/main.go b/main.go index 8aae7fa..f16862b 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,8 @@ import ( "syscall" "time" + "github.com/Rhymen/go-whatsapp" + flag "maunium.net/go/mauflag" log "maunium.net/go/maulogger/v2" @@ -36,7 +38,6 @@ import ( "maunium.net/go/mautrix-whatsapp/config" "maunium.net/go/mautrix-whatsapp/database" "maunium.net/go/mautrix-whatsapp/database/upgrades" - "maunium.net/go/mautrix-whatsapp/types" ) var ( @@ -137,14 +138,14 @@ type Bridge struct { Metrics *MetricsHandler usersByMXID map[id.UserID]*User - usersByJID map[types.WhatsAppID]*User + usersByJID map[whatsapp.JID]*User usersLock sync.Mutex managementRooms map[id.RoomID]*User managementRoomsLock sync.Mutex portalsByMXID map[id.RoomID]*Portal portalsByJID map[database.PortalKey]*Portal portalsLock sync.Mutex - puppets map[types.WhatsAppID]*Puppet + puppets map[whatsapp.JID]*Puppet puppetsByCustomMXID map[id.UserID]*Puppet puppetsLock sync.Mutex } @@ -163,11 +164,11 @@ type Crypto interface { func NewBridge() *Bridge { bridge := &Bridge{ usersByMXID: make(map[id.UserID]*User), - usersByJID: make(map[types.WhatsAppID]*User), + usersByJID: make(map[whatsapp.JID]*User), managementRooms: make(map[id.RoomID]*User), portalsByMXID: make(map[id.RoomID]*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), } diff --git a/matrix.go b/matrix.go index 3bb33e6..b413e61 100644 --- a/matrix.go +++ b/matrix.go @@ -254,6 +254,10 @@ func (mx *MatrixHandler) HandleMembership(evt *event.Event) { return } + if mx.shouldIgnoreEvent(evt) { + return + } + user := mx.bridge.GetUserByMXID(evt.Sender) if user == nil || !user.Whitelisted || !user.IsConnected() { return diff --git a/metrics.go b/metrics.go index 8db555d..7cd9cfc 100644 --- a/metrics.go +++ b/metrics.go @@ -27,11 +27,12 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" log "maunium.net/go/maulogger/v2" + "github.com/Rhymen/go-whatsapp" + "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" "maunium.net/go/mautrix-whatsapp/database" - "maunium.net/go/mautrix-whatsapp/types" ) type MetricsHandler struct { @@ -56,11 +57,11 @@ type MetricsHandler struct { unencryptedPrivateCount prometheus.Gauge connected prometheus.Gauge - connectedState map[types.WhatsAppID]bool + connectedState map[whatsapp.JID]bool loggedIn prometheus.Gauge - loggedInState map[types.WhatsAppID]bool + loggedInState map[whatsapp.JID]bool syncLocked prometheus.Gauge - syncLockedState map[types.WhatsAppID]bool + syncLockedState map[whatsapp.JID]bool bufferLength *prometheus.GaugeVec } @@ -109,17 +110,17 @@ func NewMetricsHandler(address string, log log.Logger, db *database.Database) *M Name: "bridge_logged_in", Help: "Users logged into the bridge", }), - loggedInState: make(map[types.WhatsAppID]bool), + loggedInState: make(map[whatsapp.JID]bool), connected: promauto.NewGauge(prometheus.GaugeOpts{ Name: "bridge_connected", Help: "Bridge users connected to WhatsApp", }), - connectedState: make(map[types.WhatsAppID]bool), + connectedState: make(map[whatsapp.JID]bool), syncLocked: promauto.NewGauge(prometheus.GaugeOpts{ Name: "bridge_sync_locked", 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{ Name: "bridge_buffer_size", 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() } -func (mh *MetricsHandler) TrackLoginState(jid types.WhatsAppID, loggedIn bool) { +func (mh *MetricsHandler) TrackLoginState(jid whatsapp.JID, loggedIn bool) { if !mh.running { 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 { 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 { return } diff --git a/portal.go b/portal.go index bdfa8fa..cc0dc29 100644 --- a/portal.go +++ b/portal.go @@ -40,23 +40,19 @@ import ( "sync" "time" - log "maunium.net/go/maulogger/v2" - - "maunium.net/go/mautrix/crypto/attachment" - "github.com/Rhymen/go-whatsapp" waProto "github.com/Rhymen/go-whatsapp/binary/proto" + log "maunium.net/go/maulogger/v2" "maunium.net/go/mautrix" "maunium.net/go/mautrix/appservice" + "maunium.net/go/mautrix/crypto/attachment" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/format" "maunium.net/go/mautrix/id" "maunium.net/go/mautrix/pushrules" "maunium.net/go/mautrix-whatsapp/database" - "maunium.net/go/mautrix-whatsapp/types" - "maunium.net/go/mautrix-whatsapp/whatsapp-ext" ) func (bridge *Bridge) GetPortalByMXID(mxid id.RoomID) *Portal { @@ -83,7 +79,7 @@ func (bridge *Bridge) GetAllPortals() []*Portal { 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)) } @@ -131,7 +127,7 @@ func (bridge *Bridge) NewManualPortal(key database.PortalKey) *Portal { bridge: bridge, 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), } @@ -146,7 +142,7 @@ func (bridge *Bridge) NewPortal(dbPortal *database.Portal) *Portal { bridge: bridge, 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), } @@ -171,7 +167,7 @@ type Portal struct { roomCreateLock sync.Mutex - recentlyHandled [recentlyHandledLength]types.WhatsAppMessageID + recentlyHandled [recentlyHandledLength]whatsapp.MessageID recentlyHandledLock sync.Mutex recentlyHandledIndex uint8 @@ -256,7 +252,7 @@ func (portal *Portal) handleMessage(msg PortalMessage, isBackfill bool) { portal.HandleLocationMessage(msg.source, data) case whatsapp.StubMessage: portal.HandleStubMessage(msg.source, data, isBackfill) - case whatsappExt.MessageRevocation: + case whatsapp.MessageRevocation: portal.HandleMessageRevoke(msg.source, data) case FakeMessage: portal.HandleFakeMessage(msg.source, data) @@ -265,7 +261,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 for i := start; i != start; i = (i - 1) % recentlyHandledLength { if portal.recentlyHandled[i] == id { @@ -275,7 +271,7 @@ func (portal *Portal) isRecentlyHandled(id types.WhatsAppMessageID) bool { 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) if msg != nil { return true @@ -355,7 +351,7 @@ func (portal *Portal) finishHandling(source *User, message *waProto.WebMessageIn portal.log.Debugln("Handled message", message.GetKey().GetId(), "->", mxid) } -func (portal *Portal) SyncParticipants(metadata *whatsappExt.GroupInfo) { +func (portal *Portal) SyncParticipants(metadata *whatsapp.GroupInfo) { changed := false levels, err := portal.MainIntent().PowerLevels(portal.MXID) if err != nil { @@ -413,7 +409,7 @@ func (portal *Portal) SyncParticipants(metadata *whatsappExt.GroupInfo) { } } -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) { var err error avatar, err = user.Conn.GetProfilePicThumb(portal.Key.JID) @@ -464,7 +460,7 @@ func (portal *Portal) UpdateAvatar(user *User, avatar *whatsappExt.ProfilePicInf 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 portal.Name != name { portal.log.Debugfln("Updating name %s -> %s", portal.Name, name) portal.Name = name @@ -488,7 +484,7 @@ func (portal *Portal) UpdateName(name string, setBy types.WhatsAppID, intent *ap 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 { portal.log.Debugfln("Updating topic %s -> %s", portal.Topic, topic) portal.Topic = topic @@ -974,7 +970,7 @@ func (portal *Portal) CreateMatrixRoom(user *User) error { portal.log.Infoln("Creating Matrix room. Info source:", user.MXID) - var metadata *whatsappExt.GroupInfo + var metadata *whatsapp.GroupInfo if portal.IsPrivateChat() { puppet := portal.bridge.GetPuppetByJID(portal.Key.JID) if portal.bridge.Config.Bridge.PrivateChatPortalMeta { @@ -1099,7 +1095,7 @@ func (portal *Portal) CreateMatrixRoom(user *User) error { func (portal *Portal) IsPrivateChat() bool { if portal.isPrivate == nil { - val := strings.HasSuffix(portal.Key.JID, whatsappExt.NewUserSuffix) + val := strings.HasSuffix(portal.Key.JID, whatsapp.NewUserSuffix) portal.isPrivate = &val } return *portal.isPrivate @@ -1152,7 +1148,7 @@ func (portal *Portal) SetReply(content *event.MessageEventContent, info whatsapp 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) if msg == nil || msg.IsFakeMXID() { return @@ -1794,7 +1790,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 { var caption string - var mentionedJIDs []types.WhatsAppID + var mentionedJIDs []whatsapp.JID if relaybotFormatted { caption, mentionedJIDs = portal.bridge.Formatter.ParseMatrix(content.FormattedBody) } @@ -1851,7 +1847,7 @@ func (portal *Portal) preprocessMatrixMedia(sender *User, relaybotFormatted bool type MediaUpload struct { Caption string - MentionedJIDs []types.WhatsAppID + MentionedJIDs []whatsapp.JID URL string MediaKey []byte FileEncSHA256 []byte diff --git a/provisioning.go b/provisioning.go index 33069f4..b720bdb 100644 --- a/provisioning.go +++ b/provisioning.go @@ -27,12 +27,11 @@ import ( "time" "github.com/gorilla/websocket" - log "maunium.net/go/maulogger/v2" "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 { @@ -433,7 +432,7 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) { } user.log.Debugln("Successful login via provisioning API") user.ConnectionErrors = 0 - user.JID = strings.Replace(user.Conn.Info.Wid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, 1) + user.JID = strings.Replace(user.Conn.Info.Wid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1) user.addToJIDMap() user.SetSession(&session) _ = c.WriteJSON(map[string]interface{}{ diff --git a/puppet.go b/puppet.go index 03f3ac8..7c29460 100644 --- a/puppet.go +++ b/puppet.go @@ -22,21 +22,18 @@ import ( "regexp" "strings" - log "maunium.net/go/maulogger/v2" - "github.com/Rhymen/go-whatsapp" + log "maunium.net/go/maulogger/v2" "maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/id" "maunium.net/go/mautrix-whatsapp/database" - "maunium.net/go/mautrix-whatsapp/types" - "maunium.net/go/mautrix-whatsapp/whatsapp-ext" ) 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 { userIDRegex = regexp.MustCompile(fmt.Sprintf("^@%s:%s$", bridge.Config.Bridge.FormatUsername("([0-9]+)"), @@ -47,7 +44,7 @@ func (bridge *Bridge) ParsePuppetMXID(mxid id.UserID) (types.WhatsAppID, bool) { return "", false } - jid := types.WhatsAppID(match[1] + whatsappExt.NewUserSuffix) + jid := whatsapp.JID(match[1] + whatsapp.NewUserSuffix) return jid, true } @@ -60,7 +57,7 @@ func (bridge *Bridge) GetPuppetByMXID(mxid id.UserID) *Puppet { return bridge.GetPuppetByJID(jid) } -func (bridge *Bridge) GetPuppetByJID(jid types.WhatsAppID) *Puppet { +func (bridge *Bridge) GetPuppetByJID(jid whatsapp.JID) *Puppet { bridge.puppetsLock.Lock() defer bridge.puppetsLock.Unlock() puppet, ok := bridge.puppets[jid] @@ -125,12 +122,12 @@ func (bridge *Bridge) dbPuppetsToPuppets(dbPuppets []*database.Puppet) []*Puppet return output } -func (bridge *Bridge) FormatPuppetMXID(jid types.WhatsAppID) id.UserID { +func (bridge *Bridge) FormatPuppetMXID(jid whatsapp.JID) id.UserID { return id.NewUserID( bridge.Config.Bridge.FormatUsername( strings.Replace( jid, - whatsappExt.NewUserSuffix, "", 1)), + whatsapp.NewUserSuffix, "", 1)), bridge.Config.Homeserver.Domain) } @@ -161,7 +158,7 @@ type Puppet struct { } 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 { @@ -181,7 +178,7 @@ func (puppet *Puppet) DefaultIntent() *appservice.IntentAPI { 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 { var err error avatar, err = source.Conn.GetProfilePicThumb(puppet.JID) diff --git a/types/types.go b/types/types.go deleted file mode 100644 index e58655a..0000000 --- a/types/types.go +++ /dev/null @@ -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 . - -package types - -// WhatsAppID is a WhatsApp JID. -type WhatsAppID = string - -// WhatsAppMessageID is the internal ID of a WhatsApp message. -type WhatsAppMessageID = string diff --git a/user.go b/user.go index 1edfffb..5135a31 100644 --- a/user.go +++ b/user.go @@ -40,13 +40,11 @@ import ( "maunium.net/go/mautrix/id" "maunium.net/go/mautrix-whatsapp/database" - "maunium.net/go/mautrix-whatsapp/types" - "maunium.net/go/mautrix-whatsapp/whatsapp-ext" ) type User struct { *database.User - Conn *whatsappExt.ExtendedConn + Conn *whatsapp.Conn bridge *Bridge log log.Logger @@ -91,7 +89,7 @@ func (bridge *Bridge) GetUserByMXID(userID id.UserID) *User { return user } -func (bridge *Bridge) GetUserByJID(userID types.WhatsAppID) *User { +func (bridge *Bridge) GetUserByJID(userID whatsapp.JID) *User { bridge.usersLock.Lock() defer bridge.usersLock.Unlock() user, ok := bridge.usersByJID[userID] @@ -264,7 +262,7 @@ func (user *User) Connect(evenIfNoSession bool) bool { user.connLock.Unlock() return false } - user.Conn = whatsappExt.ExtendConn(conn) + user.Conn = conn user.log.Debugln("WhatsApp connection successful") user.Conn.AddHandler(user) user.connLock.Unlock() @@ -419,7 +417,7 @@ func (user *User) Login(ce *CommandEvent) { // 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) user.ConnectionErrors = 0 - user.JID = strings.Replace(user.Conn.Info.Wid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, 1) + user.JID = strings.Replace(user.Conn.Info.Wid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1) user.addToJIDMap() user.SetSession(&session) ce.Reply("Successfully logged in, synchronizing chats...") @@ -501,7 +499,7 @@ func (user *User) sendMarkdownBridgeAlert(formatString string, args ...interface } } -func (user *User) postConnPing(conn *whatsappExt.ExtendedConn) bool { +func (user *User) postConnPing(conn *whatsapp.Conn) bool { if user.Conn != conn { user.log.Warnln("Connection changed before scheduled post-connection ping, canceling ping") return false @@ -528,7 +526,7 @@ func (user *User) postConnPing(conn *whatsappExt.ExtendedConn) bool { } } -func (user *User) intPostLogin(conn *whatsappExt.ExtendedConn) { +func (user *User) intPostLogin(conn *whatsapp.Conn) { defer user.syncWait.Done() user.lastReconnection = time.Now().Unix() user.createCommunity() @@ -559,8 +557,60 @@ func (user *User) intPostLogin(conn *whatsappExt.ExtendedConn) { } } -func (user *User) HandleStreamEvent(evt whatsappExt.StreamEvent) { - if evt.Type == whatsappExt.StreamSleep { +type InfoGetter interface { + GetInfo() whatsapp.MessageInfo +} + +func (user *User) HandleEvent(event interface{}) { + switch v := event.(type) { + case whatsapp.TextMessage, whatsapp.ImageMessage, whatsapp.StickerMessage, whatsapp.VideoMessage, + whatsapp.AudioMessage, whatsapp.DocumentMessage, whatsapp.ContactMessage, whatsapp.StubMessage, + whatsapp.LocationMessage: + info := v.(InfoGetter).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 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() { user.lastReconnection = 0 user.log.Infoln("Stream went to sleep soon after reconnection, making new post-connection ping in 20 seconds") @@ -738,7 +788,7 @@ func (user *User) syncPuppets(contacts map[string]whatsapp.Contact) { } user.log.Infoln("Syncing puppet info from 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.Sync(user, contact) } @@ -841,19 +891,11 @@ func (user *User) tryReconnect(msg string) { } } -func (user *User) ShouldCallSynchronously() bool { - 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 { +func (user *User) PortalKey(jid whatsapp.JID) database.PortalKey { 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)) } @@ -888,13 +930,11 @@ func (user *User) handleMessageLoop() { func (user *User) HandleNewContact(contact whatsapp.Contact) { user.log.Debugfln("Contact message: %+v", contact) - go func() { - if strings.HasSuffix(contact.Jid, whatsappExt.OldUserSuffix) { - contact.Jid = strings.Replace(contact.Jid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, -1) - } - puppet := user.bridge.GetPuppetByJID(contact.Jid) - puppet.UpdateName(user, contact) - }() + if strings.HasSuffix(contact.Jid, whatsapp.OldUserSuffix) { + contact.Jid = strings.Replace(contact.Jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, -1) + } + puppet := user.bridge.GetPuppetByJID(contact.Jid) + puppet.UpdateName(user, contact) } func (user *User) HandleBatteryMessage(battery whatsapp.BatteryMessage) { @@ -914,53 +954,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 { Text string ID string Alert bool } -func (user *User) HandleCallInfo(info whatsappExt.CallInfo) { +func (user *User) HandleCallInfo(info whatsapp.CallInfo) { if info.Data != nil { return } @@ -968,19 +968,19 @@ func (user *User) HandleCallInfo(info whatsappExt.CallInfo) { ID: info.ID, } switch info.Type { - case whatsappExt.CallOffer: + case whatsapp.CallOffer: if !user.bridge.Config.Bridge.CallNotices.Start { return } data.Text = "Incoming call" data.Alert = true - case whatsappExt.CallOfferVideo: + case whatsapp.CallOfferVideo: if !user.bridge.Config.Bridge.CallNotices.Start { return } data.Text = "Incoming video call" data.Alert = true - case whatsappExt.CallTerminate: + case whatsapp.CallTerminate: if !user.bridge.Config.Bridge.CallNotices.End { return } @@ -995,7 +995,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) switch info.Status { case whatsapp.PresenceUnavailable: @@ -1023,33 +1023,31 @@ func (user *User) HandlePresence(info whatsappExt.Presence) { } } -func (user *User) HandleMsgInfo(info whatsappExt.MsgInfo) { - if (info.Command == whatsappExt.MsgInfoCommandAck || info.Command == whatsappExt.MsgInfoCommandAcks) && info.Acknowledgement == whatsappExt.AckMessageRead { +func (user *User) HandleMsgInfo(info whatsapp.JSONMsgInfo) { + if (info.Command == whatsapp.MsgInfoCommandAck || info.Command == whatsapp.MsgInfoCommandAcks) && info.Acknowledgement == whatsapp.AckMessageRead { portal := user.GetPortalByJID(info.ToJID) if len(portal.MXID) == 0 { return } - go func() { - intent := user.bridge.GetPuppetByJID(info.SenderJID).IntentFor(portal) - for _, msgID := range info.IDs { - msg := user.bridge.DB.Message.GetByJID(portal.Key, msgID) - if msg == nil || msg.IsFakeMXID() { - continue - } - - err := intent.MarkRead(portal.MXID, msg.MXID) - if err != nil { - user.log.Warnln("Failed to mark message %s as read by %s: %v", msg.MXID, info.SenderJID, err) - } + intent := user.bridge.GetPuppetByJID(info.SenderJID).IntentFor(portal) + for _, msgID := range info.IDs { + msg := user.bridge.DB.Message.GetByJID(portal.Key, msgID) + if msg == nil || msg.IsFakeMXID() { + continue } - }() + + err := intent.MarkRead(portal.MXID, msg.MXID) + if err != nil { + 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) { if received.Type == "read" { - user.markSelfRead(received.Jid, received.Index) + go user.markSelfRead(received.Jid, received.Index) } else { user.log.Debugfln("Unknown received message type: %+v", received) } @@ -1057,12 +1055,12 @@ func (user *User) HandleReceivedMessage(received whatsapp.ReceivedMessage) { func (user *User) HandleReadMessage(read whatsapp.ReadMessage) { 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) { - if strings.HasSuffix(jid, whatsappExt.OldUserSuffix) { - jid = strings.Replace(jid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, -1) + if strings.HasSuffix(jid, whatsapp.OldUserSuffix) { + jid = strings.Replace(jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, -1) } puppet := user.bridge.GetPuppetByJID(user.JID) if puppet == nil { @@ -1096,17 +1094,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 { - case whatsappExt.CommandPicture: - if strings.HasSuffix(cmd.JID, whatsappExt.NewUserSuffix) { + case whatsapp.CommandPicture: + if strings.HasSuffix(cmd.JID, whatsapp.NewUserSuffix) { puppet := user.bridge.GetPuppetByJID(cmd.JID) go puppet.UpdateAvatar(user, cmd.ProfilePicInfo) } else if user.bridge.Config.Bridge.ChatMetaSync { portal := user.GetPortalByJID(cmd.JID) go portal.UpdateAvatar(user, cmd.ProfilePicInfo, true) } - case whatsappExt.CommandDisconnect: + case whatsapp.CommandDisconnect: if cmd.Kind == "replaced" { user.cleanDisconnection = true go user.sendMarkdownBridgeAlert("\u26a0 Your WhatsApp connection was closed by the server because you opened another WhatsApp Web client.\n\n" + @@ -1119,14 +1117,14 @@ func (user *User) HandleCommand(cmd whatsappExt.Command) { } } -func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) { - if cmd.Command != whatsappExt.ChatUpdateCommandAction { +func (user *User) HandleChatUpdate(cmd whatsapp.ChatUpdate) { + if cmd.Command != whatsapp.ChatUpdateCommandAction { return } portal := user.GetPortalByJID(cmd.JID) 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() { err := portal.CreateMatrixRoom(user) if err != nil { @@ -1139,11 +1137,11 @@ func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) { // These don't come down the message history :( switch cmd.Data.Action { - case whatsappExt.ChatActionAddTopic: - go portal.UpdateTopic(cmd.Data.AddTopic.Topic, cmd.Data.SenderJID, nil,true) - case whatsappExt.ChatActionRemoveTopic: - go portal.UpdateTopic("", cmd.Data.SenderJID, nil,true) - case whatsappExt.ChatActionRemove: + case whatsapp.ChatActionAddTopic: + go portal.UpdateTopic(cmd.Data.AddTopic.Topic, cmd.Data.SenderJID, nil, true) + case whatsapp.ChatActionRemoveTopic: + go portal.UpdateTopic("", cmd.Data.SenderJID, nil, true) + case whatsapp.ChatActionRemove: // 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. if !user.bridge.Config.Bridge.ChatMetaSync { @@ -1162,45 +1160,35 @@ func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) { } switch cmd.Data.Action { - case whatsappExt.ChatActionNameChange: + case whatsapp.ChatActionNameChange: 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) - case whatsappExt.ChatActionDemote: + case whatsapp.ChatActionDemote: go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, false) - case whatsappExt.ChatActionAnnounce: + case whatsapp.ChatActionAnnounce: go portal.RestrictMessageSending(cmd.Data.Announce) - case whatsappExt.ChatActionRestrict: + case whatsapp.ChatActionRestrict: go portal.RestrictMetadataChanges(cmd.Data.Restrict) - case whatsappExt.ChatActionRemove: + case whatsapp.ChatActionRemove: 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) - case whatsappExt.ChatActionIntroduce: + case whatsapp.ChatActionIntroduce: if cmd.Data.SenderJID != "unknown" { go portal.Sync(user, whatsapp.Contact{Jid: portal.Key.JID}) } } } -func (user *User) HandleJsonMessage(message string) { - var msg json.RawMessage - err := json.Unmarshal([]byte(message), &msg) - if err != nil { +func (user *User) HandleJSONMessage(message json.RawMessage) { + if !json.Valid(message) { return } - user.log.Debugln("JSON message:", message) + user.log.Debugfln("JSON message: %s", message) 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 { return !user.HasSession() || !user.IsInPortal(portal.Key) } diff --git a/whatsapp-ext/call.go b/whatsapp-ext/call.go deleted file mode 100644 index ce6d361..0000000 --- a/whatsapp-ext/call.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/whatsapp-ext/chat.go b/whatsapp-ext/chat.go deleted file mode 100644 index f444906..0000000 --- a/whatsapp-ext/chat.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/whatsapp-ext/cmd.go b/whatsapp-ext/cmd.go deleted file mode 100644 index 185a1c5..0000000 --- a/whatsapp-ext/cmd.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/whatsapp-ext/conn.go b/whatsapp-ext/conn.go deleted file mode 100644 index 12162b8..0000000 --- a/whatsapp-ext/conn.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/whatsapp-ext/group.go b/whatsapp-ext/group.go deleted file mode 100644 index 5992e39..0000000 --- a/whatsapp-ext/group.go +++ /dev/null @@ -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 . - -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 -} diff --git a/whatsapp-ext/jsonmessage.go b/whatsapp-ext/jsonmessage.go deleted file mode 100644 index d7340e1..0000000 --- a/whatsapp-ext/jsonmessage.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/whatsapp-ext/msginfo.go b/whatsapp-ext/msginfo.go deleted file mode 100644 index d6588ab..0000000 --- a/whatsapp-ext/msginfo.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/whatsapp-ext/presence.go b/whatsapp-ext/presence.go deleted file mode 100644 index 16ec982..0000000 --- a/whatsapp-ext/presence.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/whatsapp-ext/props.go b/whatsapp-ext/props.go deleted file mode 100644 index 76d99f5..0000000 --- a/whatsapp-ext/props.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/whatsapp-ext/protomessage.go b/whatsapp-ext/protomessage.go deleted file mode 100644 index 620ee46..0000000 --- a/whatsapp-ext/protomessage.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/whatsapp-ext/stream.go b/whatsapp-ext/stream.go deleted file mode 100644 index 9f8fd8b..0000000 --- a/whatsapp-ext/stream.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/whatsapp-ext/whatsapp.go b/whatsapp-ext/whatsapp.go deleted file mode 100644 index f47c62e..0000000 --- a/whatsapp-ext/whatsapp.go +++ /dev/null @@ -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 . - -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 -} From ca118e8678b220e93747f4cbf51ac189fc58d253 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 17 Feb 2021 01:22:06 +0200 Subject: [PATCH 02/10] Handle 404 errors when backfilling messages --- database/message.go | 29 +++++++++++++------ .../2021-02-17-message-sent-status.go | 12 ++++++++ database/upgrades/upgrades.go | 2 +- portal.go | 27 ++++++++--------- 4 files changed, 47 insertions(+), 23 deletions(-) create mode 100644 database/upgrades/2021-02-17-message-sent-status.go diff --git a/database/message.go b/database/message.go index 254c9d3..1b6bbbf 100644 --- a/database/message.go +++ b/database/message.go @@ -43,7 +43,7 @@ func (mq *MessageQuery) New() *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 { return nil } @@ -55,18 +55,19 @@ func (mq *MessageQuery) GetAll(chat PortalKey) (messages []*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) } 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) } func (mq *MessageQuery) GetLastInChat(chat PortalKey) *Message { - msg := mq.get("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, content "+ - "FROM message WHERE chat_jid=$1 AND chat_receiver=$2 ORDER BY timestamp DESC LIMIT 1", chat.JID, chat.Receiver) + msg := mq.get("SELECT chat_jid, chat_receiver, jid, mxid, sender, timestamp, sent, content "+ + "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 { // Old db, we don't know what the last message is. return nil @@ -91,6 +92,7 @@ type Message struct { MXID id.EventID Sender whatsapp.JID Timestamp uint64 + Sent bool Content *waProto.Message } @@ -100,7 +102,7 @@ func (msg *Message) IsFakeMXID() bool { func (msg *Message) Scan(row Scannable) *Message { 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 != sql.ErrNoRows { msg.log.Errorln("Database scan failed:", err) @@ -134,14 +136,23 @@ func (msg *Message) encodeBinaryContent() []byte { } func (msg *Message) Insert() { - _, err := msg.db.Exec("INSERT INTO message (chat_jid, chat_receiver, jid, mxid, sender, timestamp, content) "+ - "VALUES ($1, $2, $3, $4, $5, $6, $7)", - msg.Chat.JID, msg.Chat.Receiver, msg.JID, msg.MXID, msg.Sender, msg.Timestamp, msg.encodeBinaryContent()) + _, err := msg.db.Exec(`INSERT INTO message + (chat_jid, chat_receiver, jid, mxid, sender, timestamp, sent, content) + 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 { 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() { _, 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 { diff --git a/database/upgrades/2021-02-17-message-sent-status.go b/database/upgrades/2021-02-17-message-sent-status.go new file mode 100644 index 0000000..a5852b0 --- /dev/null +++ b/database/upgrades/2021-02-17-message-sent-status.go @@ -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 + }} +} diff --git a/database/upgrades/upgrades.go b/database/upgrades/upgrades.go index e224593..af54f6c 100644 --- a/database/upgrades/upgrades.go +++ b/database/upgrades/upgrades.go @@ -39,7 +39,7 @@ type upgrade struct { fn upgradeFunc } -const NumberOfUpgrades = 20 +const NumberOfUpgrades = 21 var upgrades [NumberOfUpgrades]upgrade diff --git a/portal.go b/portal.go index cc0dc29..d1703ab 100644 --- a/portal.go +++ b/portal.go @@ -283,7 +283,7 @@ func init() { 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.Chat = portal.Key msg.JID = message.GetKey().GetId() @@ -300,6 +300,7 @@ func (portal *Portal) markHandled(source *User, message *waProto.WebMessageInfo, } } msg.Content = message.Message + msg.Sent = isSent msg.Insert() portal.recentlyHandledLock.Lock() @@ -307,6 +308,7 @@ func (portal *Portal) markHandled(source *User, message *waProto.WebMessageInfo, portal.recentlyHandledIndex = (portal.recentlyHandledIndex + 1) % recentlyHandledLength portal.recentlyHandledLock.Unlock() portal.recentlyHandled[index] = msg.JID + return msg } func (portal *Portal) getMessageIntent(user *User, info whatsapp.MessageInfo) *appservice.IntentAPI { @@ -346,7 +348,7 @@ func (portal *Portal) startHandling(source *User, info whatsapp.MessageInfo) *ap } 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.log.Debugln("Handled message", message.GetKey().GetId(), "->", mxid) } @@ -735,6 +737,10 @@ func (portal *Portal) BackfillHistory(user *User, lastMessageTime uint64) error for len(lastMessageID) > 0 { portal.log.Debugln("Fetching 50 messages of history after", lastMessageID) 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 { return err } @@ -1322,7 +1328,7 @@ func (portal *Portal) HandleStubMessage(source *User, message whatsapp.StubMessa if len(eventID) == 0 { 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) { @@ -2087,12 +2093,12 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) { if info == nil { 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.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) go sender.Conn.SendRaw(info, errChan) @@ -2112,16 +2118,11 @@ func (portal *Portal) sendRaw(sender *User, evt *event.Event, info *waProto.WebM } if err != nil { 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 { portal.log.Debugfln("Handled Matrix event %s", evt.ID) portal.sendDeliveryReceipt(evt.ID) + dbMsg.MarkSent() } if errorEventID != "" { _, err = portal.MainIntent().RedactEvent(portal.MXID, errorEventID) From 7bd47fabb2d7ab45af7bf4590a60506a0eb40edf Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 18 Feb 2021 23:36:14 +0200 Subject: [PATCH 03/10] Update go-whatsapp to break everything and maybe improve things --- commands.go | 49 +++++------- config/bridge.go | 6 +- database/user.go | 6 +- go.mod | 4 +- go.sum | 6 ++ main.go | 4 +- portal.go | 4 +- provisioning.go | 52 +++++-------- puppet.go | 4 +- user.go | 191 ++++++++++++++++++++++++++++------------------- 10 files changed, 173 insertions(+), 153 deletions(-) diff --git a/commands.go b/commands.go index 4174639..b4a3b18 100644 --- a/commands.go +++ b/commands.go @@ -17,6 +17,7 @@ package main import ( + "context" "errors" "fmt" "math" @@ -254,7 +255,7 @@ func (handler *CommandHandler) CommandJoin(ce *CommandEvent) { handler.log.Debugln("%s successfully joined group %s", ce.User.MXID, jid) portal := handler.bridge.GetPortalByJID(database.GroupPortalKey(jid)) 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) } else { err = portal.CreateMatrixRoom(ce.User) @@ -411,11 +412,11 @@ func (handler *CommandHandler) CommandLogout(ce *CommandEvent) { ce.Reply("Unknown error while logging out: %v", err) return } - ce.User.Disconnect() ce.User.removeFromJIDMap() // TODO this causes a foreign key violation, which should be fixed //ce.User.JID = "" ce.User.SetSession(nil) + ce.User.DeleteConnection() ce.Reply("Logged out successfully.") } @@ -469,9 +470,10 @@ func (handler *CommandHandler) CommandDeleteSession(ce *CommandEvent) { ce.Reply("Nothing to purge: no session information stored and no active connection.") return } - ce.User.Disconnect() + //ce.User.JID = "" ce.User.removeFromJIDMap() ce.User.SetSession(nil) + ce.User.DeleteConnection() ce.Reply("Session information purged") } @@ -489,24 +491,21 @@ func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) { } wasConnected := true - sess, err := ce.User.Conn.Disconnect() + err := ce.User.Conn.Disconnect() if err == whatsapp.ErrNotConnected { wasConnected = false } else if err != nil { 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 ce.User.Session != nil { ce.User.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...") - var sess whatsapp.Session - sess, err = ce.User.Conn.RestoreWithSession(*ce.User.Session) - if err == nil { - ce.User.SetSession(&sess) - } + ce.User.Conn.SetSession(*ce.User.Session) + err = ce.User.Conn.Restore(true, ctx) } else { ce.Reply("You are not logged in.") return @@ -520,17 +519,11 @@ func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) { } if err != nil { 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...") - sess, err = ce.User.Conn.Disconnect() + err = ce.User.Conn.Disconnect() if err != nil { ce.User.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err) - } else { - ce.User.SetSession(&sess) } return } @@ -553,7 +546,7 @@ func (handler *CommandHandler) CommandDeleteConnection(ce *CommandEvent) { ce.Reply("You don't have a WhatsApp connection.") return } - ce.User.Disconnect() + ce.User.DeleteConnection() ce.Reply("Successfully disconnected. Use the `reconnect` command to reconnect.") } @@ -564,7 +557,7 @@ func (handler *CommandHandler) CommandDisconnect(ce *CommandEvent) { ce.Reply("You don't have a WhatsApp connection.") return } - sess, err := ce.User.Conn.Disconnect() + err := ce.User.Conn.Disconnect() if err == whatsapp.ErrNotConnected { ce.Reply("You were not connected.") return @@ -572,8 +565,6 @@ func (handler *CommandHandler) CommandDisconnect(ce *CommandEvent) { ce.User.log.Warnln("Error while disconnecting:", err) ce.Reply("Unknown error while disconnecting: %v", err) return - } else { - ce.User.SetSession(&sess) } ce.User.bridge.Metrics.TrackConnectionState(ce.User.JID, false) ce.Reply("Successfully disconnected. Use the `reconnect` command to reconnect.") @@ -741,9 +732,9 @@ func formatContacts(contacts bool, input map[string]whatsapp.Contact) (result [] } if contacts { - result = append(result, fmt.Sprintf("* %s / %s - `%s`", contact.Name, contact.Notify, contact.Jid[:len(contact.Jid)-len(whatsapp.NewUserSuffix)])) + result = append(result, fmt.Sprintf("* %s / %s - `%s`", contact.Name, contact.Notify, contact.JID[:len(contact.JID)-len(whatsapp.NewUserSuffix)])) } 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)) @@ -875,11 +866,11 @@ func (handler *CommandHandler) CommandPM(ce *CommandEvent) { "To create a portal anyway, use `pm --force `.") 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) - 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 { err := portal.MainIntent().EnsureInvited(portal.MXID, user.MXID) if err != nil { diff --git a/config/bridge.go b/config/bridge.go index ea5e104..f55c997 100644 --- a/config/bridge.go +++ b/config/bridge.go @@ -165,8 +165,8 @@ type UsernameTemplateArgs struct { func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8) { var buf bytes.Buffer - if index := strings.IndexRune(contact.Jid, '@'); index > 0 { - contact.Jid = "+" + contact.Jid[:index] + if index := strings.IndexRune(contact.JID, '@'); index > 0 { + contact.JID = "+" + contact.JID[:index] } bc.displaynameTemplate.Execute(&buf, contact) var quality int8 @@ -175,7 +175,7 @@ func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8 quality = 3 case len(contact.Name) > 0 || len(contact.Short) > 0: quality = 2 - case len(contact.Jid) > 0: + case len(contact.JID) > 0: quality = 1 default: quality = 0 diff --git a/database/user.go b/database/user.go index 88367e0..2a52fcd 100644 --- a/database/user.go +++ b/database/user.go @@ -93,7 +93,7 @@ func (user *User) Scan(row Scannable) *User { if len(jid.String) > 0 && len(clientID.String) > 0 { user.JID = jid.String + whatsapp.NewUserSuffix user.Session = &whatsapp.Session{ - ClientId: clientID.String, + ClientID: clientID.String, ClientToken: clientToken.String, ServerToken: serverToken.String, EncKey: encKey, @@ -139,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)`, user.MXID, 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) if err != nil { user.log.Warnfln("Failed to insert %s: %v", user.MXID, err) } @@ -158,7 +158,7 @@ func (user *User) Update() { 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`, 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) if err != nil { user.log.Warnfln("Failed to update %s: %v", user.MXID, err) diff --git a/go.mod b/go.mod index fa035b8..0ee1e54 100644 --- a/go.mod +++ b/go.mod @@ -12,8 +12,8 @@ require ( github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e gopkg.in/yaml.v2 v2.3.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 ) -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.3.22-0.20210218211744-b9f35ff6257a diff --git a/go.sum b/go.sum index 379f950..22148fd 100644 --- a/go.sum +++ b/go.sum @@ -145,6 +145,8 @@ github.com/tulir/go-whatsapp v0.3.20 h1:nK92MgruqXwk+QlaAS39xhzHNbFvJIEgUIOUrN3i github.com/tulir/go-whatsapp v0.3.20/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o= github.com/tulir/go-whatsapp v0.3.21 h1:2m7gUw4oHX4kIpMmP9VwCR7KEUK/PHhXLygPFGF9XfI= github.com/tulir/go-whatsapp v0.3.21/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o= +github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a h1:8JSW6oIAgI1TtR7wkvhNpTYhjKWBxk/eFyB8qXeOfyg= +github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= 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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -232,6 +234,10 @@ 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/maulogger/v2 v2.1.1 h1:NAZNc6XUFJzgzfewCzVoGkxNAsblLCSSEdtDuIjP0XA= maunium.net/go/maulogger/v2 v2.1.1/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= +maunium.net/go/maulogger/v2 v2.2.1 h1:qwEDOdT7OhwqvFBXhSD0lqW2O2Oc/DbP/uv3zaai0W8= +maunium.net/go/maulogger/v2 v2.2.1/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= +maunium.net/go/maulogger/v2 v2.2.2 h1:NCw+7Be1GQFm8xXJ4M2C0Q8yLBTx3c5s7UZ4y1anZMU= +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.0-rc.3/go.mod h1:TtVePxoEaw6+RZDKVajw66Yaj1lqLjH8l4FF3krsqWY= maunium.net/go/mautrix v0.8.0-rc.4 h1:3JXoL2JJPE5nh/YSw9sv9dQA9ulma9yHTMOBMBY1xdo= diff --git a/main.go b/main.go index f16862b..693ce50 100644 --- a/main.go +++ b/main.go @@ -384,11 +384,9 @@ func (bridge *Bridge) Stop() { continue } bridge.Log.Debugln("Disconnecting", user.MXID) - sess, err := user.Conn.Disconnect() + err := user.Conn.Disconnect() if err != nil { bridge.Log.Errorfln("Error while disconnecting %s: %v", user.MXID, err) - } else { - user.SetSession(&sess) } } } diff --git a/portal.go b/portal.go index d1703ab..3799339 100644 --- a/portal.go +++ b/portal.go @@ -1771,7 +1771,7 @@ func (portal *Portal) convertGifToVideo(gif []byte) ([]byte, error) { "-pix_fmt", "yuv420p", "-c:v", "libx264", "-movflags", "+faststart", "-filter:v", "crop='floor(in_w/2)*2:floor(in_h/2)*2'", outputFileName) - vcLog := portal.log.Sub("VideoConverter").WithDefaultLevel(log.LevelWarn) + vcLog := portal.log.Sub("VideoConverter").Writer(log.LevelWarn) cmd.Stdout = vcLog cmd.Stderr = vcLog @@ -2319,7 +2319,7 @@ func (portal *Portal) HandleMatrixMeta(sender *User, evt *event.Event) { return } 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: return } diff --git a/provisioning.go b/provisioning.go index b720bdb..0768883 100644 --- a/provisioning.go +++ b/provisioning.go @@ -125,7 +125,7 @@ func (prov *ProvisioningAPI) DeleteSession(w http.ResponseWriter, r *http.Reques }) return } - user.Disconnect() + user.DeleteConnection() user.SetSession(nil) jsonResponse(w, http.StatusOK, Response{true, "Session information purged"}) } @@ -139,7 +139,7 @@ func (prov *ProvisioningAPI) DeleteConnection(w http.ResponseWriter, r *http.Req }) return } - user.Disconnect() + user.DeleteConnection() jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp and connection deleted"}) } @@ -152,7 +152,7 @@ func (prov *ProvisioningAPI) Disconnect(w http.ResponseWriter, r *http.Request) }) return } - sess, err := user.Conn.Disconnect() + err := user.Conn.Disconnect() if err == whatsapp.ErrNotConnected { jsonResponse(w, http.StatusNotFound, Error{ Error: "You were not connected", @@ -166,8 +166,6 @@ func (prov *ProvisioningAPI) Disconnect(w http.ResponseWriter, r *http.Request) ErrCode: err.Error(), }) return - } else { - user.SetSession(&sess) } user.bridge.Metrics.TrackConnectionState(user.JID, false) jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp"}) @@ -190,25 +188,21 @@ func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) { user.log.Debugln("Received /reconnect request, disconnecting") wasConnected := true - sess, err := user.Conn.Disconnect() + err := user.Conn.Disconnect() if err == whatsapp.ErrNotConnected { wasConnected = false } else if err != nil { user.log.Warnln("Error while disconnecting:", err) - } else { - user.SetSession(&sess) } 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) if err == whatsapp.ErrInvalidSession { if user.Session != nil { user.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...") - sess, err = user.Conn.RestoreWithSession(*user.Session) - if err == nil { - user.SetSession(&sess) - } + user.Conn.SetSession(*user.Session) + err = user.Conn.Restore(true, r.Context()) } else { jsonResponse(w, http.StatusForbidden, Error{ Error: "You're not logged in", @@ -216,7 +210,8 @@ func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) { }) return } - } else if err == whatsapp.ErrLoginInProgress { + } + if err == whatsapp.ErrLoginInProgress { jsonResponse(w, http.StatusConflict, Error{ Error: "A login or reconnection is already in progress.", ErrCode: "login in progress", @@ -231,23 +226,14 @@ func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) { } if err != nil { user.log.Warnln("Error while reconnecting:", err) - if errors.Is(err, whatsapp.ErrRestoreSessionTimeout) { - 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), - ErrCode: err.Error(), - }) - } + jsonResponse(w, http.StatusInternalServerError, Error{ + Error: fmt.Sprintf("Unknown error while reconnecting: %v", err), + ErrCode: err.Error(), + }) user.log.Debugln("Disconnecting due to failed session restore in reconnect command...") - sess, err := user.Conn.Disconnect() + err = user.Conn.Disconnect() if err != nil { user.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err) - } else { - user.SetSession(&sess) } return } @@ -339,7 +325,7 @@ func (prov *ProvisioningAPI) Logout(w http.ResponseWriter, r *http.Request) { return } } - user.Disconnect() + user.DeleteConnection() } user.bridge.Metrics.TrackConnectionState(user.JID, false) @@ -407,7 +393,7 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) { }) 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" if err != nil { var msg string @@ -419,7 +405,7 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) { msg = "QR code scan timed out. Please try again." } else if errors.Is(err, whatsapp.ErrInvalidWebsocket) { msg = "WhatsApp connection error. Please try again." - user.Disconnect() + // TODO might need to make sure it reconnects? } else { msg = fmt.Sprintf("Unknown error while logging in: %v", err) } @@ -430,9 +416,9 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) { }) return } - user.log.Debugln("Successful login via provisioning API") + user.log.Debugln("Successful login as", jid, "via provisioning API") user.ConnectionErrors = 0 - user.JID = strings.Replace(user.Conn.Info.Wid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1) + user.JID = strings.Replace(jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1) user.addToJIDMap() user.SetSession(&session) _ = c.WriteJSON(map[string]interface{}{ diff --git a/puppet.go b/puppet.go index 7c29460..2549e9e 100644 --- a/puppet.go +++ b/puppet.go @@ -291,8 +291,8 @@ func (puppet *Puppet) Sync(source *User, contact whatsapp.Contact) { puppet.log.Errorln("Failed to ensure registered:", err) } - if contact.Jid == source.JID { - contact.Notify = source.Conn.Info.Pushname + if contact.JID == source.JID { + contact.Notify = source.pushName } update := false diff --git a/user.go b/user.go index 5135a31..4c4b873 100644 --- a/user.go +++ b/user.go @@ -17,6 +17,7 @@ package main import ( + "context" "encoding/json" "errors" "fmt" @@ -61,6 +62,7 @@ type User struct { cleanDisconnection bool batteryWarningsSent int lastReconnection int64 + pushName string chatListReceived chan struct{} syncPortalsDone chan struct{} @@ -71,8 +73,9 @@ type User struct { syncStart chan struct{} syncWait sync.WaitGroup - mgmtCreateLock sync.Mutex - connLock sync.Mutex + mgmtCreateLock sync.Mutex + connLock sync.Mutex + cancelReconnect func() } func (bridge *Bridge) GetUserByMXID(userID id.UserID) *User { @@ -234,55 +237,56 @@ func (user *User) SetSession(session *whatsapp.Session) { func (user *User) Connect(evenIfNoSession bool) bool { user.connLock.Lock() - if user.Conn != nil && user.Conn.IsConnected() { + if user.Conn != nil { user.connLock.Unlock() - return true + if user.Conn.IsConnected() { + return true + } else { + return user.RestoreSession() + } } else if !evenIfNoSession && user.Session == nil { user.connLock.Unlock() return false } - if user.Conn != nil { - user.Disconnect() - } user.log.Debugln("Connecting to WhatsApp") timeout := time.Duration(user.bridge.Config.Bridge.ConnectionTimeout) if timeout == 0 { timeout = 20 } - conn, err := whatsapp.NewConnWithOptions(&whatsapp.Options{ + user.Conn = whatsapp.NewConn(&whatsapp.Options{ Timeout: timeout * time.Second, LongClientName: user.bridge.Config.WhatsApp.OSName, ShortClientName: user.bridge.Config.WhatsApp.BrowserName, 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 = conn - user.log.Debugln("WhatsApp connection successful") - user.Conn.AddHandler(user) user.connLock.Unlock() return user.RestoreSession() } -func (user *User) Disconnect() { - sess, err := user.Conn.Disconnect() +func (user *User) DeleteConnection() { + user.connLock.Lock() + if user.Conn == nil { + user.connLock.Unlock() + return + } + err := user.Conn.Disconnect() if err != nil && err != whatsapp.ErrNotConnected { user.log.Warnln("Error disconnecting: %v", err) } - user.SetSession(&sess) user.Conn.RemoveHandlers() user.Conn = nil user.bridge.Metrics.TrackConnectionState(user.JID, false) + user.connLock.Unlock() } func (user *User) RestoreSession() bool { 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 { return true } else if err != nil { @@ -290,24 +294,23 @@ func (user *User) RestoreSession() bool { if errors.Is(err, whatsapp.ErrUnpaired) { user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp: unpaired from phone. " + "To re-pair your phone, log in again.") - user.Disconnect() user.removeFromJIDMap() //user.JID = "" user.SetSession(nil) + user.DeleteConnection() return false } else { user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp. Make sure WhatsApp " + "on your phone is reachable and use `reconnect` to try connecting again.") } user.log.Debugln("Disconnecting due to failed session restore...") - _, err := user.Conn.Disconnect() + err = user.Conn.Disconnect() if err != nil { user.log.Errorln("Failed to disconnect after failed session restore:", err) } return false } user.ConnectionErrors = 0 - user.SetSession(&sess) user.log.Debugln("Session restored successfully") user.PostLogin() } @@ -382,7 +385,7 @@ func (user *User) Login(ce *CommandEvent) { qrChan := make(chan string, 3) eventIDChan := make(chan id.EventID, 1) 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" if err != nil { var eventID id.EventID @@ -416,8 +419,9 @@ func (user *User) Login(ce *CommandEvent) { } // 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) + user.log.Debugln("Successful login as", jid, "via command") user.ConnectionErrors = 0 - user.JID = strings.Replace(user.Conn.Info.Wid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1) + user.JID = strings.Replace(jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1) user.addToJIDMap() user.SetSession(&session) ce.Reply("Successfully logged in, synchronizing chats...") @@ -499,31 +503,31 @@ func (user *User) sendMarkdownBridgeAlert(formatString string, args ...interface } } -func (user *User) postConnPing(conn *whatsapp.Conn) bool { - if user.Conn != conn { - user.log.Warnln("Connection changed before scheduled post-connection ping, canceling ping") - return false - } +func (user *User) postConnPing() bool { user.log.Debugln("Making post-connection ping") - err := conn.AdminTest() - if err != nil { - user.log.Errorfln("Post-connection ping failed: %v. Disconnecting and then reconnecting after a second", err) - sess, disconnectErr := conn.Disconnect() - if disconnectErr != nil { - user.log.Warnln("Error while disconnecting after failed post-connection ping:", disconnectErr) + var err error + 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 { - user.Session = &sess + break } - user.bridge.Metrics.TrackDisconnection(user.MXID) - go func() { - time.Sleep(1 * time.Second) - user.tryReconnect(fmt.Sprintf("Post-connection ping failed: %v", err)) - }() - return false - } else { - user.log.Debugln("Post-connection ping OK") - return true } + user.log.Errorfln("Post-connection ping failed: %v. Disconnecting and then reconnecting after a second", err) + disconnectErr := user.Conn.Disconnect() + if disconnectErr != nil { + user.log.Warnln("Error while disconnecting after failed post-connection ping:", disconnectErr) + } + user.bridge.Metrics.TrackDisconnection(user.MXID) + go func() { + time.Sleep(1 * time.Second) + user.tryReconnect(fmt.Sprintf("Post-connection ping failed: %v", err)) + }() + return false } func (user *User) intPostLogin(conn *whatsapp.Conn) { @@ -538,11 +542,11 @@ func (user *User) intPostLogin(conn *whatsapp.Conn) { user.log.Debugln("Chat list receive confirmation received in PostLogin") case <-time.After(time.Duration(user.bridge.Config.Bridge.ChatListWait) * time.Second): user.log.Warnln("Timed out waiting for chat list to arrive!") - user.postConnPing(conn) + user.postConnPing() return } - if !user.postConnPing(conn) { + if !user.postConnPing() { user.log.Debugln("Post-connection ping failed, unlocking processing of incoming messages.") return } @@ -557,16 +561,14 @@ func (user *User) intPostLogin(conn *whatsapp.Conn) { } } -type InfoGetter interface { +type NormalMessage interface { GetInfo() whatsapp.MessageInfo } func (user *User) HandleEvent(event interface{}) { switch v := event.(type) { - case whatsapp.TextMessage, whatsapp.ImageMessage, whatsapp.StickerMessage, whatsapp.VideoMessage, - whatsapp.AudioMessage, whatsapp.DocumentMessage, whatsapp.ContactMessage, whatsapp.StubMessage, - whatsapp.LocationMessage: - info := v.(InfoGetter).GetInfo() + 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} @@ -596,6 +598,8 @@ func (user *User) HandleEvent(event interface{}) { user.HandleCommand(v) case whatsapp.ChatUpdate: user.HandleChatUpdate(v) + case whatsapp.ConnInfo: + user.HandleConnInfo(v) case json.RawMessage: user.HandleJSONMessage(v) case *waProto.WebMessageInfo: @@ -614,11 +618,11 @@ func (user *User) HandleStreamEvent(evt whatsapp.StreamEvent) { if user.lastReconnection+60 > time.Now().Unix() { user.lastReconnection = 0 user.log.Infoln("Stream went to sleep soon after reconnection, making new post-connection ping in 20 seconds") - conn := user.Conn go func() { time.Sleep(20 * time.Second) // 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 { @@ -630,10 +634,10 @@ func (user *User) HandleChatList(chats []whatsapp.Chat) { user.log.Infoln("Chat list received") chatMap := make(map[string]whatsapp.Chat) for _, chat := range user.Conn.Store.Chats { - chatMap[chat.Jid] = chat + chatMap[chat.JID] = chat } for _, chat := range chats { - chatMap[chat.Jid] = chat + chatMap[chat.JID] = chat } select { case user.chatListReceived <- struct{}{}: @@ -655,14 +659,14 @@ func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool) for _, chat := range chatMap { ts, err := strconv.ParseUint(chat.LastMessageTime, 10, 64) 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 } - portal := user.GetPortalByJID(chat.Jid) + portal := user.GetPortalByJID(chat.JID) chats = append(chats, Chat{ Portal: portal, - Contact: user.Conn.Store.Contacts[chat.Jid], + Contact: user.Conn.Store.Contacts[chat.JID], LastMessageTime: ts, }) var inCommunity, ok bool @@ -777,7 +781,7 @@ func (user *User) UpdateDirectChats(chats map[id.UserID][]id.RoomID) { func (user *User) HandleContactList(contacts []whatsapp.Contact) { contactMap := make(map[string]whatsapp.Contact) for _, contact := range contacts { - contactMap[contact.Jid] = contact + contactMap[contact.JID] = contact } go user.syncPuppets(contactMap) } @@ -786,10 +790,20 @@ func (user *User) syncPuppets(contacts map[string]whatsapp.Contact) { if contacts == nil { 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") for jid, contact := range contacts { if strings.HasSuffix(jid, whatsapp.NewUserSuffix) { - puppet := user.bridge.GetPuppetByJID(contact.Jid) + puppet := user.bridge.GetPuppetByJID(contact.JID) puppet.Sync(user, contact) } } @@ -846,14 +860,18 @@ func (user *User) tryReconnect(msg string) { baseDelay = -baseDelay + 1 } delay := baseDelay - conn := user.Conn takeover := false + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + user.cancelReconnect = cancel for user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts { - if user.Conn != conn { - user.log.Debugln("Connection was recreated, aborting reconnection attempts") + select { + case <-ctx.Done(): + user.log.Debugln("tryReconnect context cancelled, aborting reconnection attempts") return + default: } - err := conn.Restore(takeover) + err := user.Conn.Restore(takeover, ctx) takeover = true if err == nil { user.ConnectionErrors = 0 @@ -863,14 +881,23 @@ func (user *User) tryReconnect(msg string) { user.PostLogin() return } else if errors.Is(err, whatsapp.ErrBadRequest) { - user.log.Infoln("Got init 400 error when trying to reconnect, resetting connection...") - sess, err := conn.Disconnect() + user.log.Warnln("Got init 400 error when trying to reconnect, resetting connection...") + err = user.Conn.Disconnect() if err != nil { 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++ user.ConnectionErrors++ if user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts { @@ -930,10 +957,10 @@ func (user *User) handleMessageLoop() { func (user *User) HandleNewContact(contact whatsapp.Contact) { user.log.Debugfln("Contact message: %+v", contact) - if strings.HasSuffix(contact.Jid, whatsapp.OldUserSuffix) { - contact.Jid = strings.Replace(contact.Jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, -1) + if strings.HasSuffix(contact.JID, whatsapp.OldUserSuffix) { + contact.JID = strings.Replace(contact.JID, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, -1) } - puppet := user.bridge.GetPuppetByJID(contact.Jid) + puppet := user.bridge.GetPuppetByJID(contact.JID) puppet.UpdateName(user, contact) } @@ -1176,11 +1203,23 @@ func (user *User) HandleChatUpdate(cmd whatsapp.ChatUpdate) { go portal.HandleWhatsAppInvite(cmd.Data.SenderJID, nil, cmd.Data.UserChange.JIDs) case whatsapp.ChatActionIntroduce: 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) HandleConnInfo(info whatsapp.ConnInfo) { + if user.Session != nil && info.Connected && len(info.ClientToken) > 0 { + user.log.Debugln("Received new tokens") + user.Session.ClientToken = info.ClientToken + user.Session.ServerToken = info.ServerToken + user.Session.Wid = info.WID + } + if len(info.PushName) > 0 { + user.pushName = info.PushName + } +} + func (user *User) HandleJSONMessage(message json.RawMessage) { if !json.Valid(message) { return From 15b43506226eb9361f1e6116b3f7fd63eae42bbd Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 19 Feb 2021 20:39:50 +0200 Subject: [PATCH 04/10] Save session to db when changed --- user.go | 1 + 1 file changed, 1 insertion(+) diff --git a/user.go b/user.go index 4c4b873..a7c64b0 100644 --- a/user.go +++ b/user.go @@ -1214,6 +1214,7 @@ func (user *User) HandleConnInfo(info whatsapp.ConnInfo) { 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 From 325e54e949a7846374f9617bb03f71243fc76cd6 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 21 Feb 2021 02:06:33 +0200 Subject: [PATCH 05/10] Update go-whatsapp --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 0ee1e54..abb4980 100644 --- a/go.mod +++ b/go.mod @@ -16,4 +16,4 @@ require ( maunium.net/go/mautrix v0.8.2 ) -replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a +replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94 diff --git a/go.sum b/go.sum index 22148fd..96b3614 100644 --- a/go.sum +++ b/go.sum @@ -147,6 +147,8 @@ github.com/tulir/go-whatsapp v0.3.21 h1:2m7gUw4oHX4kIpMmP9VwCR7KEUK/PHhXLygPFGF9 github.com/tulir/go-whatsapp v0.3.21/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o= github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a h1:8JSW6oIAgI1TtR7wkvhNpTYhjKWBxk/eFyB8qXeOfyg= github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= +github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94 h1:G4YvxLMW80U7xhcRXHZANZ6N9xnIGBNp6wlqzuSqtw4= +github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= 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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= From fcad5fb0571e71c4501bddab630053fc8389ae51 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 21 Feb 2021 13:22:06 +0200 Subject: [PATCH 06/10] Update dependencies --- go.mod | 8 +- go.sum | 277 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 281 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index abb4980..56e7d1d 100644 --- a/go.mod +++ b/go.mod @@ -5,15 +5,15 @@ go 1.14 require ( github.com/Rhymen/go-whatsapp v0.1.0 github.com/gorilla/websocket v1.4.2 - github.com/lib/pq v1.7.0 - github.com/mattn/go-sqlite3 v1.14.0 - github.com/prometheus/client_golang v1.7.0 + github.com/lib/pq v1.9.0 + github.com/mattn/go-sqlite3 v1.14.6 + github.com/prometheus/client_golang v1.9.0 github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e gopkg.in/yaml.v2 v2.3.0 maunium.net/go/mauflag v1.0.0 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.22-0.20210221000549-ec31478c7b94 diff --git a/go.sum b/go.sum index 96b3614..00321d3 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,33 @@ 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/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +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/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-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-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +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 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/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/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= @@ -21,23 +38,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/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/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/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/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/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 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/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/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/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.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.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/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.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/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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -50,68 +96,199 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq 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.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.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.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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +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/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/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/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/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/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.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/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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 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/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= +github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +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.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= +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/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-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 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-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.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 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.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/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.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.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.0 h1:wCi7urQOGBsYcQROHqpUUX4ct84xp40t9R9JX0FuA/U= 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-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-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/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.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= 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-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.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +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 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 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.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/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.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -120,13 +297,22 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P 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.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/gjson v1.6.8 h1:CTmXMClGYPAmln7652e69B7OLXfTi5ABcPPwjIWUv7w= +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.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= +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.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8= github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU= +github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/sjson v1.1.1 h1:7h1vk049Jnd5EH9NyzNiEuwYW4b5qgreBbqRC19AS3U= github.com/tidwall/sjson v1.1.1/go.mod h1:yvVuSnpEQv5cYIrO+AT6kw4QVfd5SDZoGIS7/5+fZFs= +github.com/tidwall/sjson v1.1.5 h1:wsUceI/XDyZk3J1FUvuuYlK62zJv2HO2Pzb8A5EWdUE= +github.com/tidwall/sjson v1.1.5/go.mod h1:VuJzsZnTowhSxWdOgsAnb886i4AjEyTkk7tNtsL7EYE= +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.3.13/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o= github.com/tulir/go-whatsapp v0.3.14 h1:VKXBMw6GvGKRQTceEQ9Dcg6wR+jRy5/G8SDPbisWZqs= @@ -149,64 +335,142 @@ github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a h1:8JSW6oIAgI github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94 h1:G4YvxLMW80U7xhcRXHZANZ6N9xnIGBNp6wlqzuSqtw4= github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 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-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-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-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-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= +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/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20180218175443-cbe0f9307d01/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-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-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-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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/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-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201026091529-146b70c837a4 h1:awiuzyrRjJDb+OXi9ceHO3SDxVoN3JER57mhtqkdQBs= 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-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-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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-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-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-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-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-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= 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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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-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-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-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= +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.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/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-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.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.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/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= @@ -220,9 +484,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= 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 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/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/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/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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -230,8 +501,10 @@ 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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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-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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= maunium.net/go/maulogger/v2 v2.1.1 h1:NAZNc6XUFJzgzfewCzVoGkxNAsblLCSSEdtDuIjP0XA= @@ -250,3 +523,7 @@ 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= +maunium.net/go/mautrix v0.8.3 h1:nKGdARVCf2w7thEN5GEbAjYrlLjKLX44jOdB1h+BV7U= +maunium.net/go/mautrix v0.8.3/go.mod h1:LPbb/DeAmtOPKnGbJazL9g11cO3mMAaEbLE8udd98BU= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= From a911a0c1a99857a31842098dbfa6b88bbf57b75b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 21 Feb 2021 14:18:15 +0200 Subject: [PATCH 07/10] Add better support for broadcast lists --- database/portal.go | 2 +- go.mod | 2 +- go.sum | 2 ++ portal.go | 49 +++++++++++++++++++++++++++++++++++----------- user.go | 16 +++++++++++---- 5 files changed, 54 insertions(+), 17 deletions(-) diff --git a/database/portal.go b/database/portal.go index 9db62a9..3f26fac 100644 --- a/database/portal.go +++ b/database/portal.go @@ -40,7 +40,7 @@ func GroupPortalKey(jid whatsapp.JID) PortalKey { } func NewPortalKey(jid, receiver whatsapp.JID) PortalKey { - if strings.HasSuffix(jid, "@g.us") { + if strings.HasSuffix(jid, whatsapp.GroupSuffix) { receiver = jid } return PortalKey{ diff --git a/go.mod b/go.mod index 56e7d1d..432250a 100644 --- a/go.mod +++ b/go.mod @@ -16,4 +16,4 @@ require ( maunium.net/go/mautrix v0.8.3 ) -replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94 +replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.22-0.20210221121735-6d3eaaa7bdc5 diff --git a/go.sum b/go.sum index 00321d3..7454f81 100644 --- a/go.sum +++ b/go.sum @@ -335,6 +335,8 @@ github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a h1:8JSW6oIAgI github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94 h1:G4YvxLMW80U7xhcRXHZANZ6N9xnIGBNp6wlqzuSqtw4= github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= +github.com/tulir/go-whatsapp v0.3.22-0.20210221121735-6d3eaaa7bdc5 h1:4Y5xQdpuLEt4DQavhnP/Ium1zpwIE+LeOFwVyiW4qoY= +github.com/tulir/go-whatsapp v0.3.22-0.20210221121735-6d3eaaa7bdc5/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= diff --git a/portal.go b/portal.go index 3799339..c848ed2 100644 --- a/portal.go +++ b/portal.go @@ -55,6 +55,11 @@ import ( "maunium.net/go/mautrix-whatsapp/database" ) +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 { bridge.portalsLock.Lock() defer bridge.portalsLock.Unlock() @@ -180,6 +185,7 @@ type Portal struct { messages chan PortalMessage isPrivate *bool + isBroadcast *bool hasRelaybot *bool } @@ -463,6 +469,9 @@ func (portal *Portal) UpdateAvatar(user *User, avatar *whatsapp.ProfilePicInfo, } func (portal *Portal) UpdateName(name string, setBy whatsapp.JID, intent *appservice.IntentAPI, updateInfo bool) bool { + if name == "" && portal.IsBroadcastRoom() { + name = UnnamedBroadcastName + } if portal.Name != name { portal.log.Debugfln("Updating name %s -> %s", portal.Name, name) portal.Name = name @@ -515,8 +524,14 @@ func (portal *Portal) UpdateMetadata(user *User) bool { return false } else if portal.IsStatusBroadcastRoom() { update := false - update = portal.UpdateName("WhatsApp Status Broadcast", "", nil, false) || update - update = portal.UpdateTopic("WhatsApp status updates from your contacts", "", nil, false) || update + update = portal.UpdateName(StatusBroadcastName, "", nil, false) || update + update = portal.UpdateTopic(StatusBroadcastTopic, "", nil, false) || update + return update + } else if portal.IsBroadcastRoom() { + update := false + 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 } metadata, err := user.Conn.GetGroupMetaData(portal.Key.JID) @@ -595,13 +610,9 @@ func (portal *Portal) Sync(user *User, contact whatsapp.Contact) { portal.ensureUserInvited(user) } - if portal.IsPrivateChat() { - return - } - update := false update = portal.UpdateMetadata(user) || update - if !portal.IsStatusBroadcastRoom() && portal.Avatar == "" { + if !portal.IsPrivateChat() && !portal.IsBroadcastRoom() && portal.Avatar == "" { update = portal.UpdateAvatar(user, nil, false) || update } if update { @@ -990,6 +1001,14 @@ func (portal *Portal) CreateMatrixRoom(user *User) error { } else if portal.IsStatusBroadcastRoom() { portal.Name = "WhatsApp Status Broadcast" portal.Topic = "WhatsApp status updates from your contacts" + } else if portal.IsBroadcastRoom() { + contact, ok := user.Conn.Store.Contacts[portal.Key.JID] + if ok { + portal.Name = contact.Name + } else { + portal.Name = UnnamedBroadcastName + } + portal.Topic = BroadcastTopic } else { var err error metadata, err = user.Conn.GetGroupMetaData(portal.Key.JID) @@ -1107,6 +1126,18 @@ func (portal *Portal) IsPrivateChat() bool { return *portal.isPrivate } +func (portal *Portal) IsBroadcastRoom() bool { + if portal.isBroadcast == nil { + val := strings.HasSuffix(portal.Key.JID, whatsapp.BroadcastSuffix) + portal.isBroadcast = &val + } + return *portal.isBroadcast +} + +func (portal *Portal) IsStatusBroadcastRoom() bool { + return portal.Key.JID == "status@broadcast" +} + func (portal *Portal) HasRelaybot() bool { if portal.bridge.Relaybot == nil { return false @@ -1117,10 +1148,6 @@ func (portal *Portal) HasRelaybot() bool { return *portal.hasRelaybot } -func (portal *Portal) IsStatusBroadcastRoom() bool { - return portal.Key.JID == "status@broadcast" -} - func (portal *Portal) MainIntent() *appservice.IntentAPI { if portal.IsPrivateChat() { return portal.bridge.GetPuppetByJID(portal.Key.JID).DefaultIntent() diff --git a/user.go b/user.go index a7c64b0..31318b0 100644 --- a/user.go +++ b/user.go @@ -779,14 +779,14 @@ func (user *User) UpdateDirectChats(chats map[id.UserID][]id.RoomID) { } func (user *User) HandleContactList(contacts []whatsapp.Contact) { - contactMap := make(map[string]whatsapp.Contact) + contactMap := make(map[whatsapp.JID]whatsapp.Contact) for _, contact := range contacts { contactMap[contact.JID] = contact } 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 { contacts = user.Conn.Store.Contacts } @@ -805,6 +805,9 @@ func (user *User) syncPuppets(contacts map[string]whatsapp.Contact) { if strings.HasSuffix(jid, whatsapp.NewUserSuffix) { puppet := user.bridge.GetPuppetByJID(contact.JID) 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") @@ -960,8 +963,13 @@ func (user *User) HandleNewContact(contact whatsapp.Contact) { if strings.HasSuffix(contact.JID, whatsapp.OldUserSuffix) { contact.JID = strings.Replace(contact.JID, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, -1) } - puppet := user.bridge.GetPuppetByJID(contact.JID) - puppet.UpdateName(user, contact) + if strings.HasSuffix(contact.JID, whatsapp.NewUserSuffix) { + puppet := user.bridge.GetPuppetByJID(contact.JID) + 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) { From ac2ca08007d05a35da0ed733addb80771efc178c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 21 Feb 2021 14:45:33 +0200 Subject: [PATCH 08/10] Sync broadcast list recipients too --- custompuppet.go | 2 +- go.mod | 2 +- go.sum | 2 + portal.go | 112 ++++++++++++++++++++++++++++++------------------ 4 files changed, 75 insertions(+), 43 deletions(-) diff --git a/custompuppet.go b/custompuppet.go index 372ebb0..11475fa 100644 --- a/custompuppet.go +++ b/custompuppet.go @@ -164,7 +164,7 @@ func (puppet *Puppet) ProcessResponse(resp *mautrix.RespSync, _ string) error { } for roomID, events := range resp.Rooms.Join { portal := puppet.bridge.GetPortalByMXID(roomID) - if portal == nil { + if portal == nil || portal.IsBroadcastList() { continue } for _, evt := range events.Ephemeral.Events { diff --git a/go.mod b/go.mod index 432250a..fc2cbc3 100644 --- a/go.mod +++ b/go.mod @@ -16,4 +16,4 @@ require ( maunium.net/go/mautrix v0.8.3 ) -replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.22-0.20210221121735-6d3eaaa7bdc5 +replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.22-0.20210221124355-8c89650e6a9e diff --git a/go.sum b/go.sum index 7454f81..35b6793 100644 --- a/go.sum +++ b/go.sum @@ -337,6 +337,8 @@ github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94 h1:G4YvxLMW80 github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= github.com/tulir/go-whatsapp v0.3.22-0.20210221121735-6d3eaaa7bdc5 h1:4Y5xQdpuLEt4DQavhnP/Ium1zpwIE+LeOFwVyiW4qoY= github.com/tulir/go-whatsapp v0.3.22-0.20210221121735-6d3eaaa7bdc5/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= +github.com/tulir/go-whatsapp v0.3.22-0.20210221124355-8c89650e6a9e h1:jQGaSyVl7gx44KYH1UY9JnhoLAqTEKysIhLjQJZZKVY= +github.com/tulir/go-whatsapp v0.3.22-0.20210221124355-8c89650e6a9e/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= diff --git a/portal.go b/portal.go index c848ed2..6b53613 100644 --- a/portal.go +++ b/portal.go @@ -359,6 +359,43 @@ func (portal *Portal) finishHandling(source *User, message *waProto.WebMessageIn portal.log.Debugln("Handled message", message.GetKey().GetId(), "->", mxid) } +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 levels, err := portal.MainIntent().PowerLevels(portal.MXID) @@ -366,7 +403,7 @@ func (portal *Portal) SyncParticipants(metadata *whatsapp.GroupInfo) { levels = portal.GetBasePowerLevels() changed = true } - participantMap := make(map[string]bool) + participantMap := make(map[whatsapp.JID]bool) for _, participant := range metadata.Participants { participantMap[participant.JID] = true user := portal.bridge.GetUserByJID(participant.JID) @@ -395,26 +432,7 @@ func (portal *Portal) SyncParticipants(metadata *whatsapp.GroupInfo) { portal.log.Errorln("Failed to change power levels:", err) } } - 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) - } - } - } - } - } + portal.kickExtraUsers(participantMap) } func (portal *Portal) UpdateAvatar(user *User, avatar *whatsapp.ProfilePicInfo, updateInfo bool) bool { @@ -469,7 +487,7 @@ func (portal *Portal) UpdateAvatar(user *User, avatar *whatsapp.ProfilePicInfo, } func (portal *Portal) UpdateName(name string, setBy whatsapp.JID, intent *appservice.IntentAPI, updateInfo bool) bool { - if name == "" && portal.IsBroadcastRoom() { + if name == "" && portal.IsBroadcastList() { name = UnnamedBroadcastName } if portal.Name != name { @@ -522,12 +540,12 @@ func (portal *Portal) UpdateTopic(topic string, setBy whatsapp.JID, intent *apps func (portal *Portal) UpdateMetadata(user *User) bool { if portal.IsPrivateChat() { return false - } else if portal.IsStatusBroadcastRoom() { + } else if portal.IsStatusBroadcastList() { update := false update = portal.UpdateName(StatusBroadcastName, "", nil, false) || update update = portal.UpdateTopic(StatusBroadcastTopic, "", nil, false) || update return update - } else if portal.IsBroadcastRoom() { + } else if portal.IsBroadcastList() { update := false contact, _ := user.Conn.Store.Contacts[portal.Key.JID] update = portal.UpdateName(contact.Name, "", nil, false) || update @@ -612,7 +630,7 @@ func (portal *Portal) Sync(user *User, contact whatsapp.Contact) { update := false update = portal.UpdateMetadata(user) || update - if !portal.IsPrivateChat() && !portal.IsBroadcastRoom() && portal.Avatar == "" { + if !portal.IsPrivateChat() && !portal.IsBroadcastList() && portal.Avatar == "" { update = portal.UpdateAvatar(user, nil, false) || update } if update { @@ -988,6 +1006,7 @@ func (portal *Portal) CreateMatrixRoom(user *User) error { portal.log.Infoln("Creating Matrix room. Info source:", user.MXID) var metadata *whatsapp.GroupInfo + var broadcastMetadata *whatsapp.BroadcastListInfo if portal.IsPrivateChat() { puppet := portal.bridge.GetPuppetByJID(portal.Key.JID) if portal.bridge.Config.Bridge.PrivateChatPortalMeta { @@ -998,14 +1017,19 @@ func (portal *Portal) CreateMatrixRoom(user *User) error { portal.Name = "" } portal.Topic = "WhatsApp private chat" - } else if portal.IsStatusBroadcastRoom() { + } else if portal.IsStatusBroadcastList() { portal.Name = "WhatsApp Status Broadcast" portal.Topic = "WhatsApp status updates from your contacts" - } else if portal.IsBroadcastRoom() { - contact, ok := user.Conn.Store.Contacts[portal.Key.JID] - if ok { - portal.Name = contact.Name + } else if portal.IsBroadcastList() { + var err error + broadcastMetadata, err = user.Conn.GetBroadcastMetadata(portal.Key.JID) + if err == nil && broadcastMetadata.Status == 0 { + 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 @@ -1097,6 +1121,9 @@ func (portal *Portal) CreateMatrixRoom(user *User) error { _ = customPuppet.CustomIntent().EnsureJoined(portal.MXID) } } + if broadcastMetadata != nil { + portal.SyncBroadcastRecipients(broadcastMetadata) + } user.addPortalToCommunity(portal) if portal.IsPrivateChat() { puppet := user.bridge.GetPuppetByJID(portal.Key.JID) @@ -1126,7 +1153,7 @@ func (portal *Portal) IsPrivateChat() bool { return *portal.isPrivate } -func (portal *Portal) IsBroadcastRoom() bool { +func (portal *Portal) IsBroadcastList() bool { if portal.isBroadcast == nil { val := strings.HasSuffix(portal.Key.JID, whatsapp.BroadcastSuffix) portal.isBroadcast = &val @@ -1134,7 +1161,7 @@ func (portal *Portal) IsBroadcastRoom() bool { return *portal.isBroadcast } -func (portal *Portal) IsStatusBroadcastRoom() bool { +func (portal *Portal) IsStatusBroadcastList() bool { return portal.Key.JID == "status@broadcast" } @@ -1310,8 +1337,9 @@ func (portal *Portal) HandleTextMessage(source *User, message whatsapp.TextMessa } 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 + // However, broadcast lists don't have update commands, so we handle these if it's not a backfill return } intent := portal.startHandling(source, message.Info) @@ -1341,9 +1369,9 @@ func (portal *Portal) HandleStubMessage(source *User, message whatsapp.StubMessa eventID = portal.RestrictMessageSending(message.FirstParam == "on") case waProto.WebMessageInfo_GROUP_CHANGE_RESTRICT: 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) - 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) case waProto.WebMessageInfo_GROUP_PARTICIPANT_PROMOTE: eventID = portal.ChangeAdminStatus(message.Params, true) @@ -1525,13 +1553,15 @@ func (portal *Portal) HandleWhatsAppKick(source *User, senderJID string, jids [] puppet := portal.bridge.GetPuppetByJID(jid) portal.removeUser(puppet.JID == sender.JID, senderIntent, puppet.MXID, puppet.DefaultIntent()) - user := portal.bridge.GetUserByJID(jid) - if user != nil { - var customIntent *appservice.IntentAPI - if puppet.CustomMXID == user.MXID { - customIntent = puppet.CustomIntent() + if !portal.IsBroadcastList() { + user := portal.bridge.GetUserByJID(jid) + if user != nil { + var customIntent *appservice.IntentAPI + if puppet.CustomMXID == user.MXID { + customIntent = puppet.CustomIntent() + } + portal.removeUser(puppet.JID == sender.JID, senderIntent, user.MXID, customIntent) } - portal.removeUser(puppet.JID == sender.JID, senderIntent, user.MXID, customIntent) } } } From 6227dfb669905dd4e4d136ab623e43dce074929c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 21 Feb 2021 15:00:04 +0200 Subject: [PATCH 09/10] Fix re-syncing broadcast list recipients --- portal.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/portal.go b/portal.go index 6b53613..c70396a 100644 --- a/portal.go +++ b/portal.go @@ -369,7 +369,7 @@ func (portal *Portal) kickExtraUsers(participantMap map[whatsapp.JID]bool) { if ok { _, shouldBePresent := participantMap[jid] if !shouldBePresent { - _, err := portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{ + _, err = portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{ UserID: member, Reason: "User had left this WhatsApp chat", }) @@ -547,8 +547,14 @@ func (portal *Portal) UpdateMetadata(user *User) bool { return update } else if portal.IsBroadcastList() { update := false - contact, _ := user.Conn.Store.Contacts[portal.Key.JID] - update = portal.UpdateName(contact.Name, "", nil, false) || update + 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 } @@ -1023,7 +1029,7 @@ func (portal *Portal) CreateMatrixRoom(user *User) error { } else if portal.IsBroadcastList() { var err error broadcastMetadata, err = user.Conn.GetBroadcastMetadata(portal.Key.JID) - if err == nil && broadcastMetadata.Status == 0 { + if err == nil && broadcastMetadata.Status == 200 { portal.Name = broadcastMetadata.Name } else { contact, _ := user.Conn.Store.Contacts[portal.Key.JID] From 3b22015b83bf39fad7d58f19b1a8e28a6540fab8 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 21 Feb 2021 17:11:36 +0200 Subject: [PATCH 10/10] Disable logging of all WebMessageInfos --- user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user.go b/user.go index 31318b0..21147b5 100644 --- a/user.go +++ b/user.go @@ -605,7 +605,7 @@ func (user *User) HandleEvent(event interface{}) { case *waProto.WebMessageInfo: user.updateLastConnectionIfNecessary() // TODO trace log - user.log.Debugfln("WebMessageInfo: %+v", v) + //user.log.Debugfln("WebMessageInfo: %+v", v) case *waBinary.Node: user.log.Debugfln("Unknown binary message: %+v", v) default: