Auto-play for Rabona

in #rabona28 days ago (edited)

rabona has an auto-play feature where you don't need to set formation for every match. It automatically populates the line-up based on the overall strength metric of the players.

This has one catch - your team plays 4-3-3 by default. Because of that default formation, lots of human players have been investing in 4-2-3-1, which has an advantage over 4-3-3.

So, if you want autoplay but don't want to be meta gamed, you have no option. Well, maybe you have if you know some scripting. :)

You need a, first.

        "username": "your_user",
        "formation": "443",
        "posting_key": "<pkey>"
        "username": "other_user",
        "formation": "4231",
        "posting_key": "<pkey>",

import json
import datetime
from rabona_python import RabonaClient
from lighthive.datastructures import Operation
from lighthive.client import Client
from datetime import datetime
from collections import OrderedDict

from config import ACCOUNTS


    "451": {
        PLAYER_TYPE_GOAL_KEEPER: ["p1"],
        PLAYER_TYPE_DEFENDER: ["p2", "p4", "p5", "p3"],
        PLAYER_TYPE_MIDFIELDER: ["p7", "p8", "p6", "p10", "p11"],
        PLAYER_TYPE_ATTACKER: ["p9"],
    "433": {
        PLAYER_TYPE_GOAL_KEEPER: ["p1"],
        PLAYER_TYPE_DEFENDER: ["p2", "p4", "p5", "p3"],
        PLAYER_TYPE_MIDFIELDER: ["p8", "p6", "p10"],
        PLAYER_TYPE_ATTACKER: ["p7", "p9", "p11"],
    "4231": {
        PLAYER_TYPE_GOAL_KEEPER: ["p1"],
        PLAYER_TYPE_DEFENDER: ["p5", "p4", "p3", "p2"],
        PLAYER_TYPE_MIDFIELDER: ["p8", "p6", "p11", "p10", "p7"],
        PLAYER_TYPE_ATTACKER: ["p9"],
    "442": {
        PLAYER_TYPE_GOAL_KEEPER: ["p1"],
        PLAYER_TYPE_DEFENDER: ["p5", "p4", "p3", "p2", "p6"],
        PLAYER_TYPE_MIDFIELDER: ["p7", "p8", "p10",],
        PLAYER_TYPE_ATTACKER: ["p11", "p9"],
    # todo add other tactics

    PLAYER_TYPE_GOAL_KEEPER: "goal keeper",
    PLAYER_TYPE_DEFENDER: "defender",
    PLAYER_TYPE_MIDFIELDER: "midfielder",
    PLAYER_TYPE_ATTACKER: "attacker",

def create_custom_json_op(username, match_id, formation_type, formation):
    formation_json = json.dumps(
             "username": username,
             "type": "set_formation",
             "command": {
                 "tr_var1": str(match_id),
                 "tr_var2": formation_type,
                 "tr_var3": formation,

    train_op = Operation('custom_json', {
        'required_auths': [],
        'required_posting_auths': [username, ],
        'id': 'rabona',
        'json': formation_json,
    return train_op

def get_next_match(r, username):
    now_as_str = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
    resp = r.matches(user=username, start_date=now_as_str, limit=1)
    return resp.get("matches", [])[0]

def get_available_players(r, username):
    players ="players", [])
    print(f" > Found {len(players)} players.")

    # remove the players that can't play
    players = [p for p in players if not (
            p["games_injured"] > 0 or p["games_blocked"] > 0
            or p["frozen"] > 0)]

    # sort by OS
    players = sorted(players, key=lambda x: x["overall_strength"])
    return players

def get_formation(r, user, match_id):
    lineup = r.lineup(user=user, match_id=match_id)
    if isinstance(lineup, list):
        return None
    return lineup.get("formation")

def prepare_formation(tactic, players):
    formation = []
    subs = []
    for player_type, player_numbers in LOOKUP_PER_TACTIC[tactic].items():
        target_group = [p for p in players if p["type"] == player_type]
        for player_number in player_numbers:
            picked_player = target_group.pop()
            print(f" >> Picked {picked_player['name']} as"
                  f" {PLAYER_TYPE_MAP[player_type]}. OS: "
            formation.append((player_number, picked_player["uid"]))

        subs += target_group

    formation = sorted(formation, key=lambda x: int(x[0].replace("p", "")))
    # fill the subs
    # get the sub gk first
    gk = [s for s in subs if s["type"] == PLAYER_TYPE_GOAL_KEEPER][0]
    formation.append(("p12", gk["uid"]))
    for i in range(13, 22):
            formation.append((f"p{i}", subs.pop()["uid"]))
        except IndexError:

    formation = OrderedDict(formation)
    return formation

def main():
    import logging
    r = RabonaClient(loglevel=logging.ERROR)
    for account in ACCOUNTS:
        print(f" > Analyzing next match for @{account['username']}.")
        next_match = get_next_match(r, account["username"])
        print(f" > Next match is {next_match['club_1']} "
              f"vs {next_match['club_2']}.")

        opponent_user = next_match["team_user_2"]
        if next_match["team_user_2"] == account["username"]:
            opponent_user = next_match["team_user_1"]

        formation_of_opponent = get_formation(
            r, opponent_user, next_match["match_id"]) or "433 default"
        print(f" > Opponent is @{opponent_user}.")
        if formation_of_opponent:
            print(f" > Opponent will play {formation_of_opponent}.")

        formation_of_us = get_formation(
            r, account["username"], next_match["match_id"]
        if formation_of_us and str(formation_of_us) == account["formation"]:
            print(f" > Formation is already set for this match: "
            # we need to set formation for that
            players = get_available_players(r, account["username"])
            formation = prepare_formation(account["formation"], players)

            op = create_custom_json_op(

            c = Client(keys=[account["posting_key"]])
            print(f" >>> Formation: {account['formation']} is set"
                  f" for {account['username']}.")
        print(f" > All operations are completed for {account['username']}.")
        print("=" * 64)

if __name__ == '__main__':

Example output:

Screen Shot 2020-10-29 at 17.51.30.png

It basically does what auto-play does, but you can select custom formations, meaning that you can have an auto-play with 4-2-3-1. See the full source at Github.

Update: Thanks to @steinhammer for the inspiring and help while building that.