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.

Smooth

Administrateurs
  1. Smooth a posté un record dans Tutoriels
    Messages name items by number. This script turns those numbers into the names players know, with asterobot:gamedata: when a stack's quantity changes, the console reads Blé: 43 (+3) rather than Stack 812734 (item 289): 43 (+3). A List inventory button also writes the whole inventory by name. It starts from the stacks of the Inventory watcher and leaves kamas and pods out. Read that page first if uid and gid are new to you. Find the names in the game data InventoryContentEvent gives each stack a gid: the id of the item the stack is made of. The item's name is in the game data of the bot's game version, in two tables: items has one row per item. Its id column holds the id the game uses in its messages, the gid. Its _id column is the key Asterobot gives the row, which isn't always the same number, as Lookups explains. _names holds the names of records, one row per name and per language. record_table and record_id point to the record by its key, language holds the language code, and ordinal 0 marks the record's main name. So a query finds the items by id, and joins their names on _id. Try it in the SQL tab before writing any code: On the bot's Inventory tab, with Live inventory on, open a row. Its details show the item's id after item. In Game Manager, open the bot's game version, go to its Game data tab, then to SQL. Type this query, with ids from your inventory in place of 289, 290, and click Run: SELECT i.id, n.name FROM items i JOIN _names n ON n.record_table = 'items' AND n.record_id = i._id AND n.language = 'fr' AND n.ordinal = 0 WHERE i.id IN (289, 290) Each item comes back on its own row, with its French name. SQL queries describes the tab. The script Create a package named item-names, replace the contents of its index.js with this code, and save: import { session, botInfo, botWarn } from "asterobot:bot"; import { query } from "asterobot:gamedata"; import { on, send } from "asterobot:protocol"; // A query returns 5,000 rows at most, so ids are looked up in batches. const BATCH_SIZE = 500; function namesQuery(count) { return ` SELECT i.id, n.name FROM items i JOIN _names n ON n.record_table = 'items' AND n.record_id = i._id AND n.language = ? AND n.ordinal = 0 WHERE i.id IN (${Array(count).fill("?").join(", ")})`; } const stacks = new Map(); // uid => { gid, quantity } const names = new Map(); // gid => name, kept for the whole run async function loadNames(gids) { const missing = [...new Set(gids)].filter((gid) => !names.has(gid)); for (let start = 0; start < missing.length; start += BATCH_SIZE) { const batch = missing.slice(start, start + BATCH_SIZE); const result = await query(namesQuery(batch.length), [session.language, ...batch], { limit: batch.length, }); // undefined rather than a result: this game version has no game data. if (result === undefined) throw new Error("No game data for this bot's game version"); for (const [gid, name] of result.rows) names.set(gid, name); } } function nameOf(gid) { return names.get(gid) ?? `item #${gid}`; } function updateStack(uid, quantity) { const stack = stacks.get(uid); // Only stacks from the last full inventory have an item to name. if (!stack) return; const change = quantity - stack.quantity; stack.quantity = quantity; botInfo(`${nameOf(stack.gid)}: ${quantity} (${change >= 0 ? "+" : ""}${change})`); } /** @type {BehaviorActions} */ export const actions = { listInventory: { label: "List inventory", description: "Writes every stack the character carries to the bot's console.", run() { if (stacks.size === 0) { botInfo("The game hasn't sent the inventory since the script started."); return; } const lines = [...stacks.values()].map((stack) => `${nameOf(stack.gid)} x${stack.quantity}`); for (const line of lines.sort()) botInfo(line); }, }, }; export default async function behavior(launch) { on("InventoryContentEvent", async (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; // Filled before the lookup, so quantity updates arriving meanwhile find their stack. stacks.clear(); for (const { item } of traffic.payload.objects) { if (item) stacks.set(item.uid, { gid: item.gid, quantity: item.quantity }); } try { await loadNames([...stacks.values()].map((stack) => stack.gid)); const named = [...stacks.values()].filter((stack) => names.has(stack.gid)).length; botInfo(`Inventory received: ${stacks.size} stacks, ${named} of them named`); } catch (error) { // An error that escapes a handler stops the whole script. botWarn("Couldn't look up item names:", String(error)); } }); 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 One query for the whole inventory Looking items up one at a time would take a find() call per item. A single query() with IN (?, ?, ...) names the whole inventory at once, and like every game data call that returns a promise, it does its work outside your code while the bot keeps handling messages. Game data overview explains the difference. A few details of loadNames(): namesQuery() writes one ? for each id. The values go in the params array in the order of the ? in the SQL: the language first, then the ids. Parameters keep their type, so the ids are compared as numbers. query() returns 200 rows unless you pass a limit, and 5,000 at most. Each batch asks for as many rows as it has ids, and BATCH_SIZE keeps every batch far below that maximum. Rows are arrays in the order of the SELECT, so const [gid, name] reads the id first and the name second. query() resolves to undefined when the bot's game version has no extracted game data. The script throws then, and the handler's catch writes why on the console. Names in the script's language The query asks for names in session.language. For every start made from Asteroboard that's fr, whatever language the bot itself is set to, so the names come in French. Game data overview explains why. To get another language, pass its code instead of session.language. Stacks first, names second The InventoryContentEvent handler fills stacks before it awaits the query. Handlers don't wait for each other, so a quantity update can arrive while the names are still being looked up, and it still finds its stack. Until the query returns, such a stack is written as item # followed by its gid, which is also how an item the game data doesn't know is written. names lasts for the whole run. When the inventory arrives again, only items without a name yet are looked up. Like every variable, it starts empty at the next start. The List inventory button export const actions puts a List inventory button in the package's section of the bot's Settings tab. Its run reads the same stacks and names as the handlers, and writes one line per stack. sort() compares characters one by one, so capitals come before small letters and names starting with an accented letter come after z. Asterobot keeps the last 200 lines of each bot's console, so a large inventory pushes older lines out of what it keeps. Declare actions covers buttons. Run it Check that the bot's game version has game data: in Game Manager, its row should show Ready for bot. A MITM bot (man-in-the-middle: the bot relays the game session of the Dofus client you play) can run without it, and the script then writes Couldn't look up item names: Error: No game data for this bot's game version. Extracting the data while the script runs isn't enough: start the script again afterwards. As for the Inventory watcher, start waiting on your MITM bot, start Dofus and stop at the character selection screen. Once the bot shows Connected to game server, pick it in the editor and click Run. On the bot's Console tab, keep only Script in Sources, then choose your character in Dofus. A line such as Inventory received: 57 stacks, 57 of them named appears. Play. When the quantity of one of your stacks changes, a line such as Blé: 43 (+3) appears. Open the bot's Settings tab, then Package settings, and click List inventory under Actions. A toast says Action started, then another one says listInventory finished, and the console lists your stacks by name. Ideas to take it further Another language. _names has rows for each language the game data holds. Run SELECT DISTINCT language FROM _names in the SQL tab to see which, then add a string setting with choices and pass values.language to the query instead of session.language. The item's type. Join item_types on item_types.id = items.type_id, then its name through _names in the same way. The SQL tab's Examples on items do something similar. Watch a few items. Add a list setting of item names, and write a warning when one of those stacks reaches a quantity you choose. From a name to an id. search("Blé", { table: "items" }) finds items by name. Each result's id is the row's key: read the row with record() to get its id column, the one messages use. See Lookups. The Dofus protocol reference lists the other messages you can build on, and Game data overview everything else a script can read.
  2. Smooth a posté un record dans Tutoriels
    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 kamas appears. 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 gid into 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.
  3. Smooth a posté un record dans Tutoriels
    This script writes each chat line the bot receives to the bot's Console tab, as [GUILD] Airelle: anyone up for a dungeon?. It goes further than the handler in React to messages: package settings let people pause it, keep only the channels they choose and leave out some players, their own character included, and staff lines stand out as warnings. The page assumes you've already created a package and run it on a bot. If you haven't, start with Create a package and Identify and run. The script In Library Manager, click New package and name it chat-logger. Replace the contents of index.js with the code below, and save. import { session, botInfo, botWarn } from "asterobot:bot"; import { values } from "asterobot:parameters"; import { on, send } from "asterobot:protocol"; /** @type {BehaviorParameters} */ export const parameters = { enabled: { type: "bool", label: "Log chat", default: true, }, channels: { type: "array", of: "string", label: "Channels", description: "Channel names to log, such as GUILD or PARTY. Leave it empty to log every channel.", }, ignored: { type: "array", of: "string", label: "Ignored characters", description: "Character names whose lines aren't logged. Add your own to leave out what you say.", }, }; export default async function behavior(launch) { on("ChatChannelMessageEvent", (traffic) => { // A message Asterobot couldn't decode has no payload, and an error // thrown in a handler stops the whole script. if (!traffic.payload || !values.enabled) return; const { senderName, content, fromAdmin } = traffic.payload; // A channel Asterobot has no name for reads as a number. const channel = String(traffic.payload.channel); // Read on every line, so an applied change counts from the next one. const channels = values.channels.map((name) => name.trim().toUpperCase()); if (channels.length > 0 && !channels.includes(channel)) return; const sender = senderName.toLowerCase(); if (values.ignored.some((name) => name.trim().toLowerCase() === sender)) return; const line = `[${channel}] ${senderName}: ${content}`; // Staff lines are rare and usually matter, so they show as warnings. if (fromAdmin) { botWarn(line); } else { botInfo(line); } }); // 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 The settings export const parameters declares three settings. They show on the bot's Settings tab, under Package settings, in the chat-logger section: Setting Type On the bot's page Default enabled bool Log chat, a switch On channels array of string Channels, a list with Add under its rows Empty ignored array of string Ignored characters, a list with Add under its rows Empty A list can't offer a drop-down, because choices only works on string and int settings, so people type the names themselves. The script forgives the usual slips: it trims spaces, compares channel names in capitals, and ignores upper and lower case in character names. Declare settings covers every field used here. Reading the settings on every line The handler reads values.enabled, values.channels and values.ignored each time a chat line arrives. values always holds what was last applied on the bot's page, so a change counts from the next line, with no restart. That's also why turning Log chat off doesn't stop the script: the handler still runs for each line, and returns straight away. Read settings explains values. The handler awaits nothing, so it needs no try/catch. The check at its top keeps it from reading a payload that isn't there. Channel names channel reads as the name of the channel the line was said on. The ones you'll use most: Name Channel GLOBAL The general channel, which the players around the character see GUILD Guild ALLIANCE Alliance PARTY Party TEAM Team SALES Trade, where players advertise what they sell SEEK Recruitment, to look for players or groups COMMUNITY The community channel, split by language The Dofus protocol reference lists every channel. When Asterobot has no name for a channel's value, channel reads as that value's number instead of a name. String() turns it into text, so a number typed in Channels matches it like a name would. Your own lines The game sends your character's lines back to you like everyone else's, so the logger receives them too. session doesn't say which character the bot plays, so the plain way to leave your lines out is to put your character's name in Ignored characters. The same list works for any player you'd rather not read. Staff lines fromAdmin is true on lines the game marks as staff lines. The script writes those with botWarn() instead of botInfo(), so they show at the WARN level among the INFO lines. Run it Test it on a MITM bot (man-in-the-middle: the bot relays the game session of the Dofus client you play). Bring your MITM bot online and enter the game with your character, as Your first bot shows. In the editor, pick that bot and click Run. A toast says Running on followed by the bot's name. On the bot's page, open Console. In Sources, keep only Script. Say something in the Dofus chat. A line such as [GLOBAL] Airelle: hello appears. Then try the settings: Open the bot's Settings tab, then Package settings. In the chat-logger section, click Add under Ignored characters and type your character's name. Not applied appears next to the package's name. Click Apply. A toast says chat-logger settings applied. Say something in the Dofus chat again. This time no line appears. Click Add under Channels, type GUILD and click Apply. From now on, only guild lines are logged. Turn off Log chat and click Apply. Nothing is logged any more, yet the bot's header still shows Stop: the script runs and waits for the switch to come back on. The settings are saved with the bot, so they're still there the next time the script starts. The script runs on a Full socket bot too, but chat only reaches a character that is in the game. This script identifies the bot and nothing more, so on a Full socket bot it needs a connection script that also chooses a character. Ideas to take it further Highlight some words. Add a keywords list setting, and write the lines that contain one of them with botWarn(). Ignore a person rather than a character. senderAccountId stays the same when a player switches characters. It's a 64-bit field, so it reads as a BigInt: compare it with BigInt values, as Payloads explains. Count lines per channel. Keep the counts in a Map, and add a button that writes them to the console: see Declare actions. Answer some lines. Send messages shows how to reply and check that the reply went through. Next, the Inventory watcher follows kamas, pods and item quantities.
  4. Smooth a posté un record dans Reference
    Extension modules are extra modules that Asterobot will be able to offer scripts, next to the nine built-in ones, for abilities those don't cover. Their names will start with asterobot:extension/, and a script will import them like any built-in module. Note Coming soon. No extension module is available to scripts yet. Until then, a script can use everything the built-in modules described in this reference offer. Importing one today A script that imports a module whose name starts with asterobot:extension/ doesn't start. The same goes for any other asterobot: name that isn't a built-in module, such as asterobot:fs. The reason is: resolve "asterobot:extension/<name>" imported by "<module>": built-in module "asterobot:extension/<name>" is not registered As for any import that can't be resolved, the toast after Play or Run doesn't show this text. The bot's Settings tab does: Package settings shows Couldn't read this package's settings with the reason. Error messages lists the other texts you can meet there. The modules a script can import today are: asterobot:protocol, to exchange messages with the game; asterobot:bot, for the session, the bot's console and disconnecting; asterobot:timers, for sleep(); asterobot:gamedata, to read game texts and data; asterobot:console, to write to Asterobot's log; asterobot:runtime, to read package information; asterobot:parameters, to read the package's settings.
  5. Smooth a posté un record dans Reference
    The error texts you can meet while writing and running scripts are grouped here by where they appear. Search for a part of the text you see. In the texts, a word in angle brackets such as <module> stands for something that changes: <module> is the path of a file inside Asterobot, such as library/my-first-script/1.0.0/index.js, <type> a message's name, <field> a field's name. Where you see it What it looks like Section At the top of the bot's page The behavior stopped with an error, with the full text, Copy and Dismiss The script stopped A toast after Play, Run, a load or a version switch Couldn't start the behavior, Couldn't run the package, Couldn't load the selection or Couldn't switch version, without a reason When a package doesn't load Package settings, on the bot's Settings tab, and the Add a bot dialog Couldn't read this package's settings, with the reason When a package doesn't load, and Declarations Inside the script A rejected promise, or an error thrown by a call, that your code can catch Errors a script can catch The bot's Console tab A warning or error line, while the script keeps running Console lines Toasts on Package settings Couldn't apply the settings, Couldn't start the action, or an action's name followed by failed Saving settings and running actions Under the package's manifest, after saving it The reason, or Couldn't save the manifest Saving the manifest The script stopped The behavior stopped with an error appears at the top of the bot's page when a script that had loaded stops because of an error, while starting or later. In Bot Manager, the bot's Behavior column says Failed. The game connection stays open. Fix the cause, then start the script again with Play, or with Run in the editor. The beginning of the text says where the script was: The text starts with The error happened evaluate behavior module "<module>": In the top-level code of the modules, as the script started read declarations of "<module>": While Asterobot read the parameters and actions exports. See Declarations. initialize behavior module "<module>": In the entry function, before it finished Game traffic handler failed: In a handler registered with on() or onTraffic() evaluate protocol handler selector: In a selector function passed to on() evaluate protocol wait selector: In a selector function passed to wait() parameters.onChange handler <number>: In a handler registered with onChange() unhandled Promise rejection: Anywhere: a promise rejected and nothing caught it drain JavaScript Promise jobs: In code that resumed after an await What follows is the error itself: Promise rejected: and the reason, when a promise the start was waiting for rejected. An error thrown by your code: its type and message, such as TypeError: and the message. For an error thrown synchronously, at follows, with the function's name and, in parentheses, the file, the line and the column. A message from one of Asterobot's functions, as listed in Errors a script can catch. For example, a script that sends token instead of ticketKey while starting gets: initialize behavior module "library/my-first-script/1.0.0/index.js": Promise rejected: IdentificationRequest.token: unknown protobuf field These texts can appear after one of the beginnings above, or on their own: Text Cause Fix behavior module "<module>" has no unambiguous default export index.js has no default export Add export default async function behavior(launch) behavior module "<module>" default export is not callable The default export isn't a function Export a function behavior module "<module>" default export must be async and return a Promise The default export returned something other than a promise Make it an async function dynamic import() is not supported by this Asterobot runtime The script called import() as a function Use an import statement at the top of the file JavaScript execution deadline exceeded Code ran for more than 250 ms without an await Split the work into parts separated by an await, such as await sleep(0). See Limits. behavior event-loop queue overflow More than 64 events piled up while the script was busy Keep handlers short, and move slow work out of them unhandled Promise rejection: <reason> A promise rejected with nothing to catch it: an await outside try/catch in an async handler or interceptor, or a promise started without await Wrap the await in try/catch, or add .catch() Game session closed: <reason> The game connection closed while the script was running Connect the bot again, then play the script behavior requested Game disconnect The script called disconnect() Nothing, if that was intended A script stopped with Stop shows no alert. When a package doesn't load When a script can't even be loaded, it never runs, and nothing reaches the Problems alert. The toast is all you get, without the reason: Couldn't start the behavior after Play, Couldn't run the package after Run, Couldn't load the selection after a load from the bot's menu, Couldn't switch version after changing the version. To see the reason, open the bot's Settings tab. Asterobot loads a package the same way to read its settings, so for the bot's package, Package settings shows Couldn't read this package's settings with the text. The package's settings in the Add a bot dialog show it too. For a syntax error, the editor's error badge usually points at the line as well. When the reason comes from the package's code, the text there starts with <package>@<version>: read declarations:. Syntax and imports Text Cause Fix parse module "<module>": <details> That file has a syntax error Fix the syntax. Check the editor's error badge. resolve "<specifier>" imported by "<module>": package "<package>" has no locked dependency for "<specifier>" An import names a package that isn't among the package's dependencies, or is misspelled Fix the name, or add the dependency. See Dependencies. resolve "<specifier>" imported by "<module>": module "<module>" imported by "<module>" is not installed A relative import names a file the package doesn't have Fix the path. Relative imports need the whole file name, extension included, such as ./helper.js. resolve "<specifier>" imported by "<module>": relative import "<specifier>" escapes package root "<root>" A ../ goes above the package's own folder Import only files of the package, or of a dependency by its name resolve "<specifier>" imported by "<module>": built-in module "<specifier>" is not registered The asterobot: module doesn't exist, which is the case for every asterobot:extension/ module today Import one of the nine built-in modules. See Extension modules. resolve "<specifier>" imported by "<module>": package "<package>": no installed version of "<name>" satisfies "<version>" An import such as "name@^2.0.0" asks for a version the script's packages don't include Lock a matching version in the dependencies resolve "<specifier>" imported by "<module>": package "<package>": specifier "<specifier>" has an invalid version "<version>": <details> The part after @ in the import isn't latest, a version or a version range Fix the version resolve "<specifier>" imported by "<module>": absolute module specifier "<specifier>" is forbidden An import starts with / Use a relative path or a package name resolve "<specifier>" imported by "<module>": module specifier "<specifier>" contains a backslash An import path uses \ Use / reachable module count exceeds limit 256 The script loads more than 256 modules See Limits reachable module source exceeds limit of 4194304 bytes The script's modules weigh more than 4 MiB together See Limits module graph depth <depth> exceeds limit 64 at "<module>" Imports chain more than 64 modules deep See Limits Packages and dependencies These texts start with resolve package <name>@<version>:, naming the package being loaded. A problem in one of its dependencies adds dependency "<name>" locked to version "<version>": before the reason, once per level. Text Cause Fix read package <name>@<version> sources: <details> Asterobot couldn't read that package's files, for example because that version isn't installed Install the package or the dependency at that version package <name>@<version> is not installed: parse asterobot.json: <details> The package's asterobot.json isn't valid JSON Fix the file. See The manifest. package "<name>@<version>": package "<name>" requires asterobot <range>, running <version> The package's compatibleVersion doesn't include the Asterobot version you run Update Asterobot, or change compatibleVersion. See Compatibility. package "<name>@<version>": package "<name>" has an invalid compatibleVersion constraint "<range>": <details> compatibleVersion isn't a valid version range Fix it, such as >=1.5.0 <2.0.0 package "<name>@<version>": package source integrity mismatch The files of a package installed from the marketplace no longer match the checksum recorded at install Install that version again package "<name>@<version>": empty package permission permissions in asterobot.json has an empty entry Remove it package "<name>@<version>": duplicate package permission "<permission>" permissions lists the same permission twice Remove the duplicate package "<name>@<version>": entry module "index.js" is missing The package has no index.js at its root Add it. See Package layout. package "<name>@<version>": package contains no modules The package has no .js or .mjs file Add index.js package "<name>@<version>" dependency "<specifier>" targets package named "<name>" version "<version>" A dependency whose name ends with @ and a version is locked to another version Make the version in the name and the locked version the same package <name>@<version> has an invalid name: <details> The package's name breaks the naming rules See Names and versions Messages about downloading or installing packages from the marketplace are covered in Package errors. Errors a script can catch The functions of the built-in modules report problems in two ways. Most return a promise that rejects, which await turns into an error a try/catch can catch. A few throw at once, as noted below. String(error) gives the texts of this section as they are written here, with two exceptions: an error of type TypeError gives TypeError: followed by the text, and the errors thrown by record(), findPath() and packages.info(), and the errors move() rejects with, give GoError: followed by the text. An error your code doesn't catch stops the script, with one of the texts in The script stopped. Messages and timers Text Returned by Cause Fix protocol type must be a non-empty semantic message name send(), request() The message name is missing, empty or not a string Pass the name as a string semantic protobuf message is not mapped: <type> send(), request() Asterobot has no message with that name Check the name in the Dofus protocol reference protocol options must be an object send(), request(), wait() The options aren't an object, such as a bare number Pass { timeout: 5000 } unknown protocol option "<name>" send(), request(), wait() The options have a key other than timeout and to Fix the key timeout/milliseconds must be a finite positive Number send(), request(), wait(), sleep() A timeout that isn't a number above 0, or a sleep() duration that's negative or not a number Pass a number of milliseconds timeout/milliseconds is too large send(), request(), wait(), sleep() A duration too big to be one Pass a smaller number send option "to" must be "server" or "client" send() to isn't a string Use "server" or "client" unknown send target "<value>": expected "server" or "client" send() to is another string Use "server" or "client" protocol selector cannot be empty wait(); thrown by on(), intercept() The selector is "" Pass a name, "*" or a function protocol selector must be a semantic type, '*', or predicate wait(); thrown by on(), intercept() The selector is neither a string nor a function Pass a name, "*" or a function protocol.on handler must be callable, protocol.onTraffic handler must be callable, protocol.intercept handler must be callable Thrown as a TypeError The handler isn't a function Pass a function maximum behavior handlers reached Thrown as a TypeError by on(), onTraffic(), intercept(), onChange() 256 handlers of that kind are registered Remove handlers you no longer need with off() or offChange() protocol wait timed out wait() Nothing matched before the timeout Check the selector, or wait longer context deadline exceeded request() No response came back before the timeout. Most Dofus requests never get one. Use send() and then wait() for the event the game sends mitm relay refuses to identify: this Game session was already authenticated by the real DOFUS client request() The script sent IdentificationRequest on a MITM bot Identify only when launch.reason === "initial" behavior resource limit exceeded: maximum pending operations reached send(), request(), table(), find(), search(), query(), move() 32 operations are pending Await operations before starting more behavior resource limit exceeded: maximum waits reached wait() 128 waits are open Give every wait() a timeout behavior resource limit exceeded: maximum timers reached sleep() 128 sleeps are pending Await sleeps instead of starting many at once On a MITM bot, send() of an IdentificationRequest to the server doesn't reject: see Console lines. Payload errors send() and request() reject with these texts when a payload doesn't fit its message. An interceptor's { payload } gives the same texts on the bot's console, after build replacement payload for <type>:. Each text starts with the path of the field: the message's name, then the field names separated by dots, with [<index>] for an item of a list and ["<key>"] for a value of a map. Text Cause Fix <type>: expected an object The payload is missing, null or undefined Pass {} for a message without fields <path>: expected an object, got <type> The payload, or a nested message, isn't an object Pass an object <path>.<field>: unknown protobuf field The message has no such field Check the field names in the Dofus protocol reference IdentificationRequest.token: unknown protobuf field The script copies an old example that sends token and lang Send ticketKey and languageCode, as in Identify and run <path>: fields "<name>" and "<name>" address the same protobuf field The same field is given under both spellings, such as ticketKey and ticket_key Keep one <path>: oneof <group> selects both "<field>" and "<field>" Two alternatives of the same group are set Set only one <path>.<field>: explicit undefined is not a protobuf value A field is set to undefined Leave the field out <path>.<field>: null is only valid for a message field A field that isn't a nested message is set to null Leave the field out <path>: expected an array A list field got something other than an array Pass an array <path>: invalid array length The value passed for a list has an unusable length Pass a real array <path>: expected a plain object map A map field got something other than an object Pass an object <path>: invalid boolean map key "<key>", and the same with int32, uint32, int64 or uint64 A map key can't be read as the map's key type Use keys such as "true" or "12" <path>: expected boolean A boolean field got another type Pass true or false <path>: expected string A string field got another type Pass a string <path>: expected Uint8Array or ArrayBuffer A bytes field got another type Pass a Uint8Array <path>: expected integral Number A 32-bit integer field got a fraction or something that isn't a number Pass a whole number <path>: Number is outside int32 range The number doesn't fit a signed 32-bit integer Pass a smaller number <path>: expected integral Number in uint32 range An unsigned 32-bit field got a fraction, a negative number or something too big Pass a whole number from 0 to 4294967295 <path>: expected signed 64-bit BigInt or decimal string A 64-bit integer field got a number too big to be exact, a fraction, a string that isn't digits, or another type Pass a BigInt, such as 123456789012n <path>: expected unsigned 64-bit BigInt or decimal string The same, for an unsigned field, or a negative value Pass a positive BigInt <path>: expected Number A floating point field got something that isn't a number Pass a number <path>: unknown enum symbol "<name>" The enum has no value with that name Check the value names in the Dofus protocol reference <path>: expected enum symbol or number An enum field got neither a name nor a whole number Pass the value's name as a string Game data Text Returned by Cause Fix gamedata.text key must be a string or integer Thrown as a TypeError by text() The key is a fraction, a number outside the 32-bit range, a BigInt, undefined or an object Check the value before passing it gamedata.record table must be a non-empty string Thrown as a TypeError by record() The table name is missing or empty Pass the table's name gamedata.record id must be an integer Thrown as a TypeError by record() The id isn't a whole number, is too big, or is a BigInt or a string Pass a regular whole number datacenter: no such table: "<table>" Thrown by record(); table(), find(), search() This game version has no table with that name Check the table's name in the Game page's game data gamedata.table name must be a non-empty string table() The name is missing or empty Pass the table's name gamedata.find table must be a non-empty string find() The name is missing or empty Pass the table's name gamedata.find filter must be a non-empty object find() The filter is missing, empty, an array or not an object Pass { column: value } datacenter: no such column: table "<table>" has no column "<column>" find() A filter column doesn't exist in that table Fix the column's name gamedata.search query must be a non-empty string search() The query is missing or empty Pass a name gamedata.search options.table must be a string search() options.table isn't a string Pass the table's name as a string datacenter: no text for this language: "<language>" search() The game data has no names in the script's language Extract the game data again, or check session.language gamedata.query sql must be a non-empty string query() The SQL is missing or empty Pass the SQL as a string gamedata.query params must be an array query() params isn't an array Pass an array, such as [31] datacenter: empty query query() The SQL holds only spaces Pass a statement datacenter: query is <size> bytes, over the 65536 byte limit query() The SQL text is too long Shorten the query datacenter: followed by the database's message query() The SQL is wrong, names a table or column that doesn't exist, or writes, in which case the message mentions attempt to write a readonly database Fix the query. Try it in the SQL tab. Pathfinding Text Returned by Cause Fix pathfinding.findPath mapId must be an integer Number, BigInt or decimal string Thrown as a TypeError by findPath() The map id is a fraction, a number above Number.MAX_SAFE_INTEGER, a string that isn't digits, or another type Pass currentMap().mapId, or the id as a BigInt pathfinding.findPath from must be an integer cell id, and the same with to Thrown as a TypeError by findPath() The cell isn't a whole number, or is a BigInt or a string Pass a cell id from 0 to 559 pathfinding.findPath options must be an object Thrown as a TypeError by findPath() The options are neither an object, null nor undefined Pass an object, or nothing pathfinding.findPath options has no property "<name>" Thrown as a TypeError by findPath() The options have a key other than occupied, cautious and disableDiagonals Fix the key pathfinding.findPath options.occupied must be an array of cell ids Thrown as a TypeError by findPath() occupied isn't an array Pass an array of cell ids pathfinding.findPath options.occupied[<index>] must be an integer cell id Thrown as a TypeError by findPath() That item isn't a whole number Pass cell ids only pathfinding.findPath options.cautious must be a boolean, and the same with disableDiagonals Thrown as a TypeError by findPath() The option isn't a boolean Pass true or false pathfinding.findPath: map <mapId>, cell <from> to cell <to>: pathfinding: not a map cell, cell ids go from 0 to 559: <cell> Thrown as a TypeError by findPath() from or to is outside 0 to 559. For a cell of occupied, the text ends with occupied cell <cell>. Pass cell ids from 0 to 559 failed to load map <mapId> for pathfinding: datacenter: no such record: maps/<mapId> Thrown by findPath() The bot's game version has no map with that id Check the map id failed to open the datacenter: followed by the reason Thrown by findPath() Asterobot can't read the game data of the bot's game version Extract the game data pathfinding.cellToPoint cell must be an integer cell id, pathfinding.distance a must be an integer cell id and the same with b, pathfinding.direction from must be an integer cell id and the same with to Thrown as a TypeError by cellToPoint(), distance(), direction() The cell isn't a whole number, or is a BigInt or a string Pass a cell id from 0 to 559 pathfinding: not a map cell, cell ids go from 0 to 559, after pathfinding.cellToPoint cell:, pathfinding.distance cells: or pathfinding.direction cells: Thrown as a TypeError by cellToPoint(), distance(), direction() A cell is outside 0 to 559 Pass cell ids from 0 to 559 pathfinding.pointToCell x and y must be integers Thrown as a TypeError by pointToCell() x or y isn't a whole number Pass whole numbers Movement Text Returned by Cause Fix movement.move cell must be an integer cell id move(), as a TypeError The cell isn't a whole number, or is a BigInt or a string Pass a cell id from 0 to 559 movement.move cell: pathfinding: not a map cell, cell ids go from 0 to 559 move(), as a TypeError The cell is outside 0 to 559 Pass a cell id from 0 to 559 movement.move options must be an object move(), as a TypeError The options are neither an object, null nor undefined Pass an object, or nothing movement.move options has no property "<name>" move(), as a TypeError The options have a key other than cautious and timeout Fix the key movement.move options.cautious must be a boolean move(), as a TypeError cautious isn't a boolean Pass true or false movement.move options.timeout: timeout/milliseconds must be a finite positive Number move(), as a TypeError timeout isn't a number above 0 Pass a number of milliseconds move: the bot isn't connected to a game server move() The bot has no game connection Connect the bot first move: the character is already walking; await the move in progress first move() Another walk of the bot is under way, from a script or from Asteroboard Await each move() before starting the next move: the character is still walking move() The character hasn't finished a walk it didn't ask for, such as one the player started on a MITM bot Wait for that walk to end move: the character's map and cell aren't known yet move() Asterobot hasn't seen the character's map yet, or where the map places the character Wait until the character is on a loaded map move: the game has no path from cell <cell> to cell <cell> move() The Dofus client itself would find no route Pick another cell move: the server didn't grant the walk in time move() No MapMovementEvent granted the walk within the timeout, which is also how a refused walk ends Check the cell, or give the server longer with timeout move: another walk of the character started before this one ended move() Another walk of the character started first, or the walk was cancelled Nothing to fix in the call move: the server didn't acknowledge the end of the walk in time move() No MapMovementConfirmResponse came within the walk's time plus the timeout Give the server longer with timeout move: the game session closed during the walk move() The game connection closed before the walk ended Connect the bot again asterobot:movement describes each case. Packages and settings Text Returned by Cause Fix packages.info requires a module or package name Thrown as a TypeError by packages.info() The name is missing, empty or not a string Pass a package name package "<name>" is not installed Thrown by packages.info() No package of the running script has that name, asterobot:parameters included Check the name. See asterobot:runtime. package "<name>" is ambiguous across <count> locked versions Thrown by packages.info() The dependencies lock several versions of that package Nothing to fix in the call: the name can't pick one parameters.onChange handler must be callable Thrown as a TypeError by onChange() The handler isn't a function Pass a function 'Set' on a dynamic object returned false Thrown as a TypeError by assigning to a property of values values is read-only Don't assign to values Could not delete property "<name>" of a dynamic object Thrown as a TypeError by deleting a property of values values is read-only Don't delete from values While the script stops A call made while the script is stopping rejects, or throws, with the reason the script is stopping, and the promises still pending when it stops reject with that reason too. After Stop, the reason is behavior runtime closed. There's nothing to fix: the script is ending. Console lines These lines appear on the bot's Console tab. The script keeps running. Line Level Cause Fix Refused to forward a message that would re-authenticate a relayed session Warning On a MITM bot, the script sent IdentificationRequest to the server. Asterobot didn't forward it, and send() resolved anyway. Identify only when launch.reason === "initial", as in Identify and run interceptor failed: <error> Error An interceptor threw, or ran for more than 250 ms Fix the handler, and keep it quick interceptor selector failed: <error> Error An interceptor's selector function threw Fix the selector build replacement payload for <type>: <error> Error An interceptor returned a { payload } that doesn't fit the message. <error> is one of the payload errors. Fix the payload re-encode <type>: <error> Error Asterobot couldn't rebuild the message with the new fields Return { payload } with the same message's fields cannot replace the payload of <wire name>: this build has no name for it, so there is nothing to build a replacement from - use { raw } instead Error An interceptor returned { payload } for a message without a readable name Return { raw }, or leave that message alone A line is written for each message on which an interceptor fails. When several interceptors fail on the same message, only the first failure is written, and the tenth if there are that many, ending with (10 times). An async interceptor whose promise rejects doesn't write a line here: it stops the script with unhandled Promise rejection: <error>. Saving settings and running actions These appear as toasts on Package settings, on the bot's Settings tab. Toast Text under it Cause Fix Couldn't apply the settings <name>: <reason> A value doesn't fit its declaration. The reasons are listed in Declarations. Nothing from the form is saved. Correct the value, then Apply again Couldn't apply the settings package "<package>" declares no parameter "<name>" The form holds a setting the package doesn't declare any more Click Refresh, then apply again Couldn't apply the settings package "<package>" is not part of this bot's behavior The bot's package changed since the form was loaded Click Refresh Couldn't start the action <argument>: <reason> An argument doesn't fit its declaration Correct the value in the dialog Couldn't start the action the running behavior declares no action "<action>" on package "<package>" The running script doesn't declare that action, for example because you added it in the editor without starting the script again Start the script again Couldn't start the action no behavior is running on this bot The script isn't running Play the script first Couldn't start the action behavior event-loop queue overflow The script has 32 operations pending Try again in a moment The action's name followed by failed The error the action's run threw, or its promise's rejection reason The action failed. The script keeps running. Fix run When the declarations themselves can't be read, Package settings shows Couldn't read this package's settings instead of the form: Declarations lists those texts. Saving the manifest When you save a package's asterobot.json from Asteroboard and Asterobot refuses it, the reason shows under the manifest, or Couldn't save the manifest when there's none. Dependencies explains how to declare them. The first four texts below come after invalid package manifest:. The last three are checked first, for dependencies that have no URL, and come on their own. Text Cause Fix empty dependency specifier A dependency has no name Name the dependency dependency "<specifier>": version embedded in its own specifier: not a valid exact version: <details> The version written after @ in the dependency's name isn't one exact version Write an exact version, such as name@1.2.0 dependency "<specifier>": version "<version>" conflicts with the version already in its own specifier The dependency's name holds one version and its version field another Make them the same, or remove one dependency "<specifier>": invalid version "<version>": <details> The version isn't latest, a version or a version range Fix the version dependency "<specifier>" locks version "<version>" in its specifier but declares "<version>" The version in the name and the declared version differ Make them the same dependency "<specifier>" needs an exact version A dependency without a URL has no exact version Give an exact version dependency "<specifier>" needs <name>@<version> installed, or a URL to fetch it from A dependency without a URL isn't installed at that version Install it, or give its asterobot.net URL
  6. Smooth a posté un record dans Reference
    Asterobot runs every script within fixed limits, so that one script can't hold up Asterobot or the other bots. They're the same for every bot and every package, and a package can't change them. Here is each one, with its value and what happens past it. Running code Limit Value Past the limit Code running without a pause 250 ms The script stops, and the Problems alert shows a text containing JavaScript execution deadline exceeded. The game connection stays open. Function calls nested in each other 1024 The script stops. A try/catch can't catch this. A pause is an await, or the end of the function Asterobot called. Each of these counts as one run, with its own 250 ms: the top level of the modules, when the script starts; the entry function, up to its first await; one call of a handler: from on(), onTraffic(), intercept(), onChange(), a selector function, or an action's run; code resuming after an await, together with any other code resuming at the same moment. The text in the alert says which it was, for example Game traffic handler failed: JavaScript execution deadline exceeded for a handler, or drain JavaScript Promise jobs: JavaScript execution deadline exceeded for code that resumed after an await. The place in your code follows it. Three cases don't stop the script: In an interceptor, the message goes on unchanged, and the bot's Console shows interceptor failed: JavaScript execution deadline exceeded. In an action's run, the toast for the action says it failed, with the same text. While Asterobot reads the package's declarations, reading fails. See Declarations. Handlers, waits and sleeps Limit Value Past the limit Message handlers registered at once with on(), onTraffic() and intercept() together 256 The call throws a TypeError with maximum behavior handlers reached Handlers registered at once with onChange() 256, counted apart from message handlers The call throws a TypeError with maximum behavior handlers reached wait() calls not settled yet 128 The new wait() rejects with behavior resource limit exceeded: maximum waits reached sleep() calls not settled yet 128 The new sleep() rejects with behavior resource limit exceeded: maximum timers reached A handler removed with off() or offChange(), and a wait or a sleep that settled, frees its place. A wait() without a timeout that never matches keeps its place until the script stops. Pending operations Limit Value Past the limit Operations waiting for their result: send(), request(), table(), find(), search() and query() together 32 The new call's promise rejects with behavior resource limit exceeded: maximum pending operations reached text() and record() answer right away and don't count. An operation frees its place as soon as its promise settles. While all 32 places are taken, a click on one of the package's action buttons shows Couldn't start the action with behavior event-loop queue overflow. Settings applied at that moment are saved, but the running script only gets them at its next start. Events waiting for the script Limit Value Past the limit Events waiting to be handled 64 The script stops with behavior event-loop queue overflow The events are the messages that arrive, operations that finish, sleeps and wait timeouts that end, applied settings and clicked actions. A script handles them one at a time, so they pile up while its code is busy, for example in a handler that runs long or awaits nothing. A MITM bot relaying a busy game session receives many messages each second: keep handlers short. A message waiting for an interceptor's decision is the exception. When the queue is full, it goes on unchanged instead of stopping the script. On a MITM bot, Asterobot never slows the game down for what watches it. When the script or the Network tool can't keep up with the messages, Asterobot leaves some out of what they receive, and the game still gets them. A Full socket bot waits instead, and misses nothing. Timeouts Limit Value Past the limit send() and request() 30 seconds unless you pass { timeout } The promise rejects. For request(), with context deadline exceeded. wait() None unless you pass { timeout } With a timeout, the promise rejects with protocol wait timed out. Without one, it waits until the script stops. table(), find(), search() and query() 30 seconds, fixed The promise rejects An interceptor's decision 50 ms The message goes on unchanged, and nothing is written anywhere Packages and modules Limit Value Past the limit Modules the script loads, counting the built-in modules it imports 256 The script doesn't start: reachable module count exceeds limit 256 Total size of those modules 4 MiB (4,194,304 bytes) The script doesn't start: reachable module source exceeds limit of 4194304 bytes Chain of imports, from index.js down 64 modules The script doesn't start: module graph depth <depth> exceeds limit 64 at "<module>" When a script doesn't start for one of these reasons, the toast after Play or Run doesn't say why. The bot's Settings tab does: Package settings shows Couldn't read this package's settings with the text. Only the .js and .mjs files of a package are part of its code. A package archive you import is at most 32 MiB: see Zip files and the command line. Game data Limit Value Past the limit Rows returned by one call of table(), find(), search() or query() 200 by default, 5000 at most A larger limit is lowered to 5000. query() sets truncated to true when rows were left out. Length of the SQL passed to query() 65,536 bytes The promise rejects with datacenter: query is <size> bytes, over the 65536 byte limit Time for one query(), waiting for its turn included 10 seconds The query is interrupted, and the promise rejects Size of a single value a query builds 16 MiB The query fails Queries running at once on one game version, across every bot and Asteroboard 4 The next query waits for its turn, and the wait counts toward its 10 seconds Consoles Limit Value Past the limit Lines Asterobot keeps for each bot's Console 200 A Console tab opened later starts with the last 200 lines only Interceptor failures written to the bot's Console for one message The first, and the tenth When several interceptors fail on the same message, the other failures aren't written. The count starts again with each message. Error messages explains every text on this page, and Async and timing shows how to write scripts that stay within the limits.
  7. Smooth a posté un record dans Reference
    A package puts settings and buttons on a bot's page by declaring them in its script. The complete schema is below. Declare settings and Declare actions walk through it with examples, and asterobot:parameters describes how a script reads the values. Where declarations go A package declares settings and actions as two named exports of its index.js: export const parameters = { maxFights: { type: "int", label: "Stop after", default: 20, min: 1 }, }; export const actions = { recall: { label: "Recall to zaap", async run() { // What the button does. }, }, }; Both exports are optional. Exports with these names in other files of the package are ignored. Every package of the running script can declare its own: the bot's package, and each dependency the script really imports. A dependency listed in asterobot.json that nothing imports declares nothing. Settings and actions appear on the bot's page in the order you write them. These are ordinary JavaScript values, so a default can be computed or imported from another file. How Asterobot reads them Asterobot runs the top level of the package's modules without calling the default export, then reads the two exports. It does so when a bot's Settings tab shows Package settings, when you pick the package in the Add a bot dialog, and each time a script starts. At that moment there's no bot and no game session, so some functions behave differently when the top level of a module calls them: Called at the top level While Asterobot reads declarations send(), request(), wait(), sleep(), disconnect(), currentMap(), and every function of asterobot:gamedata and asterobot:pathfinding Throws, and reading fails with an error that ends with not available while reading a package's declarations move() Rejects with an error that ends with not available while reading a package's declarations on(), onTraffic(), intercept(), off(), onChange(), offChange() Accepted, and does nothing session Holds empty values values An empty object botDebug() and the other console functions of asterobot:bot and asterobot:console Write nothing packages.info() Answers as usual Reading also fails when a top-level await never settles, with package awaits at import time, which cannot be answered without a Game session, and when top-level code runs for more than 250 ms without awaiting, with JavaScript execution deadline exceeded. A rejected promise that nothing catches doesn't make reading fail. So keep the top level of your modules to imports, declarations and quick computations. Settings export const parameters is an object. Each key is a setting's name, the name the script reads in values, and each value is an object with these fields: Field Type Required Description type string Yes "bool", "string", "int", "double", "array" or "map" of string For array and map only The type of the array's items, or of the map's values: "bool", "string", "int" or "double". Refused on the other types. label string No Shown next to the input. Default: the setting's name. description string No A line of help shown with the input placeholder string No Shown in an empty text box or number box unit string No A short word shown after a number box, such as kamas or seconds default depends on type No The value until someone changes it. Without a default, or with null, the setting starts at its type's zero value. choices array No For string and int only: the values allowed. See Choices. min number No The smallest value allowed, for int and double max number No The largest value allowed, for int and double Any other field is ignored without an error, so a misspelled field such as labell silently does nothing. A label, description, placeholder or unit that isn't a string is ignored too, and so is a min or max that isn't a number. Types type The script reads On the bot's page Zero value bool A boolean A switch false string A string A text box "" int A whole number, never a BigInt A number box that steps by 1 0 double A number A number box 0 array An array A list with an input per item, a remove button on each, and Add [] map An object with string keys A list of key and value rows, with Add an entry. A row with an empty key is dropped. {} A setting with choices shows a drop-down list, whatever its type, and its zero value is the first choice. Values that fit The default, and every value applied from the bot's page, has to fit the declaration. Asterobot never converts one type into another: bool takes true or false, nothing else. string takes a string. A number isn't turned into text. int takes a whole, finite number. 3.0 is fine, and 3.5 is refused rather than rounded. double takes a finite number: not NaN, not Infinity. array takes an array whose every item fits of. map takes an object whose every value fits of. With choices, the value has to be one of the choices. With min or max, an int or double value has to be within the bounds, the bounds themselves included. min and max don't apply to a setting that has choices, nor to the items of an array or a map. A saved value that stops fitting after you change a declaration is ignored, and the setting falls back to its default. Choices choices takes bare values, objects with a value and a label, or a mix of both. A bare value is its own label. export const parameters = { slot: { type: "int", label: "Slot", choices: [1, 2, 3], default: 2 }, mode: { type: "string", label: "Strategy", choices: [ { value: "safe", label: "Avoid groups" }, { value: "greedy", label: "Fight everything" }, ], default: "safe", }, }; Every choice's value has to fit the setting's type, and a default has to be one of the choices. Actions export const actions is an object. Each key is an action's name, and each value is an object with these fields: Field Type Required Description label string No The button's text. Default: the action's name. description string No Shown as the button's tooltip, and at the top of the arguments dialog args object No The arguments the bot's page asks for before running the action. Each one is declared exactly like a setting, with the same fields and rules, but its value isn't saved. run function Yes What the button does run(args) receives an object holding every declared argument: the value typed in the dialog, or the argument's default when none was given. An action without args receives {}. run may be async. import { send } from "asterobot:protocol"; export const actions = { say: { label: "Say something", args: { text: { type: "string", label: "Text", default: "Hello" } }, async run({ text }) { await send("ChatChannelMessageRequest", { channel: "GLOBAL", content: text }); }, }, }; Here is what happens around an action: Its button is in the package's section of Package settings, on the bot's Settings tab. It can only be clicked while a script is running. Otherwise it's disabled, with the tooltip The bot has to be playing a behavior before an action can run. An action with arguments opens a dialog titled with its label, with one field per argument, reset to the defaults every time it opens, and Cancel and Run buttons. The arguments are checked against the declarations before anything runs. A value that doesn't fit shows Couldn't start the action with the reason, and run isn't called. Once the action is on its way, a toast says Action started. When run returns, or its promise settles, a second toast says the action's name followed by finished, or followed by failed with the error. A failure toast stays until you close it. An error in run never stops the script. Asterobot finds the actions before it calls the default export, so the buttons work even when the entry function never finishes. A button runs the action of the script that's running. After you change an action in the editor, start the script again to use the new code. Types for the editor The editor knows these types for declarations: BehaviorParameters, BehaviorActions, BehaviorParameter, BehaviorAction, BehaviorChoice, BehaviorParameterType and BehaviorScalarType. Name them in a JSDoc comment to get completion on the fields: /** @type {BehaviorParameters} */ export const parameters = { enabled: { type: "bool", label: "Answer in chat", default: true }, }; Errors When declarations can't be read, Package settings on the bot's Settings tab, and the package's settings in the Add a bot dialog, show Couldn't read this package's settings with the reason. The text starts with the package's name and version and read declarations:, such as: my-first-script@1.0.0: read declarations: package "my-first-script" parameter "enabled": parameter "enabled" default: want a bool, got string In the texts below, <package> is the package's name, <name> a setting's name, <action> an action's name and <argument> an argument's name. Errors that also stop the script from starting These say an export doesn't have the right shape. They also make every start fail, with the Problems alert showing read declarations of "<module>": followed by the same text. Error Cause package "<package>": parameters: must be an object, got <type> parameters is exported, but isn't an object package "<package>": parameters: "<name>" must be an object A setting isn't an object package "<package>": actions: must be an object, got <type> actions is exported, but isn't an object package "<package>": actions: "<action>" must be an object An action isn't an object package "<package>": actions: "<action>" declares no run function An action has no run package "<package>": actions: "<action>": run must be a function An action's run isn't a function package "<package>": actions: "<action>": args: must be an object, got <type> args isn't an object package "<package>": actions: "<action>": args: "<argument>" must be an object An argument isn't an object Errors in a setting or an argument These don't stop a start: the script starts, but with no settings at all. They begin with package "<package>" parameter "<name>": for a setting, or with package "<package>" action "<action>": argument "<argument>": for an action's argument. Error Cause parameter "<name>" declares unknown type "<type>" type is missing or isn't one of the six types parameter "<name>" is a <type> and must declare its element type as `of` An array or map without of parameter "<name>" declares element type "<of>", which is not one of bool/string/int/double of isn't one of the four scalar types parameter "<name>" is a <type> and cannot declare an element type of on a type other than array and map parameter "<name>" declares choices, which only a string or an int may do choices on a type other than string and int choices must be an array, got <type> choices isn't an array choice <index> has no value A choice object without value. Choices count from 0. parameter "<name>" choice <index>: <reason> A choice's value doesn't fit the type parameter "<name>" declares min <min> above max <max> min is greater than max parameter "<name>" default: <reason> The default doesn't fit the declaration parameter has no name A setting's name is the empty string Reasons a value doesn't fit These reasons follow default: or choice <index>: in the errors above. The same texts appear when a value applied from the bot's page, or an action's argument, doesn't fit. Reason Cause want a bool, got <type> A bool got something other than true or false want a string, got <type> A string got something other than a string want an int, got <type> An int got something other than a number want a whole number, got <value> An int got a fraction, or Infinity or NaN want a number, got <type> A double got something other than a number want a finite number, got <value> A double got NaN or Infinity want an array, got <type> An array got something other than an array item <index>: <reason> An array item doesn't fit of want a map, got <type> A map got something other than an object key "<key>": <reason> A map value doesn't fit of <value> is not one of <choices> The value isn't among the choices, listed with commas <value> is below the minimum <min> The value is lower than min <value> is above the maximum <max> The value is higher than max <type> is the name Asterobot gives the value's type: string, bool, int64 for a whole number written in the script, float64 for other numbers and for every number sent from the bot's page, []interface {} for an array and map[string]interface {} for an object. Error messages lists the texts you can meet everywhere else.
  8. Smooth a posté un record dans Reference
    @asterobot:base is an official package of ready-made functions that most bots need. For now it re-exports move(), the built-in function of asterobot:movement that walks the bot's character to a cell of its map. Note Coming soon. @asterobot:base isn't on the marketplace yet, so no script can install it. You don't need it to walk: import move() from asterobot:movement. Add it to a package Once @asterobot:base is in your library, declare it in your package's dependencies, as Dependencies shows, then import move() by that name: import { move } from "@asterobot:base"; It's the same function as the one asterobot:movement exports, and asterobot:movement describes its options, errors and limits.
  9. Smooth a posté un record dans Reference
    asterobot:movement walks the bot's character. Its one function, move(), walks the character to a cell of the map it stands on and resolves once the walk is over. The same call works on a Full socket bot and on a MITM bot (man-in-the-middle: the bot relays the game session of a Dofus client that someone plays). import { move } from "asterobot:movement"; This is the module's only export. move() move(cell, options?) returns Promise<number>. Parameter Type Description cell number The cell to walk to, a whole number from 0 to 559 options.cautious boolean Walk instead of running, whatever the distance. Default false. options.timeout number How many milliseconds the server gets to grant the walk, and then to acknowledge its end once the walk is over. Default 5000. It resolves with the cell the character stands on, once the server has acknowledged the end of the walk. try { const cell = await move(301); botInfo("Standing on cell", cell); } catch (error) { botWarn("Couldn't walk:", String(error)); } How it walks It reads where the character and the other actors stand, as currentMap() does, and plans the route to cell the way findPath() does, around every other actor. When cell can't be reached, the route ends on the reachable cell closest to it. When there's nowhere to walk, move() resolves right away with the character's cell and sends nothing. It sends MapMovementRequest and waits for the server's MapMovementEvent granting the walk. On a Full socket bot, it waits as long as the Dofus client takes to walk the cells the server granted, then sends MapMovementConfirmRequest. On a MITM bot it sends nothing more: the Dofus client walks the character on screen and confirms the walk itself. It resolves when the server answers with MapMovementConfirmResponse. When the server grants a shorter walk than the one asked for, move() times the walk the server granted and resolves with the cell where it ends. On a Full socket bot the confirmation leaves as early as a Dofus client could send it. The path explains how that time is found, and why it can be wrong for a mounted character. One walk at a time A bot walks one walk at a time, whoever asks for it. While a walk is under way, a second move() rejects at once and sends nothing, and so does a walk started with Walk here on the bot's map in Asteroboard. Await each move() before starting the next. move() also refuses to start while the character is still walking a walk it didn't ask for, such as one the player started on a MITM bot. That walk counts until the server acknowledges its end, or for one second at most past the time the Dofus client takes to walk it. The walk belongs to the bot, not to your script. If the script stops during a walk, the promise of move() rejects with the reason the script stopped, and the walk still ends and is confirmed, so the server doesn't keep the character walking. Doing something else during the walk move() doesn't hold up the rest of your script. Other code keeps running while the character walks, and on a Full socket bot the confirmation still leaves on time. To send a chat message while walking, start both together: const [cell] = await Promise.all([ move(301), send("ChatChannelMessageRequest", { channel: "GLOBAL", content: "Hello" }), ]); Don't keep the promise of move() in a variable to await later. If it rejects before your code reaches the await, nothing catches it and the script stops with unhandled Promise rejection:. Await it, put it in a Promise.all, or give it a .catch(). Until the end of the walk is acknowledged, the server considers the character still walking. Wait for move() to resolve before an action that needs the character standing, such as using an element of the map. Errors A mistake in the arguments rejects the promise with a TypeError, and nothing is sent: Error Cause movement.move cell must be an integer cell id cell isn't a whole number in the 32-bit range, or is a BigInt or a string movement.move cell: pathfinding: not a map cell, cell ids go from 0 to 559 cell is outside 0 to 559 movement.move options must be an object options is set, not to null or undefined, and isn't an object movement.move options has no property "<name>" options has a key other than cautious and timeout movement.move options.cautious must be a boolean cautious is set to something other than true, false, null or undefined movement.move options.timeout: timeout/milliseconds must be a finite positive Number timeout isn't a number above 0 A walk that is refused or fails rejects it with an error whose message is one of these texts: Text Cause move: the bot isn't connected to a game server The bot has no game connection. Nothing is sent. move: the character is already walking; await the move in progress first Another walk of the bot is under way, from a script or from Asteroboard. Nothing is sent. move: the character is still walking The character hasn't finished a walk it didn't ask for. Nothing is sent. move: the character's map and cell aren't known yet Asterobot hasn't seen the character's map yet, or where the map places the character, for example right after the character enters the game. Nothing is sent. move: the game has no path from cell <cell> to cell <cell> The Dofus client itself would find no route. Nothing is sent. move: the server didn't grant the walk in time No MapMovementEvent granted the walk within timeout. A walk the server refuses ends this way too. move: another walk of the character started before this one ended A new walk of the character started first, or its walk was cancelled, for example because the player clicked elsewhere on a MITM bot move: the server didn't acknowledge the end of the walk in time No MapMovementConfirmResponse came within the walk's time plus timeout move: the game session closed during the walk The game connection closed before the walk ended behavior resource limit exceeded: maximum pending operations reached 32 operations are pending, and a walk counts as one until it ends. Nothing is sent. It also rejects with the texts of findPath() when the game data can't be read, such as failed to load map <mapId> for pathfinding: datacenter: no such record: maps/<mapId>. asterobot:pathfinding lists them. String(error) gives TypeError: or GoError: followed by the text. Limits move() walks on the map the character stands on. It doesn't change maps. It's made for moving outside fights, like asterobot:pathfinding. Walk along a path shows the messages move() exchanges, for a script that walks by hand.
  10. Smooth a posté un record dans Reference
    asterobot:pathfinding computes moves on a map the way the Dofus client does. findPath() gives the route the client would walk from one cell to another, the key cells to send for it and how long the walk lasts. The other functions work with cell positions: where a cell sits on the map's grid, and the distance and the direction between two cells. It only computes. To walk, call move() from asterobot:movement, or send the walk yourself as Walk along a path shows. import { findPath, cellToPoint, pointToCell, distance, direction } from "asterobot:pathfinding"; These are the module's only exports. There's no pathfinding object to import. Export Returns What it does findPath() A path or undefined, right away Finds the route from one cell of a map to another cellToPoint() A point, right away Gives a cell's position on the grid pointToCell() A cell or undefined, right away Gives the cell at a grid position distance() A number, right away Measures the grid distance between two cells direction() A direction or undefined, right away Gives the direction from one cell to another None of them returns a promise. Like the functions of asterobot:gamedata, they throw when the top level of a module calls them while Asterobot reads the package's declarations: see Declarations. Everything here is about moving outside fights. Moves in a fight follow other rules, and this module doesn't cover them. Cells and directions A map has 560 cells, numbered 0 to 559. Every function takes cell ids as regular whole numbers: a BigInt or a string is refused. A direction is a number from 0 to 7: Number Direction on screen 0 East 1 South-east 2 South 3 South-west 4 West 5 North-west 6 North 7 North-east findPath() findPath(mapId, from, to, options?) returns a Path, or undefined. Parameter Type Description mapId BigInt, number or string The map's id: a BigInt such as currentMap().mapId, a whole number up to Number.MAX_SAFE_INTEGER, or the id written in digits, such as "154010883". Any map of the bot's game version works, not only the one the character stands on. from number The cell the walk starts from to number The cell to walk to options.occupied number[] The cells other actors stand on, as described below. Default none. options.cautious boolean The cautious flag the walk will be sent with. It changes how the character moves, and so the walk's times. Default false. options.disableDiagonals boolean true only for a character whose look has no diagonal walking animations. Player characters have them, so leave it false. It reads the map from the game data of the bot's game version and returns the route the Dofus client would take. When to can't be reached, the route ends on the reachable cell closest to it instead, and end tells you which. findPath() returns undefined only when the Dofus client itself would find no route at all, which doesn't happen on the game's maps. Occupied cells The Dofus client's router makes a cell with an actor on it more expensive to walk onto, so its routes tend to go around actors. To get the route the client would walk, pass in occupied the cell of every actor the server shows on the map except the bot's own character: other players, monster groups, NPCs, merchants, tax collectors, prisms and the like. Items on the ground, marks and glyphs don't count. currentMap() lists the actors: import { currentMap } from "asterobot:bot"; import { findPath } from "asterobot:pathfinding"; const map = currentMap(); if (map?.cellId !== undefined) { const others = map.actors.filter((actor) => actor.id !== map.characterId); const path = findPath(map.mapId, map.cellId, 301, { occupied: others.map((actor) => actor.cellId), }); } The path Property Type Value start number The cell the route starts from: from end number The cell the route ends on: to when the router reached it, otherwise the reachable cell closest to to steps array The route's steps, in walking order. Each one is an object with cell, the cell the character leaves, and direction, the direction it walks from there. keyCells number[] What MapMovementRequest carries in keyCells for this route. Send them as they are. Empty when end is start: there's nowhere to walk, and the Dofus client sends nothing. gait string "walk" or "run": how the character moves along the route duration number How long the walk lasts, in milliseconds: from its start to the moment the Dofus client sends MapMovementConfirmRequest. 0 when end is start. stepEnds number[] When each step ends, in milliseconds from the start of the walk. The last one is duration. A character walks a route of 3 cells or fewer, the start included, and runs a longer one. With cautious: true it always walks. duration is the earliest moment a Dofus client can confirm the walk. A real client only ends a step when it draws its next frame, so it confirms a little later: in walks recorded from the Dofus client, the confirmation left 10 to 48 ms after duration. The times are those of a character on foot. A mounted character runs at a speed Asterobot doesn't cover yet, so its duration can be wrong. Errors Error Type Cause pathfinding.findPath mapId must be an integer Number, BigInt or decimal string TypeError mapId is a fraction, a number above Number.MAX_SAFE_INTEGER, a string that isn't a whole number written in digits, or another type pathfinding.findPath from must be an integer cell id, and the same with to TypeError The cell isn't a whole number in the 32-bit range, or is a BigInt or a string pathfinding.findPath options must be an object TypeError options is set, not to null or undefined, and isn't an object: an array or a number, for example pathfinding.findPath options has no property "<name>" TypeError options has a key other than occupied, cautious and disableDiagonals, such as the misspelled ocupied pathfinding.findPath options.occupied must be an array of cell ids TypeError occupied isn't an array pathfinding.findPath options.occupied[<index>] must be an integer cell id TypeError That item isn't a whole number in the 32-bit range pathfinding.findPath options.cautious must be a boolean, and the same with disableDiagonals TypeError The option is set to something other than true, false, null or undefined pathfinding.findPath: map <mapId>, cell <from> to cell <to>: pathfinding: not a map cell, cell ids go from 0 to 559: <cell> TypeError from or to is a whole number outside 0 to 559. For a cell of occupied, the text ends with occupied cell <cell> instead. failed to load map <mapId> for pathfinding: datacenter: no such record: maps/<mapId> Error The bot's game version has no map with that id failed to open the datacenter: followed by the reason Error Asterobot can't read the game data of the bot's game version, for example because it isn't extracted. See Extract game data. String(error) gives TypeError: or GoError: followed by the text. cellToPoint() cellToPoint(cell) returns { x, y }, the cell's position on the map's grid. cellToPoint(300); // { x: 17, y: -4 } The grid is the map turned 45 degrees, so both numbers change along a row of the screen. One cell to the east adds 1 to x and 1 to y. One cell to the south adds 1 to x and takes 1 from y. Error Type Cause pathfinding.cellToPoint cell must be an integer cell id TypeError cell isn't a whole number in the 32-bit range, or is a BigInt or a string pathfinding.cellToPoint cell: pathfinding: not a map cell, cell ids go from 0 to 559 TypeError cell is outside 0 to 559 pointToCell() pointToCell(x, y) returns the cell at that grid position, or undefined when the position is off the map. pointToCell(18, -3); // 301 pointToCell(0, 1); // undefined It throws a TypeError with pathfinding.pointToCell x and y must be integers when x or y isn't a whole number in the 32-bit range, or is a BigInt or a string. distance() distance(a, b) returns the grid distance between two cells, as the Dofus client measures it: how far apart their x are, plus how far apart their y are. distance(300, 301); // 2 So the next cell to the south-east, south-west, north-east or north-west is 1 away, and the next cell to the east, west, north or south is 2 away. Error Type Cause pathfinding.distance a must be an integer cell id, and the same with b TypeError The cell isn't a whole number in the 32-bit range, or is a BigInt or a string pathfinding.distance cells: pathfinding: not a map cell, cell ids go from 0 to 559 TypeError A cell is outside 0 to 559 direction() direction(from, to) returns the direction from one cell to another, or undefined when the two cells aren't on one line. direction(300, 301); // 0, east direction(300, 316); // undefined Two cells on one line give that line's direction, whatever their distance. The same cell twice gives 1, as it does in the Dofus client. Error Type Cause pathfinding.direction from must be an integer cell id, and the same with to TypeError The cell isn't a whole number in the 32-bit range, or is a BigInt or a string pathfinding.direction cells: pathfinding: not a map cell, cell ids go from 0 to 559 TypeError A cell is outside 0 to 559 Walk along a path A walk takes two exchanges with the game server: The script sends MapMovementRequest with the path's keyCells, the cautious flag and the map's id. The server grants the walk with MapMovementEvent, whose cells list every cell of the walk. The server sends that message for every actor that walks on the map, so compare its characterId with the character's. It can also grant a shorter walk than the one asked for. Once the walk is over, the character confirms it with MapMovementConfirmRequest, which has no field, and the server answers with MapMovementConfirmResponse. Until then, the server considers the character still walking. Who sends the confirmation depends on the bot: Bot Who sends MapMovementConfirmRequest Full socket Your script, duration milliseconds after MapMovementEvent arrived. Nothing else will. MITM, where session.shared is true The Dofus client, once it has walked the character on screen. Your script only waits for the server's answer. import { currentMap, session } from "asterobot:bot"; import { findPath } from "asterobot:pathfinding"; import { send, wait } from "asterobot:protocol"; import { sleep } from "asterobot:timers"; async function walkTo(cell) { const map = currentMap(); if (map?.cellId === undefined) return; const others = map.actors.filter((actor) => actor.id !== map.characterId); const path = findPath(map.mapId, map.cellId, cell, { occupied: others.map((actor) => actor.cellId), }); if (!path || path.keyCells.length === 0) return; await Promise.all([ wait( (traffic) => traffic.type === "MapMovementEvent" && traffic.payload?.characterId === map.characterId, { timeout: 5000 }, ), send("MapMovementRequest", { keyCells: path.keyCells, cautious: false, mapId: map.mapId }), ]); if (session.shared) { // The Dofus client confirms the walk once it has walked it. await wait("MapMovementConfirmResponse", { timeout: path.duration + 5000 }); return; } await sleep(path.duration); await Promise.all([ wait("MapMovementConfirmResponse", { timeout: 5000 }), send("MapMovementConfirmRequest", {}), ]); } wait() comes first in each Promise.all, so it's already listening when the message goes out, as Send messages explains. This sample times the confirmation with the route it asked for. When the server grants a shorter walk, time the walk it granted instead, for example with findPath() from the first of its cells to the last. Start one walk at a time: a walk started before the previous one ends would start from a cell the character hasn't reached yet. To stop a walk before its end, the Dofus client sends MapMovementCancelRequest with the cell the character stopped on, and the server doesn't answer it. stepEnds tells you which step the character has reached at a given moment. move() does all of this in one call, shorter walks and both kinds of bots included. asterobot:bot describes currentMap(), and the Dofus protocol reference lists the fields of the movement messages.
  11. Smooth a posté un record dans Reference
    asterobot:parameters gives a script the current values of its package's settings: the ones declared with export const parameters and shown on the bot's Settings tab, under Package settings. Declarations describes how to declare them, and Read settings shows how to use them. import { values, onChange, offChange } from "asterobot:parameters"; Whose settings Each package gets its own copy of this module. Any file of a package that imports it reads that package's settings, declared in that package's index.js, and nothing else. A dependency reads its own settings, never those of the package that uses it, and no package can read another package's settings. Settings in dependencies explains how that looks on the bot's page. values values is a read-only object with one property per declared setting. Declared type Value in values bool A boolean string A string int A whole number. It's a regular number, not a BigInt, unlike 64-bit fields in game messages. double A number array An array of values of the of type map An object whose keys are strings and whose values have the of type The values are the ones saved for this bot, on top of the declared defaults. Settings are saved under the package's name, whatever its version, so they carry over when the bot moves to a new version. A saved value that no longer fits the declaration, because the setting was renamed, removed or given another type, is ignored, and the default applies. values is live. When someone clicks Apply on the bot's page, the running script reads the new values from its next read on, without restarting. A package's settings are applied all at once, so a script never reads half of an edit. Don't assign to values or delete from it. Package code always runs as a module, which JavaScript makes strict, so Asterobot's refusal throws: assigning a property throws a TypeError with 'Set' on a dynamic object returned false, and deleting one throws Could not delete property "<name>" of a dynamic object. Either way the setting keeps the value set on the bot's page. Object.keys(values) lists the settings, in no particular order, and "name" in values tells whether a setting exists. When Asterobot can't read the package's declarations, the bot's page shows Couldn't read this package's settings, and the script starts with no settings at all: values is empty, and every read returns undefined. While Asterobot reads the declarations themselves, values is an empty object too, so a declaration can't depend on a setting's value. onChange() onChange(handler) returns a handle, { id }, where id is a BigInt. Parameter Type Description handler function Called with { name, value } for each setting whose value changed When settings of this package are applied to the running script, handler runs once for each setting whose value actually changed, with the setting's name and its new value, of the type shown in the table above. Settings applied with the value they already had don't call it, and arrays and maps count as changed only when their content differs. It doesn't run when the script starts, and it never runs for another package's settings. With several handlers, the order they run in isn't defined. Settings applied while no script runs are saved, and the next start reads them from values. In the rare case where the script already has 32 operations pending when someone applies settings, the settings are saved, but the running script only gets them at its next start. See Limits. An error in a handler stops the script: What happened Text in the Problems alert The handler threw parameters.onChange handler <number>: <error> An async handler's promise rejected unhandled Promise rejection: <error> onChange() throws a TypeError at once when: Error Cause parameters.onChange handler must be callable handler isn't a function maximum behavior handlers reached 256 onChange() handlers are already registered. This count is separate from the message handlers of asterobot:protocol. If the script is already stopping, onChange() throws the reason it's stopping. onChange(({ name, value }) => { botInfo(`Setting ${name} is now ${value}`); }); offChange() offChange(handle) returns undefined. It removes the handler that onChange() registered, given the handle it returned. It never throws: a handle that was already removed, or anything else, is ignored. It doesn't remove handlers registered with on(), onTraffic() or intercept(): use off() for those. Types Type Definition ParameterScalar A boolean, a string or a number ParameterValue A ParameterScalar, a read-only array of them, or a read-only object of them ParameterChange { name: string, value: ParameterValue }, what onChange() handlers receive ParameterSubscription { id: bigint }, the handle onChange() returns When a value applied from the bot's page doesn't fit its declaration, nothing is saved and the page shows Couldn't apply the settings with the reason. Error messages lists those reasons.
  12. Smooth a posté un record dans Reference
    asterobot:runtime lets a script read facts about the packages it's made of: its own package, the packages it depends on, and the built-in asterobot: modules. Nothing in it can change a package. import { packages } from "asterobot:runtime"; packages is the module's only export. It's a frozen object with one function, info(). There's no flat info export, so it can't be confused with info() from asterobot:console. packages.info() packages.info(name) returns a frozen PackageInfo object. Parameter Type Description name string A package name, such as "my-first-script" or "@alice:pathfinder", or the name of a built-in module, such as "asterobot:protocol" The names it knows are: every package in the running script: the bot's package and, all the way down, the packages its dependencies lock; the built-in modules asterobot:protocol, asterobot:bot, asterobot:timers, asterobot:gamedata, asterobot:console and asterobot:runtime. asterobot:parameters isn't one of them: each package has its own copy of that module, and asking for it throws package "asterobot:parameters" is not installed. An inline script, loaded as code rather than as a library package, is the package api-script with the version local. packages.info() throws when: Error Type Cause packages.info requires a module or package name TypeError name is missing, empty or not a string. Despite the wording, a module path isn't accepted. package "<name>" is not installed Error No package in the running script has that name package "<name>" is ambiguous across <count> locked versions Error The script's dependencies lock more than one version of that package, so the name alone doesn't say which const self = packages.info("my-first-script"); botInfo(`${self.name} ${self.version}, ${self.provenance}`); On a local package, this writes my-first-script 1.0.0, local to the bot's console. packages.info() also answers while Asterobot reads the package's declarations, so a declaration can use it. PackageInfo Property Type Value name string The package's name version string The package's version. "embedded" for a built-in module. publisher string The part before the colon in @publisher:name: "asterobot" for official packages and built-in modules, "" for a local package provenance string "local", "official", "community", or "builtin" for a built-in module integrity string The checksum Asterobot recorded when it installed the package from the marketplace, starting with sha256-. "" when there's none, as for a local package, and "embedded" for a built-in module. permissions array of strings The permissions listed in the package's asterobot.json. An empty array when it lists none, and always for a built-in module. compatibleVersion string The Asterobot versions the package declared it works with, such as ">=1.5.0 <2.0.0", or "" The type in the editor also lists "builtin-native" as a possible provenance. Asterobot never returns it. A package's provenance comes from its name: a name starting with @asterobot: is official, another name starting with @ is community, and a name without @ is local. Names and versions explains the rules, The manifest describes asterobot.json, and Compatibility covers compatibleVersion. Permissions permissions only reports what a package declares. Asterobot doesn't check it today: any package can use every function of every built-in module, whatever it lists. Note Coming soon. Asterobot will enforce the permissions a package declares. Until then, treat the list as information for the people who install your package. Permissions has the details.
  13. Smooth a posté un record dans Reference
    asterobot:console writes to Asterobot's own log, the one shown in Server > Console and in the terminal Asterobot runs in. It's for a script's diagnostics. To show something to the person watching a bot, use botInfo() and the other functions of asterobot:bot instead: they write to that bot's own console. import { console, debug, log, info, warn, error } from "asterobot:console"; Scripts have no global console. The console here is an object this module exports, so it has to be imported like the rest. Exports Export Level of the line debug(...values) debug log(...values) info. log is the same function as info. info(...values) info warn(...values) warn error(...values) error console A frozen object with the same five functions: console.debug(), console.log(), console.info(), console.warn() and console.error() Each function takes any number of values, writes them as one line joined with spaces, and returns undefined. They never throw. info("Route computed in", elapsed, "ms"); console.warn("No zaap found for map", mapId); Where the lines appear Lines go to Server > Console and to the terminal. By default, debug lines are left out of both: set Server level to DEBUG on Server > Console to see them there. In Server > Console, a line doesn't say which bot wrote it. When several bots run scripts, write the bot's name or something that identifies it into the text yourself. How values print Values are turned into text the same way as for botInfo(): strings, numbers and booleans print as JavaScript would, a BigInt prints without its n, undefined and null print <nil>, an array prints as [1 2 3], an object as map[name:value], and an Error as map[]. Log String(error) for errors. The table on asterobot:bot has every case. At a module's top level Code at the top level of a module also runs while Asterobot reads the package's declarations, and what these functions write at that moment is thrown away. The same code runs again when the script starts, and its lines appear then. Logging and debugging compares the two consoles and when to use each.
  14. Smooth a posté un record dans Reference
    asterobot:gamedata reads the texts and the data of the game version the bot uses: items, monsters, maps, spells and everything else Asterobot extracts from the game files. It only reads. Game data overview, Lookups and SQL show how to use it. import { text, record, table, find, search, query } from "asterobot:gamedata"; These are the module's only exports. There's no gamedata object to import. Export Returns What it reads text() A string or undefined, right away One game text, by id or key record() An object or undefined, right away One row of a table, by id table() A promise of an array The rows of a table, a page at a time find() A promise of an array The rows whose columns equal given values search() A promise of an array Records found by their name query() A promise of a result The answer to a read-only SQL query What the data is The data comes from the game version the bot is set to. text() and search() answer in the language the script was started with, session.language, which is fr for every start made from Asteroboard today. Asterobot looks for the data when the script starts. When that game version has no extracted game data at that moment, record() returns undefined, and table(), find(), search() and query() resolve with undefined rather than an array, until the script starts again after the data is extracted. When Asterobot can't load the game's texts in that language, text() returns undefined for every key. Extract game data explains how to get the data. Table and column names, such as monsters or name_id, are the ones of the extracted data, and they can change between game versions. Browse them on the Game page's game data (see Game data), and try queries in the SQL tab. In the rows these functions return, numbers are regular JavaScript numbers, not BigInt. A packed column arrives as an array of objects, and a JSON column as the value it holds. record(), table() and find() leave empty columns out of their objects. table(), find(), search() and query() share these rules: They reject after 30 seconds, and there's no option to change that. Each call counts as one of the 32 operations a script can have pending at once, along with send() and request(). Past that, the promise rejects with behavior resource limit exceeded: maximum pending operations reached. limit defaults to 200 rows and can't go above 5000. A limit or offset that isn't a whole number greater than 0 is ignored. text() text(key) returns a string, or undefined. Parameter Type Description key number or string A text id, as found in columns such as name_id, or a text key such as "ui.common.classic" It returns the text in the script's language, or undefined when no text has that id or key. When the game's texts are loaded, text() throws a TypeError with gamedata.text key must be a string or integer for any other key: a fraction, a number outside the 32-bit range, a BigInt, undefined or an object. Check a value exists before passing it. record() record(table, id) returns an object, or undefined. Parameter Type Description table string The table's name, such as "monsters" id number The record's id: a whole number, not a BigInt It returns the row with that id, or undefined when there's none. Error Type Cause gamedata.record table must be a non-empty string TypeError table is missing, empty or not a string gamedata.record id must be an integer TypeError id isn't a whole number, is too big to be exact in JavaScript, or is a BigInt or a string datacenter: no such table: "<table>" Error This game version has no table with that name. String(error) gives GoError: datacenter: no such table: "<table>". const monster = record("monsters", 31); if (monster) botInfo("Monster 31 is", text(monster.name_id)); table() table(name, options?) returns Promise<object[]>. Parameter Type Description name string The table's name options.limit number How many rows to return. Default 200, at most 5000. options.offset number How many rows to skip first. Default 0. It resolves with the rows, in the order of their ids. A call without options returns the first 200 rows, not the whole table. The promise rejects with gamedata.table name must be a non-empty string when name isn't a non-empty string, and with datacenter: no such table: "<name>" for a table this game version doesn't have. const page = await table("items", { limit: 50, offset: 100 }); find() find(name, filter, options?) returns Promise<object[]>. Parameter Type Description name string The table's name filter object Column names and the values they must equal. A row matches when every column equals its value. options.limit number How many rows to return. Default 200, at most 5000. offset is ignored. Error Cause gamedata.find table must be a non-empty string name isn't a non-empty string gamedata.find filter must be a non-empty object filter is missing, empty, an array, or not an object datacenter: no such table: "<name>" This game version has no table with that name datacenter: no such column: table "<name>" has no column "<column>" A filter column doesn't exist in the table const spots = await find("maps__interactive_elements", { gfx_id: 1018 }); search() search(query, options?) returns Promise<Match[]>. Parameter Type Description query string The name, or part of it options.table string Only search this table's records, such as "monsters". Without it, every named record is searched. options.limit number How many matches to return. Default 200, at most 5000. Each word of query matches the beginning of a word, so a partial name works. Shorter names come first. Without options.table, the results take one match from each table in turn, so the first results show every kind of thing that has that name. A query made only of spaces resolves with an empty array. Error Cause gamedata.search query must be a non-empty string query isn't a non-empty string gamedata.search options.table must be a string options.table is there but isn't a string datacenter: no such table: "<table>" This game version has no table with that name datacenter: no text for this language: "<language>" The extracted data has no names in the script's language const [hit] = await search("Bouftou Royal", { table: "monsters" }); if (hit) botInfo(hit.name, "is monster", hit.id); query() query(sql, params?, options?) returns Promise<QueryResult>. Parameter Type Description sql string One or more SQL statements params array Values for the ? placeholders, in order options.limit number How many rows to return. Default 200, at most 5000. The game data is opened read-only, so any statement that writes fails, and attaching another database is refused. Every statement in sql runs, and the result is the last one's: "SELECT 1; SELECT 2" answers 2. A query runs for at most 10 seconds, and its text can't be longer than 65,536 bytes. Pass values through params instead of writing them into the SQL: a number written into the text arrives as text, and doesn't equal the number stored in a column. In rows, an empty value is null. Error Cause gamedata.query sql must be a non-empty string sql isn't a non-empty string gamedata.query params must be an array params is there but isn't an array datacenter: empty query sql holds only spaces datacenter: query is <size> bytes, over the 65536 byte limit The SQL text is too long datacenter: followed by the database's message The SQL is wrong, names a table or column that doesn't exist, or tries to write, in which case the message mentions attempt to write a readonly database The promise also rejects when the query runs for more than 10 seconds. const { columns, rows, truncated } = await query( "SELECT _id, name_id FROM monsters WHERE _id = ?", [31], ); Types Type Shape Row An object whose keys are the table's column names Window { limit?: number, offset?: number }, the options of table() and find() SearchOptions { table?: string, limit?: number } QueryOptions { limit?: number } Match { table: string, id: number, nameId: number, name: string }: the record's table and id, to pass to record(), the id of the text its name comes from, and the name in the script's language QueryResult { columns: string[], rows: unknown[][], truncated: boolean }. columns lists the column names in order, and they can repeat, which is why each row is an array aligned with them. truncated is true when the query had more rows than limit.
  15. Smooth a posté un record dans Reference
    asterobot:timers has a single export, sleep(). Scripts have no setTimeout() or setInterval(), so pauses, delays and repeated tasks are all built with it. import { sleep } from "asterobot:timers"; sleep() sleep(milliseconds) returns Promise<void>. Parameter Type Description milliseconds number How long to wait. 0 and fractions such as 2.5 are accepted. The promise resolves once the time has passed and the script gets to it: when other events are waiting to be handled, it resolves after them. While a script awaits sleep(), its message handlers keep running. The promise rejects with: Error Cause timeout/milliseconds must be a finite positive Number milliseconds is negative, NaN or Infinity, or isn't a number at all. A BigInt such as 1000n and a string such as "1000" are refused too. timeout/milliseconds is too large milliseconds is too big to be a duration, more than about 292 years behavior resource limit exceeded: maximum timers reached 128 calls to sleep() are already pending. See Limits. When the script stops while a sleep() is pending, its promise rejects with the reason the script stopped, such as behavior runtime closed after Stop. Repeating a task Start the loop without awaiting it, so the entry function can finish. A start only counts as successful once that function has returned, which matters for launch.reason. import { session, botInfo, botWarn } from "asterobot:bot"; import { send } from "asterobot:protocol"; import { sleep } from "asterobot:timers"; async function everyMinute() { while (true) { await sleep(60000); botInfo("Still running"); } } export default async function behavior(launch) { // Not awaited, and caught: a rejection nobody catches stops the script. everyMinute().catch((error) => botWarn("Timer loop stopped:", 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, }); } } When you click Stop, the pending sleep() rejects, so the console shows Timer loop stopped: behavior runtime closed as the script ends. Don't poll in a tight loop to wait for a message: await wait(selector, { timeout }) from asterobot:protocol resolves as soon as the message arrives. And never keep the script busy without an await: code that runs for more than 250 ms in one go stops the script. Async and timing explains why.

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.