fortnitego

package module
v0.0.0-...-af514bb Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 7, 2019 License: MIT Imports: 14 Imported by: 0

README

fortnite-go

An interfacer for the Fortnite game API. Can retrieve player statistics and global leaderboard information by specified platform type. Handles authentication, token renewal, and token destruction upon program exit.

🍻 Tip me!

Setup

In order to authenticate successfully with the Epic game servers, you must have an Epic Account, and Fortnite installed on the PC you are using. Two authentication tokens must be extracted on client and game launch.

To obtain header tokens:

  • Install & Open Fiddler 4
  • In Tools -> Options -> HTTPS, enable "Capture HTTPS CONNECTs" and "Decrypt HTTPS traffic"
  • Start your Epic Games Launcher.
  • You will see a request with /account/api/oauth/token. Click on it and then click Inspectors tab to get the header (Copy Authorization header content and remove "basic ") => This header is your Launcher Token
  • Launch Fortnite
  • You will see again a request with /account/api/oauth/token. Click on it and then click Inspectors tab to get the header (Copy Authorization header content and remove "basic ") => This header is your Game Token

V2 Stats

This package has been updated to the latest V2 stats from Epic. There are no more platforms. All stats are broken up between input type (keyboardmouse, gamepad, and touch).

V1 Stats Compatibility - Deprecated

To retrieve stats via V1 stats, please use the QueryPlayerV1 method:

// Retrieve player info and stats by Account ID.
player, err := s.QueryPlayerV1("", "AccountID")
if err != nil {
	fmt.Println(err)
}

Usage

See Godoc for in-depth documentation.

Player Stats

To retrieve a player's information and statistics for Battle Royale:

// Create the session.
sess := fortnitego.Create("USERNAME", "PASSWORD", "LAUNCHER-TOKEN", "GAME-TOKEN")

// Retrieve player info and stats by Username.
player, err := s.QueryPlayer("PlayerName", "")
if err != nil {
	fmt.Println(err)
}

// Retrieve player info and stats by Account ID.
player, err := s.QueryPlayer("", "AccountID")
if err != nil {
	fmt.Println(err)
}

If the player exists, a result may look like the example below. (Represented in JSON)

{
  "AccountInfo": {
    "AccountID": "6cd40c1722f2497fa1d2145b26da88e3",
  },
  "Stats": {
    "Solo": {
			"Touch": {
	      "Wins": 23,
	      "Top10": 86,
	      "Top25": 154,
	      "KillDeathRatio": "3.13",
	      "WinPercentage": "6.74",
	      "Matches": 341,
	      "Kills": 995,
	      "MinutesPlayed": 2174,
	      "KillsPerMatch": "2.92",
	      "KillsPerMinute": "0.46",
	      "Score": 56247
			},
			"Gamepad": {
	      "Wins": 23,
	      "Top10": 86,
	      "Top25": 154,
	      "KillDeathRatio": "3.13",
	      "WinPercentage": "6.74",
	      "Matches": 341,
	      "Kills": 995,
	      "MinutesPlayed": 2174,
	      "KillsPerMatch": "2.92",
	      "KillsPerMinute": "0.46",
	      "Score": 56247
			},
			"KeyboardMouse": {
	      "Wins": 23,
	      "Top10": 86,
	      "Top25": 154,
	      "KillDeathRatio": "3.13",
	      "WinPercentage": "6.74",
	      "Matches": 341,
	      "Kills": 995,
	      "MinutesPlayed": 2174,
	      "KillsPerMatch": "2.92",
	      "KillsPerMinute": "0.46",
	      "Score": 56247
			},
    },
    "Duo": {
			"Touch": {
	      "Wins": 45,
	      "Top5": 89,
	      "Top12": 149,
	      "KillDeathRatio": "3.27",
	      "WinPercentage": "11.03",
	      "Matches": 408,
	      "Kills": 1186,
	      "MinutesPlayed": 1465,
	      "KillsPerMatch": "2.91",
	      "KillsPerMinute": "0.81",
	      "Score": 91499
			},
			"Gamepad": {
	      "Wins": 45,
	      "Top5": 89,
	      "Top12": 149,
	      "KillDeathRatio": "3.27",
	      "WinPercentage": "11.03",
	      "Matches": 408,
	      "Kills": 1186,
	      "MinutesPlayed": 1465,
	      "KillsPerMatch": "2.91",
	      "KillsPerMinute": "0.81",
	      "Score": 91499
			},
			"KeyboardMouse": {
	      "Wins": 45,
	      "Top5": 89,
	      "Top12": 149,
	      "KillDeathRatio": "3.27",
	      "WinPercentage": "11.03",
	      "Matches": 408,
	      "Kills": 1186,
	      "MinutesPlayed": 1465,
	      "KillsPerMatch": "2.91",
	      "KillsPerMinute": "0.81",
	      "Score": 91499
			},
    },
    "Squad": {
			"Touch": {
	      "Wins": 116,
	      "Top3": 190,
	      "Top6": 305,
	      "KillDeathRatio": "3.60",
	      "WinPercentage": "14.23",
	      "Matches": 815,
	      "Kills": 2516,
	      "MinutesPlayed": 3143,
	      "KillsPerMatch": "3.09",
	      "KillsPerMinute": "0.80",
	      "Score": 253462
			},
			"Gamepad": {
	      "Wins": 116,
	      "Top3": 190,
	      "Top6": 305,
	      "KillDeathRatio": "3.60",
	      "WinPercentage": "14.23",
	      "Matches": 815,
	      "Kills": 2516,
	      "MinutesPlayed": 3143,
	      "KillsPerMatch": "3.09",
	      "KillsPerMinute": "0.80",
	      "Score": 253462
			},
			"KeyboardMouse": {
	      "Wins": 116,
	      "Top3": 190,
	      "Top6": 305,
	      "KillDeathRatio": "3.60",
	      "WinPercentage": "14.23",
	      "Matches": 815,
	      "Kills": 2516,
	      "MinutesPlayed": 3143,
	      "KillsPerMatch": "3.09",
	      "KillsPerMinute": "0.80",
	      "Score": 253462
			},
    }
  }
}
Leaderboard

