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.

Sending

Send messages to the game server or to the Dofus client with send(), use request() for the few requests that get a response, and understand order, timeouts and errors.

A script acts in the game by sending the messages the Dofus client would send. send() does that for almost everything, request() is for the few requests the game answers with a response, and the to option turns a message around, towards the client. Send messages builds a first example.

Warning

Everything a script sends goes out as the bot's character. On a MITM bot (man-in-the-middle: the bot relays the game session of the Dofus client you play), that's your character, in your game, and other players see what it says.

Tip

For chat messages, asterobot:chat is the safer way: a ChatChannelMessageRequest sent with send() goes around the bot's chat guard, which waits out a channel's cooldown and backs off when the game warns about spam.

send()

await send(type, payload, options);
Argument What to pass
type The message's readable name, such as "ChatChannelMessageRequest".
payload An object with the fields to send. For a message without fields, pass {}: leaving the payload out is an error. Payloads explains how values convert.
options Optional. to, "server" or "client", and correlated, a boolean, as in { to: "client" }. timeout is accepted and ignored.

send() builds the message, puts it in line to go out, and resolves at once. It doesn't wait for an answer: Waiting shows how to wait for one. A wait() started right after await send() always gets the answer.

The payload is checked first. When it doesn't fit the message, the promise rejects and nothing goes out. A message that passes the checks but can't be written later stops the script: with Game session closed when the connection closed, or with send and the message's name in the error otherwise.

The message goes to the game server as a request with uid -1. onTraffic() handlers receive it as an outbound message, and the Network tool shows it when it's selected. Interceptors are never asked about it, which is why an interceptor can call send().

Messages sent with a uid

A few messages leave the Dofus client with a uid of their own although nothing ever answers them: ClientIdRequest, ClientChallengeInitRequest and ClientChallengeProofRequest, the three answers to the server's proof of identity, and BasicLatencyStatsRequest. In the Network tool they show a uid in Traffic, and no response with that uid ever comes back In.

To send one the same way, pass correlated: true:

await send("BasicLatencyStatsRequest", { latency: 31 }, { correlated: true });

The message takes the next uid, the one request() would have used, and the promise resolves once it's in line, like any send(). request() would wait its whole timeout for a response that never comes.

The numbers follow the Dofus client's. On every game connection, the client's first numbered request is #2, even after changing server, and so is a Full socket bot's. Each send() with correlated: true and each request() then takes the next one.

Use correlated only for the messages the client numbers: every other message goes out with -1, as the client sends it. correlated can't be combined with to: "client", since a message toward the client is delivered as an event, and events have no uid.

Order

Messages leave in the order your script calls send() and request(), awaited or not, on every PC:

// ContextReadyRequest first, every time.
send("ContextReadyRequest", { mapId });
send("MapInformationRequest", { mapId });

disconnect() and the movement functions of asterobot:movement wait their turn too: they start once the messages sent before them have left. And messages your script sent still leave if it stops, whether by Stop, a reload or an error.

Sending to the client

With { to: "client" }, a message travels the other way: it's framed as an event from the server and delivered inbound.

await send(
  "ChatChannelMessageEvent",
  { channel: "PARTY", senderName: "Tester", content: "!say hello" },
  { to: "client" },
);

Where it goes depends on the bot:

Bot Where the message goes
Full socket To your script only. The bot is the client, so there's no one else to deliver it to, and nothing reaches the server.
MITM To your Dofus client, which acts on it as if the server had sent it, and to your script.

Either way, on() handlers and waits receive it as an inbound event with uid -1. That makes it a handy way to try a handler without waiting for the game: the example above triggers the !say handler from Waiting, whose reply then really goes to the server. On a MITM bot, your Dofus client believes whatever you send it this way, so only send it messages the server could really send.

to accepts "server", the default, or "client". The Network tool's Send a game message offers the same choice as To the server and To the client.

request()

const response = await request(type, payload, options);

request() sends a message with a uid of its own, then resolves with the traffic object of the response that carries the same uid. It takes the same arguments as send(). to and correlated are accepted but ignored: a response can only come from the server, and a request always carries a uid.

