Aller au contenu
Afficher dans l'application

Une meilleure façon de naviguer. En savoir plus.

Asterobot

Une application plein écran sur votre écran d'accueil avec notifications push, badges et plus encore.

Pour installer cette application sur iOS et iPadOS
  1. Appuyez sur Icône de partage dans Safari
  2. Faites défiler le menu et appuyez sur Ajouter à l'écran d'accueil.
  3. Appuyez sur Ajouter dans le coin supérieur droit.
Pour installer cette application sur Android
  1. Appuyez sur le menu à trois points (⋮) dans le coin supérieur droit du navigateur.
  2. Appuyez sur Ajouter à l'écran d'accueil ou Installer l'application.
  3. Confirmez en appuyant sur Installer.

asterobot:bot

The launch object, the session a script runs on, the map its character stands on, the memory its scripts share, disconnect(), onStopRequest(), and the four functions that write to the bot's console.

asterobot:bot gives a script what belongs to its own bot: the facts about the game session, the map its character stands on, a memory every script on the bot shares, a way to close the game connection, a way to hear that the bot's schedule wants it to stop, and the bot's console. The launch argument of the entry function is described on this page too, because session repeats its two values.

import { session, currentMap, memory, disconnect, onStopRequest, offStopRequest, botDebug, botInfo, botWarn, botError } from "asterobot:bot";

These are the module's only exports. There's no bot object to import.

The launch object

Asterobot starts a script by calling the default export of the package's index.js with one argument, launch:

import { session, botInfo } from "asterobot:bot";
import { send } from "asterobot:protocol";

export default async function behavior(launch) {
  botInfo("Launch:", launch.reason, launch.generation);

  // A new game connection starts with this message. MITM bots never launch
  // with "initial": the Dofus client has already identified their session.
  if (launch.reason === "initial") {
    await send("IdentificationRequest", {
      ticketKey: session.gameToken,
      languageCode: session.language,
    });
  }
}

On the first start of a Full socket bot, the console shows Launch: initial 1.

Property Type Value
reason string "initial", "resume" or "reload", as described below
generation BigInt Which start this is on the current game connection: 1n for the first, then one more for each start. A start whose script fails while starting still counts. A start that fails before any code runs, such as one with a syntax error, doesn't. A new game connection counts from 1n again.
reason When a script starts with it
"initial" On a Full socket bot's game connection, as long as no script has started successfully on it yet. A start is successful once the default export's function has finished.
"resume" Every later start on the same connection, such as Play after Stop. On a MITM bot, every start, the first one included.
"reload" When a running script is replaced without being stopped first. No button in Asteroboard does that.

The default export must be a function that returns a promise, usually an async function. The entry function explains what to do for each reason.

session

session is a frozen object. Its values are set when the script starts and don't change while it runs, except scheduleEndsAt.

Property Type Value
gameToken string The ticket the login server issued for this game connection. A Full socket script sends it as ticketKey in IdentificationRequest.
serverId number The game server chosen when signing in, fixed for the whole connection. On a MITM bot, the server the Dofus client connected to. It says nothing about where the character is.
language string The language code the script was started with. Right now every start made from Asteroboard, with Play, Run or a load from the bot's menu, gives fr, whatever language the bot itself is set to. text() and search() from asterobot:gamedata use this language too.
launchReason string The same value as launch.reason
behaviorGeneration BigInt The same value as launch.generation
connectionId BigInt Which of the bot's game connections the script runs on: one more each time the bot gets a new one, signing in or relaying a Dofus client. Store it next to what you keep in memory for one connection.
shared boolean true on a MITM bot, where a person plays the same character through their Dofus client. false on a Full socket bot.
scheduleEndsAt Date or undefined When the bot's schedule will ask the script to stop: the end of the current time slot, or the moment it hands over to a time slot that plays another package. undefined outside a time slot, when the bot has no schedule or it's switched off, and when the bot is always connected with one package. It's read again each time you access it, so a schedule saved while the script runs shows up immediately.

