Merge pull request #11 from mautrix/segment-to-rudderstack
add generic analytics section for segment compatible api
This commit is contained in:
commit
15bb0f8d18
7 changed files with 53 additions and 40 deletions
|
@ -26,27 +26,26 @@ import (
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
const SegmentURL = "https://api.segment.io/v1/track"
|
type AnalyticsClient struct {
|
||||||
|
url string
|
||||||
type SegmentClient struct {
|
|
||||||
key string
|
key string
|
||||||
userID string
|
userID string
|
||||||
log zerolog.Logger
|
log zerolog.Logger
|
||||||
client http.Client
|
client http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var Segment SegmentClient
|
var Analytics AnalyticsClient
|
||||||
|
|
||||||
func (sc *SegmentClient) trackSync(userID id.UserID, event string, properties map[string]interface{}) error {
|
func (ac *AnalyticsClient) trackSync(userID id.UserID, event string, properties map[string]interface{}) error {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
var segmentUserID string
|
var analyticsUserId string
|
||||||
if Segment.userID != "" {
|
if Analytics.userID != "" {
|
||||||
segmentUserID = Segment.userID
|
analyticsUserId = Analytics.userID
|
||||||
} else {
|
} else {
|
||||||
segmentUserID = userID.String()
|
analyticsUserId = userID.String()
|
||||||
}
|
}
|
||||||
err := json.NewEncoder(&buf).Encode(map[string]interface{}{
|
err := json.NewEncoder(&buf).Encode(map[string]interface{}{
|
||||||
"userId": segmentUserID,
|
"userId": analyticsUserId,
|
||||||
"event": event,
|
"event": event,
|
||||||
"properties": properties,
|
"properties": properties,
|
||||||
})
|
})
|
||||||
|
@ -54,12 +53,12 @@ func (sc *SegmentClient) trackSync(userID id.UserID, event string, properties ma
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", SegmentURL, &buf)
|
req, err := http.NewRequest(http.MethodPost, ac.url, &buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req.SetBasicAuth(sc.key, "")
|
req.SetBasicAuth(ac.key, "")
|
||||||
resp, err := sc.client.Do(req)
|
resp, err := ac.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -70,12 +69,12 @@ func (sc *SegmentClient) trackSync(userID id.UserID, event string, properties ma
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *SegmentClient) IsEnabled() bool {
|
func (ac *AnalyticsClient) IsEnabled() bool {
|
||||||
return len(sc.key) > 0
|
return len(ac.key) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *SegmentClient) Track(userID id.UserID, event string, properties ...map[string]interface{}) {
|
func (ac *AnalyticsClient) Track(userID id.UserID, event string, properties ...map[string]interface{}) {
|
||||||
if !sc.IsEnabled() {
|
if !ac.IsEnabled() {
|
||||||
return
|
return
|
||||||
} else if len(properties) > 1 {
|
} else if len(properties) > 1 {
|
||||||
panic("Track should be called with at most one property map")
|
panic("Track should be called with at most one property map")
|
||||||
|
@ -87,11 +86,11 @@ func (sc *SegmentClient) Track(userID id.UserID, event string, properties ...map
|
||||||
props = properties[0]
|
props = properties[0]
|
||||||
}
|
}
|
||||||
props["bridge"] = "gmessages"
|
props["bridge"] = "gmessages"
|
||||||
err := sc.trackSync(userID, event, props)
|
err := ac.trackSync(userID, event, props)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sc.log.Err(err).Str("event", event).Msg("Error tracking event")
|
ac.log.Err(err).Str("event", event).Msg("Error tracking event")
|
||||||
} else {
|
} else {
|
||||||
sc.log.Debug().Str("event", event).Msg("Tracked event")
|
ac.log.Debug().Str("event", event).Msg("Tracked event")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
|
@ -24,8 +24,11 @@ import (
|
||||||
type Config struct {
|
type Config struct {
|
||||||
*bridgeconfig.BaseConfig `yaml:",inline"`
|
*bridgeconfig.BaseConfig `yaml:",inline"`
|
||||||
|
|
||||||
SegmentKey string `yaml:"segment_key"`
|
Analytics struct {
|
||||||
SegmentUserID string `yaml:"segment_user_id"`
|
Host string `yaml:"host"`
|
||||||
|
Token string `yaml:"token"`
|
||||||
|
UserID string `yaml:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
Metrics struct {
|
Metrics struct {
|
||||||
Enabled bool `yaml:"enabled"`
|
Enabled bool `yaml:"enabled"`
|
||||||
|
|
|
@ -25,8 +25,9 @@ import (
|
||||||
func DoUpgrade(helper *up.Helper) {
|
func DoUpgrade(helper *up.Helper) {
|
||||||
bridgeconfig.Upgrader.DoUpgrade(helper)
|
bridgeconfig.Upgrader.DoUpgrade(helper)
|
||||||
|
|
||||||
helper.Copy(up.Str|up.Null, "segment_key")
|
helper.Copy(up.Str|up.Null, "analytics", "host")
|
||||||
helper.Copy(up.Str|up.Null, "segment_user_id")
|
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.Bool, "metrics", "enabled")
|
||||||
helper.Copy(up.Str, "metrics", "listen")
|
helper.Copy(up.Str, "metrics", "listen")
|
||||||
|
@ -104,7 +105,7 @@ var SpacedBlocks = [][]string{
|
||||||
{"appservice", "database"},
|
{"appservice", "database"},
|
||||||
{"appservice", "id"},
|
{"appservice", "id"},
|
||||||
{"appservice", "as_token"},
|
{"appservice", "as_token"},
|
||||||
{"segment_key"},
|
{"analytics"},
|
||||||
{"metrics"},
|
{"metrics"},
|
||||||
{"google_messages"},
|
{"google_messages"},
|
||||||
{"bridge"},
|
{"bridge"},
|
||||||
|
|
|
@ -76,10 +76,14 @@ appservice:
|
||||||
as_token: "This value is generated when generating the registration"
|
as_token: "This value is generated when generating the registration"
|
||||||
hs_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-compatible analytics endpoint for tracking some events, like provisioning API login and encryption errors.
|
||||||
segment_key: null
|
analytics:
|
||||||
# Optional user_id to use when sending Segment events. If null, defaults to using mxID.
|
# Hostname of the tracking server. The path is hardcoded to /v1/track
|
||||||
segment_user_id: null
|
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.
|
# Prometheus config.
|
||||||
metrics:
|
metrics:
|
||||||
|
|
20
main.go
20
main.go
|
@ -18,6 +18,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"go.mau.fi/util/configupgrade"
|
"go.mau.fi/util/configupgrade"
|
||||||
|
@ -81,13 +82,18 @@ func (br *GMBridge) Init() {
|
||||||
util.BrowserDetailsMessage.DeviceType = gmproto.DeviceType(deviceVal)
|
util.BrowserDetailsMessage.DeviceType = gmproto.DeviceType(deviceVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
Segment.log = br.ZLog.With().Str("component", "segment").Logger()
|
Analytics.log = br.ZLog.With().Str("component", "segment").Logger()
|
||||||
Segment.key = br.Config.SegmentKey
|
Analytics.url = (&url.URL{
|
||||||
Segment.userID = br.Config.SegmentUserID
|
Scheme: "https",
|
||||||
if Segment.IsEnabled() {
|
Host: br.Config.Analytics.Host,
|
||||||
Segment.log.Info().Msg("Segment metrics are enabled")
|
Path: "/v1/track",
|
||||||
if Segment.userID != "" {
|
}).String()
|
||||||
Segment.log.Info().Str("user_id", Segment.userID).Msg("Overriding Segment user ID")
|
Analytics.key = br.Config.Analytics.Token
|
||||||
|
Analytics.userID = br.Config.Analytics.UserID
|
||||||
|
if Analytics.IsEnabled() {
|
||||||
|
Analytics.log.Info().Msg("Analytics are enabled")
|
||||||
|
if Analytics.userID != "" {
|
||||||
|
Analytics.log.Info().Str("user_id", Analytics.userID).Msg("Overriding Analytics user ID")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -359,11 +359,11 @@ Loop:
|
||||||
switch {
|
switch {
|
||||||
case item.qr != "":
|
case item.qr != "":
|
||||||
log.Debug().Msg("Got code in QR channel")
|
log.Debug().Msg("Got code in QR channel")
|
||||||
Segment.Track(user.MXID, "$qrcode_retrieved")
|
Analytics.Track(user.MXID, "$qrcode_retrieved")
|
||||||
jsonResponse(w, http.StatusOK, LoginResponse{Status: "qr", Code: item.qr})
|
jsonResponse(w, http.StatusOK, LoginResponse{Status: "qr", Code: item.qr})
|
||||||
case item.err != nil:
|
case item.err != nil:
|
||||||
log.Err(item.err).Msg("Got error in QR channel")
|
log.Err(item.err).Msg("Got error in QR channel")
|
||||||
Segment.Track(user.MXID, "$login_failure")
|
Analytics.Track(user.MXID, "$login_failure")
|
||||||
var resp LoginResponse
|
var resp LoginResponse
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(item.err, ErrLoginTimeout):
|
case errors.Is(item.err, ErrLoginTimeout):
|
||||||
|
@ -375,7 +375,7 @@ Loop:
|
||||||
jsonResponse(w, http.StatusOK, resp)
|
jsonResponse(w, http.StatusOK, resp)
|
||||||
case item.success:
|
case item.success:
|
||||||
log.Debug().Msg("Got pair success in QR channel")
|
log.Debug().Msg("Got pair success in QR channel")
|
||||||
Segment.Track(user.MXID, "$login_success")
|
Analytics.Track(user.MXID, "$login_success")
|
||||||
jsonResponse(w, http.StatusOK, LoginResponse{Status: "success"})
|
jsonResponse(w, http.StatusOK, LoginResponse{Status: "success"})
|
||||||
default:
|
default:
|
||||||
log.Error().Any("item_data", item).Msg("Unknown item in QR channel")
|
log.Error().Any("item_data", item).Msg("Unknown item in QR channel")
|
||||||
|
|
2
user.go
2
user.go
|
@ -403,7 +403,7 @@ func (user *User) Login(maxAttempts int) (<-chan qrChannelItem, error) {
|
||||||
user.loginInProgress.Store(false)
|
user.loginInProgress.Store(false)
|
||||||
return nil, fmt.Errorf("failed to connect to Google Messages: %w", err)
|
return nil, fmt.Errorf("failed to connect to Google Messages: %w", err)
|
||||||
}
|
}
|
||||||
Segment.Track(user.MXID, "$login_start")
|
Analytics.Track(user.MXID, "$login_start")
|
||||||
ch := make(chan qrChannelItem, maxAttempts+2)
|
ch := make(chan qrChannelItem, maxAttempts+2)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
user.cancelLogin = cancel
|
user.cancelLogin = cancel
|
||||||
|
|
Loading…
Reference in a new issue