googs

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

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

Go to latest
Published: Jul 2, 2025 License: BSD-3-Clause Imports: 13 Imported by: 0

README

Go OGS

Summary

googs is a Go package implements REST and Realtime APIs of OGS (online-go.com).

Status

The package allows users to authenticate, connect to games, receive game events, and submit moves as a player or watch as an observer.

Usage

First request an OGS application, with Authorization grant type set to Resource owner password-based, keep note of the client ID and unhashed client secret. Note empty client secret must be used if the Client type is Public.

Login once and persist credentials
client := googs.NewClient(clientID, clientSecret)
err := client.Login(username, password)
// if err != nil { ... }

client.Save(secretFile)

// Use REST API
overview, err := client.Overview())
// if err != nil { ... }
fmt.Printf("Total %d active games\n", len(overview.ActiveGames))

// Use Realtime API
client.GameConnect(12345)

client.OnGameData(gameID, func(g *googs.Game) {
	fmt.Printf("Received game data %s\n", g)
})
Load a client from a credential file
client, err := googs.LoadClient(secretFile)
// if err != nil { ... }

// Websocket is connected, ready to use the APIs
Demo

See example usages in demo/ which is a working minimal OGS client program that you can use to watch and play games on OGS.

And check out ymattw/tenuki for a full OGS client application!

Documentation

Overview

client.OnGameData(12345, func(g *googs.Game) {
	fmt.Printf("Received game data %s\n", g)
})

2. Load a client from a credential file

client, err := googs.LoadClient(secretFile)
// if err != nil { ... }

// Websocket is connected, ready to use the APIs

See real examples in demo/ which is a working minimal OGS client program that you can use to watch and play games on OGS.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type A1Coordinate

type A1Coordinate struct {
	Col rune // 'A', 'B', ... (skip 'I')
	Row int  // 1, 2, ...
}

A1Coordinate is coordinate represented in format "A1", note letter 'I' is skipped.

func NewA1Coordinate

func NewA1Coordinate(coord string) (*A1Coordinate, error)

A1Coordinate creates an instance from a coordinate string in format "A1".

func (A1Coordinate) String

func (c A1Coordinate) String() string

func (A1Coordinate) ToOriginCoordinate

func (c A1Coordinate) ToOriginCoordinate(boardSize int) (*OriginCoordinate, error)

type Auth

type Auth struct {
	ChatAuth         string `json:"chat_auth"`
	NotificationAuth string `json:"notification_auth"`
	UserJWT          string `json:"user_jwt"`
}

Auth holds authentication credentials for OGS Realtime APIs.

type Client

type Client struct {
	ClientID     string `json:"client_id"`
	ClientSecret string `json:"client_secret,omitempty"`
	Token               // Embedded
	Auth                // Embedded

	// Not to persist
	Username string `json:"-"`
	UserID   int64  `json:"-"`
	// contains filtered or unexported fields
}

Client represents an authenticated client with credentials and tokens.

func LoadClient

func LoadClient(secretFile string) (*Client, error)

Load stores Client credentials from a JSON file previously written via Save(), also establishes websocket connection to OGS so the Client is ready to use right after. Caller should always check error first, because an incomplete client may be returned for caller to access available information (e.g. to prefill Client ID in a login form).

func NewClient

func NewClient(clientID, clientSecret string) *Client

NewClient creates a Client instance with the given client ID and secret, Login() should be called for authentication.

func (*Client) AboutMe

func (c *Client) AboutMe() (*User, error)

func (*Client) ChatJoin

func (c *Client) ChatJoin(gameID int64) error

func (*Client) Disconnect

func (c *Client) Disconnect()

func (*Client) Game

func (c *Client) Game(gameID int64) (*Game, error)

Game fetches general game information, mostly static.

func (*Client) GameChat

func (c *Client) GameChat(gameID int64, moveNumber int, message string) error

GameChat sends a messaage to the game, this is not hidden or personal.

func (*Client) GameConnect