Every function behaves the same whatever shared says. It exists so that a script that acts on its own, moving or fighting, can decide to stay passive while someone else is playing.

scheduleEndsAt lets a script avoid starting something long just before it's asked to stop:

const end = session.scheduleEndsAt;
if (end && end.getTime() - Date.now() < 30 * 60_000) {
  botInfo("Less than 30 minutes left in this time slot: no dungeon now");
}

Caution

Never log session.gameToken or show it to anyone. It's the ticket to the bot's game session, and a stranger who has it could take that session over.

While Asterobot reads a package's declarations, there's no session yet. session then holds empty values: "" for the strings, 0 for serverId, "initial", 1n, 0n for connectionId, false and undefined for scheduleEndsAt. See Declarations.

currentMap()

currentMap() returns the map the bot's character stands on, or undefined until the character has entered a map.

import { currentMap, botInfo } from "asterobot:bot";

const map = currentMap();

if (map?.cellId !== undefined) {
  botInfo("Map", map.mapId, "cell", map.cellId, "actors", map.actors.length);
}

For a character on cell 300 of map 154010883, next to a monster group, the console shows Map 154010883 cell 300 actors 2.

Property Type Value
mapId BigInt The map's id
characterId BigInt The id of the bot's character, the one MapMovementEvent gives in characterId when the character walks. Absent until the character is selected.
cellId number The cell the character stands on. Absent while the map doesn't place the character yet.
actors array Every actor the server placed on the map, the character included, sorted by id. Each one is an object with id, a BigInt, and cellId, a number. A monster group also has monsters: its monsters, the main creature the map shows first, each { id, grade, level }, id being a key of the game data's monsters table. A non-player character, such as a banker, has npc: true.
elements array Every interactive element on the map (doors, zaaps, resources, workshops, exits), sorted by id. Each one is an object with id, the number useElement() takes, and skills, what it offers right now: objects with id, the skill id (a row of the game data's skills table), and instanceUid, which offer of that skill on this element. An element whose only skill is 339, Point Out an Exit, is an exit to the next map.

Asterobot follows the map itself, for the whole game session, from the messages the bot sends and receives: MapCurrentEvent when the server names the map the character is now on, or MapCurrentInstanceEvent for an instanced map, MapComplementaryInformationEvent for the actors already there, then GameRolePlayShowActorsEvent, MapMovementEvent and ContextRemoveElementEvent for the actors that come, walk and leave, and MapMovementRefusedEvent for the cell the server keeps the character on when it refuses a walk. The interactive elements come with MapComplementaryInformationEvent too, and InteractiveMapUpdateEvent and InteractiveElementUpdatedEvent keep them current. So a script started or reloaded in the middle of a session reads where the character is, not only what it saw since it started. Each call returns a new object holding the values of that moment.

On a map change, the server names the new map with MapCurrentEvent first, or with MapCurrentInstanceEvent for an instanced map, whose mapId plays the same part, and sends none of its contents until the client has said it's ready, with ContextReadyRequest and MapInformationRequest. From then on, currentMap() describes the new map, with no actors and no cellId: the character has left the previous map, and nothing of the new one has arrived. MapComplementaryInformationEvent then places the actors and the character. On a Full socket bot, sending the two requests is up to a script: @asterobot:base does it for every map. After a map change made by changeMap(), an exit's useElement() or travel(), Asterobot sends them itself unless a script said it does with answerMapLoads(), as base does. A character is in the world, ready to act, once cellId is set. A move() asked for before that rejects with move: the character's map and cell aren't known yet.

An actor's cellId is the cell its walk ends on from the moment the server grants the walk, before the actor gets there. When the character's own walk stops early, for example because the player clicked elsewhere on a MITM bot, the MapMovementCancelRequest sent for it moves the character to the cell where it stopped. Fights aren't followed.

memory

memory is the bot's shared memory: keys that name values, which every script the bot runs can read and write, and which outlive each of them.

import { memory, session, botInfo } from "asterobot:bot";
import { on, send } from "asterobot:protocol";

const KEY = "@alice:chat-count/count";

export default async function behavior(launch) {
  on("ChatChannelMessageEvent", () => {
    // Only a count stored on this game connection goes on: after a
    // reconnection, it starts over.
    const stored = memory.get(KEY);
    const count = (stored?.connection === session.connectionId ? stored.count : 0) + 1;

    memory.set(KEY, { connection: session.connectionId, count });
    botInfo(`${count} chat messages on this connection`);
  });

  // A new game connection starts with this message. MITM bots never launch
  // with "initial": the Dofus client has already identified their session.
  if (launch.reason === "initial") {
    await send("IdentificationRequest", {
      ticketKey: session.gameToken,
      languageCode: session.language,
    });
  }
}

Stop then Play doesn't reset the count: the next chat message on the same connection shows 4 chat messages on this connection after three. Call the functions from the entry function or a handler, not from the top level of a module: see the last paragraph of this section.

A script's own variables are gone after Stop, a reload or Run in the editor, while the bot and its game connection carry on. What the script needs to find again goes here. Bot memory explains how to use it, with how long it lasts and how to keep a connection's state.

Function Returns What it does
memory.get(key) The value, or undefined Reads a copy of the value stored under key.
memory.set(key, value) undefined Stores a copy of value under key, replacing what was there.
memory.delete(key) boolean Removes key. Returns whether it was there.
memory.keys() array of strings Every stored key, sorted.
  • It belongs to the bot. It survives Stop, Play, reloads and reconnections. It's empty again when Asterobot restarts, when the bot is deleted, and when changing the bot's account or game version rebuilds it.
  • One memory per bot, shared by every package it runs. A value one package stores is there for any other to read. Start your keys with your package's name, as in @alice:farm/progress, so two packages never pick the same key by accident. Nothing enforces it.
  • Values are plain data: null, booleans, numbers, strings, BigInts, arrays and plain objects, nested at most 32 levels. Anything else throws a TypeError, naming where it is in the value: undefined (use delete() instead), functions, NaN and infinities, Map, Date, symbols and instances of a class.
  • Values are copies. Changing an object you read back changes nothing until you set() it again. An object's keys come back sorted by name.
  • For one connection only, store session.connectionId next to the value, as the example does, and ignore a value whose id isn't the current one. launch.reason can't tell you: a start that fails half way leaves the next one "initial" on the same connection.
  • It's bounded: 1024 keys of up to 256 bytes, 64 KiB per value, 1 MiB per bot. Past a limit, set() throws an Error that names it, such as memory: the value is larger than 64 KiB.

All four functions are synchronous: the memory is in Asterobot's own memory, not on disk. While Asterobot reads a package's declarations there's no bot, so get() returns undefined, keys() an empty array, and set() and delete() throw.

The bot's page shows the memory under Tools > Memory, and refreshes it when a script changes it.

disconnect()

disconnect() returns undefined.

It closes the bot's game connection once the messages the script sent before it have left, and returns right away, without waiting for the connection to close. The script stops as a result, and the bot's page shows The behavior stopped with an error with the text behavior requested Game disconnect. Calling disconnect() again, or while the script is already stopping, does nothing.

Warning

On a MITM bot, the connection is the game session of the person playing. disconnect() closes it for their Dofus client too. Check session.shared first if that matters to your package.

Asterobot doesn't connect the bot again by itself, unless the bot's schedule is in one of its time slots. Disconnect, delete and restart describes what happens next from the player's side. To end a session when a time slot of the schedule ends, return from an onStopRequest() handler instead: the bot is then stopped without an error.

onStopRequest()

onStopRequest(handler) returns a handle, { id }, where id is a BigInt.

Argument Type Value
handler function Called with { reason, deadline } when Asterobot wants the script to stop. It may be async.
Property Type Value
reason string Why: "schedule", meaning a time slot of the bot's schedule ended, or handed over to one that plays another package. It's the only reason for now.
deadline Date When Asterobot stops the script anyway.

When a time slot of the bot's schedule ends, or hands over to a time slot that plays another package, Asterobot calls each handler registered at that moment, once. Meanwhile the script carries on as usual: its handlers, sends, trips and actions all keep working. The script counts as done once every handler has returned, or its returned promise has settled. Asterobot then stops the script and disconnects the bot, or plays the next package in the game the bot is still in. It does so at deadline at the latest. The time between the request and deadline is the schedule's Time to finish, from 0 to 30 minutes.

A script that registered no handler is stopped immediately, and so is any script when the time to finish is 0. A handler registered after the request isn't called for it.

import { onStopRequest, session, botInfo, botWarn } from "asterobot:bot";
import { send } from "asterobot:protocol";
import { sleep } from "asterobot:timers";

let stopping = false;
let round = Promise.resolve();

onStopRequest(async ({ deadline }) => {
  stopping = true;
  botInfo(`Asked to stop by ${deadline.toLocaleTimeString()}`);

  // An error escaping a handler stops the script.
  try {
    // The round under way finishes, and no new one starts.
    await round;
  } catch (error) {
    botWarn(String(error));
  }
});

async function loop() {
  while (!stopping) {
    // Stands for one round of work: a fight, a harvest, a trip.
    round = sleep(60_000);
    await round;
  }
}

export default async function behavior(launch) {
  // A new game connection starts with this message. MITM bots never launch
  // with "initial": the Dofus client has already identified their session.
  if (launch.reason === "initial") {
    await send("IdentificationRequest", {
      ticketKey: session.gameToken,
      languageCode: session.language,
    });
  }

  void loop();
}

Return from the handler rather than calling disconnect(): the bot is then stopped and disconnected without an error, as with the Disconnect button.

An error thrown by a handler stops the script, as in any other handler, and the bot is disconnected immediately. If the promise a handler returned rejects, the error is written to the bot's console starting with onStopRequest handler: , and the handler counts as done.

Problem What happens
handler isn't a function onStopRequest() throws a TypeError with onStopRequest handler must be callable
256 handlers are already registered with onStopRequest() onStopRequest() throws a TypeError with maximum behavior handlers reached

While Asterobot reads a package's declarations, onStopRequest() returns a handle as usual, and the handler is never called.

offStopRequest()

offStopRequest(handle) returns undefined. It removes the handler registered with the onStopRequest() call that returned handle. A handle that's already removed, or anything else, does nothing.

botDebug(), botInfo(), botWarn() and botError()

Each takes any number of values and returns undefined:

botInfo("Kamas:", traffic.payload?.kamas);
botWarn("Reply not delivered:", String(error));

The line goes to the bot's Console tab as a Script line, at the level the function names: debug, info, warn or error. Lines of every level show there, debug included. The values are joined with spaces. These functions never throw.

Asterobot turns each value into text on its own, and only strings, numbers and booleans come out the way JavaScript would print them:

Value Printed as
A string As it is
A number 42, 1.5
A BigInt Its digits, without the n: 1500
A boolean true, false
undefined or null <nil>
An array Items separated by spaces, in brackets: [1 2 3]
An object map[ then its properties sorted by name: map[kamas:1500 name:Airelle]
An Error map[], so log String(error) instead

To print an object readably, build the text yourself, for example with a template string. JSON.stringify() throws on any value that holds a BigInt, with the message Do not know how to serialize a BigInt, and message payloads often do.

When you open a bot's Console tab, it starts with the last 200 lines Asterobot kept for that bot.

Code at the top level of a module also runs while Asterobot reads the package's declarations. What these functions write at that moment is thrown away.

To write to Asterobot's own log instead, use asterobot:console. Logging and debugging compares the two and suggests a way to track down a problem.

Compte

Navigation

Recherche

Recherche

Configurer les notifications push du navigateur

Chrome (Android)
  1. Appuyez sur l'icône de cadenas à côté de la barre d'adresse.
  2. Tap Autorisations → Notifications.
  3. Ajustez vos préférences.
Chrome (Desktop)
  1. Cliquez sur l'icône représentant un cadenas dans la barre d'adresse..
  2. Select Paramètres du site.
  3. Find Notifications et ajustez vos préférences.