Skip to content

Commit

Permalink
V1.0 (#1)
Browse files Browse the repository at this point in the history
* Login support

* Updated README

* Real-time watcher
  • Loading branch information
jj0e authored Apr 10, 2021
1 parent bdcb4b3 commit 0efae7a
Show file tree
Hide file tree
Showing 14 changed files with 341 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GeoAssist
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,24 @@
# geoguessr-assist
Looks at what other players are correctly guessing and tells you which country to guess
<p><img src="https://www.geoguessr.com/_next/static/images/logo-dd3c3286e6d14f72653800dbdf5340a0.svg" width="200" alt="Geo Guessr logo"></p>

__GeoAssist__ is a tool that can be used to see what other players are correctly guessing in GeoGuessr Battle Royale. Don't know what country you're in? No worries, if someone else guesses the correct answer, GeoAssist will notify you immediately so that you make it to the end!

# Getting Started

### Usage

GeoAssist is extremley user-friendly. To get started, simply join a game of battle royale. Once you join a lobby, look in the url bar of your browser. You will see a unique identifier known as a [uuid](https://en.wikipedia.org/wiki/Universally_unique_identifier). Copy + paste this into GeoAssist.

##### Example of URL which contains uuid
```
https://www.geoguessr.com/battle-royale/c09a6b49-9878-4d80-9380-c6b274d94768
```

Sit back and relax! GeoAssist will start watching the game and as soon as someone guesses the correct answer, you will be notified!

#### Note: GeoAssist is not able to see the guess before the lock-in. Therefore GeoAssist is not meant to work alone. You are meant to use it as a tool to help you.

---

### Support

Please open an [issue](https://github.com/jj0e/geoguessr-assist/issues) if you are having trouble using the application. Any other form of contact will be ignored.
17 changes: 17 additions & 0 deletions constants/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package constants

import "fmt"

const (
Scheme = "https"
GeoGuessrHostname = "www.geoguessr.com"
GeoGuessrGameHostname = "game-server.geoguessr.com"
GeoCoderApiKey = "USLblbtiXCvYxK15p2Y1ckAoopm4rkG3"
)

var (
GeoGuessrHost = fmt.Sprintf("%s://%s", Scheme, GeoGuessrHostname)
GeoGuessrGameHost = fmt.Sprintf("%s://%s", Scheme, GeoGuessrGameHostname)
GeoGuessrLoginEndpoint = GeoGuessrHost + "/api/v3/accounts/signin"
GeoGuessrBattleRoyaleEndpoint = GeoGuessrGameHost + "/api/battle-royale"
)
72 changes: 72 additions & 0 deletions files/files.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package files

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"os/user"
)

func New() *Manager {
userInfo, _ := user.Current()
homeDir := fmt.Sprintf("%s/GeoAssist", userInfo.HomeDir)

fileManager := &Manager{
Directory: homeDir,
ConfigJSONPath: fmt.Sprintf("%s/config.json", homeDir),
}
validate(fileManager)
return fileManager
}

func fileExists(filename string) bool {
exists := true
_, err := os.Stat(filename)
if os.IsNotExist(err) {
exists = false
}
return exists
}

func validate(fileManager *Manager) {
if !fileExists(fileManager.Directory) {
dir := os.Mkdir(fileManager.Directory, 0777)
if dir != nil {
log.Fatal(dir)
}
}

if !fileExists(fileManager.ConfigJSONPath) {
file, _ := os.Create(fileManager.ConfigJSONPath)
file.Close()
config := Config{
Email: "",
Password: "",
}

f, _ := json.MarshalIndent(config, "", " ")
err := ioutil.WriteFile(fileManager.ConfigJSONPath, f, 0644)
if err != nil {
log.Fatal(err)
}
}
}

func (fm *Manager) LoadConfig() Config {
var config Config
file, _ := os.Open(fm.ConfigJSONPath)
defer file.Close()
byteValue, _ := ioutil.ReadAll(file)
json.Unmarshal(byteValue, &config)
return config
}

func (fm *Manager) UpdateConfig(conf *Config) {
f, _ := json.MarshalIndent(conf, "", " ")
err := ioutil.WriteFile(fm.ConfigJSONPath, f, 0644)
if err != nil {
log.Fatal(err)
}
}
11 changes: 11 additions & 0 deletions files/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package files

type Manager struct {
Directory string
ConfigJSONPath string
}

type Config struct {
Email string `json:"email"`
Password string `json:"password"`
}
11 changes: 11 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/jj0e/geoguessr-assist

go 1.16

require (
github.com/go-resty/resty/v2 v2.6.0
github.com/jasonwinn/geocoder v0.0.0-20190118154513-0a8a678400b8 // indirect
github.com/kelvins/geocoder v0.0.0-20200113010004-f579500e9e27
github.com/pariz/gountries v0.0.0-20200430155801-1c6a393df9c7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
18 changes: 18 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
github.com/go-resty/resty/v2 v2.6.0 h1:joIR5PNLM2EFqqESUjCMGXrWmXNHEU9CEiK813oKYS4=
github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q=
github.com/jasonwinn/geocoder v0.0.0-20190118154513-0a8a678400b8 h1:uPGRm1zi3S0S+tO1/GPb/pYKwOCSQJo1RAJworXeyIQ=
github.com/jasonwinn/geocoder v0.0.0-20190118154513-0a8a678400b8/go.mod h1:XwxSdnAz5DLGGj2CjNso68u5ZdAKyXi1KbkX4GZDZZw=
github.com/kelvins/geocoder v0.0.0-20200113010004-f579500e9e27 h1:ekI686mLb7Nxb8fgczSM1iV235ypZpOZwL/ANcGZ9Lg=
github.com/kelvins/geocoder v0.0.0-20200113010004-f579500e9e27/go.mod h1:JaVDVP24FJxa8OtNO5T1A2WKgstNreJGyK1PvBRzPW0=
github.com/pariz/gountries v0.0.0-20200430155801-1c6a393df9c7 h1:GneNkGCnFPoBkaOd03qsvXSV+ZRkZedaN0DNJCruuI0=
github.com/pariz/gountries v0.0.0-20200430155801-1c6a393df9c7/go.mod h1:U0ETmPPEsfd7CpUKNMYi68xIOL8Ww4jPZlaqNngcwqs=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
19 changes: 19 additions & 0 deletions guess/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package guess

import (
"github.com/jj0e/geoguessr-assist/constants"
)

func (game *GameInstance) Login() {
resp, _ := game.GameClient.R().
SetFormData(map[string]string{
"email": game.Username,
"password": game.Password,
}).
Post(constants.GeoGuessrLoginEndpoint)
for _, cookie := range resp.Cookies() {
if cookie.Name == "_ncfa" {
game.AuthCookie = cookie.Value
}
}
}
63 changes: 63 additions & 0 deletions guess/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package guess

import (
"fmt"
"log"
"time"

"github.com/go-resty/resty/v2"
"github.com/jj0e/geoguessr-assist/constants"
"github.com/jj0e/geoguessr-assist/utils"
"github.com/jasonwinn/geocoder"
"github.com/pariz/gountries"
)

func New(username string, password string) *GameInstance {
session := &GameInstance{
GameClient: resty.New(),
Username: username,
Password: password,
}
session.Login()
geocoder.SetAPIKey(constants.GeoCoderApiKey)
return session
}

func (game *GameInstance) Join(uuid string) {
game.Endpoint = constants.GeoGuessrBattleRoyaleEndpoint + "/" + uuid
}

func (game *GameInstance) GetGameData() *GameResult {
resp, err := game.GameClient.R().SetResult(&GameResult{}).Get(game.Endpoint)
if err != nil {
log.Fatal(err)
}
result := resp.Result().(*GameResult)
return result
}

func (game *GameInstance) Watch() {
isEnded := false
checkedRounds := 0
for !isEnded {
result := game.GetGameData()
if checkedRounds < result.RoundNumber {
currentLat := result.Rounds[result.RoundNumber-1].Latitude
currentLon := result.Rounds[result.RoundNumber-1].Longitude
address, err := geocoder.ReverseGeocode(currentLat, currentLon)
if err != nil {
fmt.Print(utils.GetTimestamp(), fmt.Sprintf("[Round %d] Unable to locate country\n", result.RoundNumber))
} else {
countryCode := address.CountryCode
query := gountries.New()
country, _ := query.FindCountryByAlpha(countryCode)
fmt.Print(utils.GetTimestamp(), fmt.Sprintf("[Round %d] %s\n", result.RoundNumber, country.Name.Common))
}
checkedRounds++
}
isEnded = result.IsEnded
time.Sleep(time.Second)
}
fmt.Print(utils.GetTimestamp(), "The game ended, hopefully you won!\n")
time.Sleep(time.Hour * 24)
}
33 changes: 33 additions & 0 deletions guess/guess.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package guess

import (
"bufio"
"fmt"
"os"
"strings"

"github.com/jj0e/geoguessr-assist/files"
"github.com/jj0e/geoguessr-assist/utils"
)

func Run(uuid string) {
fm := files.New()
conf := fm.LoadConfig()
if conf.Email == "" || conf.Password == "" {
fmt.Print(utils.GetTimestamp(), "Enter login email: ")
reader := bufio.NewReader(os.Stdin)
email, _ := reader.ReadString('\n')
email = strings.TrimSuffix(email, "\r\n")
fmt.Print(utils.GetTimestamp(), "Enter login password: ")
reader = bufio.NewReader(os.Stdin)
password, _ := reader.ReadString('\n')
password = strings.TrimSuffix(password, "\r\n")
conf.Email = email
conf.Password = password
fm.UpdateConfig(&conf)
}

instance := New(conf.Email, conf.Password)
instance.Join(uuid)
instance.Watch()
}
10 changes: 10 additions & 0 deletions guess/guess_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package guess

import (
"testing"
)

func TestFetchGameInfo(t *testing.T) {
instance := New("74089117-d46c-49cc-b6dc-1336004d27aa")
t.Error(instance.QueryCorrectGuesses())
}
33 changes: 33 additions & 0 deletions guess/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package guess

import (
"github.com/go-resty/resty/v2"
)

type GameInstance struct {
Endpoint string
GameClient *resty.Client
Username string
Password string
AuthCookie string
}

type GameResult struct {
GameId string `json:"gameId"`
RoundNumber int `json:"currentRoundNumber"`
Players []struct {
ID string `json:"playerId"`
Guesses []struct {
ID string `json:"id"`
RoundNumber int `json:"roundNumber"`
CountryCode string `json:"countryCode"`
Correct bool `json:"wasCorrect"`
} `json:"guesses"`
} `json:"players"`
Rounds []struct {
Number int `json:"roundNumber"`
Latitude float64 `json:"lat"`
Longitude float64 `json:"lng"`
} `json:"rounds"`
IsEnded bool `json:"hasGameEnded"`
}
22 changes: 22 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

import (
"bufio"
"fmt"
"os"
"strings"

"github.com/jj0e/geoguessr-assist/guess"
"github.com/jj0e/geoguessr-assist/utils"
)

func main() {
fmt.Print(utils.GetTimestamp(), "Welcome to GeoAssist\n")
fmt.Print(utils.GetTimestamp(), "Enter uuid: ")

reader := bufio.NewReader(os.Stdin)
uuid, _ := reader.ReadString('\n')
uuid = strings.TrimSuffix(uuid, "\r\n")

guess.Run(uuid)
}
7 changes: 7 additions & 0 deletions utils/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package utils

import "time"

func GetTimestamp() string {
return "[" + time.Now().Format("01-02-2006 15:04:05") + "] "
}

1 comment on commit 0efae7a

@matty022
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you use this?

Please sign in to comment.