diff --git a/.gitignore b/.gitignore
index 31f546f..63d61a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.idea
+logs/*
*.session
*.json
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0f28fca..f24619f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.1.0
+ rev: v4.4.0
hooks:
- id: trailing-whitespace
exclude_types: [markdown]
@@ -9,7 +9,7 @@ repos:
- id: check-added-large-files
- repo: https://github.com/tekwizely/pre-commit-golang
- rev: v1.0.0-beta.5
+ rev: v1.0.0-rc.1
hooks:
- id: go-imports-repo
args:
diff --git a/ROADMAP.md b/ROADMAP.md
index d945dd1..e3eb792 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -69,10 +69,10 @@
* [x] At startup
* [x] When receiving invite
* [x] When receiving message
- * [ ] Private chat creation by inviting Matrix puppet of WhatsApp user to new room
- * [ ] Option to use own Matrix account for messages sent from WhatsApp mobile/other web clients
+ * [ ] Private chat creation by inviting Matrix puppet of GroupMe user to new room
+ * [ ] Option to use own Matrix account for messages sent from GroupMe mobile/other web clients
* [ ] Shared group chat portals
-[1] Basic feature works. Improvements are TODO.
-[2] May already work
-[3] May not be possible
+[1] Basic feature works. Improvements are TODO.
+[2] May already work
+[3] May not be possible
diff --git a/config/config.go b/config/config.go
index 5ec3548..27cdad4 100644
--- a/config/config.go
+++ b/config/config.go
@@ -24,13 +24,15 @@ import (
type Config struct {
*bridgeconfig.BaseConfig `yaml:",inline"`
- SegmentKey string `yaml:"segment_key"`
+ SegmentKey string `yaml:"segment_key"`
+ SegmentUserID string `yaml:"segment_user_id"`
Metrics struct {
Enabled bool `yaml:"enabled"`
Listen string `yaml:"listen"`
} `yaml:"metrics"`
+ // TODO need these?
GroupMe struct {
OSName string `yaml:"os_name"`
BrowserName string `yaml:"browser_name"`
diff --git a/custompuppet.go b/custompuppet.go
index d32d346..847e57d 100644
--- a/custompuppet.go
+++ b/custompuppet.go
@@ -98,7 +98,7 @@ func (br *GMBridge) newDoublePuppetClient(mxid id.UserID, accessToken string) (*
homeserverURL, found := br.Config.Bridge.DoublePuppetServerMap[homeserver]
if !found {
if homeserver == br.AS.HomeserverDomain {
- homeserverURL = br.AS.HomeserverURL
+ homeserverURL = ""
} else if br.Config.Bridge.DoublePuppetAllowDiscovery {
resp, err := mautrix.DiscoverClientAPI(homeserver)
if err != nil {
@@ -110,14 +110,7 @@ func (br *GMBridge) newDoublePuppetClient(mxid id.UserID, accessToken string) (*
return nil, fmt.Errorf("double puppeting from %s is not allowed", homeserver)
}
}
- client, err := mautrix.NewClient(homeserverURL, mxid, accessToken)
- if err != nil {
- return nil, err
- }
- client.Logger = br.AS.Log.Sub(mxid.String())
- client.Client = br.AS.HTTPClient
- client.DefaultHTTPRetries = br.AS.DefaultHTTPRetries
- return client, nil
+ return br.AS.NewExternalMautrixClient(mxid, accessToken, homeserverURL)
}
func (puppet *Puppet) newCustomIntent() (*appservice.IntentAPI, error) {
diff --git a/database/upgrades/upgrades.go b/database/upgrades/upgrades.go
index 25ffe6f..b651547 100644
--- a/database/upgrades/upgrades.go
+++ b/database/upgrades/upgrades.go
@@ -1,4 +1,4 @@
-// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
+// mautrix-groupme - A Matrix-GroupMe puppeting bridge.
// Copyright (C) 2022 Tulir Asokan
//
// This program is free software: you can redistribute it and/or modify
diff --git a/example-config.yaml b/example-config.yaml
index 9618aee..15ebe4d 100644
--- a/example-config.yaml
+++ b/example-config.yaml
@@ -9,7 +9,7 @@ homeserver:
# Standard Matrix homeservers like Synapse, Dendrite and Conduit should just use "standard" here.
software: standard
# The URL to push real-time bridge status to.
- # If set, the bridge will make POST requests to this URL whenever a user's whatsapp connection state changes.
+ # If set, the bridge will make POST requests to this URL whenever a user's GroupMe connection state changes.
# The bridge will use the appservice as_token to authorize requests.
status_endpoint: null
# Endpoint for reporting per-message status.
@@ -29,10 +29,11 @@ appservice:
# Database config.
database:
- # The database type. "sqlite3" and "postgres" are supported.
+ # The database type. "sqlite3-fk-wal" and "postgres" are supported.
type: postgres
# The database URI.
- # SQLite: File name is enough. https://github.com/mattn/go-sqlite3#connection-string
+ # SQLite: A raw file path is supported, but `file:?_txlock=immediate` is recommended.
+ # https://github.com/mattn/go-sqlite3#connection-string
# Postgres: Connection string. For example, postgres://user:password@host/database?sslmode=disable
# To connect via Unix socket, use something like postgres:///dbname?host=/var/run/postgresql
uri: postgres://user:password@host/database?sslmode=disable
@@ -53,7 +54,11 @@ appservice:
# Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty
# to leave display name/avatar as-is.
displayname: GroupMe bridge bot
- avatar: mxc://malhotra.cc/YTWNAdhgJhYOPsKIxyfFZsrA
+ avatar: mxc://nevarro.space/eoAJPcSuTEvffoNycrXjvsmj
+
+ # Whether or not to receive ephemeral events via appservice transactions.
+ # Requires MSC2409 support (i.e. Synapse 1.22+).
+ ephemeral_events: true
# Authentication tokens for AS <-> HS communication. Autogenerated; do not modify.
as_token: "This value is generated when generating the registration"
@@ -80,8 +85,8 @@ groupme:
# Bridge config
bridge:
- # Localpart template of MXIDs for WhatsApp users.
- # {{.}} is replaced with the phone number of the WhatsApp user.
+ # Localpart template of MXIDs for GroupMe users.
+ # {{.}} is replaced with the phone number of the GroupMe user.
username_template: groupme_{{.}}
# Displayname template for GroupMe users.
# {{call .UserID.String}} - the number GroupMe assigns to the user
@@ -91,7 +96,7 @@ bridge:
# Should the bridge create a space for each logged-in user and add bridged rooms to it?
# Users who logged in before turning this on should run `!wa sync space` to create and fill the space for the first time.
personal_filtering_spaces: false
- # Should the bridge send a read receipt from the bridge bot when a message has been sent to WhatsApp?
+ # Should the bridge send a read receipt from the bridge bot when a message has been sent to GroupMe?
delivery_receipts: false
# Whether the bridge should send the message status as a custom com.beeper.message_send_status event.
message_status_events: false
@@ -158,7 +163,7 @@ bridge:
# manually.
login_shared_secret: null
- # Whether or not to invite own WhatsApp user's Matrix puppet into private
+ # Whether or not to invite own GroupMe user's Matrix puppet into private
# chat portals when backfilling if needed.
# This always uses the default puppet instead of custom puppets due to
# rate limits and timestamp massaging.
@@ -172,11 +177,11 @@ bridge:
# except if the config file is not writable.
resend_bridge_info: false
- # Whether or not thumbnails from WhatsApp should be sent.
+ # Whether or not thumbnails from GroupMe should be sent.
# They're disabled by default due to very low resolution.
- whatsapp_thumbnail: false
+ groupme_thumbnail: false
- # Allow invite permission for user. User can invite any bots to room with whatsapp
+ # Allow invite permission for user. User can invite any bots to room with GroupMe
# users (private chat and groups)
allow_user_invite: false
@@ -222,7 +227,7 @@ bridge:
# verified - Require manual per-device verification
# (currently only possible by modifying the `trust` column in the `crypto_device` database table).
verification_levels:
- # Minimum level for which the bridge should send keys to when bridging messages from WhatsApp to Matrix.
+ # Minimum level for which the bridge should send keys to when bridging messages from GroupMe to Matrix.
receive: unverified
# Minimum level that the bridge should accept for incoming Matrix messages.
send: unverified
@@ -256,7 +261,7 @@ bridge:
# 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.
+ # user - Access to use the bridge to chat with a GroupMe account.
# admin - User level and some additional administration tools
# Permitted keys:
# * - All Matrix users
@@ -267,18 +272,15 @@ bridge:
"example.com": user
"@admin:example.com": admin
-# Logging config.
+# Logging config. See https://github.com/tulir/zeroconfig for details.
logging:
- # The directory for log files. Will be created if not found.
- directory: ./logs
- # Available variables: .Date for the file date and .Index for different log files on the same day.
- file_name_format: "{{.Date}}-{{.Index}}.log"
- # Date format for file names in the Go time format: https://golang.org/pkg/time/#pkg-constants
- file_date_format: 2006-01-02
- # Log file permissions.
- file_mode: 0600
- # Timestamp format for log entries in the Go time format.
- timestamp_format: Jan _2, 2006 15:04:05
- # Minimum severity for log messages.
- # Options: debug, info, warn, error, fatal
- print_level: debug
+ min_level: debug
+ writers:
+ - type: stdout
+ format: pretty-colored
+ - type: file
+ format: json
+ filename: ./logs/mautrix-groupme.log
+ max_size: 100
+ max_backups: 10
+ compress: true
diff --git a/formatting.go b/formatting.go
index ac2273c..67fa750 100644
--- a/formatting.go
+++ b/formatting.go
@@ -18,127 +18,33 @@ package main
import (
"fmt"
- "regexp"
- "strings"
+ "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/format"
- "maunium.net/go/mautrix/id"
-
- "github.com/beeper/groupme-lib"
+ "maunium.net/go/mautrix/util/variationselector"
)
-var italicRegex = regexp.MustCompile("([\\s>~*]|^)_(.+?)_([^a-zA-Z\\d]|$)")
-var boldRegex = regexp.MustCompile("([\\s>_~]|^)\\*(.+?)\\*([^a-zA-Z\\d]|$)")
-var strikethroughRegex = regexp.MustCompile("([\\s>_*]|^)~(.+?)~([^a-zA-Z\\d]|$)")
-var codeBlockRegex = regexp.MustCompile("```(?:.|\n)+?```")
+const formatterContextAllowedMentionsKey = "com.beeper.groupme.allowed_mentions"
-const mentionedGMIDsContextKey = "net.maunium.groupme.mentioned_gmids"
-
-type Formatter struct {
- bridge *GMBridge
-
- matrixHTMLParser *format.HTMLParser
-
- waReplString map[*regexp.Regexp]string
- waReplFunc map[*regexp.Regexp]func(string) string
- waReplFuncText map[*regexp.Regexp]func(string) string
-}
-
-func NewFormatter(bridge *GMBridge) *Formatter {
- formatter := &Formatter{
- bridge: bridge,
- matrixHTMLParser: &format.HTMLParser{
- TabsToSpaces: 4,
- Newline: "\n",
-
- PillConverter: func(displayname, mxid, eventID string, ctx format.Context) string {
- if mxid[0] == '@' {
- puppet := bridge.GetPuppetByMXID(id.UserID(mxid))
- if puppet != nil {
- gmids, ok := ctx[mentionedGMIDsContextKey].([]groupme.ID)
- if !ok {
- ctx[mentionedGMIDsContextKey] = []groupme.ID{puppet.GMID}
- } else {
- ctx[mentionedGMIDsContextKey] = append(gmids, puppet.GMID)
- }
- return "@" + puppet.PhoneNumber()
- }
- }
- return mxid
- },
- BoldConverter: func(text string, _ format.Context) string {
- return fmt.Sprintf("*%s*", text)
- },
- ItalicConverter: func(text string, _ format.Context) string {
- return fmt.Sprintf("_%s_", text)
- },
- StrikethroughConverter: func(text string, _ format.Context) string {
- return fmt.Sprintf("~%s~", text)
- },
- MonospaceConverter: func(text string, _ format.Context) string {
- return fmt.Sprintf("```%s```", text)
- },
- MonospaceBlockConverter: func(text, language string, _ format.Context) string {
- return fmt.Sprintf("```%s```", text)
- },
- },
- waReplString: map[*regexp.Regexp]string{
- italicRegex: "$1$2$3",
- boldRegex: "$1$2$3",
- strikethroughRegex: "$1$2$3",
- },
+func (br *GMBridge) pillConverter(displayname, mxid, eventID string, ctx format.Context) string {
+ // GroupMe only supports user mentions.
+ if len(mxid) == 0 || mxid[0] != '@' {
+ return displayname
}
- formatter.waReplFunc = map[*regexp.Regexp]func(string) string{
- codeBlockRegex: func(str string) string {
- str = str[3 : len(str)-3]
- if strings.ContainsRune(str, '\n') {
- return fmt.Sprintf("%s
", str)
- }
- return fmt.Sprintf("%s
", str)
- },
+
+ return fmt.Sprintf("@%s", displayname)
+}
+
+var matrixHTMLParser = &format.HTMLParser{
+ TabsToSpaces: 4,
+ Newline: "\n",
+ HorizontalLine: "\n---\n",
+}
+
+func (portal *Portal) parseMatrixHTML(content *event.MessageEventContent) string {
+ if content.Format == event.FormatHTML && len(content.FormattedBody) > 0 {
+ return variationselector.FullyQualify(matrixHTMLParser.Parse(content.FormattedBody, format.NewContext()))
+ } else {
+ return variationselector.FullyQualify(content.Body)
}
- formatter.waReplFuncText = map[*regexp.Regexp]func(string) string{}
- return formatter
-}
-
-//func (formatter *Formatter) getMatrixInfoByJID(jid groupme.ID) (mxid id.UserID, displayname string) {
-// if user := formatter.bridge.GetUserByJID(jid); user != nil {
-// mxid = user.MXID
-// displayname = string(user.MXID)
-// } else if puppet := formatter.bridge.GetPuppetByJID(jid); puppet != nil {
-// mxid = puppet.MXID
-// displayname = puppet.Displayname
-// }
-// return
-//}
-
-//func (formatter *Formatter) ParseWhatsApp(content *event.MessageEventContent, mentionedJIDs []groupme.ID) {
-// output := html.EscapeString(content.Body)
-// for regex, replacement := range formatter.waReplString {
-// output = regex.ReplaceAllString(output, replacement)
-// }
-// for regex, replacer := range formatter.waReplFunc {
-// output = regex.ReplaceAllStringFunc(output, replacer)
-// }
-// for _, jid := range mentionedJIDs {
-// mxid, displayname := formatter.getMatrixInfoByJID(jid)
-// number := "@" + strings.Replace(jid, whatsappExt.NewUserSuffix, "", 1)
-// output = strings.Replace(output, number, fmt.Sprintf(`%s`, mxid, displayname), -1)
-// content.Body = strings.Replace(content.Body, number, displayname, -1)
-// }
-// if output != content.Body {
-// output = strings.Replace(output, "\n", "
", -1)
-// content.FormattedBody = output
-// content.Format = event.FormatHTML
-// for regex, replacer := range formatter.waReplFuncText {
-// content.Body = regex.ReplaceAllStringFunc(content.Body, replacer)
-// }
-// }
-//}
-
-func (formatter *Formatter) ParseMatrix(html string) (string, []groupme.ID) {
- ctx := make(format.Context)
- result := formatter.matrixHTMLParser.Parse(html, ctx)
- mentionedJIDs, _ := ctx[mentionedGMIDsContextKey].([]groupme.ID)
- return result, mentionedJIDs
}
diff --git a/go.mod b/go.mod
index ef5dba8..5221a25 100644
--- a/go.mod
+++ b/go.mod
@@ -3,42 +3,43 @@ module github.com/beeper/groupme
go 1.19
require (
- github.com/Rhymen/go-whatsapp v0.1.1
github.com/beeper/groupme-lib v0.2.1-0.20221021205945-8f23e04eea71
github.com/gabriel-vasile/mimetype v1.1.2
- github.com/gorilla/websocket v1.5.0
github.com/karmanyaahm/wray v0.0.0-20210303233435-756d58657c14
github.com/lib/pq v1.10.7
- github.com/mattn/go-sqlite3 v1.14.15
+ github.com/mattn/go-sqlite3 v1.14.16
github.com/prometheus/client_golang v1.9.0
- maunium.net/go/maulogger/v2 v2.3.2
- maunium.net/go/mautrix v0.12.3-0.20221020190005-d0c13d2f04a1
+ maunium.net/go/maulogger/v2 v2.4.1
+ maunium.net/go/mautrix v0.15.0
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
+ github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
+ github.com/gorilla/websocket v1.5.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
- github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.15.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
- github.com/rs/zerolog v1.28.0 // indirect
- github.com/tidwall/gjson v1.14.3 // indirect
+ github.com/rs/zerolog v1.29.0 // indirect
+ github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
- github.com/yuin/goldmark v1.5.2 // indirect
- golang.org/x/crypto v0.0.0-20221012134737-56aed061732a // indirect
- golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect
- golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
+ github.com/yuin/goldmark v1.5.4 // indirect
+ go.mau.fi/zeroconfig v0.1.2 // indirect
+ golang.org/x/crypto v0.6.0 // indirect
+ golang.org/x/net v0.6.0 // indirect
+ golang.org/x/sys v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.25.0 // indirect
+ gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
maunium.net/go/mauflag v1.0.0 // indirect
)
diff --git a/go.sum b/go.sum
index 2f806a8..73c99d9 100644
--- a/go.sum
+++ b/go.sum
@@ -1,16 +1,8 @@
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/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
-github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
-github.com/Rhymen/go-whatsapp v0.1.1 h1:OK+bCugQcr2YjyYKeDzULqCtM50TPUFM6LvQtszKfcw=
-github.com/Rhymen/go-whatsapp v0.1.1/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk=
-github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME=
-github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU=
-github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw=
-github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM=
github.com/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=
@@ -48,6 +40,7 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z
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/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
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=
@@ -89,7 +82,6 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er
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.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@@ -124,8 +116,6 @@ github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
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.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -184,17 +174,15 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b
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-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
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-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
-github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
+github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
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=
@@ -241,7 +229,6 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0
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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -279,8 +266,8 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn
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/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
-github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
-github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
+github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
+github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
github.com/russross/blackfriday/v2 v2.0.1/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=
@@ -289,7 +276,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
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-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
@@ -307,10 +293,10 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
-github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
+github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
@@ -321,10 +307,12 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1
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=
-github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU=
-github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
+github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
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.mau.fi/zeroconfig v0.1.2 h1:DKOydWnhPMn65GbXZOafgkPm11BvFashZWLct0dGFto=
+go.mau.fi/zeroconfig v0.1.2/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70=
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=
@@ -337,14 +325,13 @@ 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-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-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg=
-golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
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=
@@ -370,8 +357,8 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
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-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
-golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
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=
@@ -390,7 +377,6 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
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-20190222072716-a9d3bda3a223/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=
@@ -405,8 +391,8 @@ golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -434,7 +420,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
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-20180831171423-11092d34479b/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=
@@ -469,6 +454,8 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
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=
@@ -487,9 +474,9 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
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.3.2 h1:1XmIYmMd3PoQfp9J+PaHhpt80zpfmMqaShzUTC7FwY0=
-maunium.net/go/maulogger/v2 v2.3.2/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
-maunium.net/go/mautrix v0.12.3-0.20221020190005-d0c13d2f04a1 h1:daraaP+GcSrFLgVckFpp+ciVrtQeG5s2w3Fi8AInaj8=
-maunium.net/go/mautrix v0.12.3-0.20221020190005-d0c13d2f04a1/go.mod h1:bCw45Qx/m9qsz7eazmbe7Rzq5ZbTPzwRE1UgX2S9DXs=
+maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
+maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
+maunium.net/go/mautrix v0.15.0 h1:gkK9HXc1SSPwY7qOAqchzj2xxYqiOYeee8lr28A2g/o=
+maunium.net/go/mautrix v0.15.0/go.mod h1:1v8QVDd7q/eJ+eg4sgeOSEafBAFhkt4ab2i97M3IkNQ=
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=
diff --git a/main.go b/main.go
index 0d8971e..05a1992 100644
--- a/main.go
+++ b/main.go
@@ -20,6 +20,7 @@ import (
_ "embed"
"sync"
+ "maunium.net/go/mautrix"
"maunium.net/go/mautrix/bridge"
"maunium.net/go/mautrix/bridge/commands"
"maunium.net/go/mautrix/bridge/status"
@@ -48,7 +49,6 @@ type GMBridge struct {
Config *config.Config
DB *database.Database
Provisioning *ProvisioningAPI
- Formatter *Formatter
Metrics *MetricsHandler
usersByMXID map[id.UserID]*User
@@ -70,10 +70,16 @@ func (br *GMBridge) Init() {
br.CommandProcessor = commands.NewProcessor(&br.Bridge)
br.RegisterCommands()
+ matrixHTMLParser.PillConverter = br.pillConverter
+
Segment.log = br.Log.Sub("Segment")
Segment.key = br.Config.SegmentKey
+ Segment.userID = br.Config.SegmentUserID
if Segment.IsEnabled() {
Segment.log.Infoln("Segment metrics are enabled")
+ if Segment.userID != "" {
+ Segment.log.Infoln("Overriding Segment user_id with %v", Segment.userID)
+ }
}
br.DB = database.New(br.Bridge.DB, br.Log.Sub("Database"))
@@ -83,47 +89,18 @@ func (br *GMBridge) Init() {
br.Provisioning = &ProvisioningAPI{bridge: br}
}
- br.Formatter = NewFormatter(br)
br.Metrics = NewMetricsHandler(br.Config.Metrics.Listen, br.Log.Sub("Metrics"), br.DB)
br.MatrixHandler.TrackEventDuration = br.Metrics.TrackMatrixEvent
}
-func (bridge *GMBridge) Start() {
- if bridge.Provisioning != nil {
- bridge.Log.Debugln("Initializing provisioning API")
- bridge.Provisioning.Init()
+func (br *GMBridge) Start() {
+ if br.Provisioning != nil {
+ br.Log.Debugln("Initializing provisioning API")
+ br.Provisioning.Init()
}
- go bridge.StartUsers()
- if bridge.Config.Metrics.Enabled {
- go bridge.Metrics.Start()
- }
-}
-
-func (bridge *GMBridge) UpdateBotProfile() {
- bridge.Log.Debugln("Updating bot profile")
- botConfig := bridge.Config.AppService.Bot
-
- var err error
- var mxc id.ContentURI
- if botConfig.Avatar == "remove" {
- err = bridge.Bot.SetAvatarURL(mxc)
- } else if len(botConfig.Avatar) > 0 {
- mxc, err = id.ParseContentURI(botConfig.Avatar)
- if err == nil {
- err = bridge.Bot.SetAvatarURL(mxc)
- }
- }
- if err != nil {
- bridge.Log.Warnln("Failed to update bot avatar:", err)
- }
-
- if botConfig.Displayname == "remove" {
- err = bridge.Bot.SetDisplayName("")
- } else if len(botConfig.Avatar) > 0 {
- err = bridge.Bot.SetDisplayName(botConfig.Displayname)
- }
- if err != nil {
- bridge.Log.Warnln("Failed to update bot displayname:", err)
+ go br.StartUsers()
+ if br.Config.Metrics.Enabled {
+ go br.Metrics.Start()
}
}
@@ -131,7 +108,7 @@ func (br *GMBridge) StartUsers() {
br.Log.Debugln("Starting users")
foundAnySessions := false
for _, user := range br.GetAllUsers() {
- if user.GMID.String() != "" {
+ if user.GMID.Valid() {
foundAnySessions = true
}
go user.Connect()
@@ -174,6 +151,20 @@ func (br *GMBridge) GetConfigPtr() interface{} {
return br.Config
}
+const unstableFeatureBatchSending = "org.matrix.msc2716"
+
+func (br *GMBridge) CheckFeatures(versions *mautrix.RespVersions) (string, bool) {
+ if br.Config.Bridge.HistorySync.Backfill {
+ supported, known := versions.UnstableFeatures[unstableFeatureBatchSending]
+ if !known {
+ return "Backfilling is enabled in bridge config, but homeserver does not support MSC2716 batch sending", false
+ } else if !supported {
+ return "Backfilling is enabled in bridge config, but MSC2716 batch sending is not enabled on homeserver", false
+ }
+ }
+ return "", true
+}
+
func main() {
br := &GMBridge{
usersByMXID: make(map[id.UserID]*User),
diff --git a/messagetracking.go b/messagetracking.go
index a264f8f..346da66 100644
--- a/messagetracking.go
+++ b/messagetracking.go
@@ -97,7 +97,6 @@ func (portal *Portal) sendStatusEvent(evtID, lastRetry id.EventID, err error) {
content.Reason, content.Status, _, _, content.Message = errorToStatusReason(err)
content.Error = err.Error()
}
- content.FillLegacyBooleans()
_, err = intent.SendMessageEvent(portal.MXID, event.BeeperMessageStatus, &content)
if err != nil {
portal.log.Warnln("Failed to send message status event:", err)
diff --git a/portal.go b/portal.go
index e26b461..5f7b10d 100644
--- a/portal.go
+++ b/portal.go
@@ -37,7 +37,6 @@ import (
"maunium.net/go/mautrix/bridge/bridgeconfig"
"maunium.net/go/mautrix/crypto/attachment"
- "github.com/Rhymen/go-whatsapp"
"github.com/gabriel-vasile/mimetype"
"github.com/beeper/groupme-lib"
@@ -385,7 +384,7 @@ func (portal *Portal) SyncParticipants(metadata *groupme.Group) {
if !shouldBePresent {
_, err := portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{
UserID: member,
- Reason: "User had left this WhatsApp chat",
+ Reason: "User had left this GroupMe chat",
})
if err != nil {
portal.log.Warnfln("Failed to kick user %s who had left: %v", member, err)
@@ -1158,7 +1157,6 @@ func (portal *Portal) HandleTextMessage(source *User, message *groupme.Message)
sendText = sendText && text
}
- // portal.bridge.Formatter.ParseWhatsApp(content, message.ContextInfo.MentionedJID)
// portal.SetReply(content, message.ContextInfo)
//TODO: mentions
content := &event.MessageEventContent{
@@ -1262,108 +1260,6 @@ func (portal *Portal) HandleTextMessage(source *User, message *groupme.Message)
// return
// }
-func (portal *Portal) HandleLocationMessage(source *User, message whatsapp.LocationMessage) {
- // intent := portal.startHandling(source, message.Info)
- // if intent == nil {
- // return
- // }
- //
- // url := message.Url
- // if len(url) == 0 {
- // url = fmt.Sprintf("https://maps.google.com/?q=%.5f,%.5f", message.DegreesLatitude, message.DegreesLongitude)
- // }
- // name := message.Name
- // if len(name) == 0 {
- // latChar := 'N'
- // if message.DegreesLatitude < 0 {
- // latChar = 'S'
- // }
- // longChar := 'E'
- // if message.DegreesLongitude < 0 {
- // longChar = 'W'
- // }
- // name = fmt.Sprintf("%.4f° %c %.4f° %c", math.Abs(message.DegreesLatitude), latChar, math.Abs(message.DegreesLongitude), longChar)
- // }
- //
- // content := &event.MessageEventContent{
- // MsgType: event.MsgLocation,
- // Body: fmt.Sprintf("Location: %s\n%s\n%s", name, message.Address, url),
- // Format: event.FormatHTML,
- // FormattedBody: fmt.Sprintf("Location: %s
%s", url, name, message.Address),
- // GeoURI: fmt.Sprintf("geo:%.5f,%.5f", message.DegreesLatitude, message.DegreesLongitude),
- // }
- //
- // if len(message.JpegThumbnail) > 0 {
- // thumbnailMime := http.DetectContentType(message.JpegThumbnail)
- // uploadedThumbnail, _ := intent.UploadBytes(message.JpegThumbnail, thumbnailMime)
- // if uploadedThumbnail != nil {
- // cfg, _, _ := image.DecodeConfig(bytes.NewReader(message.JpegThumbnail))
- // content.Info = &event.FileInfo{
- // ThumbnailInfo: &event.FileInfo{
- // Size: len(message.JpegThumbnail),
- // Width: cfg.Width,
- // Height: cfg.Height,
- // MimeType: thumbnailMime,
- // },
- // ThumbnailURL: uploadedThumbnail.ContentURI.CUString(),
- // }
- // }
- // }
- //
- // portal.SetReply(content, message.ContextInfo)
- //
- // _, _ = intent.UserTyping(portal.MXID, false, 0)
- // resp, err := portal.sendMessage(intent, event.EventMessage, content, int64(message.Info.Timestamp*1000))
- // if err != nil {
- // portal.log.Errorfln("Failed to handle message %s: %v", message.Info.Id, err)
- // return
- // }
- // portal.finishHandling(source, message.Info.Source, resp.EventID)
- //}
-
- //func (portal *Portal) HandleContactMessage(source *User, message whatsapp.ContactMessage) {
- // intent := portal.startHandling(source, message.Info)
- // if intent == nil {
- // return
- // }
- //
- // fileName := fmt.Sprintf("%s.vcf", message.DisplayName)
- // data := []byte(message.Vcard)
- // mimeType := "text/vcard"
- // data, uploadMimeType, file := portal.encryptFile(data, mimeType)
- //
- // uploadResp, err := intent.UploadBytesWithName(data, uploadMimeType, fileName)
- // if err != nil {
- // portal.log.Errorfln("Failed to upload vcard of %s: %v", message.DisplayName, err)
- // return
- // }
- //
- // content := &event.MessageEventContent{
- // Body: fileName,
- // MsgType: event.MsgFile,
- // File: file,
- // Info: &event.FileInfo{
- // MimeType: mimeType,
- // Size: len(message.Vcard),
- // },
- // }
- // if content.File != nil {
- // content.File.URL = uploadResp.ContentURI.CUString()
- // } else {
- // content.URL = uploadResp.ContentURI.CUString()
- // }
- //
- // portal.SetReply(content, message.ContextInfo)
- //
- // _, _ = intent.UserTyping(portal.MXID, false, 0)
- // resp, err := portal.sendMessage(intent, event.EventMessage, content, int64(message.Info.Timestamp*1000))
- // if err != nil {
- // portal.log.Errorfln("Failed to handle message %s: %v", message.Info.Id, err)
- // return
- // }
- // portal.finishHandling(source, message.Info.Source, resp.EventID)
-}
-
func (portal *Portal) sendMediaBridgeFailure(source *User, intent *appservice.IntentAPI, message groupme.Message, bridgeErr error) {
portal.log.Errorfln("Failed to bridge media for %s: %v", message.UserID.String(), bridgeErr)
resp, err := portal.sendMessage(intent, event.EventMessage, &event.MessageEventContent{
@@ -1467,93 +1363,13 @@ func (portal *Portal) convertMatrixMessage(sender *User, evt *event.Event) ([]*g
case event.MsgText, event.MsgEmote, event.MsgNotice:
text := content.Body
if content.Format == event.FormatHTML {
- text, _ = portal.bridge.Formatter.ParseMatrix(content.FormattedBody)
- //TODO mentions
+ text = portal.parseMatrixHTML(content)
}
if content.MsgType == event.MsgEmote && !relaybotFormatted {
text = "/me " + text
}
info.Text = text
- // if ctxInfo.StanzaId != nil || ctxInfo.MentionedJid != nil {
- // info.Message.ExtendedTextMessage = &waProto.ExtendedTextMessage{
- // Text: &text,
- // ContextInfo: ctxInfo,
- // }
- // }
- //else {
- // info.Message.Conversation = &text
- // }
- // case event.MsgImage:
- // media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaImage)
- // if media == nil {
- // return nil, sender
- // }
- // ctxInfo.MentionedJid = media.MentionedJIDs
- // info.Message.ImageMessage = &waProto.ImageMessage{
- // ContextInfo: ctxInfo,
- // Caption: &media.Caption,
- // JpegThumbnail: media.Thumbnail,
- // Url: &media.URL,
- // MediaKey: media.MediaKey,
- // Mimetype: &content.GetInfo().MimeType,
- // FileEncSha256: media.FileEncSHA256,
- // FileSha256: media.FileSHA256,
- // FileLength: &media.FileLength,
- // }
- // case event.MsgVideo:
- // gifPlayback := content.GetInfo().MimeType == "image/gif"
- // media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaVideo)
- // if media == nil {
- // return nil, sender
- // }
- // duration := uint32(content.GetInfo().Duration)
- // ctxInfo.MentionedJid = media.MentionedJIDs
- // info.Message.VideoMessage = &waProto.VideoMessage{
- // ContextInfo: ctxInfo,
- // Caption: &media.Caption,
- // JpegThumbnail: media.Thumbnail,
- // Url: &media.URL,
- // MediaKey: media.MediaKey,
- // Mimetype: &content.GetInfo().MimeType,
- // GifPlayback: &gifPlayback,
- // Seconds: &duration,
- // FileEncSha256: media.FileEncSHA256,
- // FileSha256: media.FileSHA256,
- // FileLength: &media.FileLength,
- // }
- // case event.MsgAudio:
- // media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaAudio)
- // if media == nil {
- // return nil, sender
- // }
- // duration := uint32(content.GetInfo().Duration)
- // info.Message.AudioMessage = &waProto.AudioMessage{
- // ContextInfo: ctxInfo,
- // Url: &media.URL,
- // MediaKey: media.MediaKey,
- // Mimetype: &content.GetInfo().MimeType,
- // Seconds: &duration,
- // FileEncSha256: media.FileEncSHA256,
- // FileSha256: media.FileSHA256,
- // FileLength: &media.FileLength,
- // }
- // case event.MsgFile:
- // media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaDocument)
- // if media == nil {
- // return nil, sender
- // }
- // info.Message.DocumentMessage = &waProto.DocumentMessage{
- // ContextInfo: ctxInfo,
- // Url: &media.URL,
- // Title: &content.Body,
- // FileName: &content.Body,
- // MediaKey: media.MediaKey,
- // Mimetype: &content.GetInfo().MimeType,
- // FileEncSha256: media.FileEncSHA256,
- // FileSha256: media.FileSHA256,
- // FileLength: &media.FileLength,
- // }
default:
portal.log.Debugln("Unhandled Matrix event %s: unknown msgtype %s", evt.ID, content.MsgType)
return nil, sender
@@ -1562,13 +1378,6 @@ func (portal *Portal) convertMatrixMessage(sender *User, evt *event.Event) ([]*g
}
func (portal *Portal) wasMessageSent(sender *User, id string) bool {
- // _, err := sender.Conn.LoadMessagesAfter(portal.Key.JID, id, true, 0)
- // if err != nil {
- // if err != whatsapp.ErrServerRespondedWith404 {
- // portal.log.Warnfln("Failed to check if message was bridged without response: %v", err)
- // }
- // return false
- // }
return true
}
@@ -1620,98 +1429,9 @@ func (portal *Portal) sendRaw(sender *User, evt *event.Event, info *groupme.Mess
}
}
return m, nil
- // errChan := make(chan error, 1)
- // go sender.Conn.SendRaw(info, errChan)
-
- // var err error
- // var errorEventID id.EventID
- // select {
- // case err = <-errChan:
- // var statusResp whatsapp.StatusResponse
- // if !isRetry && errors.As(err, &statusResp) && statusResp.Status == 599 {
- // portal.log.Debugfln("599 status response sending %s to WhatsApp (%+v), retrying...", evt.ID, statusResp)
- // errorEventID = portal.sendErrorMessage(fmt.Sprintf("%v. The bridge will retry in 5 seconds.", err))
- // time.Sleep(5 * time.Second)
- // portal.sendRaw(sender, evt, info, true)
- // }
- // case <-time.After(time.Duration(portal.bridge.Config.Bridge.ConnectionTimeout) * time.Second):
- // if portal.bridge.Config.Bridge.FetchMessageOnTimeout && portal.wasMessageSent(sender, info.Key.GetId()) {
- // portal.log.Debugln("Matrix event %s was bridged, but response didn't arrive within timeout")
- // portal.sendDeliveryReceipt(evt.ID)
- // } else {
- // portal.log.Warnfln("Response when bridging Matrix event %s is taking long to arrive", evt.ID)
- // errorEventID = portal.sendErrorMessage(timeout.Error())
- // }
- // err = <-errChan
- // }
- // 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 data: %+v", statusResp)
- // }
- // portal.sendErrorMessage(err.Error())
- // } else {
- // portal.log.Debugfln("Handled Matrix event %s", evt.ID)
- // portal.sendDeliveryReceipt(evt.ID)
- // }
- // if errorEventID != "" {
- // _, err = portal.MainIntent().RedactEvent(portal.MXID, errorEventID)
- // if err != nil {
- // portal.log.Warnfln("Failed to redact timeout warning message %s: %v", errorEventID, err)
- // }
- // }
}
func (portal *Portal) HandleMatrixRedaction(sender *User, evt *event.Event) {
- // if portal.IsPrivateChat() && sender.JID != portal.Key.Receiver {
- // return
- // }
-
- // msg := portal.bridge.DB.Message.GetByMXID(evt.Redacts)
- // if msg == nil || msg.Sender != sender.JID {
- // return
- // }
-
- // ts := uint64(evt.Timestamp / 1000)
- // status := waProto.WebMessageInfo_PENDING
- // protoMsgType := waProto.ProtocolMessage_REVOKE
- // fromMe := true
- // info := &waProto.WebMessageInfo{
- // Key: &waProto.MessageKey{
- // FromMe: &fromMe,
- // Id: makeMessageID(),
- // RemoteJid: &portal.Key.JID,
- // },
- // MessageTimestamp: &ts,
- // Message: &waProto.Message{
- // ProtocolMessage: &waProto.ProtocolMessage{
- // Type: &protoMsgType,
- // Key: &waProto.MessageKey{
- // FromMe: &fromMe,
- // Id: &msg.JID,
- // RemoteJid: &portal.Key.JID,
- // },
- // },
- // },
- // Status: &status,
- // }
- // errChan := make(chan error, 1)
- // go sender.Conn.SendRaw(info, errChan)
-
- // var err error
- // select {
- // case err = <-errChan:
- // case <-time.After(time.Duration(portal.bridge.Config.Bridge.ConnectionTimeout) * time.Second):
- // portal.log.Warnfln("Response when bridging Matrix redaction %s is taking long to arrive", evt.ID)
- // err = <-errChan
- // }
- // if err != nil {
- // portal.log.Errorfln("Error handling Matrix redaction %s: %v", evt.ID, err)
- // } else {
- // portal.log.Debugln("Handled Matrix redaction %s of %s", evt.ID, evt.Redacts)
- // portal.sendDeliveryReceipt(evt.ID)
- // }
}
func (portal *Portal) Delete() {
@@ -1811,25 +1531,7 @@ func (portal *Portal) HandleMatrixLeave(sender *User) {
}
func (portal *Portal) HandleMatrixKick(sender *User, evt *event.Event) {
- // puppet := portal.bridge.GetPuppetByMXID(id.UserID(evt.GetStateKey()))
- // if puppet != nil {
- // resp, err := sender.Conn.RemoveMember(portal.Key.JID, []string{puppet.JID})
- // if err != nil {
- // portal.log.Errorfln("Failed to kick %s from group as %s: %v", puppet.JID, sender.MXID, err)
- // return
- // }
- // portal.log.Infoln("Kick %s response: %s", puppet.JID, <-resp)
- // }
}
func (portal *Portal) HandleMatrixInvite(sender *User, evt *event.Event) {
- // puppet := portal.bridge.GetPuppetByMXID(id.UserID(evt.GetStateKey()))
- // if puppet != nil {
- // resp, err := sender.Conn.AddMember(portal.Key.JID, []string{puppet.JID})
- // if err != nil {
- // portal.log.Errorfln("Failed to add %s to group as %s: %v", puppet.JID, sender.MXID, err)
- // return
- // }
- // portal.log.Infoln("Add %s response: %s", puppet.JID, <-resp)
- // }
}
diff --git a/provisioning.go b/provisioning.go
index 0afa7e3..fe3fdcb 100644
--- a/provisioning.go
+++ b/provisioning.go
@@ -21,7 +21,6 @@ import (
"net/http"
"strings"
- "github.com/gorilla/websocket"
log "maunium.net/go/maulogger/v2"
"maunium.net/go/mautrix/id"
@@ -37,13 +36,6 @@ func (prov *ProvisioningAPI) Init() {
prov.log.Debugln("Enabling provisioning API at", prov.bridge.Config.Bridge.Provisioning.Prefix)
r := prov.bridge.AS.Router.PathPrefix(prov.bridge.Config.Bridge.Provisioning.Prefix).Subrouter()
r.Use(prov.AuthMiddleware)
- r.HandleFunc("/ping", prov.Ping).Methods(http.MethodGet)
- r.HandleFunc("/login", prov.Login)
- r.HandleFunc("/logout", prov.Logout).Methods(http.MethodPost)
- r.HandleFunc("/delete_session", prov.DeleteSession).Methods(http.MethodPost)
- r.HandleFunc("/delete_connection", prov.DeleteConnection).Methods(http.MethodPost)
- r.HandleFunc("/disconnect", prov.Disconnect).Methods(http.MethodPost)
- r.HandleFunc("/reconnect", prov.Reconnect).Methods(http.MethodPost)
}
func (prov *ProvisioningAPI) AuthMiddleware(h http.Handler) http.Handler {
@@ -53,8 +45,8 @@ func (prov *ProvisioningAPI) AuthMiddleware(h http.Handler) http.Handler {
authParts := strings.Split(r.Header.Get("Sec-WebSocket-Protocol"), ",")
for _, part := range authParts {
part = strings.TrimSpace(part)
- if strings.HasPrefix(part, "net.maunium.whatsapp.auth-") {
- auth = part[len("net.maunium.whatsapp.auth-"):]
+ if strings.HasPrefix(part, "com.beeper.groupme.auth-") {
+ auth = part[len("com.beeper.groupme.auth-"):]
break
}
}
@@ -85,326 +77,8 @@ type Response struct {
Status string `json:"status"`
}
-func (prov *ProvisioningAPI) DeleteSession(w http.ResponseWriter, r *http.Request) {
- // user := r.Context().Value("user").(*User)
- // if user.Session == nil && user.Conn == nil {
- // jsonResponse(w, http.StatusNotFound, Error{
- // Error: "Nothing to purge: no session information stored and no active connection.",
- // ErrCode: "no session",
- // })
- // return
- // }
- // user.SetSession(nil)
- // if user.Conn != nil {
- // _, _ = user.Conn.Disconnect()
- // user.Conn.RemoveHandlers()
- // user.Conn = nil
- // user.bridge.Metrics.TrackConnectionState(user.JID, false)
- // }
- // jsonResponse(w, http.StatusOK, Response{true, "Session information purged"})
-}
-
-func (prov *ProvisioningAPI) DeleteConnection(w http.ResponseWriter, r *http.Request) {
- // user := r.Context().Value("user").(*User)
- // if user.Conn == nil {
- // jsonResponse(w, http.StatusNotFound, Error{
- // Error: "You don't have a WhatsApp connection.",
- // ErrCode: "not connected",
- // })
- // return
- // }
- // sess, err := user.Conn.Disconnect()
- // if err == nil && len(sess.Wid) > 0 {
- // user.SetSession(&sess)
- // }
- // user.Conn.RemoveHandlers()
- // user.Conn = nil
- // user.bridge.Metrics.TrackConnectionState(user.JID, false)
- // jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp and connection deleted"})
-}
-
-func (prov *ProvisioningAPI) Disconnect(w http.ResponseWriter, r *http.Request) {
- user := r.Context().Value("user").(*User)
- if user.Conn == nil {
- jsonResponse(w, http.StatusNotFound, Error{
- Error: "You don't have a WhatsApp connection.",
- ErrCode: "no connection",
- })
- return
- }
- //sess, err :=
- //user.Conn.Stop(context.TODO())
- // if err == whatsapp.ErrNotConnected {
- // jsonResponse(w, http.StatusNotFound, Error{
- // Error: "You were not connected",
- // ErrCode: "not connected",
- // })
- // return
- // } else if err != nil {
- // user.log.Warnln("Error while disconnecting:", err)
- // jsonResponse(w, http.StatusInternalServerError, Error{
- // Error: fmt.Sprintf("Unknown error while disconnecting: %v", err),
- // ErrCode: err.Error(),
- // })
- // return
- // } else if len(sess.Wid) > 0 {
- // user.SetSession(&sess)
- // }
- user.bridge.Metrics.TrackConnectionState(user.GMID, false)
- jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp"})
-}
-
-func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) {
- // user := r.Context().Value("user").(*User)
- // if user.Conn == nil {
- // if user.Session == nil {
- // jsonResponse(w, http.StatusForbidden, Error{
- // Error: "No existing connection and no session. Please log in first.",
- // ErrCode: "no session",
- // })
- // } else {
- // user.Connect(false)
- // jsonResponse(w, http.StatusOK, Response{true, "Created connection to WhatsApp."})
- // }
- // return
- // }
-
- // wasConnected := true
- // sess, err := user.Conn.Disconnect()
- // if err == whatsapp.ErrNotConnected {
- // wasConnected = false
- // } else if err != nil {
- // user.log.Warnln("Error while disconnecting:", err)
- // } else if len(sess.Wid) > 0 {
- // user.SetSession(&sess)
- // }
-
- // err = user.Conn.Restore()
- // if err == whatsapp.ErrInvalidSession {
- // if user.Session != nil {
- // user.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...")
- // var sess whatsapp.Session
- // sess, err = user.Conn.RestoreWithSession(*user.Session)
- // if err == nil {
- // user.SetSession(&sess)
- // }
- // } else {
- // jsonResponse(w, http.StatusForbidden, Error{
- // Error: "You're not logged in",
- // ErrCode: "not logged in",
- // })
- // return
- // }
- // } else if err == whatsapp.ErrLoginInProgress {
- // jsonResponse(w, http.StatusConflict, Error{
- // Error: "A login or reconnection is already in progress.",
- // ErrCode: "login in progress",
- // })
- // return
- // } else if err == whatsapp.ErrAlreadyLoggedIn {
- // jsonResponse(w, http.StatusConflict, Error{
- // Error: "You were already connected.",
- // ErrCode: err.Error(),
- // })
- // return
- // }
- // if err != nil {
- // user.log.Warnln("Error while reconnecting:", err)
- // if err.Error() == "restore session connection timed out" {
- // 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(),
- // })
- // }
- // user.log.Debugln("Disconnecting due to failed session restore in reconnect command...")
- // sess, err := user.Conn.Disconnect()
- // if err != nil {
- // user.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err)
- // } else if len(sess.Wid) > 0 {
- // user.SetSession(&sess)
- // }
- // return
- // }
- // user.ConnectionErrors = 0
- // user.PostLogin()
-
- // var msg string
- // if wasConnected {
- // msg = "Reconnected successfully."
- // } else {
- // msg = "Connected successfully."
- // }
-
- // jsonResponse(w, http.StatusOK, Response{true, msg})
-}
-
-func (prov *ProvisioningAPI) Ping(w http.ResponseWriter, r *http.Request) {
- // user := r.Context().Value("user").(*User)
- // wa := map[string]interface{}{
- // "has_session": user.Client != nil,
- // "management_room": user.ManagementRoom,
- // "jid": user.JID,
- // "conn": nil,
- // "ping": nil,
- // }
- // if user.Conn != nil {
- // wa["conn"] = map[string]interface{}{
- // "is_connected": user.IsConnected(),
- // "is_logged_in": user.IsLoggedIn(),
- // "is_login_in_progress": user.IsLoginInProgress(),
- // }
- // err := user.Conn.AdminTest()
- // wa["ping"] = map[string]interface{}{
- // "ok": err == nil,
- // "err": err,
- // }
- // }
- // resp := map[string]interface{}{
- // "mxid": user.MXID,
- // "admin": user.Admin,
- // "whitelisted": user.Whitelisted,
- // "relaybot_whitelisted": user.RelaybotWhitelisted,
- // "whatsapp": wa,
- // }
- // jsonResponse(w, http.StatusOK, resp)
-}
-
func jsonResponse(w http.ResponseWriter, status int, response interface{}) {
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(response)
}
-
-func (prov *ProvisioningAPI) Logout(w http.ResponseWriter, r *http.Request) {
- // user := r.Context().Value("user").(*User)
- // if user.Session == nil {
- // jsonResponse(w, http.StatusNotFound, Error{
- // Error: "You're not logged in",
- // ErrCode: "not logged in",
- // })
- // return
- // }
-
- // force := strings.ToLower(r.URL.Query().Get("force")) != "false"
-
- // if user.Conn == nil {
- // if !force {
- // jsonResponse(w, http.StatusNotFound, Error{
- // Error: "You're not connected",
- // ErrCode: "not connected",
- // })
- // }
- // } else {
- // err := user.Conn.Logout()
- // if err != nil {
- // user.log.Warnln("Error while logging out:", err)
- // if !force {
- // jsonResponse(w, http.StatusInternalServerError, Error{
- // Error: fmt.Sprintf("Unknown error while logging out: %v", err),
- // ErrCode: err.Error(),
- // })
- // return
- // }
- // }
- // _, err = user.Conn.Disconnect()
- // if err != nil {
- // user.log.Warnln("Error while disconnecting after logout:", err)
- // }
- // user.Conn.RemoveHandlers()
- // user.Conn = nil
- // }
-
- // user.bridge.Metrics.TrackConnectionState(user.JID, false)
- // user.removeFromJIDMap()
-
- // // TODO this causes a foreign key violation, which should be fixed
- // //ce.User.JID = ""
- // user.SetSession(nil)
- // jsonResponse(w, http.StatusOK, Response{true, "Logged out successfully."})
-}
-
-var upgrader = websocket.Upgrader{
- CheckOrigin: func(r *http.Request) bool {
- return true
- },
- Subprotocols: []string{"net.maunium.whatsapp.login"},
-}
-
-func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
- // userID := r.URL.Query().Get("user_id")
- // user := prov.bridge.GetUserByMXID(id.UserID(userID))
-
- // if len(ce.Args) < 1 {
- // // Return error that the token needs to be longer than 0 length
- // // ce.Reply(`Get your access token from https://dev.groupme.com/ which should be the first argument to login`)
- // return
- // }
- // user.Token = ce.Args[0]
-
- // user.addToJIDMap()
- // // ce.Reply("Successfully logged in, synchronizing chats...")
- // user.PostLogin()
- // user.Connect()
-
- // c, err := upgrader.Upgrade(w, r, nil)
- // if err != nil {
- // prov.log.Errorfln("Failed to upgrade connection to websocket:", err)
- // return
- // }
- // defer c.Close()
-
- // if !user.Connect(true) {
- // user.log.Debugln("Connect() returned false, assuming error was logged elsewhere and canceling login.")
- // _ = c.WriteJSON(Error{
- // Error: "Failed to connect to WhatsApp",
- // ErrCode: "connection error",
- // })
- // return
- // }
-
- // qrChan := make(chan string, 3)
- // go func() {
- // for code := range qrChan {
- // if code == "stop" {
- // return
- // }
- // _ = c.WriteJSON(map[string]interface{}{
- // "code": code,
- // })
- // }
- // }()
- // session, err := user.Conn.LoginWithRetry(qrChan, user.bridge.Config.Bridge.LoginQRRegenCount)
- // qrChan <- "stop"
- // if err != nil {
- // var msg string
- // if err == whatsapp.ErrAlreadyLoggedIn {
- // msg = "You're already logged in"
- // } else if err == whatsapp.ErrLoginInProgress {
- // msg = "You have a login in progress already."
- // } else if err == whatsapp.ErrLoginTimedOut {
- // msg = "QR code scan timed out. Please try again."
- // } else {
- // user.log.Warnln("Failed to log in:", err)
- // msg = fmt.Sprintf("Unknown error while logging in: %v", err)
- // }
- // _ = c.WriteJSON(Error{
- // Error: msg,
- // ErrCode: err.Error(),
- // })
- // return
- // }
- // user.ConnectionErrors = 0
- // user.JID = strings.Replace(user.Conn.Info.Wid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, 1)
- // user.addToJIDMap()
- // user.SetSession(&session)
- // _ = c.WriteJSON(map[string]interface{}{
- // "success": true,
- // "jid": user.JID,
- // })
- // user.PostLogin()
-}
diff --git a/segment.go b/segment.go
index f026ad8..b5a90d9 100644
--- a/segment.go
+++ b/segment.go
@@ -30,6 +30,7 @@ const SegmentURL = "https://api.segment.io/v1/track"
type SegmentClient struct {
key string
+ userID string
log log.Logger
client http.Client
}
@@ -38,8 +39,14 @@ var Segment SegmentClient
func (sc *SegmentClient) trackSync(userID id.UserID, event string, properties map[string]interface{}) error {
var buf bytes.Buffer
+ var segmentUserID string
+ if Segment.userID != "" {
+ segmentUserID = Segment.userID
+ } else {
+ segmentUserID = userID.String()
+ }
err := json.NewEncoder(&buf).Encode(map[string]interface{}{
- "userId": userID,
+ "userId": segmentUserID,
"event": event,
"properties": properties,
})
diff --git a/user.go b/user.go
index 2c0494e..469579d 100644
--- a/user.go
+++ b/user.go
@@ -26,9 +26,6 @@ import (
"time"
log "maunium.net/go/maulogger/v2"
-
- "github.com/Rhymen/go-whatsapp"
-
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/appservice"
"maunium.net/go/mautrix/bridge"
@@ -206,7 +203,7 @@ func (br *GMBridge) NewUser(dbUser *database.User) *User {
user.PermissionLevel = user.bridge.Config.Bridge.Permissions.Get(user.MXID)
user.Whitelisted = user.PermissionLevel >= bridgeconfig.PermissionLevelUser
user.Admin = user.PermissionLevel >= bridgeconfig.PermissionLevelAdmin
- user.BridgeState = br.NewBridgeStateQueue(user, user.log)
+ user.BridgeState = br.NewBridgeStateQueue(user)
go user.handleMessageLoop()
go user.runMessageRingBuffer()
return user
@@ -332,14 +329,6 @@ func (user *User) SetManagementRoom(roomID id.RoomID) {
user.Update()
}
-func (user *User) SetSession(session *whatsapp.Session) {
- // user.Session = session
- // if session == nil {
- // user.LastConnection = 0
- // }
- // user.Update()
-}
-
func (user *User) Connect() bool {
if user.Conn != nil {
return true
@@ -347,14 +336,14 @@ func (user *User) Connect() bool {
return false
}
- user.log.Debugln("Connecting to GroupMe")
+ user.log.Debugfln("Connecting to GroupMe")
timeout := time.Duration(user.bridge.Config.GroupMe.ConnectionTimeout)
if timeout == 0 {
timeout = 20
}
conn := groupme.NewPushSubscription(context.Background())
user.Conn = &conn
- user.Conn.StartListening(context.TODO(), groupmeext.NewFayeClient(user.log))
+ user.Conn.StartListening(context.Background(), groupmeext.NewFayeClient(user.log))
user.Conn.AddFullHandler(user)
//TODO: typing notification?
@@ -737,112 +726,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
- }
- go user.syncPuppets(contactMap)
-}
-
-func (user *User) syncPuppets(contacts map[string]whatsapp.Contact) {
- // if contacts == nil {
- // contacts = user.Conn.Store.Contacts
- // }
- // user.log.Infoln("Syncing puppet info from contacts")
- // for jid, contact := range contacts {
- // if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) {
- // puppet := user.bridge.GetPuppetByJID(contact.Jid)
- // puppet.Sync(user, contact)
- // }
- // }
- // user.log.Infoln("Finished syncing puppet info from contacts")
-}
-
-func (user *User) updateLastConnectionIfNecessary() {
- // if user.LastConnection+60 < uint64(time.Now().Unix()) {
- // user.UpdateLastConnection()
- // }
-}
-
func (user *User) HandleError(err error) {
- if !errors.Is(err, whatsapp.ErrInvalidWsData) {
- user.log.Errorfln("WhatsApp error: %v", err)
- }
- if closed, ok := err.(*whatsapp.ErrConnectionClosed); ok {
- user.bridge.Metrics.TrackDisconnection(user.MXID)
- if closed.Code == 1000 && user.cleanDisconnection {
- user.bridge.Metrics.TrackConnectionState(user.GMID, false)
- user.cleanDisconnection = false
- user.log.Infoln("Clean disconnection by server")
- return
- }
- go user.tryReconnect(fmt.Sprintf("Your WhatsApp connection was closed with websocket status code %d", closed.Code))
- } else if failed, ok := err.(*whatsapp.ErrConnectionFailed); ok {
- user.bridge.Metrics.TrackDisconnection(user.MXID)
- user.ConnectionErrors++
- go user.tryReconnect(fmt.Sprintf("Your WhatsApp connection failed: %v", failed.Err))
- }
- // Otherwise unknown error, probably mostly harmless
-}
-
-func (user *User) tryReconnect(msg string) {
- // user.bridge.Metrics.TrackConnectionState(user.JID, false)
- // if user.ConnectionErrors > user.bridge.Config.Bridge.MaxConnectionAttempts {
- // user.sendMarkdownBridgeAlert("%s. Use the `reconnect` command to reconnect.", msg)
- // return
- // }
- // if user.bridge.Config.Bridge.ReportConnectionRetry {
- // user.sendBridgeNotice("%s. Reconnecting...", msg)
- // // Don't want the same error to be repeated
- // msg = ""
- // }
- // var tries uint
- // var exponentialBackoff bool
- // baseDelay := time.Duration(user.bridge.Config.Bridge.ConnectionRetryDelay)
- // if baseDelay < 0 {
- // exponentialBackoff = true
- // baseDelay = -baseDelay + 1
- // }
- // delay := baseDelay
- // for user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
- // err := user.Conn.Restore()
- // if err == nil {
- // user.ConnectionErrors = 0
- // if user.bridge.Config.Bridge.ReportConnectionRetry {
- // user.sendBridgeNotice("Reconnected successfully")
- // }
- // user.PostLogin()
- // return
- // } else if err.Error() == "init responded with 400" {
- // user.log.Infoln("Got init 400 error when trying to reconnect, resetting connection...")
- // sess, err := user.Conn.Disconnect()
- // if err != nil {
- // user.log.Debugln("Error while disconnecting for connection reset:", err)
- // }
- // if len(sess.Wid) > 0 {
- // user.SetSession(&sess)
- // }
- // }
- // user.log.Errorln("Error while trying to reconnect after disconnection:", err)
- // tries++
- // user.ConnectionErrors++
- // if user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
- // if exponentialBackoff {
- // delay = (1 << tries) + baseDelay
- // }
- // if user.bridge.Config.Bridge.ReportConnectionRetry {
- // user.sendBridgeNotice("Reconnection attempt failed: %v. Retrying in %d seconds...", err, delay)
- // }
- // time.Sleep(delay * time.Second)
- // }
- // }
-
- // if user.bridge.Config.Bridge.ReportConnectionRetry {
- // user.sendMarkdownBridgeAlert("%d reconnection attempts failed. Use the `reconnect` command to try to reconnect manually.", tries)
- // } else {
- // user.sendMarkdownBridgeAlert("\u26a0 %s. Additionally, %d reconnection attempts failed. Use the `reconnect` command to try to reconnect.", msg, tries)
- // }
}
func (user *User) ShouldCallSynchronously() bool {
@@ -850,7 +734,7 @@ func (user *User) ShouldCallSynchronously() bool {
}
func (user *User) HandleJSONParseError(err error) {
- user.log.Errorln("WhatsApp JSON parse error:", err)
+ user.log.Errorln("GroupMe JSON parse error:", err)
}
func (user *User) PortalKey(gmid groupme.ID) database.PortalKey {
@@ -893,34 +777,6 @@ 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)
-// }()
-//}
-
-//func (user *User) HandleBatteryMessage(battery whatsapp.BatteryMessage) {
-// user.log.Debugfln("Battery message: %+v", battery)
-// var notice string
-// if !battery.Plugged && battery.Percentage < 15 && user.batteryWarningsSent < 1 {
-// notice = fmt.Sprintf("Phone battery low (%d %% remaining)", battery.Percentage)
-// user.batteryWarningsSent = 1
-// } else if !battery.Plugged && battery.Percentage < 5 && user.batteryWarningsSent < 2 {
-// notice = fmt.Sprintf("Phone battery very low (%d %% remaining)", battery.Percentage)
-// user.batteryWarningsSent = 2
-// } else if battery.Percentage > 15 || battery.Plugged {
-// user.batteryWarningsSent = 0
-// }
-// if notice != "" {
-// go user.sendBridgeNotice("%s", notice)
-// }
-//}
-
func (user *User) HandleTextMessage(message groupme.Message) {
id := database.ParsePortalKey(message.GroupID.String())
@@ -983,270 +839,15 @@ func (user *User) HandleNewNickname(groupID, userID groupme.ID, name string) {
func (user *User) HandleNewAvatarInGroup(groupID, userID groupme.ID, url string) {
puppet := user.bridge.GetPuppetByGMID(userID)
- if puppet != nil {
- puppet.UpdateAvatar(user, false)
- }
+ puppet.UpdateAvatar(user, false)
}
func (user *User) HandleMembers(_ groupme.ID, _ []groupme.Member, _ bool) {
user.HandleChatList()
}
-//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) 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) {
-// if info.Data != nil {
-// return
-// }
-// data := FakeMessage{
-// ID: info.ID,
-// }
-// switch info.Type {
-// case whatsappExt.CallOffer:
-// if !user.bridge.Config.Bridge.CallNotices.Start {
-// return
-// }
-// data.Text = "Incoming call"
-// data.Alert = true
-// case whatsappExt.CallOfferVideo:
-// if !user.bridge.Config.Bridge.CallNotices.Start {
-// return
-// }
-// data.Text = "Incoming video call"
-// data.Alert = true
-// case whatsappExt.CallTerminate:
-// if !user.bridge.Config.Bridge.CallNotices.End {
-// return
-// }
-// data.Text = "Call ended"
-// data.ID += "E"
-// default:
-// return
-// }
-// portal := user.GetPortalByJID(info.From)
-// if portal != nil {
-// portal.messages <- PortalMessage{info.From, user, data, 0}
-// }
-//}
-
-//func (user *User) HandlePresence(info whatsappExt.Presence) {
-// puppet := user.bridge.GetPuppetByJID(info.SenderJID)
-// switch info.Status {
-// case whatsapp.PresenceUnavailable:
-// _ = puppet.DefaultIntent().SetPresence("offline")
-// case whatsapp.PresenceAvailable:
-// if len(puppet.typingIn) > 0 && puppet.typingAt+15 > time.Now().Unix() {
-// portal := user.bridge.GetPortalByMXID(puppet.typingIn)
-// _, _ = puppet.IntentFor(portal).UserTyping(puppet.typingIn, false, 0)
-// puppet.typingIn = ""
-// puppet.typingAt = 0
-// } else {
-// _ = puppet.DefaultIntent().SetPresence("online")
-// }
-// case whatsapp.PresenceComposing:
-// portal := user.GetPortalByJID(info.JID)
-// if len(puppet.typingIn) > 0 && puppet.typingAt+15 > time.Now().Unix() {
-// if puppet.typingIn == portal.MXID {
-// return
-// }
-// _, _ = puppet.IntentFor(portal).UserTyping(puppet.typingIn, false, 0)
-// }
-// puppet.typingIn = portal.MXID
-// puppet.typingAt = time.Now().Unix()
-// _, _ = puppet.IntentFor(portal).UserTyping(portal.MXID, true, 15*1000)
-// }
-//}
-//
-//func (user *User) HandleMsgInfo(info whatsappExt.MsgInfo) {
-// if (info.Command == whatsappExt.MsgInfoCommandAck || info.Command == whatsappExt.MsgInfoCommandAcks) && info.Acknowledgement == whatsappExt.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 {
-// 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)
-// } else {
-// user.log.Debugfln("Unknown received message type: %+v", received)
-// }
-//}
-//
-//func (user *User) HandleReadMessage(read whatsapp.ReadMessage) {
-// user.log.Debugfln("Received chat read message: %+v", read)
-// 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)
-// }
-// puppet := user.bridge.GetPuppetByJID(user.JID)
-// if puppet == nil {
-// return
-// }
-// intent := puppet.CustomIntent()
-// if intent == nil {
-// return
-// }
-// portal := user.GetPortalByJID(jid)
-// if portal == nil {
-// return
-// }
-// var message *database.Message
-// if messageID == "" {
-// message = user.bridge.DB.Message.GetLastInChat(portal.Key)
-// if message == nil {
-// return
-// }
-// user.log.Debugfln("User read chat %s/%s in WhatsApp mobile (last known event: %s/%s)", portal.Key.JID, portal.MXID, message.JID, message.MXID)
-// } else {
-// message = user.bridge.DB.Message.GetByJID(portal.Key, messageID)
-// if message == nil {
-// return
-// }
-// user.log.Debugfln("User read message %s/%s in %s/%s in WhatsApp mobile", message.JID, message.MXID, portal.Key.JID, portal.MXID)
-// }
-// err := intent.MarkRead(portal.MXID, message.MXID)
-// if err != nil {
-// user.log.Warnfln("Failed to bridge own read receipt in %s: %v", jid, err)
-// }
-//}
-//
-//func (user *User) HandleCommand(cmd whatsappExt.Command) {
-// switch cmd.Type {
-// case whatsappExt.CommandPicture:
-// if strings.HasSuffix(cmd.JID, whatsappExt.NewUserSuffix) {
-// puppet := user.bridge.GetPuppetByJID(cmd.JID)
-// go puppet.UpdateAvatar(user, cmd.ProfilePicInfo)
-// } else {
-// portal := user.GetPortalByJID(cmd.JID)
-// go portal.UpdateAvatar(user, cmd.ProfilePicInfo, true)
-// }
-// case whatsappExt.CommandDisconnect:
-// user.cleanDisconnection = true
-// if cmd.Kind == "replaced" {
-// go user.sendMarkdownBridgeAlert("\u26a0 Your WhatsApp connection was closed by the server because you opened another WhatsApp Web client.\n\n" +
-// "Use the `reconnect` command to disconnect the other client and resume bridging.")
-// } else {
-// user.log.Warnln("Unknown kind of disconnect:", string(cmd.Raw))
-// go user.sendMarkdownBridgeAlert("\u26a0 Your WhatsApp connection was closed by the server (reason code: %s).\n\n"+
-// "Use the `reconnect` command to reconnect.", cmd.Kind)
-// }
-// }
-//}
-//
-//func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
-// if cmd.Command != whatsappExt.ChatUpdateCommandAction {
-// return
-// }
-//
-// portal := user.GetPortalByJID(cmd.JID)
-// if len(portal.MXID) == 0 {
-// if cmd.Data.Action == whatsappExt.ChatActionIntroduce || cmd.Data.Action == whatsappExt.ChatActionCreate {
-// go func() {
-// err := portal.CreateMatrixRoom(user)
-// if err != nil {
-// user.log.Errorln("Failed to create portal room after receiving join event:", err)
-// }
-// }()
-// }
-// return
-// }
-//
-// switch cmd.Data.Action {
-// case whatsappExt.ChatActionNameChange:
-// go portal.UpdateName(cmd.Data.NameChange.Name, cmd.Data.SenderJID, true)
-// case whatsappExt.ChatActionAddTopic:
-// go portal.UpdateTopic(cmd.Data.AddTopic.Topic, cmd.Data.SenderJID, true)
-// case whatsappExt.ChatActionRemoveTopic:
-// go portal.UpdateTopic("", cmd.Data.SenderJID, true)
-// case whatsappExt.ChatActionPromote:
-// go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, true)
-// case whatsappExt.ChatActionDemote:
-// go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, false)
-// case whatsappExt.ChatActionAnnounce:
-// go portal.RestrictMessageSending(cmd.Data.Announce)
-// case whatsappExt.ChatActionRestrict:
-// go portal.RestrictMetadataChanges(cmd.Data.Restrict)
-// case whatsappExt.ChatActionRemove:
-// go portal.HandleWhatsAppKick(cmd.Data.SenderJID, cmd.Data.UserChange.JIDs)
-// case whatsappExt.ChatActionAdd:
-// go portal.HandleWhatsAppInvite(cmd.Data.SenderJID, cmd.Data.UserChange.JIDs)
-// //case whatsappExt.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 {
-// return
-// }
-// user.log.Debugln("JSON message:", 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)
-//}