func (c *Client) GameConnect(gameID int64) error

GameConnect connects to a game, client should call On... functions to start watching events.

func (*Client) GameDisconnect

func (c *Client) GameDisconnect(gameID int64) error

GameDisconnect disconnects a game.

func (*Client) GameListQuery

func (c *Client) GameListQuery(list GameListType, from, limit int, where *GameListWhere, timeout time.Duration) (*GameListResponse, error)

func (*Client) GameMove

func (c *Client) GameMove(gameID int64, x, y int) error

GameMove submits a move (GameConnect must be called first).

func (*Client) GameRemovedStonesAccept

func (c *Client) GameRemovedStonesAccept(gameID int64, g *GameState) error

func (*Client) GameResign

func (c *Client) GameResign(gameID int64) error

func (*Client) GameState

func (c *Client) GameState(gameID int64) (*GameState, error)

GameState fetches current game information with board spanshot.

func (*Client) Get

func (c *Client) Get(uri string, params url.Values, ptr any) error

Get sends a GET request.

func (*Client) Identify

func (c *Client) Identify() error

Identify verifies Client access token and populate Username & UserID fields.

func (*Client) LoggedIn

func (c *Client) LoggedIn() bool

LoggedIn returns whether the client is logged in, without validating credentials.

func (*Client) Login

func (c *Client) Login(username, password string) error

Login authenticates the Client with the given username and password, also establishes websocket connection to OGS. The Client instance is ready to use right after.

func (*Client) MaybeRefresh

func (c *Client) MaybeRefresh(deadline time.Duration) (bool, error)

MaybeRefresh validates the expiry of Client credentials and refresh credentials on demand, a true value is returned when refresh happened successfully. Save() is expected to persist the new credentials.

func (*Client) NetPing

func (c *Client) NetPing(drift, latency int64) error

func (*Client) OnActiveGame

func (c *Client) OnActiveGame(fn func(*GameListEntry)) error

func (*Client) OnClock

func (c *Client) OnClock(gameID int64, fn func(*Clock)) error

OnClock starts watching clock events.

func (*Client) OnGameChat

func (c *Client) OnGameChat(gameID int64, fn func(*GameChat)) error

func (*Client) OnGameData

func (c *Client) OnGameData(gameID int64, fn func(*Game)) error

OnGameData starts watching gamedata events.

func (*Client) OnGamePhase

func (c *Client) OnGamePhase(gameID int64, fn func(GamePhase)) error

OnGamePhase starts watching game phase changes.

func (*Client) OnGameRemovedStones

func (c *Client) OnGameRemovedStones(gameID int64, fn func(*RemovedStones)) error

OnGameRemovedStones starts watching game removed stones changes.

func (*Client) OnGameRemovedStonesAccepted

func (c *Client) OnGameRemovedStonesAccepted(gameID int64, fn func(*RemovedStonesAccepted)) error

OnGameRemovedStones starts watching game removed stones acceptance.

func (*Client) OnMove

func (c *Client) OnMove(gameID int64, fn func(*GameMove)) error

OnMove starts watching game move events.

func (*Client) OnNetPong

func (c *Client) OnNetPong(fn func(drift, latency int64)) error

func (*Client) Overview

func (c *Client) Overview() (*Overview, error)

Overview returns active games.

func (*Client) PassTurn

func (c *Client) PassTurn(gameID int64) error

func (*Client) Save

func (c *Client) Save(secretFile string) error

Save stores authenticated Client credentials into a file in JSON format. This is recommended practice right after logged in via Login() once.

type Clock

type Clock struct {
	BlackPlayerID   int64      `json:"black_player_id"`
	BlackTime       PlayerTime `json:"black_time"`
	CurrentPlayerID int64      `json:"current_player"`
	Expiration      Timestamp
	GameID          int64     `json:"game_id"`
	LastMove        Timestamp `json:"last_move"`
	PausedSince     Timestamp `json:"paused_since"`
	Title           string
	WhitePlayerID   int64      `json:"white_player_id"`
	WhiteTime       PlayerTime `json:"white_time"`
	StartMode       bool
	Now             Timestamp // Only for OnClock
}

