# Chrono API Docs
Chrono Bot offers a basic python API, that allows you to control bots and clients.
###### Simple Bot example
```python=
from __future__ import annotations
import chrono_api.core as core
from chrono_api.utils import get_nearest_monster
from chrono_api.script import ScriptBase
class Script(ScriptBase): # the name of the class HAS to be Script
async def run(self) -> None:
# check if there is at least one client
if not (clients := core.get_clients()):
self.log('There is no running client') # you can see logged messages in the Script window
return
# get the first client
client = clients[0]
# we're gonna loop until the client get's disconnected
while client.is_connected:
# find the nearest monster
monster = get_nearest_monster(client)
self.log(f'Current target: { monster.name }')
# kill him! >:(
while monster.is_alive:
self.log(f'Using a auto-attack on { monster.name } with { monster.hp }% HP')
await client.attack(monster)
```
## Client API
Client API allows you to interact directly with the nostale client, such as moving with your character, attacking, and checking near monsters/players. Of course, not everything is implemented right now in this API, like collecting items, but I'll try to add those things progressively. (anyways almost everything can be implemented by sending packets)
:::spoiler {state="closed"} ###### Code that represents Client API
```python=
class Entity:
@property
def entity_id(self) -> int: ...
@property
def client_id(self) -> int: ...
@property
def entity_type(self) -> str: ...
@property
def x(self) -> int: ...
@property
def y(self) -> int: ...
@property
def name(self) -> str: ...
class LivingEntity(Entity):
@property
def hp(self) -> int: ...
@property
def max_hp(self) -> int: ...
@property
def mp(self) -> int: ...
@property
def max_mp(self) -> int: ...
@property
def can_attack(self) -> bool: ...
@property
def can_move(self) -> bool: ...
@property
def is_alive(self) -> bool:
""" Does not raise an error when the monster is not present on the map, in that case it returns False """
class Player(LivingEntity): ...
class Monster(LivingEntity): ...
class Npc(LivingEntity): ...
class Item(Entity): ...
class Portal:
@property
def entity_id(self) -> int: ...
@property
def x(self) -> int: ...
@property
def y(self) -> int: ...
@property
def destination_map(self) -> int: ...
class Skill:
@property
def id(self) -> int: ...
@property
def name(self) -> str: ...
@property
def cast_id(self) -> int: ...
@property
def is_on_cooldown(self) -> bool: ...
class InventoryItems:
@property
def client_id(self) -> str: ...
@property
def name(self) -> str: ...
@property
def item_id(self) -> int: ...
@property
def inventory_type(self) -> str: ...
@property
def slot(self) -> int: ...
@property
def amount(self) -> int: ...
class Client:
@property
def is_connected(self) -> bool: ...
@property
def client_id(self) -> str: ...
@property
def character(self) -> Player: ...
@property
def monsters(self) -> list[Monster]: ...
@property
def players(self) -> list[Player]: ...
@property
def items(self) -> list[Item]: ...
@property
def npcs(self) -> list[Npc]: ...
@property
def portals(self) -> list[Portal]: ...
@property
def skills(self) -> list[Skill]: ...
@property
def inventory_items(self) -> list[InventoryItems]: ...
async def walk(self, x: int, y: int): ...
async def walk_in_range(self, x: int, y: int, range: float): ...
async def use_skill(self, skill: Skill, target: Entity): ...
async def attack(self, target: Entity):
""" Uses auto-attack on the target, if the auto-attack is on cooldown, it waits for it to finish """
def send_packet(self, packet: str) -> None: ...
def get_clients() -> list[Client]: ...
```
:::
---
As you can see the only way of getting client object is using the `get_clients` function, that gives you the `list[Client]`.
Almost all methods are properties, which means that you can't modify the values of Entities/Client.
###### Print names of all players on the map
```python=
from __future__ import annotations
import chrono_api.core as core
from chrono_api.script import ScriptBase
class Script(ScriptBase): # the name of the class HAS to be Script
async def run(self) -> None:
# check if there is at least one client
if not (clients := core.get_clients()):
self.log('There is no running client') # you can see logged messages in the Script window
return
# get the first client
client = clients[0]
for player in client.players:
self.log(f'Player with name: { player.name }')
```
## Loot Bot API
Loot Bot API allows you to interact directly with the Loot Bot instances. For now there are only methods to start, stop, change the configuration and client. This can be great if you want to have for example IC script that automtically joins the IC (stops to bot, joins the IC, changes Loot Bot config to IC, does IC, stops the bot, selects the old config, and continues botting).
:::spoiler {state="closed"} ###### Code that represents Loot Bot API
```python=
class LootBot:
@property
def is_running(self) -> bool: ...
def start(self) -> None: ...
def stop(self) -> None: ...
@property
def bot_id(self) -> str: ...
@property
def client_id(self) -> Optional[str]: ...
@client_id.setter
def client_id(self, client_id: str) -> None: ...
@property
def config_name(self) -> str: ...
@config_name.setter
def config_name(self, config_name: str) -> None: ...
def get_loot_bots() -> list[LootBot]: ...
def add_loot_bot() -> LootBot: ...
```
:::
---
## Event listening API
This bot also provides API for listening all incomming Client Events, (client events can be for example received packet, sent packet, entity moved packet etc., all events are gonna be posted later.).
The easiest way how to describe this API is probably example.
```python=
from __future__ import annotations
from typing import Any
from chrono_api.script import ScriptBase
from chrono_api.script import GLOBAL_EVENT_SUBSCRIBER
import chrono_api.core as core
class Script(ScriptBase):
async def handle_event(self, event: Any):
event = event['event']
if event['kind'] == 'ReceivePacket' \
and not event['packet'].startswith('mv'):
self.log(event['packet'])
async def run(self) -> None:
self.log('Script is running')
self.subscribe_client(GLOBAL_EVENT_SUBSCRIBER, self.handle_event)
await self.wait_forever()
```
In this example we're listing for events from all the clients and then we're logging all received packets that doesn't start with `mv`.
## IC Bot Example
```python=
from __future__ import annotations
from typing import Any
from chrono_api.script import ScriptBase
from chrono_api.script import GLOBAL_EVENT_SUBSCRIBER
from chrono_api.utils import get_client_by_id
from chrono_api.utils import random_sleep
from chrono_api.core import Client
from chrono_api.events import EventKind
class Script(ScriptBase):
""" IC Bot, that joins IC and goes to the bottom of the map """
async def join_ic(self, client: Client):
client.send_packet('guri 508')
client.send_packet('#guri^506')
async def handle_events(self, event: Any):
client_id = event['client_id']
event = event['event']
if event['kind'] == EventKind.RECEIVE_PACKET \
and event['packet'] == 'qnamli 1 #guri^506 379 0 0 0':
if client := get_client_by_id(client_id):
await random_sleep(1000, 2000)
await self.join_ic(client)
elif event['kind'] == EventKind.MAP_CHANGE \
and event['map_id'] == 2004:
if client := get_client_by_id(client_id):
await random_sleep(2000, 6000)
await client.walk(37, 66)
async def run(self) -> None:
self.subscribe_client(GLOBAL_EVENT_SUBSCRIBER, self.handle_events)
await self.wait_forever()
```