package main import ( "context" "encoding/json" "fmt" "io" "net/http" "net/url" "os" "path/filepath" "strings" "time" ) func userStoreDir(userID string) string { cfg, err := os.UserConfigDir() if err != nil { cfg = filepath.Join(os.Getenv("HOME"), ".config") } slug := strings.NewReplacer("@", "", ":", "_", "/", "_").Replace(userID) return filepath.Join(cfg, "matrix_client_pc", slug) } // whoami issues GET /_matrix/client/v3/account/whoami against the homeserver with the // provided access_token and returns (user_id, device_id, err). It's used before // MatrixClientInit because mautrix.NewClient wants user_id up front. func whoami(ctx context.Context, homeserver, accessToken string) (string, string, error) { if ctx == nil { ctx = context.Background() } u, err := url.JoinPath(homeserver, "/_matrix/client/v3/account/whoami") if err != nil { return "", "", fmt.Errorf("joinpath: %w", err) } req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) if err != nil { return "", "", err } req.Header.Set("Authorization", "Bearer "+accessToken) cl := &http.Client{Timeout: 15 * time.Second} resp, err := cl.Do(req) if err != nil { return "", "", err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode != 200 { return "", "", fmt.Errorf("whoami: %s: %s", resp.Status, body) } var out struct { UserID string `json:"user_id"` DeviceID string `json:"device_id"` } if err := json.Unmarshal(body, &out); err != nil { return "", "", fmt.Errorf("whoami parse: %w", err) } return out.UserID, out.DeviceID, nil }