ソースを参照

feat: set custom identify properties and X-Super-Properties that matches the official client's behavior

ayn2op 8 ヶ月 前
コミット
81183a66c0
5 ファイル変更95 行追加57 行削除
  1. 19 3
      cmd/state.go
  2. 5 3
      go.mod
  3. 11 5
      go.sum
  4. 55 45
      internal/consts/consts.go
  5. 5 1
      internal/login/form.go

+ 19 - 3
cmd/state.go

@@ -21,7 +21,12 @@ import (
 
 func openState(token string) error {
 	props := consts.GetIdentifyProps()
-	api.UserAgent = props.BrowserUserAgent
+	if browserUserAgent, ok := props["browser_user_agent"]; ok {
+		if val, ok := browserUserAgent.(string); ok {
+			api.UserAgent = val
+		}
+	}
+
 	gateway.DefaultIdentity = props
 	gateway.DefaultPresence = &gateway.UpdatePresenceCommand{
 		Status: app.cfg.Status,
@@ -56,11 +61,23 @@ func openState(token string) error {
 func getHeaders(props gateway.IdentifyProperties) http.Header {
 	header := make(http.Header)
 
+	// These properties are only sent when identifying with the gateway and are not included in the X-Super-Properties header.
+	delete(props, "is_fast_connect")
+	delete(props, "gateway_connect_reasons")
+
 	if rawProps, err := json.Marshal(props); err == nil {
 		propsHeader := base64.StdEncoding.EncodeToString(rawProps)
 		header.Set("X-Super-Properties", propsHeader)
 	}
 
+	if systemLocale, ok := props["system_locale"]; ok {
+		if val, ok := systemLocale.(string); ok {
+			header.Set("X-Discord-Locale", string(val))
+		}
+	}
+
+	header.Set("X-Debug-Options", "bugReporterEnabled")
+
 	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers
 	header.Set("Accept", "*/*")
 	header.Set("Accept-Language", "en-US,en;q=0.7")
@@ -69,8 +86,7 @@ func getHeaders(props gateway.IdentifyProperties) http.Header {
 	header.Set("Sec-Fetch-Dest", "empty")
 	header.Set("Sec-Fetch-Mode", "cors")
 	header.Set("Sec-Fetch-Site", "same-origin")
-	header.Set("X-Debug-Options", "bugReporterEnabled")
-	header.Set("X-Discord-Locale", string(props.SystemLocale))
+
 	return header
 }
 

+ 5 - 3
go.mod

@@ -6,15 +6,16 @@ require (
 	github.com/BurntSushi/toml v1.5.0
 	github.com/ayn2op/tview v0.0.0-20250720032506-63f04e47b15d
 	github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb
-	github.com/diamondburned/arikawa/v3 v3.5.1-0.20250703053218-19d9c3f2e011
+	github.com/diamondburned/arikawa/v3 v3.5.1-0.20250904015950-e93079e2de19
 	github.com/diamondburned/ningen/v3 v3.0.1-0.20250703054403-e5dc4cf15e84
 	github.com/gdamore/tcell/v2 v2.9.0
 	github.com/gen2brain/beeep v0.11.1
+	github.com/google/uuid v1.6.0
 	github.com/lmittmann/tint v1.1.2
 	github.com/ncruces/zenity v0.10.14
 	github.com/sahilm/fuzzy v0.1.1
 	github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
-	github.com/spf13/cobra v1.9.1
+	github.com/spf13/cobra v1.10.1
 	github.com/yuin/goldmark v1.7.13
 	github.com/zalando/go-keyring v0.2.6
 	golang.design/x/clipboard v0.7.1
@@ -43,10 +44,11 @@ require (
 	github.com/rivo/uniseg v0.4.7 // indirect
 	github.com/sergeymakinen/go-bmp v1.0.0 // indirect
 	github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect
-	github.com/spf13/pflag v1.0.7 // indirect
+	github.com/spf13/pflag v1.0.10 // indirect
 	github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
 	github.com/twmb/murmur3 v1.1.8 // indirect
 	go4.org v0.0.0-20230225012048-214862532bf5 // indirect
+	golang.org/x/crypto v0.32.0 // indirect
 	golang.org/x/exp/shiny v0.0.0-20250819193227-8b4c13bb791b // indirect
 	golang.org/x/image v0.30.0 // indirect
 	golang.org/x/mobile v0.0.0-20250813145510-f12310a0cfd9 // indirect

+ 11 - 5
go.sum

@@ -44,6 +44,8 @@ github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb h1:6S+TKObz
 github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb/go.mod h1:wf3nKtOnQqCp7kp9xB7hHnNlZ6m3NoiOxjrB9hFRq4Y=
 github.com/diamondburned/arikawa/v3 v3.5.1-0.20250703053218-19d9c3f2e011 h1:N8ylXHkkDXsbtsA/by/yZUDKmfWZJ1JjTXGpHmIiR9M=
 github.com/diamondburned/arikawa/v3 v3.5.1-0.20250703053218-19d9c3f2e011/go.mod h1:thocAM2X8lRDHuEZR5vWYaT4w+tb/vOKa1qm+r0gs5A=
+github.com/diamondburned/arikawa/v3 v3.5.1-0.20250904015950-e93079e2de19 h1:7k76A4gL8tbDryXIeuz/KM33dQv+WukPpzbbApzQajA=
+github.com/diamondburned/arikawa/v3 v3.5.1-0.20250904015950-e93079e2de19/go.mod h1:thocAM2X8lRDHuEZR5vWYaT4w+tb/vOKa1qm+r0gs5A=
 github.com/diamondburned/ningen/v3 v3.0.1-0.20250703054403-e5dc4cf15e84 h1:O/6NA4fQimjVNuT5F02kodPT2cz9Ltq2U96vxKYL0oA=
 github.com/diamondburned/ningen/v3 v3.0.1-0.20250703054403-e5dc4cf15e84/go.mod h1:UU1lud9g/GBl2+CZ8nPCe3Qk1U6fABEP1fk1sUzo7w0=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -87,6 +89,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
@@ -141,11 +145,11 @@ github.com/sergeymakinen/go-ico v1.0.0-beta.0 h1:m5qKH7uPKLdrygMWxbamVn+tl2HfiA3
 github.com/sergeymakinen/go-ico v1.0.0-beta.0/go.mod h1:wQ47mTczswBO5F0NoDt7O0IXgnV4Xy3ojrroMQzyhUk=
 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
-github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
-github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
+github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
+github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
+github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -181,6 +185,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=

+ 55 - 45
internal/consts/consts.go

@@ -3,12 +3,14 @@ package consts
 import (
 	"encoding/json"
 	"log/slog"
+	"maps"
 	"net/http"
 	"os"
 	"path/filepath"
 
 	"github.com/diamondburned/arikawa/v3/discord"
 	"github.com/diamondburned/arikawa/v3/gateway"
+	"github.com/google/uuid"
 )
 
 const Name = "discordo"
@@ -16,20 +18,50 @@ const Name = "discordo"
 const identifyPropertiesURL = "https://cordapi.dolfi.es/api/v2/properties/web"
 
 var defaultIdentifyProps = gateway.IdentifyProperties{
-	Device: "",
-
-	Browser:          "Chrome",
-	BrowserUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
-	BrowserVersion:   "138.0.0.0",
-
-	OS:        "Windows",
-	OSVersion: "10",
-
-	ClientBuildNumber: 415522,
-	ReleaseChannel:    "stable",
+	gateway.IdentifyDevice: "",
+
+	gateway.IdentifyBrowser: "Chrome",
+	"browser_version":       "140.0.0.0",
+	"browser_user_agent":    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
+
+	gateway.IdentifyOS: "Windows",
+	"os_version":       "10",
+
+	"client_build_number": 439729,
+	"client_event_source": nil,
+	"client_launch_id":    uuid.NewString(),
+	"client_app_state":    "focused",
+
+	"launch_signature":        uuid.NewString(),
+	"system_locale":           discord.EnglishUS,
+	"release_channel":         "stable",
+	"has_client_mods":         false,
+	"is_fast_connect":         false,
+	"gateway_connect_reasons": "AppSkeleton",
+
+	"referrer":                 "",
+	"referrer_current":         "",
+	"referring_domain":         "",
+	"referring_domain_current": "",
+}
 
-	SystemLocale:  discord.EnglishUS,
-	HasClientMods: false,
+type Properties struct {
+	Client struct {
+		Type           string `json:"type"`
+		BuildNumber    int    `json:"build_number"`
+		BuildHash      string `json:"build_hash"`
+		ReleaseChannel string `json:"release_channel"`
+	} `json:"client"`
+
+	Browser struct {
+		Type      string `json:"type"`
+		UserAgent string `json:"user_agent"`
+		Version   string `json:"version"`
+		OS        struct {
+			Type    string `json:"type"`
+			Version string `json:"version"`
+		} `json:"os"`
+	} `json:"browser"`
 }
 
 func GetIdentifyProps() gateway.IdentifyProperties {
@@ -39,44 +71,22 @@ func GetIdentifyProps() gateway.IdentifyProperties {
 	}
 	defer resp.Body.Close()
 
-	var props struct {
-		Client struct {
-			Type           string `json:"type"`
-			BuildNumber    int    `json:"build_number"`
-			BuildHash      string `json:"build_hash"`
-			ReleaseChannel string `json:"release_channel"`
-		} `json:"client"`
-
-		Browser struct {
-			Type      string `json:"type"`
-			UserAgent string `json:"user_agent"`
-			Version   string `json:"version"`
-			OS        struct {
-				Type    string `json:"type"`
-				Version string `json:"version"`
-			} `json:"os"`
-		} `json:"browser"`
-	}
+	var props Properties
 	if err := json.NewDecoder(resp.Body).Decode(&props); err != nil {
 		return defaultIdentifyProps
 	}
 
-	return gateway.IdentifyProperties{
-		Device: "",
-
-		Browser:          props.Browser.Type,
-		BrowserUserAgent: props.Browser.UserAgent,
-		BrowserVersion:   props.Browser.Version,
+	p := maps.Clone(defaultIdentifyProps)
+	p[gateway.IdentifyBrowser] = props.Browser.Type
+	p["browser_version"] = props.Browser.Version
+	p["browser_user_agent"] = props.Browser.UserAgent
 
-		OS:        props.Browser.OS.Type,
-		OSVersion: props.Browser.OS.Version,
+	p[gateway.IdentifyOS] = props.Browser.OS.Type
+	p["os_version"] = props.Browser.OS.Version
 
-		ClientBuildNumber: props.Client.BuildNumber,
-		ReleaseChannel:    props.Client.ReleaseChannel,
-
-		SystemLocale:  discord.EnglishUS,
-		HasClientMods: false,
-	}
+	p["release_channel"] = props.Client.ReleaseChannel
+	p["client_build_number"] = props.Client.BuildNumber
+	return p
 }
 
 var cacheDir string

+ 5 - 1
internal/login/form.go

@@ -53,7 +53,11 @@ func (f *Form) login() {
 	// Create an API client without an authentication token.
 	client := api.NewClient("")
 	props := consts.GetIdentifyProps()
-	client.UserAgent = props.BrowserUserAgent
+	if browserUserAgent, ok := props["browser_user_agent"]; ok {
+		if val, ok := browserUserAgent.(string); ok {
+			api.UserAgent = val
+		}
+	}
 
 	resp, err := client.Login(email, password)
 	if err != nil {