Every other function in this chapter observes messages that have already gone through. An interceptor is asked first, and its answer decides whether a message continues, and in what form. On a MITM bot (man-in-the-middle: the bot relays the game session of the Dofus client you play), that decides what your Dofus client and the game server receive, so read On a MITM bot before using one there.
intercept()
intercept(selector, handler); // asked about the messages the selector matches
intercept(handler); // asked about every message
intercept() registers handler and returns a handle to pass to off(). The selector is the same as for on(): a message name, "*", or a function. Listening describes them. Interceptors count towards the 256 handlers a script can register with on() and onTraffic(), and registering one throws the same errors, with protocol.intercept handler must be callable when the handler isn't a function.
The handler receives the message's traffic object and returns a decision:
| Return | What happens to the message |
|---|---|
Nothing, undefined, or any other value |
No decision. The next interceptor is asked, and when none decides, the message continues unchanged. |
"drop" |
The message stops here. |
{ payload: { ... } } |
The message continues with these fields instead of its own. See Changing a payload. |
{ raw: bytes } |
The message is replaced by these bytes. See Raw replacements. |
What "continues" covers depends on the bot. On a Full socket bot, the message reaches your script's handlers and waits, and the bot's page in Asteroboard: the Network tool, live chat and live inventory. On a MITM bot, it also reaches the other side, your Dofus client or the game server. A dropped message reaches none of them, and keeps its sequence number, which leaves a gap.
What an interceptor is asked about
| Bot | Messages interceptors decide about |
|---|---|
| Full socket | Messages from the game server. |
| MITM | Messages from the game server to your Dofus client, and from your Dofus client to the server. |
Interceptors are never asked about:
- messages a script sends with
send()orrequest(), towards the server or the client, which is why an interceptor can callsend(); - messages someone sends from the Network tool's Send a game message;
- on a MITM bot, the responses to your script's own
request()calls, and the first message of the session, your client'sIdentificationRequest.
Those messages still reach onTraffic() and the Network tool as usual.
Answer within 50 ms
While an interceptor is registered, each message it could decide about waits for your script's answer, for 50 ms at most. After that, the message continues unchanged, and an answer that comes later is ignored. The handler still runs to its end, so anything it does besides answering, such as writing to the console, still happens.
The 50 ms start when the message arrives, not when your handler is called. Interceptors run in the same queue as the rest of your script: when a handler or other code is busy, the time can run out before your interceptor is even asked. Nothing reports a late answer.
Three things follow:
- Keep interceptors short. Do the quick test in the interceptor, and anything slow somewhere else, such as an
on()handler. - An interceptor must answer synchronously. An
asyncfunction returns a promise, and a promise isn't a decision, so the message always continues unchanged. An error inside anasyncinterceptor also becomes an unhandled rejection, which stops the script. - Once a script has an interceptor, every message of the kinds above waits for the script, even the ones its selector doesn't match, because the selector is checked by the script too. Remove interceptors you no longer need with
off().
The first decisive answer wins
Interceptors are asked in the order they were registered. The first one that returns "drop", { payload } or { raw } decides, and the ones after it aren't asked. An interceptor whose selector doesn't match isn't asked, and doesn't count as an answer.
An interceptor that fails doesn't decide either, and the next one is asked. That covers a handler that throws, a function selector that throws, and a { payload } that can't be built. When an object holds both raw and payload, raw wins.
Changing a payload
Return { payload } to let a message continue with other values. This interceptor defangs links in the chat lines your handlers receive, and on a MITM bot, the lines your Dofus client shows:
intercept("ChatChannelMessageEvent", (traffic) => {
const content = traffic.payload?.content;
if (typeof content !== "string" || !content.includes("http")) return undefined;
// The spread keeps every other field: the replacement is built from this object alone.
return { payload: { ...traffic.payload, content: content.replaceAll("http", "hxxp") } };
});
Rules for { payload }:
- The message is rebuilt from your object alone. A field you leave out is sent unset, or at its zero value, so start from
...traffic.payload. Bytes of the original that Asterobot's definitions don't describe can't be carried over. - The message keeps its type, its
kind, itsuidand its wire name. To send a different message, drop this one and callsend(). - A message without a readable name can't get a new payload.
- Your handlers and the Network tool receive the replacement, not the original.
When the replacement can't be built, the original message continues unchanged, and the bot's console says why, as in build replacement payload for ChatChannelMessageEvent: ChatChannelMessageEvent.contnt: unknown protobuf field. For a message without a readable name, the line starts with cannot replace the payload of followed by its wire name.
Dropping a message
Return "drop" to stop a message. On a MITM bot, this script turns the chat lines you type starting with ! into commands for the script: they never reach the server, so other players don't see them.
import { session, botInfo } from "asterobot:bot";
import { intercept, send } from "asterobot:protocol";
export default async function behavior(launch) {
intercept("ChatChannelMessageRequest", (traffic) => {
const content = traffic.payload?.content;
if (typeof content !== "string" || !content.startsWith("!")) return undefined;
// Writing a line is quick enough to do before answering.
botInfo("Command:", content);
return "drop";
});
// 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 a Full socket bot, this interceptor is never asked anything: every message going to the server there comes from a script, and those aren't intercepted.
On a Full socket bot, dropping a message keeps it from your own handlers, which makes an interceptor a filter. Dropping a response that a request() is waiting for makes that request() time out.
Raw replacements
{ raw } replaces a message with the bytes you give, as a Uint8Array or an ArrayBuffer. Any other value doesn't count as an answer.
The bytes replace the whole message as it travels: the part that states the message's kind, uid and wire name, and the fields inside it. That's more than traffic.rawAny, which only holds the fields, and Asterobot has no function that builds those bytes for you. { raw } is only for a message nothing else can handle, when you know that format yourself.
When Asterobot can't read your bytes as a message, your handlers receive the original message. On a MITM bot, the bytes still go to the other side as they are.
Warning
On a MITM bot, a wrong { raw } sends your Dofus client or the game server bytes they can't read.
When an interceptor fails
Unlike an on() handler, an interceptor that throws doesn't stop the script, unless it's async (see Answer within 50 ms). The message continues unchanged, and the bot's console shows an error line from the script:
| The line starts with | Cause |
|---|---|
interceptor failed: |
The handler threw an error, or ran for more than 250 ms. |
interceptor selector failed: |
The function selector threw an error. |
build replacement payload for |
A field or value in { payload } doesn't fit the message. |
cannot replace the payload of |
{ payload } was returned for a message without a readable name. |
An interceptor that fails on every message writes one of these lines for each message, so fix it before it fills the console.
A late answer, a script too busy to answer in time, and an async interceptor aren't reported anywhere: the message continues unchanged.
On a MITM bot
Caution
On a MITM bot, an interceptor acts on the game of the person playing. A dropped message from the server never reaches their Dofus client, a dropped message from the client never reaches the server, and a replacement reaches the other side exactly as you built it. What their client shows and what the server knows can then differ.
While an interceptor is registered, every message of the session waits for your script, up to 50 ms each. A script that's slow to answer is felt as lag in the game.
If the script stops or runs into trouble, messages continue unchanged: a failing interceptor can't block a session.
What interceptors are good for
- Keeping noise out of your own handlers on a Full socket bot, with
"drop". - Turning chat lines into commands on a MITM bot, as above.
- Changing what your Dofus client receives on a MITM bot, with
{ payload }. - Replacing a message with a different one: return
"drop"andsend()the new message from the interceptor.
Next, Payloads lists the rules a { payload } follows, and Sessions what else changes on a MITM bot.
Aucun avis à afficher.