func (*Clock) ComputeClock

func (c *Clock) ComputeClock(tc *TimeControl, player PlayerColor) *ComputedClock

ComputeClock returns a computed clock struct of the given players.

type ClockSystem

type ClockSystem string
const (
	ClockAbsolute ClockSystem = "absolute"
	ClockByoyomi  ClockSystem = "byoyomi"
	ClockCanadian ClockSystem = "canadian"
	ClockFischer  ClockSystem = "fischer"
	ClockSimple   ClockSystem = "simple"
)

type ComputedClock

type ComputedClock struct {
	System         ClockSystem
	MainTime       float64
	PeriodsLeft    int
	PeriodTimeLeft float64 // Byoyomi only
	MovesLeft      int     // Canadian only
	BlockTimeLeft  float64 // Canadian only
	SuddenDeath    bool
	TimedOut       bool
}

func (ComputedClock) String

func (c ComputedClock) String() string

type Game

type Game struct {
	AgaHandicapScoring            bool  `json:"aga_handicap_scoring"`
	AllowSelfCapture              bool  `json:"allow_self_capture"`
	AllowSuperko                  bool  `json:"allow_superko"`
	AutomaticStoneRemoval         bool  `json:"automatic_stone_removal"`
	BlackPlayerID                 int64 `json:"black_player_id"`
	Clock                         Clock
	GameID                        int64  `json:"game_id"`
	GameName                      string `json:"game_name"`
	GroupIDs                      []any  `json:"group_ids"` // Can be []int or []string, depending on content
	Handicap                      int
	HandicapRankDifference        float32 `json:"handicap_rank_difference"`
	Height                        int
	InitialPlayer                 string `json:"initial_player"`
	Komi                          float32
	Latencies                     map[string]int64 // playerID => latencies
	Moves                         []Move
	OpponentPlaysFirstAfterResume bool   `json:"opponent_plays_first_after_resume"`
	Outcome                       string // Only when Phase is "finished"
	Phase                         GamePhase
	PlayerPool                    map[string]Player `json:"player_pool"` // Keys are player IDs (string)
	Players                       Players
	Private                       bool
	Ranked                        bool
	Removed                       string
	Rengo                         bool
	Rules                         string
	Score                         Score       // Only available when Phase is "finished"
	ScoreHandicap                 bool        `json:"score_handicap"`
	ScorePasses                   bool        `json:"score_passes"`
	ScorePrisoners                bool        `json:"score_prisoners"`
	ScoreStones                   bool        `json:"score_stones"`
	ScoreTerritory                bool        `json:"score_territory"`
	ScoreTerritoryInSeki          bool        `json:"score_territory_in_seki"`
	StartTime                     Timestamp   `json:"start_time"`
	StateVersion                  int         `json:"state_version"`
	StrictSekiMode                bool        `json:"strict_seki_mode"`
	SuperkoAlgorithm              string      `json:"superko_algorithm"`
	TimeControl                   TimeControl `json:"time_control"`
	WhiteMustPassLast             bool        `json:"white_must_pass_last"`
	WhitePlayerID                 int64       `json:"white_player_id"`
	Width                         int
	WinnerID                      int64 `json:"winner"` // Only when Phase is "finished"
}

func (*Game) BlackPlayer

func (g *Game) BlackPlayer() Player

func (*Game) BlackPlayerTitle

func (g *Game) BlackPlayerTitle() string

func (*Game) BoardSize

func (g *Game) BoardSize() int

func (*Game) IsMyGame

func (g *Game) IsMyGame(myUserID int64) bool

func (*Game) IsMyTurn

func (g *Game) IsMyTurn(myUserID int64) bool

func (*Game) Opponent

func (g *Game) Opponent(myUserID int64) Player

func (*Game) PlayerByID

func (g *Game) PlayerByID(userID int64) Player