Only a few Dofus requests are answered this way. Most are answered with an event, or not at all, and for those request() waits for its whole timeout, 30 seconds unless you pass another one, then rejects with context deadline exceeded. Use send() and wait() for them.

To know whether a request gets a response, watch your own Dofus client in the Network tool on a MITM bot. A request the client expects an answer to shows a uid in Traffic, and its response comes back In with the same uid. Finding messages explains the rows.

A request sent without a uid can still be answered with a response, which then carries -1 too. GuildWeeklyNextResetRequest is one: the Dofus client sends it plain, and GuildWeeklyNextResetResponse comes back with uid -1. request() can't wait for such a response, since it matches responses by uid. Send the request with send(), and wait() for the response by its name.

The response also reaches your handlers and waits, like any other inbound message. On a MITM bot, your Dofus client never receives it, since it didn't ask for it.

Timeouts

Function Default What the timeout covers
send() None Nothing: send() resolves as soon as its message is in line, and ignores timeout.
request() 30 seconds From the call, sending the message and receiving its response.
wait() None Waiting for a matching message.

Pass another timeout in the options, in milliseconds: { timeout: 5000 }. It must be greater than zero.

Messages that aren't sent

  • On a MITM bot, a script can't send IdentificationRequest to the server, because your Dofus client has already identified the session. send() resolves without sending anything, and the bot's console shows Refused to forward a message that would re-authenticate a relayed session. request() rejects right away instead, with mitm relay refuses to identify: this Game session was already authenticated by the real DOFUS client.
  • A message without a readable name can't be sent: send() and request() only take names Asterobot knows.
  • If Request, Event, Response or Message shows up among the message names, don't send it: those wrap every other message, and sending one rejects with game envelope cannot be sent as a payload: followed by the name.
  • A script can't send bytes of its own as a new message. Only an interceptor can put bytes on the wire, in place of a message going through: see Intercepting.

Errors

send() and request() never throw: every problem rejects the promise.

Error Cause
protocol type must be a non-empty semantic message name The name is missing, empty, or not a string.
semantic protobuf message is not mapped: ChatChanelMessageRequest Asterobot has no message by that name: a typo, a name that changed, or a message without a readable name.
ChatChannelMessageRequest: expected an object The payload is missing.
ChatChannelMessageRequest.contnt: unknown protobuf field The message has no such field. Payloads lists every payload error.
protocol options must be an object The options aren't an object.
unknown protocol option "retries" The options have a key other than timeout, to and correlated.
send option "correlated" must be a boolean correlated isn't true or false.
send option "correlated" cannot be combined with to: "client": ... Both were passed. A message toward the client has no uid.
timeout/milliseconds must be a finite positive Number timeout is 0, negative, or not a number.
send option "to" must be "server" or "client" to isn't a string.
unknown send target "clients": expected "server" or "client" to is a string other than "server" and "client".
behavior resource limit exceeded: maximum pending operations reached request() only: 32 operations are already in progress. Requests, game data lookups and movements count together; sends don't.
behavior resource limit exceeded: more than 8 MB are waiting to be sent, so the connection isn't taking them More than 8 MB of messages are waiting to be written: the connection has stopped taking them.
context deadline exceeded request() got no response within its timeout.

A complete example

Party members can ask this script for the character's pods with !pods:

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

export default async function behavior(launch) {
  let pods = "unknown";

  on("InventoryWeightEvent", (traffic) => {
    if (!traffic.payload) return;
    pods = `${traffic.payload.inventoryWeight} / ${traffic.payload.weightMax}`;
  });

  on("ChatChannelMessageEvent", async (traffic) => {
    // Only the exact command: our own answer comes back to us too.
    if (traffic.payload?.channel !== "PARTY" || traffic.payload.content !== "!pods") return;

    try {
      await send("ChatChannelMessageRequest", { channel: "PARTY", content: `Pods: ${pods}` });
    } catch (error) {
      // An error that escapes a handler stops the whole script.
      botWarn("Couldn't answer:", String(error));
    }
  });

  // 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,
    });
  }
}

The answer says unknown until the game sends an InventoryWeightEvent after the script started: the script only knows what it has received.

Next, Intercepting covers changing messages on their way through, and Payloads every rule for the values you send.

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.