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.
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. timeout, in milliseconds, and to, "server" or "client", as in { timeout: 5000, to: "client" }. |
send() builds the message, sends it, and resolves once it's sent. It doesn't wait for an answer: Waiting shows how to wait for one.
The payload is checked first. When it doesn't fit the message, the promise rejects and nothing goes out.
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().
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 is accepted but ignored: a response can only come from the server.
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. A request without a uid gets no response. Finding messages explains the rows.
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() |
30 seconds | Sending the message. |
request() |
30 seconds | 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
IdentificationRequestto the server, because your Dofus client has already identified the session.send()resolves without sending anything, and the bot's console showsRefused to forward a message that would re-authenticate a relayed session.request()rejects right away instead, withmitm 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()andrequest()only take names Asterobot knows. - If
Request,Event,ResponseorMessageshows up among the message names, don't send it: those wrap every other message, and sending one rejects withgame 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 and to. |
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 |
32 operations are already in progress. Sends, requests and game data lookups count together. |
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.
Aucun avis à afficher.