To retrieve the top 50 global wins leaderboard:

lb, err := sess.GetWinsLeaderboard(fortnitego.PC, fortnitego.Squad) // (Solo, Duo, Squad)
if err != nil {
	fmt.Println(err)
}

A typical response would look like:

[
  {
    "DisplayName": "4hs_UwatakashiТV",
    "Rank": 1,
    "Wins": 1131
  },
  {
    "DisplayName": "qoowill",
    "Rank": 2,
    "Wins": 827
  },
  {
    "DisplayName": "RedemeЯ",
    "Rank": 3,
    "Wins": 818
  },
  {
    "DisplayName": "Copy - TH",
    "Rank": 4,
    "Wins": 801
  },
  {
    "DisplayName": "TTV.vannesskwan",
    "Rank": 5,
    "Wins": 800
  },
  {
    "DisplayName": "BlooTeaTV",
    "Rank": 6,
    "Wins": 789
  },
  {
    "DisplayName": "Twitch_PuZiiyo",
    "Rank": 7,
    "Wins": 765
  },
  {
    "DisplayName": "Infamous Uniq",
    "Rank": 8,
    "Wins": 680
  },
  {
    "DisplayName": "ŁїƒεSnoopySworld",
    "Rank": 9,
    "Wins": 635
  },
  {
    "DisplayName": "tuổi lz sánh vai",
    "Rank": 10,
    "Wins": 622
  },
  {
    "DisplayName": "SaltySoji",
    "Rank": 11,
    "Wins": 620
  },
  {
    "DisplayName": "Fluuuuuuu",
    "Rank": 12,
    "Wins": 619
  },
  {
    "DisplayName": "Faze_MadGames",
    "Rank": 13,
    "Wins": 618
  },
  {
    "DisplayName": "Mafia WillzonePH",
    "Rank": 14,
    "Wins": 618
  },
  {
    "DisplayName": "IDOLˆMrTìnhˆ",
    "Rank": 15,
    "Wins": 609
  },
  {
    "DisplayName": "Twitch.DapanoTV",
    "Rank": 16,
    "Wins": 607
  },
  {
    "DisplayName": "VIP. Trung Lương",
    "Rank": 17,
    "Wins": 599
  },
  {
    "DisplayName": "Ĺαšt-Prince",
    "Rank": 18,
    "Wins": 576
  },
  {
    "DisplayName": "DongminHero_o",
    "Rank": 19,
    "Wins": 565
  },
  {
    "DisplayName": "TacoSlut.",
    "Rank": 20,
    "Wins": 563
  },
  {
    "DisplayName": "Ĺαšt-ÐεÑz ツ",
    "Rank": 21,
    "Wins": 554
  },
  {
    "DisplayName": "Pvt.alifrizani",
    "Rank": 22,
    "Wins": 544
  },
  {
    "DisplayName": "Copy - 2 TAP",
    "Rank": 23,
    "Wins": 543
  },
  {
    "DisplayName": "Death Donator",
    "Rank": 24,
    "Wins": 538
  },
  {
    "DisplayName": "Pvt.Brokutt",
    "Rank": 25,
    "Wins": 534
  },
  {
    "DisplayName": "ⓛⓞⓥⓔBetty-CosMix",
    "Rank": 26,
    "Wins": 533
  },
  {
    "DisplayName": "Pre3idium",
    "Rank": 27,
    "Wins": 528
  },
  {
    "DisplayName": "BC Mr.Spawnz",
    "Rank": 28,
    "Wins": 528
  },
  {
    "DisplayName": "CTGS.TTâm SoNy",
    "Rank": 29,
    "Wins": 525
  },
  {
    "DisplayName": "Early_Morning",
    "Rank": 30,
    "Wins": 518
  },
  {
    "DisplayName": "Kreyzi",
    "Rank": 31,
    "Wins": 516
  },
  {
    "DisplayName": "i7 - 1080 Ti",
    "Rank": 32,
    "Wins": 510
  },
  {
    "DisplayName": "sunin-",
    "Rank": 33,
    "Wins": 509
  },
  {
    "DisplayName": "1.0.1",
    "Rank": 34,
    "Wins": 502
  },
  {
    "DisplayName": "Sarkanos",
    "Rank": 35,
    "Wins": 492
  },
  {
    "DisplayName": "VexNguyen",
    "Rank": 36,
    "Wins": 487
  },
  {
    "DisplayName": "eShield DoNtm1nd",
    "Rank": 37,
    "Wins": 481
  },
  {
    "DisplayName": "Ninja O Ceifador",
    "Rank": 38,
    "Wins": 480
  },
  {
    "DisplayName": "Fa_YeuVoBan",
    "Rank": 39,
    "Wins": 473
  },
  {
    "DisplayName": "N68 3 .",
    "Rank": 40,
    "Wins": 472
  },
  {
    "DisplayName": "K- Mày Tuổi Tôm",
    "Rank": 41,
    "Wins": 471
  },
  {
    "DisplayName": "Oraculoo",
    "Rank": 42,
    "Wins": 471
  },
  {
    "DisplayName": "i7 - 6700K",
    "Rank": 43,
    "Wins": 466
  },
  {
    "DisplayName": "Fable Zamas",
    "Rank": 44,
    "Wins": 464
  },
  {
    "DisplayName": "NoCry- arT.",
    "Rank": 45,
    "Wins": 461
  },
  {
    "DisplayName": "TwitchTranq96",
    "Rank": 46,
    "Wins": 457
  },
  {
    "DisplayName": "Twitch.ItsWiKKiD",
    "Rank": 47,
    "Wins": 455
  },
  {
    "DisplayName": "이영돈",
    "Rank": 48,
    "Wins": 455
  },
  {
    "DisplayName": "Mafia Chrism-",
    "Rank": 49,
    "Wins": 455
  },
  {
    "DisplayName": "Infamous G0D",
    "Rank": 50,
    "Wins": 454
  }
]

