feat: add support for gzip & brotli
This commit is contained in:
parent
2ae53a22aa
commit
ac669bccae
9 changed files with 172 additions and 133 deletions
62
cmd/state.go
62
cmd/state.go
|
|
@ -2,16 +2,17 @@ package cmd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/ayn2op/discordo/internal/consts"
|
||||
"github.com/ayn2op/discordo/internal/http"
|
||||
"github.com/ayn2op/discordo/internal/notifications"
|
||||
"github.com/ayn2op/tview"
|
||||
"github.com/diamondburned/arikawa/v3/api"
|
||||
"github.com/diamondburned/arikawa/v3/gateway"
|
||||
"github.com/diamondburned/arikawa/v3/session"
|
||||
"github.com/diamondburned/arikawa/v3/state"
|
||||
"github.com/diamondburned/arikawa/v3/state/store/defaultstore"
|
||||
"github.com/diamondburned/arikawa/v3/utils/handler"
|
||||
"github.com/diamondburned/arikawa/v3/utils/httputil"
|
||||
"github.com/diamondburned/arikawa/v3/utils/httputil/httpdriver"
|
||||
"github.com/diamondburned/arikawa/v3/utils/ws"
|
||||
|
|
@ -20,19 +21,20 @@ import (
|
|||
)
|
||||
|
||||
func openState(token string) error {
|
||||
props := consts.GetIdentifyProps()
|
||||
if browserUserAgent, ok := props["browser_user_agent"]; ok {
|
||||
if val, ok := browserUserAgent.(string); ok {
|
||||
api.UserAgent = val
|
||||
}
|
||||
}
|
||||
identifyProps := http.IdentifyProperties()
|
||||
|
||||
gateway.DefaultIdentity = props
|
||||
api.UserAgent = http.BrowserUserAgent
|
||||
gateway.DefaultIdentity = identifyProps
|
||||
gateway.DefaultPresence = &gateway.UpdatePresenceCommand{
|
||||
Status: app.cfg.Status,
|
||||
}
|
||||
|
||||
discordState = ningen.New(token)
|
||||
id := gateway.DefaultIdentifier(token)
|
||||
id.Compress = false
|
||||
|
||||
session := session.NewCustom(id, http.NewClient(token), handler.New())
|
||||
state := state.NewFromSession(session, defaultstore.New())
|
||||
discordState = ningen.FromState(state)
|
||||
|
||||
// Handlers
|
||||
discordState.AddHandler(onRaw)
|
||||
|
|
@ -54,44 +56,10 @@ func openState(token string) error {
|
|||
slog.Error("state log", "err", err)
|
||||
}
|
||||
|
||||
discordState.OnRequest = append(discordState.OnRequest, httputil.WithHeaders(getHeaders(props)), onRequest)
|
||||
discordState.OnRequest = append(discordState.OnRequest, httputil.WithHeaders(http.Headers()), onRequest)
|
||||
return discordState.Open(context.TODO())
|
||||
}
|
||||
|
||||
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-Encoding", "gzip, deflate, br, zstd")
|
||||
header.Set("Accept-Language", "en-US,en;q=0.9")
|
||||
header.Set("Origin", "https://discord.com")
|
||||
header.Set("Priority", "u=0, i")
|
||||
header.Set("Referer", "https://discord.com/channels/@me")
|
||||
header.Set("Sec-Fetch-Dest", "empty")
|
||||
header.Set("Sec-Fetch-Mode", "cors")
|
||||
header.Set("Sec-Fetch-Site", "same-origin")
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func onRequest(r httpdriver.Request) error {
|
||||
if req, ok := r.(*httpdriver.DefaultRequest); ok {
|
||||
slog.Debug("new HTTP request", "method", req.Method, "url", req.URL)
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -4,6 +4,7 @@ go 1.25.0
|
|||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.5.0
|
||||
github.com/andybalholm/brotli v1.2.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.6.0
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -25,6 +25,8 @@ github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2
|
|||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
|
||||
github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/ayn2op/tview v0.0.0-20250720032506-63f04e47b15d h1:+Q2vA4jMu5/tU8uAyr7mqX63br1jWFEa8q/fZNPP96Y=
|
||||
github.com/ayn2op/tview v0.0.0-20250720032506-63f04e47b15d/go.mod h1:xqhotzuhTSxSudyNSHJfpD6S5C7+FFVv+JzJ8YaFNK4=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
|
|
|
|||
|
|
@ -1,98 +1,13 @@
|
|||
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"
|
||||
|
||||
const identifyPropertiesURL = "https://cordapi.dolfi.es/api/v2/properties/web"
|
||||
|
||||
var defaultIdentifyProps = gateway.IdentifyProperties{
|
||||
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": "",
|
||||
}
|
||||
|
||||
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 {
|
||||
resp, err := http.Get(identifyPropertiesURL)
|
||||
if err != nil {
|
||||
return defaultIdentifyProps
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return defaultIdentifyProps
|
||||
}
|
||||
|
||||
var props Properties
|
||||
if err := json.NewDecoder(resp.Body).Decode(&props); err != nil {
|
||||
return defaultIdentifyProps
|
||||
}
|
||||
|
||||
p := maps.Clone(defaultIdentifyProps)
|
||||
p[gateway.IdentifyBrowser] = props.Browser.Type
|
||||
p["browser_version"] = props.Browser.Version
|
||||
p["browser_user_agent"] = props.Browser.UserAgent
|
||||
|
||||
p[gateway.IdentifyOS] = props.Browser.OS.Type
|
||||
p["os_version"] = props.Browser.OS.Version
|
||||
|
||||
p["release_channel"] = props.Client.ReleaseChannel
|
||||
p["client_build_number"] = props.Client.BuildNumber
|
||||
return p
|
||||
}
|
||||
|
||||
var cacheDir string
|
||||
|
||||
func CacheDir() string {
|
||||
|
|
|
|||
17
internal/http/client.go
Normal file
17
internal/http/client.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/diamondburned/arikawa/v3/api"
|
||||
"github.com/diamondburned/arikawa/v3/utils/httputil"
|
||||
"github.com/diamondburned/arikawa/v3/utils/httputil/httpdriver"
|
||||
)
|
||||
|
||||
func NewClient(token string) *api.Client {
|
||||
httpClient := httputil.NewClient()
|
||||
stdClient := http.DefaultClient
|
||||
stdClient.Transport = NewTransport()
|
||||
httpClient.Client = httpdriver.WrapClient(*stdClient)
|
||||
return api.NewCustomClient(token, httpClient)
|
||||
}
|
||||
28
internal/http/headers.go
Normal file
28
internal/http/headers.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
stdHttp "net/http"
|
||||
)
|
||||
|
||||
func Headers() stdHttp.Header {
|
||||
headers := make(stdHttp.Header)
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers
|
||||
headers.Set("Accept", "*/*")
|
||||
headers.Set("Accept-Encoding", "gzip, deflate, br, zstd")
|
||||
headers.Set("Accept-Language", "en-US,en;q=0.9")
|
||||
headers.Set("Origin", "https://discord.com")
|
||||
headers.Set("Priority", "u=0, i")
|
||||
headers.Set("Referer", "https://discord.com/channels/@me")
|
||||
headers.Set("Sec-Fetch-Dest", "empty")
|
||||
headers.Set("Sec-Fetch-Mode", "cors")
|
||||
headers.Set("Sec-Fetch-Site", "same-origin")
|
||||
|
||||
headers.Set("X-Debug-Options", "bugReporterEnabled")
|
||||
headers.Set("X-Discord-Locale", string(Locale))
|
||||
|
||||
if superProps, err := superProps(); err == nil {
|
||||
headers.Set("X-Super-Properties", superProps)
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
65
internal/http/props.go
Normal file
65
internal/http/props.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/diamondburned/arikawa/v3/discord"
|
||||
"github.com/diamondburned/arikawa/v3/gateway"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
Browser = "Chrome"
|
||||
BrowserVersion = "140.0.0.0"
|
||||
BrowserUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
|
||||
)
|
||||
|
||||
var (
|
||||
Locale = discord.EnglishUS
|
||||
)
|
||||
|
||||
func IdentifyProperties() gateway.IdentifyProperties {
|
||||
return gateway.IdentifyProperties{
|
||||
gateway.IdentifyDevice: "",
|
||||
|
||||
gateway.IdentifyOS: "Windows",
|
||||
"os_version": "10",
|
||||
|
||||
gateway.IdentifyBrowser: Browser,
|
||||
"browser_version": BrowserVersion,
|
||||
"browser_user_agent": BrowserUserAgent,
|
||||
|
||||
"client_build_number": 447677,
|
||||
"client_event_source": nil,
|
||||
"client_launch_id": uuid.NewString(),
|
||||
"client_app_state": "focused",
|
||||
|
||||
"launch_signature": uuid.NewString(),
|
||||
"system_locale": Locale,
|
||||
"release_channel": "stable",
|
||||
"has_client_mods": false,
|
||||
|
||||
"referrer": "",
|
||||
"referrer_current": "",
|
||||
"referring_domain": "",
|
||||
"referring_domain_current": "",
|
||||
|
||||
// These properties are only sent when identifying with the gateway and are not included in the X-Super-Properties header.
|
||||
"is_fast_connect": false,
|
||||
"gateway_connect_reasons": "AppSkeleton",
|
||||
}
|
||||
}
|
||||
|
||||
func superProps() (string, error) {
|
||||
props := IdentifyProperties()
|
||||
delete(props, "is_fast_connect")
|
||||
delete(props, "gateway_connect_reasons")
|
||||
|
||||
raw, err := json.Marshal(props)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(raw), nil
|
||||
}
|
||||
42
internal/http/transport.go
Normal file
42
internal/http/transport.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/andybalholm/brotli"
|
||||
)
|
||||
|
||||
type Transport struct {
|
||||
base http.RoundTripper
|
||||
}
|
||||
|
||||
func NewTransport() *Transport {
|
||||
return &Transport{
|
||||
base: http.DefaultTransport,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
resp, err := t.base.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch resp.Header.Get("Content-Encoding") {
|
||||
case "gzip":
|
||||
resp.Body, err = gzip.NewReader(resp.Body)
|
||||
if err != nil {
|
||||
resp.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Header.Del("Content-Encoding")
|
||||
case "br":
|
||||
resp.Body = io.NopCloser(brotli.NewReader(resp.Body))
|
||||
resp.Header.Del("Content-Encoding")
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/ayn2op/discordo/internal/config"
|
||||
"github.com/ayn2op/discordo/internal/consts"
|
||||
"github.com/ayn2op/discordo/internal/http"
|
||||
"github.com/ayn2op/discordo/internal/ui"
|
||||
"github.com/ayn2op/tview"
|
||||
"github.com/diamondburned/arikawa/v3/api"
|
||||
|
|
@ -52,7 +53,7 @@ func (f *Form) login() {
|
|||
|
||||
// Create an API client without an authentication token.
|
||||
client := api.NewClient("")
|
||||
props := consts.GetIdentifyProps()
|
||||
props := http.IdentifyProperties()
|
||||
if browserUserAgent, ok := props["browser_user_agent"]; ok {
|
||||
if val, ok := browserUserAgent.(string); ok {
|
||||
api.UserAgent = val
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue