Skip to content

Instantly share code, notes, and snippets.

@pyt0xic
Last active August 3, 2025 13:15
Show Gist options
  • Save pyt0xic/95c6a511755e6652f05eea868f600d31 to your computer and use it in GitHub Desktop.
Save pyt0xic/95c6a511755e6652f05eea868f600d31 to your computer and use it in GitHub Desktop.

Revisions

  1. pyt0xic created this gist Aug 3, 2025.
    512 changes: 512 additions & 0 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,512 @@
    package main

    import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "strconv"
    "time"

    "github.com/joho/godotenv"
    )

    const (
    refragAPIEndpoint = "https://api.refrag.gg/auth/sign_in"
    refragStartServerEndpoint = "https://api.refrag.gg/cs_servers/start_new_server"
    refragRunningServersEndpoint = "https://api.refrag.gg/cs_servers/running"
    refragOrigin = "https://play.refrag.gg"
    outputFilename = "refrag_login_data.json"
    )

    // LoginRequest represents the login request payload
    type LoginRequest struct {
    Email string `json:"email"`
    Password string `json:"password"`
    Remember bool `json:"remember"`
    }

    // LoginResponse represents the API response data
    type LoginResponse struct {
    Data struct {
    Email string `json:"email"`
    Provider string `json:"provider"`
    UID string `json:"uid"`
    MatchAuthToken string `json:"match_auth_token"`
    ID int `json:"id"`
    AllowPasswordChange bool `json:"allow_password_change"`
    Name any `json:"name"`
    Nickname any `json:"nickname"`
    Image any `json:"image"`
    TeamID any `json:"team_id"`
    SteamID string `json:"steam_id"`
    IsBetaTester any `json:"is_beta_tester"`
    HasScrimAccess bool `json:"has_scrim_access"`
    AdminAccess bool `json:"admin_access"`
    CustomProAccess bool `json:"custom_pro_access"`
    WarmupKillCount int `json:"warmup_kill_count"`
    EmailSignUp bool `json:"email_sign_up"`
    ServerSettings []ServerSettings `json:"server_settings"`
    CustomCompetitor any `json:"custom_competitor"`
    CustomPlayer any `json:"custom_player"`
    CustomTeam any `json:"custom_team"`
    FaceitNickname string `json:"faceit_nickname"`
    UUID string `json:"uuid"`
    Username any `json:"username"`
    DiscordID any `json:"discord_id"`
    DiscordTag any `json:"discord_tag"`
    ReceiveEmailForNotifications bool `json:"receive_email_for_notifications"`
    ReceiveEmailForAccessCodes bool `json:"receive_email_for_access_codes"`
    OnboardedStepsCompleted int `json:"onboarded_steps_completed"`
    SashID int `json:"sash_id"`
    Level int `json:"level"`
    FaceitPlayerID any `json:"faceit_player_id"`
    CommunityMod bool `json:"community_mod"`
    SteamProfileJSON SteamProfile `json:"steam_profile_json"`
    // Other fields omitted for brevity
    } `json:"data"`
    }

    type ServerSettings struct {
    Modules []Module `json:"modules"`
    ModName string `json:"mod_name"`
    }

    type Module struct {
    Module string `json:"module"`
    Settings string `json:"settings"`
    }

    type SteamProfile struct {
    Avatar string `json:"avatar"`
    GameID string `json:"gameid"`
    SteamID string `json:"steamid"`
    Realname string `json:"realname"`
    Avatarfull string `json:"avatarfull"`
    Avatarhash string `json:"avatarhash"`
    Profileurl string `json:"profileurl"`
    Personaname string `json:"personaname"`
    Timecreated int `json:"timecreated"`
    Avatarmedium string `json:"avatarmedium"`
    Lobbysteamid string `json:"lobbysteamid"`
    Personastate int `json:"personastate"`
    Profilestate int `json:"profilestate"`
    Gameextrainfo string `json:"gameextrainfo"`
    Primaryclanid string `json:"primaryclanid"`
    Loccountrycode string `json:"loccountrycode"`
    Commentpermission int `json:"commentpermission"`
    Personastateflags int `json:"personastateflags"`
    Communityvisibilitystate int `json:"communityvisibilitystate"`
    }

    // AuthHeaders holds the authentication headers received from the API
    type AuthHeaders struct {
    AccessToken string `json:"access_token"`
    Authorization string `json:"authorization"`
    Client string `json:"client"`
    Expiry string `json:"expiry"`
    UID string `json:"uid"`
    }

    // SavedData represents the complete data structure to save to file
    type SavedData struct {
    Response LoginResponse `json:"response"`
    Headers AuthHeaders `json:"headers"`
    }

    // StartServerRequest represents the payload for starting a new server
    type StartServerRequest struct {
    ServerLocationID int `json:"server_location_id"`
    Game string `json:"game"`
    BetaServer bool `json:"betaServer"`
    SecureServer bool `json:"secureServer"`
    IsAssessment bool `json:"is_assessment"`
    LaunchSettings LaunchSettings `json:"launch_settings"`
    }

    type LaunchSettings struct {
    Mod string `json:"mod"`
    Map string `json:"map"`
    }

    // ServerResponse represents a server in the API responses
    type ServerResponse struct {
    ID int `json:"id"`
    IP string `json:"ip"`
    Port int `json:"port"`
    Status string `json:"status"`
    Password string `json:"password"`
    VACSecure bool `json:"vac_secure"`
    ActiveMap string `json:"active_map"`
    ActiveMod string `json:"active_mod"`
    ConnectedPlayers any `json:"connected_players"`
    IsAssessment bool `json:"is_assessment"`
    Game string `json:"game"`
    RawIP string `json:"raw_ip"`
    IsScrim bool `json:"is_scrim"`
    ServerLocation string `json:"server_location"`
    StartedBy User `json:"started_by"`
    Team Team `json:"team"`
    }

    type User struct {
    ID int `json:"id"`
    SteamProfile SteamProfile `json:"steam_profile"`
    }

    type Team struct {
    Name string `json:"name"`
    ID int `json:"id"`
    }

    func main() {
    fmt.Println("Logging in to refrag.gg...")

    // Load credentials from env vars or .env file
    email, password, err := loadCredentials()
    if err != nil {
    log.Fatal(err)
    }

    // Log in to the API
    response, headers, err := loginToRefrag(email, password)
    if err != nil {
    log.Fatal("Login failed: ", err)
    }

    // Save the data to file
    if err := saveLoginResponseData(response, headers); err != nil {
    log.Fatal("Failed to save response data: ", err)
    }

    // Print success message with auth headers
    printLoginSuccess(headers)

    // Load server parameters from environment variables
    mapName, mod, locationID, err := loadServerParameters()
    if err != nil {
    log.Fatal(err)
    }

    // Start a new server
    server, err := StartServer(headers, locationID, "cs2", mod, mapName)
    if err != nil {
    log.Fatal("Failed to start server: ", err)
    }
    fmt.Printf("Server started! Connect: %s:%d (Password: %s)\n", server.IP, server.Port, server.Password)

    // Loop until password is available
    maxAttempts := 30 // Avoid infinite loops
    time.Sleep(3 * time.Second)
    for i := 0; i < maxAttempts; i++ {
    // Sleep for 3 seconds at the start of each iteration
    fmt.Printf("Waiting for 3 seconds (attempt %d/%d)...\n", i+1, maxAttempts)
    time.Sleep(3 * time.Second)

    // Get running servers
    servers, err := GetRunningServers(headers)
    if err != nil {
    log.Printf("Failed to get running servers: %v. Retrying...", err)
    continue
    }

    fmt.Printf("Running Servers (%d):\n", len(servers))

    // Look for our server
    var foundServer bool
    for _, srv := range servers {
    fmt.Printf(" %s:%d - %s on %s (Status: %s, Password: %s)\n",
    srv.IP, srv.Port, srv.ActiveMod, srv.ActiveMap, srv.Status, srv.Password)

    // If this is our server and password is available
    if srv.ID == server.ID && srv.Password != "" && srv.Status == "online" {
    fmt.Printf("\nServer is ready! Connect: %s:%d (Password: %s)\n", srv.IP, srv.Port, srv.Password)
    fmt.Printf("\nconnect %s:%d; password %s\n", srv.IP, srv.Port, srv.Password)
    return
    }
    }

    if !foundServer {
    fmt.Println("Our server is not in the list. It might be still starting up...")
    } else {
    fmt.Println("Server found but password not yet available.")
    }
    }

    fmt.Println("Maximum attempts reached. Please check server status manually.")
    }

    // loadCredentials loads REFRAG_EMAIL and REFRAG_PASSWORD from environment or .env file
    func loadCredentials() (email, password string, err error) {
    // Load .env file if it exists
    if err := godotenv.Load(); err != nil {
    fmt.Println("No .env file found, using environment variables directly")
    }

    // Get credentials from environment variables
    email = os.Getenv("REFRAG_EMAIL")
    password = os.Getenv("REFRAG_PASSWORD")

    if email == "" || password == "" {
    return "", "", fmt.Errorf("REFRAG_EMAIL and REFRAG_PASSWORD environment variables must be set")
    }

    return email, password, nil
    }

    // loadServerParameters loads REFRAG_MAP, REFRAG_MOD, and REFRAG_LOCATION_ID from environment variables
    func loadServerParameters() (mapName, mod string, locationID int, err error) {
    mapName = os.Getenv("REFRAG_MAP")
    mod = os.Getenv("REFRAG_MOD")
    locationIDStr := os.Getenv("REFRAG_LOCATION_ID")

    if mapName == "" {
    mapName = "de_ancient" // Default map
    }

    if mod == "" {
    mod = "retakes" // Default mod
    }

    if locationIDStr == "" {
    locationID = 16 // Default location ID (change as needed)
    } else {
    locationID, err = strconv.Atoi(locationIDStr)
    if err != nil {
    return "", "", 0, fmt.Errorf("invalid REFRAG_LOCATION_ID: %w", err)
    }
    }

    return mapName, mod, locationID, nil
    }

    // loginToRefrag attempts to login to the Refrag API and returns the response and auth headers
    func loginToRefrag(email, password string) (*LoginResponse, *AuthHeaders, error) {
    // Create login request payload
    loginData := LoginRequest{
    Email: email,
    Password: password,
    Remember: false,
    }

    // Convert to JSON
    jsonData, err := json.Marshal(loginData)
    if err != nil {
    return nil, nil, fmt.Errorf("failed to marshal JSON: %w", err)
    }

    // Create and configure the HTTP request
    req, err := http.NewRequest(http.MethodPost, refragAPIEndpoint, bytes.NewBuffer(jsonData))
    if err != nil {
    return nil, nil, fmt.Errorf("failed to create request: %w", err)
    }

    // Set headers
    setLoginRequestHeaders(req)

    // Send the request
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
    return nil, nil, fmt.Errorf("failed to send request: %w", err)
    }
    defer resp.Body.Close()

    // Read the response body
    body, err := io.ReadAll(resp.Body)
    if err != nil {
    return nil, nil, fmt.Errorf("failed to read response: %w", err)
    }

    // Parse the JSON response
    var loginResponse LoginResponse
    err = json.Unmarshal(body, &loginResponse)
    if err != nil {
    return nil, nil, fmt.Errorf("failed to parse JSON response: %w", err)
    }

    // Extract authentication headers
    headers := &AuthHeaders{
    AccessToken: resp.Header.Get("access-token"),
    Authorization: resp.Header.Get("authorization"),
    Client: resp.Header.Get("client"),
    Expiry: resp.Header.Get("expiry"),
    UID: resp.Header.Get("uid"),
    }

    return &loginResponse, headers, nil
    }

    // setLoginRequestHeaders adds all required headers to the HTTP request
    func setLoginRequestHeaders(req *http.Request) {
    req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0")
    req.Header.Set("Accept", "application/json, text/plain, */*")
    req.Header.Set("Accept-Language", "en-US,en;q=0.5")
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Origin", refragOrigin)
    req.Header.Set("DNT", "1")
    req.Header.Set("Connection", "keep-alive")
    req.Header.Set("Referer", refragOrigin+"/")
    req.Header.Set("Sec-Fetch-Dest", "empty")
    req.Header.Set("Sec-Fetch-Mode", "cors")
    req.Header.Set("Sec-Fetch-Site", "same-site")
    req.Header.Set("Priority", "u=0")
    req.Header.Set("TE", "trailers")
    }

    // saveLoginResponseData saves the login response and headers to a JSON file
    func saveLoginResponseData(response *LoginResponse, headers *AuthHeaders) error {
    savedData := SavedData{
    Response: *response,
    Headers: *headers,
    }

    // Convert to JSON for storage
    outputJSON, err := json.MarshalIndent(savedData, "", " ")
    if err != nil {
    return fmt.Errorf("failed to create output JSON: %w", err)
    }

    // Save to file
    err = os.WriteFile(outputFilename, outputJSON, 0644)
    if err != nil {
    return fmt.Errorf("failed to write to file: %w", err)
    }

    return nil
    }

    // printLoginSuccess prints the successful login information
    func printLoginSuccess(headers *AuthHeaders) {
    fmt.Println("Login successful!")
    fmt.Println("Access Token:", headers.AccessToken)
    fmt.Println("Authorization:", headers.Authorization)
    fmt.Println("Client:", headers.Client)
    fmt.Println("Expiry:", headers.Expiry)
    fmt.Println("UID:", headers.UID)
    fmt.Println("Response data saved to", outputFilename)
    }

    // StartServer starts a new server with the given parameters
    func StartServer(auth *AuthHeaders, locationID int, game string, mod string, mapName string) (*ServerResponse, error) {
    fmt.Println("Starting a new server...")

    // Create server request payload
    requestData := StartServerRequest{
    ServerLocationID: locationID,
    Game: game,
    BetaServer: false,
    SecureServer: false,
    IsAssessment: false,
    LaunchSettings: LaunchSettings{
    Mod: mod,
    Map: mapName,
    },
    }

    // Convert to JSON
    jsonData, err := json.Marshal(requestData)
    if err != nil {
    return nil, fmt.Errorf("failed to marshal JSON: %w", err)
    }

    // Create and configure the HTTP request
    req, err := http.NewRequest(http.MethodPost, refragStartServerEndpoint, bytes.NewBuffer(jsonData))
    if err != nil {
    return nil, fmt.Errorf("failed to create request: %w", err)
    }

    // Set headers
    setRefragRequestHeaders(req, auth)
    req.Header.Set("X-GAME", game)
    req.Header.Set("X-TEAM-ID", "272803") // This could be made dynamic if needed

    // Send the request
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
    return nil, fmt.Errorf("failed to send request: %w", err)
    }
    defer resp.Body.Close()

    // Read the response body
    body, err := io.ReadAll(resp.Body)
    if err != nil {
    return nil, fmt.Errorf("failed to read response: %w", err)
    }

    // Parse the JSON response
    var serverResponse ServerResponse
    err = json.Unmarshal(body, &serverResponse)
    if err != nil {
    return nil, fmt.Errorf("failed to parse JSON response: %w", err)
    }

    return &serverResponse, nil
    }

    // GetRunningServers retrieves all currently running servers
    func GetRunningServers(auth *AuthHeaders) ([]ServerResponse, error) {
    fmt.Println("Getting running servers...")

    // Create and configure the HTTP request
    req, err := http.NewRequest(http.MethodGet, refragRunningServersEndpoint, nil)
    if err != nil {
    return nil, fmt.Errorf("failed to create request: %w", err)
    }

    // Set headers
    setRefragRequestHeaders(req, auth)
    req.Header.Set("X-GAME", "cs2")
    req.Header.Set("X-TEAM-ID", "272803") // This could be made dynamic if needed

    // Send the request
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
    return nil, fmt.Errorf("failed to send request: %w", err)
    }
    defer resp.Body.Close()

    // Read the response body
    body, err := io.ReadAll(resp.Body)
    if err != nil {
    return nil, fmt.Errorf("failed to read response: %w", err)
    }

    // Parse the JSON response
    var servers []ServerResponse
    err = json.Unmarshal(body, &servers)
    if err != nil {
    return nil, fmt.Errorf("failed to parse JSON response: %w", err)
    }

    return servers, nil
    }

    // setRefragRequestHeaders sets the necessary authentication headers for Refrag API requests
    func setRefragRequestHeaders(req *http.Request, auth *AuthHeaders) {
    req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0")
    req.Header.Set("Accept", "application/json, text/plain, */*")
    req.Header.Set("Accept-Language", "en-US,en;q=0.5")
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Origin", refragOrigin)
    req.Header.Set("DNT", "1")
    req.Header.Set("Connection", "keep-alive")
    req.Header.Set("Referer", refragOrigin+"/")
    req.Header.Set("Sec-Fetch-Dest", "empty")
    req.Header.Set("Sec-Fetch-Mode", "cors")
    req.Header.Set("Sec-Fetch-Site", "same-site")
    req.Header.Set("Priority", "u=0")
    req.Header.Set("TE", "trailers")

    // Authentication headers
    req.Header.Set("Token-Type", "Bearer")
    req.Header.Set("Access-Token", auth.AccessToken)
    req.Header.Set("Client", auth.Client)
    req.Header.Set("Uid", auth.UID)
    req.Header.Set("Expiry", auth.Expiry)
    }