func (*Game) Result

func (g *Game) Result() string

func (*Game) Status

func (g *Game) Status(state *GameState, myUserID int64) string

func (*Game) String

func (g *Game) String() string

func (*Game) URL

func (g *Game) URL() string

func (*Game) WhitePlayer

func (g *Game) WhitePlayer() Player

func (*Game) WhitePlayerTitle

func (g *Game) WhitePlayerTitle() string

func (*Game) WhoseTurn

func (g *Game) WhoseTurn(state *GameState) PlayerColor

type GameChat

type GameChat struct {
	Channel string
	Line    GameChatLine
}

type GameChatLine

type GameChatLine struct {
	ChatID       string `json:"chat_id"`
	Body         string
	Date         Timestamp
	MoveNumber   int `json:"move_number"`
	Channel      string
	PlayerID     int64 `json:"player_id"`
	Username     string
	Professional int // XXX: server response is a number 0/1
	Ranking      float32
}

type GameListEntry

type GameListEntry struct {
	ID               int64
	GroupIDs         []int64         `json:"group_ids"`
	GroupIDsMap      map[string]bool `json:"group_ids_map"`
	KidsGoGame       bool            `json:"kidsgo_game"`
	Phase            GamePhase
	Name             string
	PlayerToMove     int64 `json:"player_to_move"`
	Width            int
	Height           int
	MoveNumber       int `json:"move_number"`
	Paused           int // XXX: server response is a number 0/1
	Private          bool
	Black            Player
	White            Player
	Rengo            bool
	DroppedPlayerID  int64     `json:"dropped_player"`
	RengoCasualMode  bool      `json:"rengo_casual_mode"`
	SecondsPerMove   int64     `json:"time_per_move"`
	ClockExpiration  Timestamp `json:"clock_expiration"`
	BotGame          bool      `json:"bot_game"`
	Ranked           bool
	Handicap         int
	TournamentID     int64 `json:"tournament_id"`
	LadderID         int64 `json:"ladder_id"`
	Komi             float32
	InBeginning      bool `json:"in_beginning"`
	InMiddle         bool `json:"in_middle"`
	InEnd            bool `json:"in_end"`
	MalkovichPresent bool `json:"malkovich_present"`
}

type GameListResponse

type GameListResponse struct {
	List    GameListType
	SortBy  string `json:"by"`
	Size    int
	Where   GameListWhere
	From    int
	Limit   int
	Results []GameListEntry
}

type GameListType

type GameListType string
const (
	LiveGameList           GameListType = "live"
	CorrespondenceGameList GameListType = "corr"
	KidsGoGameList         GameListType = "kidsgo"
)

type GameListWhere

type GameListWhere struct {
	HideRanked     bool    `json:"hide_ranked"`
	HideUnranked   bool    `json:"hide_unranked"`
	RengoOnly      bool    `json:"rengo_only"`
	Hide19x19      bool    `json:"hide_19x19"`
	Hide9x9        bool    `json:"hide_9x9"`
	Hide13x13      bool    `json:"hide_13x13"`
	HideOther      bool    `json:"hide_other"`
	HideTournament bool    `json:"hide_tournament"`
	HideLadder     bool    `json:"hide_ladder"`
	HideOpen       bool    `json:"hide_open"`
	HideHandicap   bool    `json:"hide_handicap"`
	HideEven       bool    `json:"hide_even"`
	HideBotGames   bool    `json:"hide_bot_games"`
	HideBeginning  bool    `json:"hide_beginning"`
	HideMiddle     bool    `json:"hide_middle"`
	HideEnd        bool    `json:"hide_end"`
	PlayerIDs      []int64 `json:"players"`
	TournamentID   int64   `json:"tournament_id"`
	LadderID       int64   `json:"ladder_id"`
	MalkOnly       bool    `json:"malk_only"`
}

type GameMove

type GameMove struct {
	GameID     int64 `json:"game_id"`
	Move       Move
	MoveNumber int `json:"move_number"`
}

