This script keeps track of what the character carries and writes each change to the bot's console: kamas spent or earned, pods going up or down, and the new quantity of a stack. It listens to the same messages as the bot's Inventory tab, and shows how a script keeps what it learned from one message to the next.
You need a MITM bot, as Your first bot sets up, and to know how to create a package and run it, as in Create a package.
The messages it follows
| Message | What it carries | Fields the script reads |
|---|---|---|
InventoryContentEvent |
The whole inventory, sent once when the character enters the game | kamas, and objects, with one entry per stack whose item holds the stack's uid, gid and quantity |
KamasUpdateEvent |
The character's kamas | quantity, the new balance |
InventoryWeightEvent |
The character's pods | inventoryWeight, what the inventory weighs, and weightMax, the most it can weigh before the character is overloaded |
ObjectsQuantityEvent |
New quantities for stacks the character already has | object, a list of entries with objectUid and quantity |
ObjectQuantityEvent |
A new quantity for one stack | object, one entry with objectUid and quantity |
These are the messages behind the Live inventory switch of the bot's Inventory tab. That switch only changes what Asteroboard shows: the script receives the messages whether it's on or not.
The script
Create a package named inventory-watcher, replace the contents of its index.js with this code, and save:
import { session, botInfo, botWarn } from "asterobot:bot";
import { on, send } from "asterobot:protocol";
// What the script knows about the inventory. It all starts empty at each
// start, and fills in as the game reports things.
let kamas; // a BigInt, once known
let pods; // { current, max }, once known
const stacks = new Map(); // uid => { gid, quantity }
function signed(change) {
return change >= 0 ? `+${change}` : `${change}`;
}
function updateStack(uid, quantity) {
const stack = stacks.get(uid);
if (!stack) {
// The last full inventory didn't have this stack, so there's nothing to compare.
botInfo(`Stack ${uid}: ${quantity}`);
return;
}
const change = quantity - stack.quantity;
stack.quantity = quantity;
botInfo(`Stack ${uid} (item ${stack.gid}): ${quantity} (${signed(change)})`);
}
export default async function behavior(launch) {
on("InventoryContentEvent", (traffic) => {
// A message Asterobot couldn't decode has no payload, and an error
// thrown in a handler stops the whole script.
if (!traffic.payload) return;
kamas = traffic.payload.kamas;
stacks.clear();
for (const { item } of traffic.payload.objects) {
// item holds a message, so it's only there when the game set it.
if (item) stacks.set(item.uid, { gid: item.gid, quantity: item.quantity });
}
botInfo(`Inventory received: ${stacks.size} stacks, ${kamas} kamas`);
});
on("KamasUpdateEvent", (traffic) => {
const balance = traffic.payload?.quantity;
if (balance === undefined) return;
// The game sends the new balance, not what changed.
const change = kamas === undefined ? "" : ` (${signed(balance - kamas)})`;
kamas = balance;
botInfo(`Kamas: ${balance}${change}`);
});
on("InventoryWeightEvent", (traffic) => {
if (!traffic.payload) return;
const { inventoryWeight: current, weightMax: max } = traffic.payload;
// Not worth a line when neither number moved.
if (pods && pods.current === current && pods.max === max) return;
const change = pods ? ` (${signed(current - pods.current)})` : "";
pods = { current, max };
if (current > max) {
botWarn(`Pods: ${current} / ${max}${change}, overloaded`);
} else {
botInfo(`Pods: ${current} / ${max}${change}`);
}
});
on("ObjectsQuantityEvent", (traffic) => {
for (const record of traffic.payload?.object ?? []) {
updateStack(record.objectUid, record.quantity);
}
});
on("ObjectQuantityEvent", (traffic) => {
const record = traffic.payload?.object;
if (record) updateStack(record.objectUid, record.quantity);
});
// 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,
});
}
}
How it works
What the script remembers
kamas, pods and stacks sit at the top of the module, outside the handlers, so every handler reads and changes the same values. They start empty at each start of the script. Messages that arrived before the start are never delivered, and the game only sends the whole inventory as the character enters the game, so until then the script knows nothing about the stacks. Play, stop and reload explains what a new start keeps.
Totals, not changes
Each of these messages carries a new total: the new balance, the new weight, the stack's new quantity. To say what changed, the script keeps the previous value and subtracts it. The first value it gets has nothing to be compared with, so its line has no change in brackets.
Kamas are BigInt values
kamas, and quantity in KamasUpdateEvent, are 64-bit fields, so they read as BigInt values such as 125000n. Subtracting a BigInt from another works, and so does comparing a BigInt with 0, which is why signed() serves kamas and pods alike. Arithmetic that mixes a BigInt with a regular number throws a TypeError, though: kamas - 500 fails where kamas - 500n works. Pods and stack quantities are 32-bit fields, and read as regular numbers. Payloads has the rules.
Logging a BigInt writes its digits, without the n.
uid and gid
A stack has two ids. uid names this very stack, and quantity updates point to it as objectUid. gid names the item the stack is made of, and every stack of that item shares it. That's why stacks is keyed by uid. The script keeps the gid anyway: the next tutorial turns it into the item's name.
In InventoryContentEvent, each entry of objects wraps its stack. Besides item, it says where the item is equipped and whether it's a favourite. item holds a message, which is only in the payload when the game set it, so the script checks it before reading it.
What it doesn't see
A stack the character didn't have before, such as a new loot, doesn't come through these messages: Asterobot has no readable name yet for the messages the game uses for it. A stack that leaves the inventory completely can go unnoticed too. The script learns about both with the next InventoryContentEvent, when the character enters the game again. Until then, a quantity update for a stack it doesn't know only gives the uid and the new quantity.
Run it
The whole inventory only arrives as the character enters the game, so the script has to be running by then.
- Start waiting on your MITM bot (man-in-the-middle: the bot relays the game session of the Dofus client you play), start Dofus and sign in. Stop at the character selection screen. If your character is already in the game, go back to that screen.
- Once the bot's header shows Connected to game server, pick the bot in the editor and click Run.
- On the bot's page, open Console and keep only Script in Sources.
- Choose your character in Dofus. A line such as
Inventory received: 57 stacks, 125000 kamasappears. - Play as usual. When your kamas, your pods or the quantity of one of your stacks change, a line appears.
The lines look like this:
Inventory received: 57 stacks, 125000 kamas
Pods: 1250 / 3000
Kamas: 124500 (-500)
Pods: 1262 / 3000 (+12)
Stack 812734 (item 289): 43 (+3)
The script only reads. Apart from the identification on a Full socket bot, it sends nothing to the game.
Ideas to take it further
- Names instead of ids. Item names from game data turns each
gidinto the item's name. - A warning before the pods are full. Read settings builds one, with a setting for the limit.
- Kamas earned since the start. Keep the first balance the script receives, and add a button that writes the difference: see Declare actions.
- Stop a Full socket bot when its inventory is full. Sessions shows
disconnect()on the same message, and why a MITM bot must never call it.
Aucun avis à afficher.