API Python recap/ help

I’m playing around with the APIs in Python, and it’s been a struggle, so I thought I’d post a summary of where I’ve got to, and then ask for help :slight_smile:

The documentation is incomplete and out of date. There are some helpful people and posts, like @walken and @flovo and @anoek and especially @ben1182367 whose code I mostly copied. They have made it work, but it won’t hurt to have another example here. You can find other methods of doing this in the forums, but the below seems to be the current way that works.

Step 1 is to go here: Play Go at online-go.com! | OGS
and create a new application. “Authorization Grant Type” should be “Resource owner password based”. Copy the Client ID and the Client password from this screen, before you click save.

The below is my code for the rest of the process (so far). Paste in your username,password,clientId and
clientpassword at the top, and then in the emit statements, paste in userid (4 times), gamenumber (twice), and the move you want to make. Note that for the move co-ordinates, the letter i does exist i this format, and aa means A19 (for a 19x19 game)

import socketio
import requests
s = requests.Session()

username='my-username-from-ogs'
password='my-normal-password-that-I-use-for-ogs'
ClientID='as-copied'
ClientSecret='as-copied'

response=s.post('https://online-go.com/oauth2/token/',data={"grant_type":"password","username":username,"password":password,"client_id":ClientID,"client_secret":ClientSecret})
result=response.json()
token=result['access_token']

auth = requests.get('https://online-go.com/api/v1/ui/config', headers={'Authorization': 'Bearer {}'.format(token)}).json()

socket = socketio.Client(reconnection_delay_max=60, logger = True, engineio_logger=False)

socket.connect("https://online-go.com/socket.io/?EIO=3", headers={'Authorization': 'Bearer {}'.format(token)}, transports='websocket')

socket.emit("notification/connect", data={"auth": auth['notification_auth'],
                                        "player_id": my-user-id,
                                        "username": "my-username"})
                                        
socket.emit("chat/connect", data={"auth": auth['chat_auth'],
                                        "player_id": my-user-id,
                                        "username": "my-username"})

socket.emit("authenticate", data={"auth": auth['chat_auth'],
                                        "player_id": my-user-id,
                                        "username": "my-username"})

socket.emit("game/connect", data={"game_id": 46368391,
                                      "player_id": my-user-id,
                                       "chat": False})

socket.emit("game/move", data={"game_id": 46368391,
                           "player_id": my-user-id,
                           "move": "my-move"})

So this does post a move in the game for me (most of the time). You can turn either of the loggers on to see data that might hep (but that seems to stop it working sometimes?). Where I am stuck now is receiving a move from the server, when my opponent makes a move.
I have tried all combinations that I can thnk of from other examples on the forum, and on the internet, but nothing has worked.

I don’t underatnd this socketio stuff very well. Why does this code keep running after it has made the move? Is that expected?
Maybe a more reliable way would be to collect the full game state every second, and work out whether there has been a move myself. That seems wasteful though.

2 Likes

I think you need something like socket.on("game/{id}/move", cb). I’m not sure the exact route, but on() is how you listen for events in socket.io.

Edit: see how @flovo uses it here:

2 Likes

Thanks benjito! I had another look and I got it working!

@socket.on('game/46447171/move')
def catch_all(data):
    print(data['move'][0],data['move'][1])
2 Likes

Hmm, the next thing I’m trying to do doesn’t like the socketio library, and I haven’t been able to get it to work with any of the other libraries (websockets, websocket-client, others…)
Does anyone have a working syntax?

What is “the next thing I’m trying to do”

Using websocket probably won’t work. Socket.io and websocket are different things with different APIs.

I’m using beeware to create an android app. It would be annoying to have to switch to another language at this stage, but maybe I’ll have to…

Oh wow an Android app in Python? Didn’t know you could do that but that’s cool.

You still haven’t said what you’re trying to do with the API that isn’t working. I really doubt the Python socket.io implementation (this one right?) is broken.

it’s not that socketio is broken, it’s just that it’s too complicated for beeware to handle.
I thought I read that socketio is based on websockets, or did I misunderstand that?

I’m not trying to do anything other than what I’m doing with the code above.

Yeah it’s build on top of websockets, but if you try to do the same stuff with a plain websocket library, you would just be reimplementing the socketio library.

Do you have an error message or something? For all we know this could be an Android permissions error right?

But in the end, your question may be better suited for the Beeware community

I appreciate your help. The Beeware group is a good idea.
There is a long error text, with a few different errors by the looks of it.

My next step might be to look at the source code for socketio, and see if I can extract just the bits I need.

1 Like

Hello,

Do you know if there is some way to access a log of all ladder challenges for a ladder?

The chapter on ladders at API For Online Go (OGS) · Apiary is completely empty.

I want to build a tool that predicts the probability of player A receiving a ladder challenge during a given timeframe, knowing player A’s position in the ladder, their OGS rank, and their number of current ladder games.

1 Like

Does this help? https://online-go.com/api/v1/ladders/313/players

You can look at the incoming_challenges and outgoing_challenges field for each player, and process accordingly.

Of course as with many endpoints on OGS, this is paged so you won’t get all the data at once.

2 Likes

Awesome! Thanks.

If I understand this correctly, this is mostly the same data that I can see as a human when I look at a ladder’s page; with only the currently-played challenges displayed.

So I’d have to collect this data once per 3 days over a long period of time, to build a log of challenges (filtering by game_id to avoid logging the same challenge twice).

1 Like

On that page there is a list of 3578 players, with between 0 and 6 challenges per player. I’d only be downloading the information from that page, not actual games, so this is not a too large amount of data, is it?

1 Like

Yep I got it from the network tab of the 19x19 site ladder page

Correct as well - I guess that does pose an issue if you want a whole history

It’s too bad the /games endpoint isn’t exposed anymore. With the filtering syntax, it may have been possible to do something like /api/v1/games?ladder_id=313

1 Like

Another question I have is that for some reason I can get the access token with grant_type, username, password and client_id as body. However, when I attempted to use axios in JavaScript, I got an error saying unsupported grant type.

require('dotenv').config();
const axios = require('axios');

function generateAccessToken() {
	let url = 'https://online-go.com/oauth2/token/';
	let data = {
		username: process.env.USERNAME,
		password: process.env.PASSWORD,
		client_id: process.env.CLIENT_ID,
		grant_type: process.env.GRANT_TYPE,
	};

	axios.post(url, data)
	.then(resp => console.log(resp), error => console.log(error));
}
generateAccessToken();

The grant type is indeed password in the .env file.

It turned out that if I use the form-data module rather than simply a json object it request will succeed as suggested here: reactjs - axios post request to send form data - Stack Overflow

1 Like