type GameOverview

type GameOverview struct {
	Game `json:"json"` // Embedded
}

GameOverview is almost identical to Game but decoded using a different json tag.

type GamePhase

type GamePhase string
const (
	PlayPhase         GamePhase = "play"
	StoneRemovalPhase GamePhase = "stone removal"
	FinishedPhase     GamePhase = "finished"
)

type GameState

type GameState struct {
	// Phase has value "play", "stone removal", "finished" etc.
	Phase GamePhase

	// Number of moves already played.
	MoveNumber int `json:"move_number"`

	// Last move, coordinate [-1, -1] indicates a pass
	LastMove OriginCoordinate `json:"last_move"`

	// User ID of the player in turn.
	PlayerToMove int64 `json:"player_to_move"`

	// Game result, "Resignation", "2.5 points" etc.
	Outcome string

	// The 2-D array with value 0=Empty, 1=Black, 2=White
	Board   [][]int
	Removal [][]int
}

func (*GameState) BoardSize

func (g *GameState) BoardSize() int

func (*GameState) IsMyTurn

func (g *GameState) IsMyTurn(myUserID int64) bool

func (*GameState) RemovalString

func (g *GameState) RemovalString() string

type Glicko2

type Glicko2 struct {
	Deviation   float32
	GamesPlayed int64 `json:"games_played"`
	Rating      float32
	Volatility  float32
}

Glicko2 contains Glicko2 ratings of a user.

type Move

type Move struct {
	OriginCoordinate
	TimeDelta float64
}

Move is a list of [x, y, TimeDelta] values.

func (*Move) UnmarshalJSON

func (m *Move) UnmarshalJSON(data []byte) error

UnmarshalJSON is a customized JSON decoder for properly handling the different types in the Move struct.

type OGSRating

type OGSRating map[string]Glicko2

OGSRating is a map of Glicko2 ratings with keys like "overall", "19x19" etc.

func (*OGSRating) UnmarshalJSON

func (r *OGSRating) UnmarshalJSON(data []byte) error

UnmarshalJSON is a customized JSON decoder for properly handling the `"version": 5` field in the JSON returned by OGS server.

type OriginCoordinate

type OriginCoordinate struct {
	X int
	Y int
}

OriginCoordinate is zero base coordinate.

func (OriginCoordinate) IsPass

func (c OriginCoordinate) IsPass() bool

func (OriginCoordinate) String

func (c OriginCoordinate) String() string

func (OriginCoordinate) ToA1Coordinate

func (c OriginCoordinate) ToA1Coordinate(boardSize int) (*A1Coordinate, error)

type Overview

type Overview struct {
	ActiveGames []GameOverview `json:"active_games"`
}

Overview contains the overview as what users see after logged into OGS.

type Player

type Player struct {
	ID           int64
	Username     string
	Professional bool
	Rank         float32

	// Accepted removals, see RemovedStones for explanation. Make it
	// a pointer and nil means "not accepted yet".
	AcceptedStones *string `json:"accepted_stones"`
}

Player contains basic user information as part of Game.

func (*Player) Ranking

func (p *Player) Ranking() string

Ranking returns the player's OGS ranking as a string in notation like "1p", "2d", "3k" etc.

func (Player) String

func (p Player) String() string

type PlayerColor

type PlayerColor int
const (
	PlayerUnknown PlayerColor = iota
	PlayerBlack
	PlayerWhite
)

func (PlayerColor) String

func (p PlayerColor) String() string

type PlayerScore

type PlayerScore struct {
	Handicap         int
	Komi             float32
	Prisoners        int
	ScoringPositions string `json:"scoring_positions"`
	Stones           int
	Territory        float32
	Total            float32
}

type PlayerTime

