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

Tout ce qui a été publié par Smooth

  1. Smooth a posté un record dans Tutoriels
    A script that has the bot's chat module sell wheat for you, at a price from the package settings, with an ad in Trade, an answer giving the stock to whoever asks the price, and a whisper to every buyer, which the script hears. This script has the bot's chat module sell wheat for you. It posts an ad in Trade every few minutes, answers whoever asks the price with what's left in the bags, and whispers every player who asks for wheat in Trade. The price comes from the package settings, so you change it on the bot's page while the script runs. The script writes no chat logic of its own: it hands the module a chat profile and a price, and the module does the rest through its guard, waiting out each channel's cooldown and keeping to the game's limits. You need to know how to create a package and run it, as in Create a package. The script Create a package named wheat-seller, replace the contents of its index.js with this code, and save: import { botInfo, botWarn } from "asterobot:bot"; import { itemCount } from "asterobot:character"; import { onAnswer, onChat, setDryRun, setProfiles, setVariable } from "asterobot:chat"; import { values, onChange } from "asterobot:parameters"; // 289 is Wheat. const WHEAT = 289; /** @type {BehaviorParameters} */ export const parameters = { price: { type: "int", label: "Price of one wheat", unit: "kamas", default: 12, min: 1, }, dryRun: { type: "bool", label: "Dry run", description: "Write the messages in the bot's console instead of sending them.", default: true, }, }; // The chat profile the bot talks from while the script runs: one ad, and // one trigger for the buyers in Trade. const shop = { version: 1, name: "Wheat seller", // The script's own price wins over this one. variables: { price: "12" }, ads: [ { name: "Wheat in Trade", channels: ["SALES"], messages: ["{Selling|WTS} {item:289} at {price} k each, /w me", "{item:289} for sale, {price} k, /w {name}"], every: { min: 180, max: 300 }, }, ], triggers: [ { name: "buyer", when: { all: [ { channel: { is: "SALES" } }, { "sender.myBot": false }, { text: { keywords: ["wtb", "buy"] } }, { any: [{ text: { keywords: ["wheat"] } }, { items: { has: WHEAT } }] }, ], }, do: [{ whisper: ["{Hi|Hello} {sender}, I sell {item:289} at {price} k each"] }, { script: true }], }, ], }; export default async function behavior() { setVariable("price", values.price); setDryRun(values.dryRun); setProfiles([shop]); onChange(({ name }) => { if (name === "price") setVariable("price", values.price); if (name === "dryRun") setDryRun(values.dryRun); }); // Asked first about each private message the bot gets. A price question // gets the stock too; anything else is left to the profiles' replies. onAnswer(({ kind, line }) => { if (kind !== "reply" || !/\b(price|how much)\b/i.test(line.content)) return; const left = itemCount(WHEAT); if (left === undefined) return; if (left === 0) return "Sorry {sender}, no wheat left for now"; return `{Hi|Hello} {sender}, ${left} {item:289} left, {price} k each`; }); onChat((event) => { if (event.type === "trigger" && event.name === "buyer") { botInfo(`Buyer in Trade: ${event.line.senderName}: ${event.line.content}`); } if (event.type === "soldOut") botWarn("Out of wheat: the ad waits for more"); }); } How it works The profile comes from the script setProfiles([shop]) gives the bot the script's own profile, in place of the ones you picked on its Chat tab, for as long as the script runs. It's an object in the chat profile format, which Asterobot checks as it checks a saved profile: a mistake throws a TypeError naming the field. When the script stops, the bot goes back to your profiles. The ad links the wheat with {item:289}, as the game links an item, from the character's inventory. While the character has none, the ad waits, and the Chat tab says why. {Selling|WTS} picks one wording at random each time, and the ad says one of its two messages at random, never the same twice in a row. The price is a variable The messages read {price}, which setVariable() sets from the package setting. When you apply a new price on the bot's page, onChange() sets the variable again, and the next message says it. The profile, the ad's counts and its next send stay as they were. The script's variables win over the profile's, so the profile's own price is never said while the script runs. It's there because Asterobot checks the profile on its own, and warns in the console about a variable the profile reads but doesn't set. The trigger The trigger fires on a line said in Trade, by a player who isn't one of your bots, with the word wtb or buy, and either the word wheat or a link to wheat. all and any combine the tests as in a fight rule. Keywords are whole words, capitals and accents ignored, so WTB Wheat fires it and wheatgrass doesn't. Its actions run in order. The whisper waits the time a person would take to read the line and type the answer, and {sender} names who said the line. Then script: true tells the script: onChat() gets { type: "trigger", name: "buyer", line }. A player fires it at most once an hour, the default. The answer onAnswer() is asked about each private message before the profile's reply rules. A text answers: the bot whispers it back after the reading and typing time, filled in like any message, so {sender}, {price} and the item link work. return with nothing leaves the message to the reply rules, and this profile has none, so the bot says nothing. itemCount() is undefined until the game has sent the inventory. The handler then answers nothing rather than say a wrong number. The dry run The Dry run setting starts on: the bot writes every message in its console, such as Dry run: would say in Trade: ..., instead of sending it. Everything else runs as if they were sent, the ad's wait and the trigger's limit included. Turn it off once the messages read well. Run it Try it on a MITM bot whose character is in the game, with some wheat in its bags, as Your first bot sets up. On the bot's page, open the Chat tab and turn on Chat module. On a relayed session only your switch counts: a script can't turn the module on there. Leave Ads, Replies and Triggers on. In the editor, pick the bot and click Run. Back on the Chat tab, the tab says the running script sets the profile Wheat seller and the dry run, and the Ads section shows Wheat in Trade with its next send. Open the Console. Within a few seconds, a line starting Dry run: would say in Trade: writes the ad. The replies and the triggers answer neither your own bots nor yourself, so try them with a friend, or with an account this Asterobot has never played: Have them whisper the bot price?. A few seconds later, Dry run: would say to their name writes the answer, with the stock. Have them say wtb wheat in Trade. The console says Trigger buyer: with their name and line, then, a few seconds later, the whisper, then the script's Buyer in Trade: line. On the bot's Settings tab, under Package settings, change Price of one wheat and click Apply. The next ad says the new price. Turn off Dry run and click Apply. The bot now sends for real. Click Stop: the Chat tab goes back to your own profiles, and the ad stops. Ideas to take it further Sell more than wheat. Make the price a map setting of item ids to prices, set one variable per item, and give the profile an ad per item. Open hours. Add when: { flag: "open" } to the ad, and have the script call setFlag("open") and setFlag("open", false) when it decides the shop opens and closes. Get notified. Add { raise: "buyer" } to the trigger's actions, and a rule on the Alarms page that takes the trigger:buyer event and notifies Asteroboard. Ship the profile. Save the profile as chatprofiles/wheat-seller.json in the package, as Package layout shows, and pass its name to setProfiles(): people can then copy it and make it their own. The asterobot:chat reference covers every function used here.
  2. Smooth a posté un record dans Reference
    Raise an event of your own from a script for the alarms' rules to take, such as an item found under a price, and hear every event of the bot as it happens: a member of staff, a whisper, the character's name said, the chat module's events, a trigger's, and the game messages the rules hook. asterobot:alarms connects your script to the bot's alarms. It raises events of its own, which the rules on the Alarms page take like any other, and it hears every event of the bot as it happens, whatever the rules say. import { raise, onAlarm, offAlarm } from "asterobot:alarms"; These are the module's only exports. Export Returns What it does raise() Nothing Raise a script:<name> event for the bot onAlarm(), offAlarm() A subscription, nothing Hear every event of the bot raise() raise(name, text?) raises the event script:<name> for the bot, its fields name and text. A rule taking script:<name>, or Every script event (script), fires on it, and its reactions read {name} and {text}. import { raise } from "asterobot:alarms"; // A script scanning the marketplace found what it looks for. raise("cheapItem", "Wheat at 8 k"); On the Alarms page, a rule with Events script:cheapItem and the reaction Notify shows "Bot-Name: script:cheapItem" with "Wheat at 8 k" in every open Asteroboard. The script decides what's worth an alarm; the person decides what follows. onAlarm() and offAlarm() onAlarm(handler) has handler hear every event of the bot, until offAlarm(subscription) or the end of the script, whether a rule takes it or not, except the events this script raised itself with raise(): a handler that raises what it hears can't loop. Each event is an object: Field What it holds name "staff", "whisper", "mention", "spam", "refused", "soldOut", "stopped", "trigger:<name>", "script:<name>" or "game:<Message>" bot, character, server The bot's name, its character's and its server's at When it happened, a Date fields What it says, as texts: reason, who, sender, channel, text, minutes... as the alarms guide lists them A game message is an event only while a rule on the Alarms page hooks it (game:<Message>): its fields are the message's, named as in the protocol with dots for nested ones, and json the whole message. To follow a message no rule hooks, listen to it with on() instead. The handler only observes: what it returns is ignored, and it can be async. A handler that throws stops the script. The script hears its own raise() too. import { onAlarm } from "asterobot:alarms"; import { stopTravel } from "asterobot:movement"; import { botWarn } from "asterobot:bot"; // Stop walking as soon as a member of staff shows up on the map, whatever the // rules on the Alarms page do. onAlarm(({ name, fields }) => { if (name !== "staff" || fields.reason !== "map") return; botWarn(`${fields.who} is on the map: standing still`); stopTravel(); }); While Asterobot reads declarations When Asterobot reads a package's settings, steps and conditions, it runs the top level of its modules without a bot (see Declarations). Every function of asterobot:alarms then throws, since there's no bot to raise an event for. Errors Error Type Cause alarms.raise name must be a non-empty string TypeError The name isn't a string, or is empty alarms.raise text must be a string TypeError The text is given and isn't a string alarms.onAlarm handler must be callable TypeError The handler isn't a function alarms.raise: this bot has no alarms Error Called while Asterobot reads declarations
  3. Smooth a posté un record dans Reference
    Say things in the game chat from a script through the bot's chat guard, which waits out a channel's cooldown and keeps to the server's limits, with templates that pick random wording, fill in the character's level, map or kamas, and link items from its inventory; switch the bot's chat module and pick its chat profiles while the script runs; answer the private messages the bot gets, and hear what the module does. asterobot:chat lets your script say things in the game chat through the bot's chat module, and set the module while it runs. Every message the bot sends goes through the module's guard, which keeps to the limits the game server sets and says: your script's messages, the ones typed in the bot's Console, the module's ads, replies and triggers' messages, and the alarms' lines. import { say, whisper, pause, resume, setVariable, variables, setFlag, flags, setDryRun, dryRun, enable, disable, enabled, setParts, parts, setProfiles, profiles, onChat, offChat, onAnswer, offAnswer, } from "asterobot:chat"; These are the module's only exports. There's no chat object grouping them. Export Returns What it does say() A promise Say a message in a channel whisper() A promise Whisper a message to a character pause(), resume() Nothing Stop sending for a while, or start again setVariable(), variables() Nothing, an object Set or read your own {variables} setFlag(), flags() Nothing, an array Set the flags the profiles' conditions test setDryRun(), dryRun() Nothing, a boolean Write messages to the console instead of sending them enable(), disable(), enabled() Nothing, a boolean Switch the chat module on or off setParts(), parts() Nothing, an object Switch parts of the module, such as its ads setProfiles(), profiles() Nothing, an array Pick the chat profiles the bot talks from onChat(), offChat() A subscription, nothing Hear what the module does onAnswer(), offAnswer() A subscription, nothing Answer the private messages the bot gets say() and whisper() return a promise. The other functions are synchronous. The guard The guard is the bot's one queue for chat messages. It sends one message at a time, and waits for the server's answer before the next: the server saying the message back, a refusal, or 5 seconds without an answer, which count as sent. Rule What happens A channel's cooldown When the server answers "Use of this channel is restricted. You will be able to post again in 37 seconds.", the guard waits 37 seconds and 1 more, then sends the message once more. Messages to other channels go on meanwhile. Refused a second time, the message is dropped. At most 6 messages a minute Counted for the bot, whispers included, 6 unless the bot's Chat tab sets another number, from 1 to 30. A message past the limit waits its turn. "Too much spam!" Nothing is sent for 5 minutes, then 10, 20, 40, and 60 at most for each warning after it. An hour without a warning starts over at 5. A mute When a member of staff mutes the character, nothing is sent until the mute ends, plus a minute. The game's "is letting you speak again" ends it at once. A channel that refuses the character No guild, no group, a level or a subscription short, a beginners' channel, prison: your messages to that channel are refused for the rest of the game session. "This channel cannot be used on this map" lasts until the next map. Items that can't be shown When the channel refuses item links, the message goes again with each item written as [its name], and so do the next messages to that channel this session. The person's messages, typed in the Console, go first. A pause, a back-off or the limit doesn't hold them back: only a channel's cooldown does. The alarms' lines come next, and a pause doesn't hold them back either. Then come the answers to commands, the replies and the triggers' messages, and last your script's messages and the ads. say() and whisper() say(channel, text) says a message in a channel. whisper(name, text) whispers it to the character of that name. Argument What it is channel A channel as the protocol names it: "SALES" is Trade, "SEEK" Recruitment, "GLOBAL" General, "GUILD", "PARTY" the group, "ALLIANCE"... name The name of the character to whisper text A template The promise resolves once the server took the message, with { dryRun: false }, or { dryRun: true } in a dry run. It rejects with an Error whose reason says why the message wasn't sent: reason Why "notInGame" The character isn't in the game "paused" Sending is paused "spam" The guard is backing off after "Too much spam!" "muted" A member of staff muted the character "cooldown" The channel was still in its cooldown when the message went once more "refused" The server refused the message or its channel, or the whisper's recipient isn't connected "tooLong" The message is past the game's 256 characters or 16 item links "template" The template can't be read "missingVariable" A variable has no value "missingItem" An item to link isn't in the inventory "stopped" The script stopped before the message went "sendFailed" The message didn't reach the server import { say, setVariable } from "asterobot:chat"; import { botWarn } from "asterobot:bot"; setVariable("price", 12); try { await say("SALES", "{Selling|WTS} {item:289} at {price} k each, /w me"); } catch (error) { if (error.reason === "missingItem") botWarn("No more wheat to sell"); else throw error; } When the script stops, its messages that are still waiting are dropped. A message already sent waits for its answer. Templates A template is the text of a message with parts that are filled in each time it is said. Write Says {Hi|Hello|Hey} One of the choices, at random each time. Choices can be nested, {Hi|Hello {there|you}}, and empty, {!|}. {item:289} A link to that item, from the character's inventory, as the game makes one. An item in the bags is linked before a worn one. {name} The character's name {level} Its level {server} Its server's name {subarea}, {area} Where it is, in the bot's language {position} A link to its map, as the game makes [4,-19] typed in its chat box: {{map,4,-19,1}} in the text, which the game shows as [4,-19] {kamas} Its kamas {pods} Its pods, used and maximum, such as 1200/3400 {time} The computer's time, HH:MM {anything else} A variable you set with setVariable() Nothing broken goes out: a message isn't sent when one of its variables has no value, an item it links isn't in the inventory, or, filled in, it's longer than the game's 256 characters or links more than 16 items. The length counts each item link as the game's code for it, which is longer than the item's name, so that the server never finds the message too long. A template can't be read when a { has no } or the reverse, when {} is empty, when a part like {a {name}} nests without a |, or when it has a colon that isn't an item link, such as {a:b}. pause() and resume() pause(seconds) stops the module's and your scripts' messages for that many seconds. Without seconds, it lasts until resume(). The messages waiting are dropped with "paused", and so are the ones said while it lasts. The person's messages from the Console still go. A pause outlives the script that set it, like a button the person pressed. import { pause } from "asterobot:chat"; // Nothing more for ten minutes. pause(600); setVariable() and variables() setVariable(name, value) sets a variable your messages read as {name}. A number or a boolean is written as text; null or undefined deletes the variable. variables() returns your variables, as a copy. The character's own variables, such as {name} or {level}, win over yours of the same name, and yours win over the bot's chat profiles'. Your variables last until the script stops, or until a newer script, after a reload, sets its own. The module's ads read them too. setFlag() and flags() setFlag(name) sets a flag, which the flag test of the bot's chat profiles reads (Conditions); setFlag(name, false) clears it. flags() returns the names of the flags set. Your flags last until the script stops, or until a newer script sets its own. import { setFlag } from "asterobot:chat"; // The profile's "shop open" ad runs from now on. setFlag("shopOpen"); setDryRun() and dryRun() setDryRun(true) writes the module's and your scripts' messages to the bot's console instead of sending them: Dry run: would say in Trade: Selling [Wheat] at 12 k each. Everything else goes on as if they were sent, the limit of 6 a minute included. The person's messages from the Console are always sent. dryRun() says whether a dry run is on. The dry run lasts until the script stops, or until a newer script sets its own. enable(), disable() and enabled() enable() switches the bot's chat module on while the script runs, and disable() off: with the module off, the bot posts no ad, answers no private message, fires no trigger, obeys no command and leaves its status alone. enabled() says whether the module runs now. The switch the person saved on the Chat tab comes back when the script stops. On a relayed session, only the person's switch counts: enable() and disable() change nothing there, and enabled() says what the person set. setParts() and parts() setParts(parts) switches the parts it names while the script runs, the others staying as they are: setParts({ ads: false }) keeps the module on but stops its ads. parts() returns every part, on or off: { ads, replies, triggers, commands, status }. setProfiles() and profiles() setProfiles(list) sets the bot's chat profiles while the script runs, in order, in place of the person's. Each item is the name of a profile, as the bot's Chat tab lists it, or a profile object in the chat profile format, which crosses into Asterobot as JSON.stringify() writes it and is checked as a saved profile is. profiles() returns the profiles in effect, as plain objects in that format, copies: changing one changes nothing until you pass it to setProfiles(). import { setProfiles, setVariable } from "asterobot:chat"; setVariable("price", 11); setProfiles([ "Sell wheat in Astrub", { version: 1, name: "Script ad", ads: [{ channels: ["SALES"], messages: ["{item:289} at {price} k"], every: { min: 300, max: 600 } }], }, ]); The ads of a new list start over: their sends counted from zero, their first send as soon as they can. Setting the same profiles again keeps their counts. A profile's warnings, such as a variable no profile sets, go to the bot's console as chat.setProfiles[1]: <warning>. onChat() and offChat() onChat(handler) has handler hear what the module does, in the order it happens, until offChat(subscription) or the end of the script. Each event is an object: Field What it holds type "sent": a line went, or was written to the console in a dry run. "replied": a reply went. "refused": a line wasn't sent. "stopped": an ad's stop ended it. "soldOut": an item an ad links ran out. "backoff": every line waits, after "Too much spam!" or a mute. dryRun Whether the line was written to the console instead of sent who Who said the line: "person", "alarm", "command", "reply", "trigger", "script" or "ad" where In which channel, or to whom; for a stop or an item sold out, the ad text The line reason, detail Why a line wasn't sent, as say() rejects; which stop ended an ad; which item ran out; why every line waits Each field without a value is left out. The handler only observes: what it returns is ignored, and it can be async. A handler that throws stops the script. A trigger with the script action gives an event of its own when that action's turn comes, after the actions before it are done, { type: "trigger", name, line }: the trigger's name, and the line it fired on, { senderName, senderAccountId, senderCharacterId, content, channel }, the ids as BigInts. A trigger without that action fires without the script hearing it. import { onChat, whisper } from "asterobot:chat"; onChat(async (event) => { if (event.type !== "trigger" || event.name !== "wtb") return; const wanted = event.line.content.match(/\d+/)?.[0] ?? "some"; await whisper(event.line.senderName, `{Hi|Hello} ${event.line.senderName}, I can sell you ${wanted}`); }); import { onChat } from "asterobot:chat"; import { botWarn } from "asterobot:bot"; onChat(({ type, where, detail }) => { if (type === "soldOut") botWarn(`${where}: ${detail} sold out, restocking`); }); onAnswer() and offAnswer() onAnswer(handler) has handler asked first about each private message the bot's replies get, until offAnswer(subscription) or the end of the script. It's asked with { kind: "reply", line }, line being the message: { senderName, senderAccountId, senderCharacterId, content, channel }, the ids as BigInts. It answers: Answer What the bot does A text Whispers it back, as a template where {sender} names who wrote, after the time a person would take to read and type it { skip: true } Says nothing Nothing, null, undefined or an empty text The next handler is asked, then the profiles' reply rules decide The handler can be async: the bot waits up to 30 seconds for its answer, the reading and typing time running meanwhile. Past that, or when it throws or answers something else, the reply rules decide and the console says so. Its answers aren't counted by the profiles' answers per player, so it can hold a conversation. It's never asked about a message from one of your own bots or from a member of staff. It's also asked about a command a profile takes but doesn't know, with { kind: "command", line, command, args }: !sell 10 wheat from a trusted sender is command "sell", args ["10", "wheat"]. A text answers the sender; nothing leaves the command ignored. import { onAnswer } from "asterobot:chat"; import { memory } from "asterobot:bot"; onAnswer(({ kind, line }) => { if (kind !== "reply" || !/price|prix/i.test(line.content)) return; return `{Hi|Hello} {sender}, ${memory.get("wheatPrice") ?? 12} k each`; }); While Asterobot reads declarations When Asterobot reads a package's settings, steps and conditions, it runs the top level of its modules without a bot (see Declarations). Every function of asterobot:chat then throws, or rejects for say() and whisper(), since there's no chat module to talk through. Errors These are thrown at once, or reject the promise of say() and whisper(): Error Type Cause chat.say channel "<name>" isn't a channel: write it as the protocol does, "SALES" for Trade TypeError The channel name is unknown chat: PRIVATE isn't a channel to say something in Error A whisper's channel, or one the game doesn't show: use whisper() chat.say must be a string, chat.whisper must be a string, chat.whisper name must be a string TypeError An argument isn't a string chat.pause seconds must be a positive number TypeError Seconds that are 0, negative, or not a number chat.setVariable name must be a string TypeError The name isn't a string chat.setVariable value must be a string, a number or a boolean TypeError An object, an array or a function as the value chat.setFlag name must be a string TypeError The name isn't a string chat.onChat handler must be callable, chat.onAnswer handler must be callable TypeError The handler isn't a function maximum behavior handlers reached TypeError The script holds as many handlers as a script can chat.setParts takes an object, { ads: false } TypeError The parts aren't an object chat.setParts: chat: invalid settings: "<part>" isn't a part: write ads, replies, triggers, commands or status TypeError An unknown part chat.setProfiles takes an array of profiles' names or profile objects TypeError The list isn't an array chat.setProfiles[<i>] must be a profile's name or a profile object TypeError An item that is neither chat.setProfiles[<i>]: chat: malformed profile: <field>: <why> TypeError A profile object Asterobot can't accept, naming the field chat.setProfiles: chatprofiles: no profile has this name: "<name>" Error A name no profile has chat.setProfiles: chat: malformed profile: <field>: <why> Error A saved profile, named in the list, that can't be used chat.<function>: this bot has no chat module Error Asterobot is reading declarations The bot's Console shows these lines: Line Cause Dry run: would say in Trade: <message> A message in a dry run Waiting 38 s for the cooldown in Trade, then sending the line once more The server answered that the channel is in its cooldown The server said "Too much spam!": nothing is sent for 5 minutes A spam warning <moderator> muted the character for 15 minutes: nothing is sent until 21:40 A mute The mute is lifted: lines are sent again The mute ended Not said in Trade: <reason> A message typed in the Console, a reply or a command's answer that couldn't be sent Not answered <sender>: <reason> A reply whose message can't be filled Command !stop from <sender>: the script is stopped A command, and what the bot did Trigger wtb: <sender> in Trade: <line> A trigger fired Trigger wtb: whisper not done: <reason> One of a trigger's actions failed Trigger wtb: the chat is paused for 600 s A trigger's pauseChat Dry run: would invite <sender> to the party, Dry run: would ignore <sender>, Dry run: would play smiley 3 A trigger's action in a dry run Couldn't ignore <sender>: the list is full The game refused a trigger's ignore Status set to away by the chat profiles The status part set the character's status The status changed elsewhere: the chat profiles leave it The status changed from the bot's header, the person's client or a script Status not set: <reason>, Status not set back: <reason> The status part couldn't set the status, or take back the one before The answer hook didn't answer <sender> within 30 s: left to the rules An onAnswer() handler took too long chat answer hook failed: <error>: the reply rules decide, chat answer hook rejected: <error>: the reply rules decide An onAnswer() handler threw, or its promise rejected chat answer hook answered something else than a text, { skip: true } or nothing: the reply rules decide An onAnswer() handler's answer of another kind
  4. Smooth a posté un record dans Types
    One modifier on your weapon attacks: which property, how it combines, and by how much. Kind struct · Area Game.Common One modifier on the character's weapon attacks: the weapon counterpart of SpellModifier. The full set arrives in CharacterCharacteristics.weapon_modifiers. HOW TO STORE IT 1. Store it like a SpellModifier whose spell id is 0: the official client keeps weapon modifiers in the same modifier table, under spell 0. 2. Key it by the character and WeaponModifier.modifier_type (SpellModifierType: which property it changes). Under that key keep one entry per WeaponModifier.action_type (SpellModifierActionType); a new modifier with the same action REPLACES the entry. 3. The entry's amount is WeaponModifier.value. A weapon modifier has no equipment part: the client stores 0 there. HOW TO USE IT Combine the amount with the weapon's value as SpellModifierActionType explains for spells: ACTION_SET replaces, BOOST adds, REMOVE_BOOST subtracts. Not sent on its own — a record carried by CharacterCharacteristics.weapon_modifiers, never sent on its own. inferred Used by CharacterCharacteristics FightControlEvent Fields #FieldTypeMeaningYou set it 1valueint32The modifier's amount. The client stores it where a spell modifier keeps its context amount; the equipment amount is 0 for a weapon.never 2modifier_typeSpellModifierTypeWhich property of the weapon attack is modified. A SpellModifierType; part of the entry's key.never 3action_typeSpellModifierActionTypeHow the amount combines with the weapon's value: add, subtract or replace. A SpellModifierActionType. One entry per action is kept for each modifier type.never
  5. Smooth a posté un record dans Types
    A guild or alliance member's social card: the record a character's social-member branch carries. Kind struct · Area Game.Common A member of a guild or alliance, as carried by CharacterBasicInformation.social_member. Beta declares it as breed, gender, state, hours since last connection, account id, status, rank id, enrollment date, AvA role and a guild-member block (given experience, mood, achievement points, note); this build's members are not named yet, so read it by structure only. Not sent on its own — a record carried by CharacterBasicInformation.social_member, never sent on its own. inferred Used by CharacterBasicInformation No fields you may set.
  6. Smooth a posté un record dans Types
    How the estate agency sorts the houses for sale. Kind enum · Area Game.Common Set in HousesToSellFiltersRequest.order_by. Both values are named: price ascending or descending. The official client's sort menu has more orders than this enum: its area sort sends 2 and 3, and its name sort 4 and 5. RealEstateOrder declares none of them. Not sent on its own — an enum carried by HousesToSellFiltersRequest.order_by, never sent on its own. inferred Used by HousesToSellFiltersRequest Values ValueNumberMeaning PRICE_ASC0Cheapest first. PRICE_DSC1Most expensive first.
  7. Smooth a posté un record dans Types
    A guild rank: the record a character's rank branch carries. Kind struct · Area Game.Common A guild rank, as carried by CharacterBasicInformation.rank. Beta declares it as an id, a name, its order and icon, and its rights; this build's members are not named yet, so read it by structure only. Not sent on its own — a record carried by CharacterBasicInformation.rank, never sent on its own. inferred Used by CharacterBasicInformation No fields you may set.
  8. Smooth a posté un record dans Requests
    Sets the share of experience your character keeps, from 0 to 100 percent. Kind request · Area Game.Character TO LIMIT THE EXPERIENCE YOUR CHARACTER RECEIVES 1. Send PlayerExperienceSetRequest.percentage = the share to keep, from 0 to 100. 100 removes the limit. 2. Wait for ServerExperienceModifierEvent: the official client takes the current limit only from that message, never from what it sent. The official client offers 0 to 100 in a number popup, and sends only when the value differs from the current limit. While the limit is below 100, its fight result window shows an "experience limited" warning. When you may send it Requires character_loaded inferred Raised by: the player confirms a new value in the experience-limit popup (character sheet, XP gauge menu or fight result window) proven Related messages You must then send ServerExperienceModifierEvent inferred — the client never stores the value it sends; its only write of the limit is from ServerExperienceModifierEvent Fields #FieldTypeMeaningYou set it 1percentageint32The share of experience to keep, from 0 to 100. 100 removes the limit. Observed: 50required
  9. Smooth a posté un record dans Types
    A character's details as a member of your party. Kind struct · Area Game.Common The party-member branch of CharacterBaseInformation: what the party shows about one member. Beta declares its common party stats, initiative, alignment, map coordinates, status, companions and rank; this build's members are not named yet, so read it by structure only. Not sent on its own — a record carried by CharacterBaseInformation.party_member_information, never sent on its own. inferred Used by CharacterBaseInformation No fields you may set.
  10. A character's details as a member of a party you are invited to. Kind struct · Area Game.Common The party-invitation branch of CharacterBaseInformation: what an invitation shows about one member of the inviting party. Beta declares its map coordinates and companions; this build's members are not named yet, so read it by structure only. Not sent on its own — a record carried by CharacterBaseInformation.party_invitation_member_information, never sent on its own. inferred Used by CharacterBaseInformation No fields you may set.
  11. Smooth a posté un record dans Requests
    Put a player's account on your ignore list, looked up by character name. Kind request · Area Game.Social Ignore a player: their account goes on your ignore list. TO SEND IT 1. Put the player in IgnoreRequest.player_search, a PlayerSearch: a character name goes in its by-name branch; an account (nickname#tag) goes in its by-account branch, nickname and tag apart. 2. Leave every other member unset. The server answers with IgnoreEvent: the added entry on success, or why it was refused (not found, yourself, list full, already there). The client sends it from "Ignore" in a player's context menu, from the ignore box of a party invitation, and from the add box of the contacts panel's ignored tab. Blocking a player is a different request, with the same shape. When you may send it Requires character_loaded inferred Related messages You must then send IgnoreEvent observed — the server answers with the added entry, or with the failure reason Fields #FieldTypeMeaningYou set it 1player_searchPlayerSearchThe player to ignore. A PlayerSearch: the by-name branch for a character name, the by-account branch for nickname plus tag.required
  12. Smooth a posté un record dans Types
    The success branch of an ignore result: the account now on your ignore list. Kind struct · Area Game.Social Set as IgnoreEvent.success when the server accepted IgnoreRequest. The account is ignored for the rest of the session. TO HANDLE IT 1. Read IgnoreEvent.Success.ignored, a ContactInformation. 2. Add it to your ignore list. Nothing sends that list again: it lasts only for the session. 3. Tell the user, naming the account as nickname#tag from ContactInformation.account_tag. Not sent on its own — it is only ever the success branch of IgnoreEvent. inferred Used by IgnoreEvent Fields #FieldTypeMeaningYou set it 1ignoredContactInformationThe account you now ignore. A ContactInformation: the account id and its account tag.never
  13. Smooth a posté un record dans Types
    The failure branch of an ignore result: why the account was not ignored. Kind struct · Area Game.Social Set as IgnoreEvent.error when the server refused IgnoreRequest. Nothing was added to the ignore list. TO HANDLE IT 1. Read IgnoreEvent.Error.reason, an AddFailureReason. 2. Tell the user why (the official client's text for each value is listed on AddFailureReason). 3. Leave your ignore list as it was. Not sent on its own — it is only ever the error branch of IgnoreEvent. inferred Used by IgnoreEvent Fields #FieldTypeMeaningYou set it 1reasonAddFailureReasonWhy the server refused to ignore the account. An AddFailureReason.never
  14. Smooth a posté un record dans Events
    The outcome of an ignore request: the account now ignored, or why it was refused. Kind event · Area Game.Social Answers IgnoreRequest with one of two outcomes, carried in a oneof. TO HANDLE IT 1. Check which branch of IgnoreEvent.result is set. 2. IgnoreEvent.success: the account is now on your ignore list. Its payload is an IgnoreEvent.Success: add IgnoreEvent.Success.ignored, a ContactInformation, to your list, keyed by its account id. The client also prints a chat line naming the account as nickname#tag. 3. IgnoreEvent.error: nothing was added. Its payload is an IgnoreEvent.Error: tell the user its reason, an AddFailureReason. 4. Ignore every member outside the oneof: no client code reads it. When you may send it Requires character_loaded inferred Related messages Answers IgnoreRequest Fields #FieldTypeMeaningYou set it 1successIgnoreEvent.SuccessSet when the account was added; carries the new ignore-list entry. Its payload is an IgnoreEvent.Success, whose one member is the added ContactInformation.never 2errorIgnoreEvent.ErrorSet when the request was refused; carries the reason. Its payload is an IgnoreEvent.Error, whose one member is the AddFailureReason.never
  15. Smooth a posté un record dans Requests
    Asks the estate agency for one page of the houses for sale that match the current search. Kind request · Area Game.House TO SEE A PAGE OF HOUSES FOR SALE 1. Set the search first with HousesToSellFiltersRequest (the official client sends it, then this one for page 1). 2. Send HousesToSellRequest.page_index = the page you want. 3. The answer is HousesToSellEvent: that page's houses and the number of pages. The official client starts at page 1. Its next button stops at the last page (HousesToSellEvent.total_page), and turning a page sends only this message: the filters are not sent again. When you may send it Requires context_ready inferred Raised by: the player turns a page of the estate-agency results, or a new search asks for page 1 proven Related messages You must then send HousesToSellEvent inferred — the window shows the page the answer carries Fields #FieldTypeMeaningYou set it 2page_indexint32The page you want, starting at 1. Observed: 1required
  16. Smooth a posté un record dans Requests
    Sets the search of the estate agency: which houses for sale to list, and in what order. Kind request · Area Game.House TO SEARCH THE HOUSES FOR SALE 1. Send this with your filters: - HousesToSellFiltersRequest.area_id: an area id, or -1 for any area; - HousesToSellFiltersRequest.at_least_room_number and HousesToSellFiltersRequest.at_least_chest_number: minimums; - HousesToSellFiltersRequest.skill_requested: a skill the house must offer, or 0 for any; - HousesToSellFiltersRequest.price_max: the highest price, in kamas; - HousesToSellFiltersRequest.order_by: the sort, a RealEstateOrder. 2. Then send HousesToSellRequest for page 1. The official client always sends the two together, this one first. 3. The answer is HousesToSellEvent, the first page of results. TO TURN PAGES, send only HousesToSellRequest: the official client does not resend the filters, so the server keeps the last ones. When you may send it Requires context_ready inferred Raised by: the estate-agency window opens, or the player presses Search (or Enter), or sorts by price or name proven Related messages You will then receive HousesToSellEvent inferred — the official client sends HousesToSellRequest right after it, and the page that comes back follows these filters Fields #FieldTypeMeaningYou set it 1skill_requestedint32Only houses offering this skill (a skill id), or 0 for any skill. Observed: 0required 2order_byRealEstateOrderHow to sort the results, a RealEstateOrder. The official client's price sort sends PRICE_ASC or PRICE_DSC. Its area sort sends 2 or 3 and its name sort 4 or 5, numbers RealEstateOrder does not declare.optional 3price_maxint64The highest price, in kamas. Observed: 2147483647 With the price box empty or 0, the official client sends 2147483647.required 4at_least_chest_numberint32Only houses with at least this many chests. The official client sends the number written on the selected entry of its chests menu.required 5area_idint32Only houses in this area (an area id), or -1 for any area. Observed: -1required 7at_least_room_numberint32Only houses with at least this many rooms. The official client sends the number written on the selected entry of its rooms menu.required
  17. Smooth a posté un record dans Events
    One page of the houses for sale that match your current estate-agency search. Kind event · Area Game.House Answers HousesToSellRequest. The search is the last HousesToSellFiltersRequest you sent. TO READ IT 1. HousesToSellEvent.houses holds this page's houses, one entry each. 2. HousesToSellEvent.page_index is the page this is. HousesToSellEvent.total_page is how many pages the search has. The official client shows its page bar only when there is more than one page, and its next button stops at the last page. 3. To see another page, send HousesToSellRequest with that page. 4. To search again with other filters, send HousesToSellFiltersRequest, then HousesToSellRequest for page 1. 5. Ignore every other member: no client code reads it. When you may send it Requires context_ready inferred Related messages Answers HousesToSellRequest Arrives after HousesToSellFiltersRequest Fields #FieldTypeMeaningYou set it 1page_indexint32The page this list is. The official client keeps it as its current page, and its previous / next buttons ask for the page before or after it.never 3housesrepeated type not documented yetThe houses for sale on this page. Each entry is a nested HouseToSell record (price, owner, location, rooms, chests, skills...). It has no signature yet, so its members keep their obfuscated names.never 4total_pageint32How many pages the search has.never
  18. Smooth a posté un record dans Requests
    Puts one of your houses on sale at a price, or takes it off sale. Kind request · Area Game.House TO PUT A HOUSE ON SALE 1. Send HouseSellRequest.instance_id = your house, HouseSellRequest.amount = the price in kamas, HouseSellRequest.for_sale = true. 2. Set HouseSellRequest.is_inside = true when you are standing inside that house, false otherwise. TO TAKE IT OFF SALE Send the same message with HouseSellRequest.for_sale = false. The official client still sends the current price. When you may send it Requires context_ready inferred Raised by: the player validates a sale price, or cancels a sale, in the house window proven Related messages Reply not yet recovered: the server's sale update (HouseSellingUpdateEvent in beta) has no signature here yet. Fields #FieldTypeMeaningYou set it 1amountint64The sale price, in kamas. Taking a house off sale, the official client sends the current price here too.required 2is_insidebooltrue when you send it from inside that house.optional 3instance_idint32Your house: its instance id.required 4for_salebooltrue puts the house on sale (or changes its price); false takes it off sale. false is the protobuf default, so taking a house off sale leaves it off the wire.optional
  19. Smooth a posté un record dans Requests
    Buys the house whose sale window is open, at the price you confirm. Kind request · Area Game.House TO BUY A HOUSE 1. Open the house's sale window (the official client does it from the house itself; that path has no signature yet). 2. Send HouseBuyRequest.proposed_price = the sale price the window shows. The message names no house: the server applies it to the house whose sale you are looking at. When you may send it Requires context_ready inferred Raised by: the player confirms the purchase in the house window proven Related messages Reply not yet recovered: the purchase result (HouseBuyResultEvent in beta) has no signature here, and the 3.7.12.12 client reads no answer to this request. Fields #FieldTypeMeaningYou set it 1proposed_priceint64The price you agree to pay, in kamas: the sale price the window shows.required
  20. Smooth a posté un record dans Types
    A guild's identity and social card: the record a character's guild branch carries. Kind struct · Area Game.Common A guild, as carried by CharacterInformation.guild_information. Beta declares it as an id, a name, a level and its social information (emblem, leader, member count, last activity, recruitment); this build's members are not named yet, so read it by structure only. Not sent on its own — a record carried by CharacterInformation.guild_information and HumanOption.guild_information, never sent on its own. inferred Used by CharacterInformation HumanOption No fields you may set.
  21. Smooth a posté un record dans Types
    Dungeon details a party carries when it joins: an int32 and an optional int64, not named yet. Kind struct · Area Game.Party The dungeon block of PartyJoinEvent. The official client copies its int32 into its party state (where -1 means "no dungeon" when the block is absent) and keeps its optional int64 beside it; neither member is named yet, so read it by structure only. Not sent on its own — a record carried by PartyJoinEvent.dungeon, never sent on its own. inferred Used by PartyJoinEvent No fields you may set.
  22. Smooth a posté un record dans Types
    One account on a contact list (ignored, blocked): its account id and its account tag. Kind struct · Area Game.Social Who an entry of a contact list is: an Ankama ACCOUNT, not a character. TO READ IT 1. Keep ContactInformation.account_id: it is the key of the entry. Removing the account from the list again is done by this id, not by name. 2. Show ContactInformation.account_tag as nickname#tag (see AccountTag). 3. Ignore every other member: no client code reads them. The client turns each entry into one line of its contacts panel, and when an entry is added it prints the account tag in the chat. Not sent on its own — a record carried inside the contact-list events, such as the success branch of IgnoreEvent. inferred Used by BlockListEvent IgnoreEvent.Success Fields #FieldTypeMeaningYou set it 1account_tagAccountTagThe account's public name, shown as nickname#tag. An AccountTag.never 2account_idint64The Ankama account's id: the key of the entry. An account id, not a character id: one entry covers every character of the account.never
  23. Smooth a posté un record dans Types
    The successful branch of a character selection, carrying the character now being played. Kind struct · Area Game.Character.Management The branch that means you are in. It carries the Character the server accepted - which is authoritative, so take the character from here rather than assuming you got the one you asked for. The client wraps this character and hands it to its player-state service; that handoff is what actually enters play. Not sent on its own — it is only ever the success branch of CharacterSelectionEvent. inferred Used by CharacterSelectionEvent Fields #FieldTypeMeaningYou set it 1characterCharacterThe character the server selected for play. A Character. Authoritative - it is what you are now playing.never
  24. Smooth a posté un record dans Types
    A character's rendered appearance and breed, nested inside its basic information. Kind struct · Area Game.Character.Management The appearance layer of a Character. Ankama calls it CharacterInformation since 3.5.9.11 (beta and 3.2-3.4: CharacterLook); the field that holds it is still CharacterBasicInformation.character_look. look is a full EntityLook - the same appearance record used everywhere else in the protocol, so whatever you already do with actor looks works here. breed_id is the class. It is the cheapest useful thing in this record: it tells you what the character IS without resolving any art. Two dates come with the character when it is selected (empty in the character list): CharacterInformation.connection_date is when this session started and CharacterInformation.last_disconnection_date when the previous one ended. Both are ISO-8601 UTC strings. Sales made between the two were made while you were away. THE COMPLEMENT IS A ONEOF - EXACTLY ONE BRANCH IS SET. Besides CharacterInformation.base_information it can be CharacterInformation.guild_information (a GuildInformation) or CharacterInformation.alliance_information (an AllianceInformation). Not sent on its own — it is only ever a branch of CharacterBasicInformation. inferred Used by CharacterBasicInformation Fields #FieldTypeMeaningYou set it 2last_disconnection_datestringWhen the character's previous session ended, as an ISO-8601 UTC timestamp. (ISO-8601 date-time (UTC)) Observed: 2026-10-26T08:26:49.450Z The client's sales history starts its 'offline sales' window (sales made while you were away) at this date. Empty in the character list; set when the character is selected (observed).never 3breed_idint32The character's class. (breed id)never 5connection_datestringWhen the character's current session started, as an ISO-8601 UTC timestamp. (ISO-8601 date-time (UTC)) Observed: 2026-10-28T14:35:15.681888024Z The client's sales history keeps only sales from this date on when 'current session' is ticked, and ends its 'offline sales' window here. Empty in the character list; set when the character is selected (observed).never 6lookEntityLookThe character's appearance. An EntityLook.never 1base_informationCharacterBaseInformationThe base-information branch read by the client's character projection. See CharacterBaseInformation.never 4guild_informationGuildInformationThe character's guild, when the carrying message describes one. A GuildInformation: the oneof branch whose type the type-name table spells GuildInformation.never 7alliance_informationAllianceInformationThe character's alliance, when the carrying message describes one. An AllianceInformation: the oneof branch whose type the type-name table spells AllianceInformation.never
  25. Smooth a posté un record dans Types
    A character's epic-mode death state and counters. Kind struct · Area Game.Common The epic-mode branch of CharacterBaseInformation: whether the character is alive, dead or awaiting confirmation, and its death counters. Beta declares a death state, a death count and the highest level reached at death; this build's members are not named yet, so read it by structure only. Not sent on its own — a record carried by CharacterBaseInformation.epic_information, never sent on its own. inferred Used by CharacterBaseInformation No fields you may set.

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.