forked from MirrorHub/mautrix-whatsapp
Allow sending analytics to custom server
This commit is contained in:
parent
e1edb644c0
commit
d2110f6ee7
8 changed files with 54 additions and 41 deletions
|
@ -26,27 +26,26 @@ import (
|
|||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
const SegmentURL = "https://api.segment.io/v1/track"
|
||||
|
||||
type SegmentClient struct {
|
||||
type AnalyticsClient struct {
|
||||
url string
|
||||
key string
|
||||
userID string
|
||||
log log.Logger
|
||||
client http.Client
|
||||
}
|
||||
|
||||
var Segment SegmentClient
|
||||
var Analytics AnalyticsClient
|
||||
|
||||
func (sc *SegmentClient) trackSync(userID id.UserID, event string, properties map[string]interface{}) error {
|
||||
func (sc *AnalyticsClient) trackSync(userID id.UserID, event string, properties map[string]interface{}) error {
|
||||
var buf bytes.Buffer
|
||||
var segmentUserID string
|
||||
if Segment.userID != "" {
|
||||
segmentUserID = Segment.userID
|
||||
var analyticsUserID string
|
||||
if Analytics.userID != "" {
|
||||
analyticsUserID = Analytics.userID
|
||||
} else {
|
||||
segmentUserID = userID.String()
|
||||
analyticsUserID = userID.String()
|
||||
}
|
||||
err := json.NewEncoder(&buf).Encode(map[string]interface{}{
|
||||
"userId": segmentUserID,
|
||||
"userId": analyticsUserID,
|
||||
"event": event,
|
||||
"properties": properties,
|
||||
})
|
||||
|
@ -54,7 +53,7 @@ func (sc *SegmentClient) trackSync(userID id.UserID, event string, properties ma
|
|||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", SegmentURL, &buf)
|
||||
req, err := http.NewRequest(http.MethodPost, sc.url, &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -70,11 +69,11 @@ func (sc *SegmentClient) trackSync(userID id.UserID, event string, properties ma
|
|||
return nil
|
||||
}
|
||||
|
||||
func (sc *SegmentClient) IsEnabled() bool {
|
||||
func (sc *AnalyticsClient) IsEnabled() bool {
|
||||
return len(sc.key) > 0
|
||||
}
|
||||
|
||||
func (sc *SegmentClient) Track(userID id.UserID, event string, properties ...map[string]interface{}) {
|
||||
func (sc *AnalyticsClient) Track(userID id.UserID, event string, properties ...map[string]interface{}) {
|
||||
if !sc.IsEnabled() {
|
||||
return
|
||||
} else if len(properties) > 1 {
|
|
@ -24,8 +24,11 @@ import (
|
|||
type Config struct {
|
||||
*bridgeconfig.BaseConfig `yaml:",inline"`
|
||||
|
||||
SegmentKey string `yaml:"segment_key"`
|
||||
SegmentUserID string `yaml:"segment_user_id"`
|
||||
Analytics struct {
|
||||
Host string `yaml:"host"`
|
||||
Token string `yaml:"token"`
|
||||
UserID string `yaml:"user_id"`
|
||||
}
|
||||
|
||||
Metrics struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
|
|
|
@ -27,8 +27,9 @@ import (
|
|||
func DoUpgrade(helper *up.Helper) {
|
||||
bridgeconfig.Upgrader.DoUpgrade(helper)
|
||||
|
||||
helper.Copy(up.Str|up.Null, "segment_key")
|
||||
helper.Copy(up.Str|up.Null, "segment_user_id")
|
||||
helper.Copy(up.Str|up.Null, "analytics", "host")
|
||||
helper.Copy(up.Str|up.Null, "analytics", "token")
|
||||
helper.Copy(up.Str|up.Null, "analytics", "user_id")
|
||||
|
||||
helper.Copy(up.Bool, "metrics", "enabled")
|
||||
helper.Copy(up.Str, "metrics", "listen")
|
||||
|
@ -181,7 +182,7 @@ var SpacedBlocks = [][]string{
|
|||
{"appservice", "database"},
|
||||
{"appservice", "id"},
|
||||
{"appservice", "as_token"},
|
||||
{"segment_key"},
|
||||
{"analytics"},
|
||||
{"metrics"},
|
||||
{"whatsapp"},
|
||||
{"bridge"},
|
||||
|
|
|
@ -76,10 +76,14 @@ appservice:
|
|||
as_token: "This value is generated when generating the registration"
|
||||
hs_token: "This value is generated when generating the registration"
|
||||
|
||||
# Segment API key to track some events, like provisioning API login and encryption errors.
|
||||
segment_key: null
|
||||
# Optional user_id to use when sending Segment events. If null, defaults to using mxID.
|
||||
segment_user_id: null
|
||||
# Segment-compatible analytics endpoint for tracking some events, like provisioning API login and encryption errors.
|
||||
analytics:
|
||||
# Hostname of the tracking server. The path is hardcoded to /v1/track
|
||||
host: api.segment.io
|
||||
# API key to send with tracking requests. Tracking is disabled if this is null.
|
||||
token: null
|
||||
# Optional user ID for tracking events. If null, defaults to using Matrix user ID.
|
||||
user_id: null
|
||||
|
||||
# Prometheus config.
|
||||
metrics:
|
||||
|
|
20
main.go
20
main.go
|
@ -19,6 +19,7 @@ package main
|
|||
import (
|
||||
_ "embed"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -90,13 +91,18 @@ func (br *WABridge) Init() {
|
|||
br.EventProcessor.On(TypeMSC3381PollResponse, br.MatrixHandler.HandleMessage)
|
||||
br.EventProcessor.On(TypeMSC3381V2PollResponse, br.MatrixHandler.HandleMessage)
|
||||
|
||||
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)
|
||||
Analytics.log = br.Log.Sub("Analytics")
|
||||
Analytics.url = (&url.URL{
|
||||
Scheme: "https",
|
||||
Host: br.Config.Analytics.Host,
|
||||
Path: "/v1/track",
|
||||
}).String()
|
||||
Analytics.key = br.Config.Analytics.Token
|
||||
Analytics.userID = br.Config.Analytics.UserID
|
||||
if Analytics.IsEnabled() {
|
||||
Analytics.log.Infoln("Analytics metrics are enabled")
|
||||
if Analytics.userID != "" {
|
||||
Analytics.log.Infoln("Overriding analytics user_id with %v", Analytics.userID)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -773,7 +773,7 @@ func (portal *Portal) handleUndecryptableMessage(source *User, evt *events.Undec
|
|||
if evt.IsUnavailable {
|
||||
metricType = "unavailable"
|
||||
}
|
||||
Segment.Track(source.MXID, "WhatsApp undecryptable message", map[string]interface{}{
|
||||
Analytics.Track(source.MXID, "WhatsApp undecryptable message", map[string]interface{}{
|
||||
"messageID": evt.Info.ID,
|
||||
"undecryptableType": metricType,
|
||||
})
|
||||
|
@ -849,7 +849,7 @@ func (portal *Portal) handleMessage(source *User, evt *events.Message, historica
|
|||
if evt.UnavailableRequestID != "" {
|
||||
resolveType = "phone"
|
||||
}
|
||||
Segment.Track(source.MXID, "WhatsApp undecryptable message resolved", map[string]interface{}{
|
||||
Analytics.Track(source.MXID, "WhatsApp undecryptable message resolved", map[string]interface{}{
|
||||
"messageID": evt.Info.ID,
|
||||
"resolveType": resolveType,
|
||||
})
|
||||
|
|
|
@ -692,7 +692,7 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
user.log.Debugln("Started login via provisioning API")
|
||||
Segment.Track(user.MXID, "$login_start")
|
||||
Analytics.Track(user.MXID, "$login_start")
|
||||
|
||||
for {
|
||||
select {
|
||||
|
@ -701,7 +701,7 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
|
|||
case whatsmeow.QRChannelSuccess.Event:
|
||||
jid := user.Client.Store.ID
|
||||
user.log.Debugln("Successful login as", jid, "via provisioning API")
|
||||
Segment.Track(user.MXID, "$login_success")
|
||||
Analytics.Track(user.MXID, "$login_success")
|
||||
_ = c.WriteJSON(map[string]interface{}{
|
||||
"success": true,
|
||||
"jid": jid,
|
||||
|
@ -711,7 +711,7 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
|
|||
case whatsmeow.QRChannelTimeout.Event:
|
||||
user.log.Debugln("Login via provisioning API timed out")
|
||||
errCode := "login timed out"
|
||||
Segment.Track(user.MXID, "$login_failure", map[string]interface{}{"error": errCode})
|
||||
Analytics.Track(user.MXID, "$login_failure", map[string]interface{}{"error": errCode})
|
||||
_ = c.WriteJSON(Error{
|
||||
Error: "QR code scan timed out. Please try again.",
|
||||
ErrCode: errCode,
|
||||
|
@ -719,7 +719,7 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
|
|||
case whatsmeow.QRChannelErrUnexpectedEvent.Event:
|
||||
user.log.Debugln("Login via provisioning API failed due to unexpected event")
|
||||
errCode := "unexpected event"
|
||||
Segment.Track(user.MXID, "$login_failure", map[string]interface{}{"error": errCode})
|
||||
Analytics.Track(user.MXID, "$login_failure", map[string]interface{}{"error": errCode})
|
||||
_ = c.WriteJSON(Error{
|
||||
Error: "Got unexpected event while waiting for QRs, perhaps you're already logged in?",
|
||||
ErrCode: errCode,
|
||||
|
@ -727,14 +727,14 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
|
|||
case whatsmeow.QRChannelClientOutdated.Event:
|
||||
user.log.Debugln("Login via provisioning API failed due to outdated client")
|
||||
errCode := "bridge outdated"
|
||||
Segment.Track(user.MXID, "$login_failure", map[string]interface{}{"error": errCode})
|
||||
Analytics.Track(user.MXID, "$login_failure", map[string]interface{}{"error": errCode})
|
||||
_ = c.WriteJSON(Error{
|
||||
Error: "Got client outdated error while waiting for QRs. The bridge must be updated to continue.",
|
||||
ErrCode: errCode,
|
||||
})
|
||||
case whatsmeow.QRChannelScannedWithoutMultidevice.Event:
|
||||
errCode := "multidevice not enabled"
|
||||
Segment.Track(user.MXID, "$login_failure", map[string]interface{}{"error": errCode})
|
||||
Analytics.Track(user.MXID, "$login_failure", map[string]interface{}{"error": errCode})
|
||||
_ = c.WriteJSON(Error{
|
||||
Error: "Please enable the WhatsApp multidevice beta and scan the QR code again.",
|
||||
ErrCode: errCode,
|
||||
|
@ -742,13 +742,13 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
|
|||
continue
|
||||
case "error":
|
||||
errCode := "fatal error"
|
||||
Segment.Track(user.MXID, "$login_failure", map[string]interface{}{"error": errCode})
|
||||
Analytics.Track(user.MXID, "$login_failure", map[string]interface{}{"error": errCode})
|
||||
_ = c.WriteJSON(Error{
|
||||
Error: "Fatal error while logging in",
|
||||
ErrCode: errCode,
|
||||
})
|
||||
case "code":
|
||||
Segment.Track(user.MXID, "$qrcode_retrieved")
|
||||
Analytics.Track(user.MXID, "$qrcode_retrieved")
|
||||
_ = c.WriteJSON(map[string]interface{}{
|
||||
"code": evt.Code,
|
||||
"timeout": int(evt.Timeout.Seconds()),
|
||||
|
|
4
user.go
4
user.go
|
@ -498,7 +498,7 @@ func (user *User) createClient(sess *store.Device) {
|
|||
user.Client.SetForceActiveDeliveryReceipts(user.bridge.Config.Bridge.ForceActiveDeliveryReceipts)
|
||||
user.Client.AutomaticMessageRerequestFromPhone = true
|
||||
user.Client.GetMessageForRetry = func(requester, to types.JID, id types.MessageID) *waProto.Message {
|
||||
Segment.Track(user.MXID, "WhatsApp incoming retry (message not found)", map[string]interface{}{
|
||||
Analytics.Track(user.MXID, "WhatsApp incoming retry (message not found)", map[string]interface{}{
|
||||
"requester": user.obfuscateJID(requester),
|
||||
"messageID": id,
|
||||
})
|
||||
|
@ -506,7 +506,7 @@ func (user *User) createClient(sess *store.Device) {
|
|||
return nil
|
||||
}
|
||||
user.Client.PreRetryCallback = func(receipt *events.Receipt, messageID types.MessageID, retryCount int, msg *waProto.Message) bool {
|
||||
Segment.Track(user.MXID, "WhatsApp incoming retry (accepted)", map[string]interface{}{
|
||||
Analytics.Track(user.MXID, "WhatsApp incoming retry (accepted)", map[string]interface{}{
|
||||
"requester": user.obfuscateJID(receipt.Sender),
|
||||
"messageID": messageID,
|
||||
"retryCount": retryCount,
|
||||
|
|
Loading…
Reference in a new issue