Send a message with send(), wait for the game's answer with wait(), know when request() applies, and handle timeouts.
Reading the game is half of what a script does. Here the chat script learns to answer: when someone says !hello, the bot replies in chat and checks that its reply went through.
Warning
On a MITM bot (man-in-the-middle: the bot relays the game session of the Dofus client you play), everything the script sends goes out as your character. The reply on this page is a real chat message that other players can see.
Tip
This page sends the chat message itself, to learn the protocol. In a real package, asterobot:chat's say() sends it through the bot's chat guard, which waits out a channel's cooldown and keeps to the game's limits.
Try the message by hand first
Before writing code, send the message from the Network tool. If it works there, you know the payload is right.
- On the bot's page, open Tools > Network > Send a game message.
- Keep To the server selected, and pick
ChatChannelMessageRequestin the list of messages. - Type some text in
contentand leave the other fields as they are. Empty fields aren't sent, and achannelleft out meansGLOBAL, the general channel. - Check the payload under This is what will be sent., then click Send ChatChannelMessageRequest.
Your line appears in the game. If ChatChannelMessageEvent is selected in Settings, Traffic also shows your request going Out and your line coming back In.
send()
await send("ChatChannelMessageRequest", { channel: "GLOBAL", content: "Hello" });
send() takes a message name and a payload holding the message's fields. It builds the message and puts it in line to go to the game server, and its promise resolves at once: it doesn't wait for an answer. Messages leave in the order you send them.
The promise rejects when the message can't be built:
| Problem | Error |
|---|---|
| A field the message doesn't have | ChatChannelMessageRequest.contnt: unknown protobuf field |
| A value of the wrong type | ChatChannelMessageRequest.content: expected string |
| A channel name that doesn't exist | ChatChannelMessageRequest.channel: unknown enum symbol "GENERAL" |
| A message name Asterobot can't send | semantic protobuf message is not mapped: MyMessageRequest |
channel takes a channel's name as a string, such as "GLOBAL", "GUILD" or "PARTY", or the channel's number. The Dofus protocol reference lists every channel.
Wait for the answer
The game doesn't answer a chat line with a response. It sends the line to everyone on the channel as a ChatChannelMessageEvent, you included. To know your line went through, wait for that event.
Send, then wait:
await send("ChatChannelMessageRequest", { channel: "GLOBAL", content: "Hello" });
await wait(
(traffic) => traffic.type === "ChatChannelMessageEvent" && traffic.payload?.content === "Hello",
{ timeout: 5000 },
);
A wait only sees messages the bot handles after wait() is called. send() resolves before any answer can be handled, so a wait started right after await send() always gets the answer, however fast it comes. Waiting explains more.
wait() resolves with the next inbound message that matches, and you choose what matches: a message name, or a function that returns true for the message you want. The function above checks the name and the text, so another player's line doesn't count.
wait() has no timeout of its own. A wait that never matches stays open until the script stops, so always pass { timeout }, in milliseconds. When the time runs out, the promise rejects with protocol wait timed out.
request()
request() sends a message and resolves with the game's response to it. Only a handful of Dofus requests get a response, matched to them by uid, and none of them is something a first script needs to send. For every other message nothing comes back: request() waits until its timeout, 30 seconds unless you pass another one, and then rejects with context deadline exceeded.
So for almost everything, wait for the event the game answers with while you send(), as this page does. Sending covers request(), and the to option that sends a message to the Dofus client instead of the server.
Timeouts
| Function | Default timeout | To change it |
|---|---|---|
send() |
None needed: it resolves as soon as the message is in line | |
request() |
30 seconds | { timeout: milliseconds } as the third argument |
wait() |
None: it waits until the script stops | { timeout: milliseconds } as the second argument |
A timeout must be a positive number of milliseconds. Anything else rejects with timeout/milliseconds must be a finite positive Number.
The complete script
import { session, botInfo, botWarn } from "asterobot:bot";
import { on, send, wait } from "asterobot:protocol";
const TRIGGER = "!hello";
const REPLY = "Hello from my first script";
export default async function behavior(launch) {
on("ChatChannelMessageEvent", async (traffic) => {
// Only the exact trigger: the game also sends our own reply back to us,
// and answering that echo would start a loop.
if (traffic.payload?.content !== TRIGGER) return;
try {
await send("ChatChannelMessageRequest", { channel: "GLOBAL", content: REPLY });
// Started right after the send, so even a quick echo isn't missed.
await wait(
(echo) => echo.type === "ChatChannelMessageEvent" && echo.payload?.content === REPLY,
{ timeout: 5000 },
);
botInfo("Reply delivered");
} catch (error) {
// An error that escapes a handler stops the whole script.
botWarn("Reply not delivered:", 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 handler is async so that it can await. Handlers don't wait for each other: while this one waits for the echo, the bot keeps handling other messages, the echo included, which is how wait() gets to see it.
The try/catch keeps a failed reply from ending the script, and String(error) turns the error into text the console can print.
Anyone who says !hello on a channel your character receives gets an answer in the general channel, so stop the script when you're done testing.
Test it
- Run the package on your MITM bot.
- Say
!helloin the Dofus chat. - The bot answers in the general channel, and its Console shows
Reply delivered.
If the console shows Reply not delivered: followed by a reason, select ChatChannelMessageRequest and ChatChannelMessageEvent in the Network tool's Settings, try again, and check in Traffic whether the request went out and whether the line came back.
Next, Add a setting lets people change the trigger and the reply without editing your code. Waiting covers every way to wait for a message.