type PlayerTime struct {
	// Non Rengo games
	PeriodTime     float64 `json:"period_time"`
	PeriodTimeLeft float64 `json:"period_time_left"` // Byoyomi only
	Periods        int
	ThinkingTime   float64 `json:"thinking_time"`
	MovesLeft      int     `json:"moves_left"` // Canadian only
	BlockTime      float64 `json:"block_time"` // Canadian only

	// Only for Rengo games
	Value Timestamp
}

func (*PlayerTime) UnmarshalJSON

func (t *PlayerTime) UnmarshalJSON(data []byte) error

UnmarshalJSON is a customized JSON decoder for properly handling the different type of clock details in the Clock struct.

type Players

type Players struct {
	Black Player
	White Player
}

type RemovedStones

type RemovedStones struct {
	// Result removal string is a sequence of SGF coordinates, e.g.
	// "edhdid" is equivalent to origin coordinates (3,4) (3,7) (3,8).
	AllRemoved string `json:"all_removed"`

	// Removal changes
	Removed bool
	Stones  string
}

RemovedStones is the response of Realtime API "game/:id/removed_stones".

type RemovedStonesAccepted

type RemovedStonesAccepted struct {
	PlayerID int64 `json:"player_id"`

	// Result removal string is a sequence of SGF coordinates, e.g.
	// "edhdid" is equivalent to origin coordinates (3,4) (3,7) (3,8).
	Stones  string
	Players Players

	// This will change to "finished" when both sides accepted
	Phase GamePhase
	Score Score

	// Only available when Phase is "finished"
	EndTime  Timestamp `json:"end_time"`
	Outcome  string
	WinnerID int64 `json:"winner"`
}

RemovedStonesAccepted is the response of Realtime API "game/:id/removed_stones_accepted".

func (*RemovedStonesAccepted) Result

func (r *RemovedStonesAccepted) Result() string

type Score

type Score struct {
	Black PlayerScore
	White PlayerScore
}

type TimeControl

type TimeControl struct {
	System          ClockSystem
	Speed           string
	PauseOnWeekends bool `json:"pause_on_weekends"`

	// Absolute
	TotalTime float64 `json:"total_time"`

	// Byoyomi
	MainTime   float64 `json:"main_time"`   // Also for Canadian
	PeriodTime float64 `json:"period_time"` // Also for Canadian
	Periods    int
	PeriodsMax int `json:"periods_max"`
	PeriodsMin int `json:"periods_min"`

	// Canadian
	StonesPerPeriod int `json:"stones_per_period"`

	// Fischer
	InitialTime   float64 `json:"initial_time"`
	TimeIncrement float64 `json:"time_increment"`
	MaxTime       float64 `json:"max_time"`

	// Simple
	PerMove float64 `json:"per_move"`
}

func (TimeControl) String

func (t TimeControl) String() string

type Timestamp

type Timestamp struct {
	time.Time
}

Timestamp is a customized Time struct.

func (*Timestamp) UnmarshalJSON

func (t *Timestamp) UnmarshalJSON(b []byte) error

UnmarshalJSON is a customized JSON decoder for properly handling timestamps represented in both seconds or milliseconds.

type Token

type Token struct {
	AccessToken  string    `json:"access_token"`
	TokenType    string    `json:"-"` // Ignore, always "Bearer"
	ExpiresIn    int64     `json:"expires_in,omitempty"`
	RefreshToken string    `json:"refresh_token"`
	ExpiresAt    time.Time `json:"expires_at,omitempty"`
}

Token represents an OAuth-compatible token structure.

type User

type User struct {
	ID           int64
	Username     string
	Country      string
	Professional bool
	About        string
	Ranking      float32
	Ratings      OGSRating
	IsBot        bool   `json:"is_bot"`
	IsFriend     bool   `json:"is_friend"`
	UIClass      string `json:"ui_class"`
}

User contains full profile of a user

Directories

Path Synopsis
Package main offers a minimal OGS client mainly to showcase usage of the googs package, as well as serving as a debug tool for developing googs.
Package main offers a minimal OGS client mainly to showcase usage of the googs package, as well as serving as a debug tool for developing googs.

Jump to

Keyboard shortcuts

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