Special Thanks

To qlaffont for fortnite-api, which this project was largely based off of and inspired by.

Documentation

Index

Constants

View Source
const (
	PC            = "pc"
	Xbox          = "xb1"
	PS4           = "ps4"
	TOUCH         = "touch"
	GAMEPAD       = "gamepad"
	KEYBOARDMOUSE = "keyboardmouse"
)

Platform types

View Source
const (
	Solo  = "_defaultsolo"
	Duo   = "_defaultduo"
	Squad = "_defaultsquad"
)

Name identifiers for group type. Used in parsing URLs and responses.

View Source
const (
	SoloV1  = "_p2"
	DuoV1   = "_p10"
	SquadV1 = "_p9"
)

Name identifiers for group type. Used in parsing URLs and responses.

View Source
const Version = "0.1"

Version is the package version.

Variables

View Source
var (
	AuthBearer = "Bearer"
	AuthBasic  = "Basic"
)

Authentication header types

View Source
var ErrNotFound = Error{"Character not found."}

ErrNotFound is returned when we receive a 404 when attempting to query a player.

Functions

This section is empty.

Types

type AccountInfo

type AccountInfo struct {
	AccountID string
	Username  string
	Platform  string
}

AccountInfo contains basic information about the user.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is our HTTP client for this package to interface with Epic's API.

func (*Client) Do

func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, int, error)

Do processes a prepared HTTP request with the client provided. An interface is passed in to decode the response into.

func (*Client) NewRequest

func (c *Client) NewRequest(method, url string, body io.Reader) (*http.Request, error)

NewRequest prepares a new HTTP request and sets the necessary headers.

type Error

type Error struct {
	// contains filtered or unexported fields
}

func (*Error) Error

func (e *Error) Error() string

type FinalStats

type FinalStats struct {
	Solo                  *Stats
	Duo                   *Stats
	Squad                 *Stats
	Level                 int
	PercentUntilNextLevel int
}

Stats is the structure which holds the player's stats for the 3 different game modes offered in Battle Royal.

type GlobalWinsLeaderboard

type GlobalWinsLeaderboard []leaderboardEntry

GlobalWinsLeaderboard contains an array of the top X players by wins on a specific platform and party mode.

type Player

type Player struct {
	AccountInfo AccountInfo
	Stats       *FinalStats
	RawStats    map[string]int
}

Player is the hierarchical struct used to contain information regarding a player's account info and stats.

type PlayerV1

type PlayerV1 struct {
	AccountInfo AccountInfo
	Stats       StatsV1
	RawStats    map[string]int
}

type Session

type Session struct {
	AccessToken  string
	ExpiresAt    string
	RefreshToken string
	AccountID    string
	ClientID     string
	// contains filtered or unexported fields
}

Session holds connection information regarding a successful authentication with an Epic account to Epic's API. Will hold a Client to use for interfacing with said API, and retain information about our authenticated session with them.

func Create

func Create(email string, password string, launcherToken string, gameToken string) (*Session, int, error)

Create opens a new connection to Epic and authenticates into the game to obtain the necessary access tokens.

func Create2fa

func Create2fa(email string, password string, code string, launcherToken string, gameToken string) (*Session, error)

Create opens a new connection to Epic and authenticates into the game to obtain the necessary access tokens.

func (*Session) CheckStatus

func (s *Session) CheckStatus() (bool, error)

CheckStatus checks the status of the Fortnite game service. Will return false with error containing the status message from Epic.

func (*Session) GetWinsLeaderboard

func (s *Session) GetWinsLeaderboard(platform, groupType string) (*GlobalWinsLeaderboard, error)

GetWinsLeaderboard returns the top 50 players and their rank position based on global wins for a specific platform, and party/group type.

func (*Session) Kill

func (s *Session) Kill() error

Kill terminates an existing session by sending a DELETE request to deactivate the session on Epic's servers.

func (*Session) QueryPlayer

func (s *Session) QueryPlayer(name string, accountId string) (*Player, error)

QueryPlayer looks up a player by their username and platform, and returns information about that player, namely, the statistics for the 3 different party modes.

func (*Session) QueryPlayerById

func (s *Session) QueryPlayerById(accountId string) (*statsResponse, error)

func (*Session) QueryPlayerByIdV1

func (s *Session) QueryPlayerByIdV1(accountId string) (*statsResponseV1, error)

func (*Session) QueryPlayerV1

func (s *Session) QueryPlayerV1(name string, accountId string, platform string) (*PlayerV1, error)

QueryPlayer V1 looks up a player by their username and platform, and returns information about that player, namely, the statistics for the 3 different party modes.

func (*Session) Refresh

func (s *Session) Refresh() error

Refresh renews a session by obtaining a new access token, and replacing the hold one. Intended use it for an automatic goroutine to handle scheduling of renewal. Previous token is automatically invalidated on Epic's end.

type Stats

type Stats struct {
	Touch         statDetails
	Gamepad       statDetails
	KeyboardMouse statDetails
}

type StatsV1

type StatsV1 struct {
	Solo  statDetails
	Duo   statDetails
	Squad statDetails
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL