├── README.md ├── flat-bet.js ├── gui.png ├── labouchere.js ├── martingale.js ├── payout-martingale.js ├── random.js └── seed.js /README.md: -------------------------------------------------------------------------------- 1 | # bustadice's Script Editor API 2 | The following fields are provided on `this`: 3 | - `balance`: Your last known balance 4 | - `bankroll`: The last known bankroll 5 | - `maxProfit`: The profit limit in satoshis based on the last known bankroll 6 | - `username`: The user name of the account running the script 7 | 8 | These fields are only updated when you call `this.bet`. 9 | 10 | ## API Methods 11 | All methods are provided on `this` and return a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises). Because the script is wrapped in an [async function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), you may use the [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) operator to conveniently handle Promises. 12 | 13 | ### bet(size, target) 14 | Places a bet and returns a Promise that resolves with the result. 15 | 16 | `size` is the amount to bet in satoshis. Only wager sizes that are divisible by 100 (whole bits) are valid. 17 | 18 | `target` is the target multiplier, e.g. `1.23` for 1.23x. 19 | 20 | The result has the following form: 21 | ```js 22 | { 23 | id: string, // bet ID 24 | timestamp: string, // RFC2822-compliant date string 25 | value: number, // bet size in satoshi (same as size argument) 26 | target: number, // target multiplier (same as target argument) 27 | multiplier: number, // bet outcome 28 | bankroll: number, // bankroll in satoshis after the bet 29 | balance: number, // user balance in satoshis after the bet 30 | nonce: number // the bet's nonce 31 | } 32 | ``` 33 | 34 | ### clearLog() 35 | Clears the log. 36 | 37 | ### log(...arguments) 38 | Outputs the given arguments to the log. If you want to log objects other than strings and numbers, don't forget to convert them to strings first, e.g. by using [JSON.stringify](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). 39 | 40 | ### notify(message) 41 | If you want to emit browser notifications from the script, 42 | use `notify(message)`. This is good for sending yourself messages 43 | about notable events (like if the script is stopping itself) when 44 | you are viewing another browser tab or application. 45 | 46 | Note that you must have browser notifications enabled for bustadice.com. 47 | 48 | You may not always want a script to bother you with browser notifications. 49 | You can toggle this behavior in the [notification settings](https://bustadice.com/play/notifications). 50 | When script notifications are disabled, `notify()` messages will simply 51 | appear in the script logs. 52 | 53 | ### newSeedPair(seed) 54 | Reveals the previous seed pair and generates a new one using the given client seed. If no client seed is provided, one is generated randomly. 55 | 56 | Calling `newSeedPair` will fail if the current seed pair is unused, i.e. if no bets have been made with it yet. 57 | 58 | The result has the following form: 59 | ```js 60 | { 61 | prev_server_seed: string, // previous server seed 62 | prev_client_seed: string, // previous client seed 63 | 64 | server_seed_hash: string // hash of new server seed 65 | } 66 | ``` 67 | 68 | ### resetStatistics() 69 | Resets the statistics shown beneath the multiplier. 70 | 71 | ### setClientSeed(seed) 72 | Set the client seed. If no client seed is provided, one is generated randomly. 73 | 74 | Calling `setClientSeed` will fail if the current seed pair has already been used, i.e. if bets have been made with it. 75 | 76 | ### skip() 77 | Skips the next bet. 78 | 79 | The result has the following form: 80 | ```js 81 | { 82 | id: string, // bet ID 83 | timestamp: string, // RFC2822-compliant date string 84 | multiplier: number // bet outcome 85 | } 86 | ``` 87 | 88 | ### stop() 89 | Instructs the script editor to stop the script. 90 | 91 | 92 | ## Chat messages 93 | Scripts can react to chat messages in public channels by listening on this for the `PUBLIC_CHAT_MESSAGE` event: 94 | ```js 95 | this.on("PUBLIC_CHAT_MESSAGE", payload => this.log(`${payload.sender} said ${payload.body} in the ${payload.channel} channel.`)) 96 | ``` 97 | 98 | The payload passed to the callback function has the following form: 99 | ```js 100 | { 101 | id: number, // message ID 102 | timestamp: string, // RFC2822-compliant date string 103 | channel: string, // name of the chat channel in which the message was received 104 | sender: string, // user name of the message author 105 | body: string // contents of the message 106 | } 107 | ``` 108 | 109 | 110 | ## GUI Configuration 111 | bustadice supports optional GUI configuration of scripts, allowing the player to configure the script's parameters in a user-friendly manner. 112 | 113 | The GUI-configurable options are defined by creating a `config` variable on the first line of the script. `config` **must** be a `var` (not a `const` or `let`) and **must** be on the first line (not even preceded by comments). bustadice will parse the `config` variable into a form. When it runs the script will have access to the values provided in the form by the player. 114 | 115 | A minimal `config` object could look like this: 116 | 117 | ```javascript 118 | var config = { 119 | clientSeed: { label: "Client seed", type: "text", value: "new client seed" } 120 | }; 121 | ``` 122 | 123 | We define a configuration variable called `clientSeed`, give it the label *Client seed* and the initial value *new client seed*. This is what the resulting UI looks like: 124 | 125 | ![rendered sample configuration form](gui.png) 126 | 127 | In our script, we can then access the value the player chose: 128 | 129 | ```javascript 130 | await this.log(config.clientSeed.value); 131 | ``` 132 | 133 | ### Input types 134 | 135 | The example `config` object above only uses a single configuration variable of type `text`, but there are several types that we can use: 136 | 137 | #### `balance` 138 | A field representing an amount given in bits. The input is automatically converted to satoshi before being supplied to the script. 139 | 140 | ```javascript 141 | var config = { 142 | baseBet: { label: "Base bet", type: "balance", value: 100 } 143 | }; 144 | ``` 145 | 146 | #### `checkbox` 147 | A checkbox that allows you to active a single option (or not). The value provided to the script will be `true` if the box is checked and `false` otherwise. 148 | 149 | ```javascript 150 | var config = { 151 | stopOnWin: { label: "Stop on win", type: "checkbox", value: true }, 152 | }; 153 | ``` 154 | 155 | #### `multiplier` 156 | A multiplier. The value is parsed as a floating-point number before being provided to the script. 157 | 158 | ```javascript 159 | var config = { 160 | target: { label: "Target multiplier", type: "multiplier", value: 2.00 } 161 | }; 162 | ``` 163 | 164 | #### `noop` 165 | A label without a form input, useful as part of a radio field (see below) or as a header to a group of fields. 166 | 167 | ```javascript 168 | var config = { 169 | basicHeader: { label: "Basic Options", type: "noop" } 170 | }; 171 | ``` 172 | 173 | #### `number` 174 | A generic number. The value is parsed as a floating-point number before being provided to the script. 175 | 176 | ```javascript 177 | var config = { 178 | target: { label: "Stop after wins", type: "number", value: 5 } 179 | }; 180 | ``` 181 | 182 | #### `radio` 183 | A group of fields that lets you choose exactly one of several options. 184 | 185 | ```javascript 186 | var config = { 187 | loss: { 188 | label: "On loss", type: "radio", value: "increase", options: { 189 | base: { label: "Return to base bet", type: "noop" }, 190 | increase: { label: "Increase bet by", type: "multiplier", value: 2 } 191 | } 192 | } 193 | }; 194 | ``` 195 | 196 | `config.loss.value` will be a string and either `base` or `increase`. If `increase` was chosen, the multiplier's value is accessible at `config.loss.options.increase.value`. 197 | 198 | #### `combobox` 199 | A dropdown box that lets you choose one of several options. 200 | 201 | ```javascript 202 | var config = { 203 | color: { 204 | type: "combobox", 205 | // optional: true, 206 | value: "red", 207 | label: "Favorite color", 208 | options: { 209 | red: { label: "Red" }, 210 | yellow: { label: "Yellow" }, 211 | blue: { label: "Blue" } 212 | } 213 | } 214 | }; 215 | 216 | log(config.color.value); 217 | ``` 218 | 219 | If configured to `optional: true`, then a "None" option will be added and, 220 | when selected, `config.color.value` will be `undefined`. 221 | 222 | Otherwise, be sure to provide an initial `value` setting that points to one 223 | of the keys in your `options` mapping. 224 | 225 | #### `text` 226 | A simple text field that provides a string to the script. 227 | 228 | ```javascript 229 | var config = { 230 | clientSeed: { label: "Client seed", type: "text", value: "new client seed" } 231 | }; 232 | ``` 233 | 234 | 235 | ## Examples 236 | - [Flat Bet](flat-bet.js), which places the same bet repeatedly until the target multiplier is hit 237 | - [Martingale](martingale.js), which follows the [Martingale system](https://en.wikipedia.org/wiki/Martingale_(betting_system)) 238 | - [Payout Martingale](payout-martingale.js), which is similar to Martingale but increases the target multiplier 239 | - [Labouchère](labouchere.js), which follows the [Labouchère system](https://en.wikipedia.org/wiki/Labouch%C3%A8re_system) 240 | - [Random](random.js), which bets random amounts with random target multipliers. 241 | - [Seed](seed.js), which requests a new seed pair and then sets a new client seed 242 | -------------------------------------------------------------------------------- /flat-bet.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | wager: { label: "Wager", type: "balance", value: 100 }, 3 | target: { label: "Target", type: "multiplier", value: 1000 } 4 | }; 5 | 6 | while (true) { 7 | const { multiplier } = await this.bet(config.wager.value, config.target.value) 8 | if (multiplier >= config.target.value) { 9 | break 10 | } 11 | } -------------------------------------------------------------------------------- /gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bustadice/scripts/06151990a76d0135e16dd9e90e790ccfe97ea5ad/gui.png -------------------------------------------------------------------------------- /labouchere.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | loop: { label: "Continue playing after successfully clearing a list?", type: "checkbox", value: true }, 3 | 4 | // This is the initial list to start the Labouchere system with. When set to 5 | // play in a loop, this is also the list that is used after successfully 6 | // completing one list. 7 | baseList: { label: "Base list", type: "text", value: "[100, 200, 300]" }, 8 | 9 | // The target multiplier. The system generally requires even money bets, 10 | // i.e. 2.00x payout. Changing this probably also require adapting more code 11 | // below. 12 | target: { label: "Target", type: "multiplier", value: 2 } 13 | } 14 | 15 | 16 | let baseList; 17 | try { 18 | baseList = JSON.parse(config.baseList.value) 19 | } catch (error ) { 20 | await this.log("received invalid base list") 21 | await this.stop() 22 | } 23 | 24 | let list = baseList.slice(); 25 | this.log(`Starting Labouchere with base list [${list}].`); 26 | 27 | for (;;) { 28 | // sum first item with last item if it exists 29 | const wager = list[0] + (list.length > 1 ? list[list.length-1] : 0) 30 | 31 | // make the bet and wait for the result 32 | const { multiplier } = await this.bet(wager, config.target.value); 33 | 34 | if (multiplier < config.target.value) { // loss 35 | list.push(wager); 36 | this.log(`Bet ${wager} satoshis. Lost. New list: [${list}].`); 37 | } else { // win 38 | list = list.slice(1, -1); 39 | const cleared = list.length === 0; 40 | if (cleared && config.loop.value) { 41 | list = baseList.slice(); 42 | this.log(`Bet ${wager} satoshis. Won. Reset list [${list}]`); 43 | } else if (cleared && !config.loop.value) { 44 | this.log(`Bet ${wager} satoshis. Won. Script finished.`); 45 | break; 46 | } else { 47 | this.log(`Bet ${wager} satoshis. Won. New list [${list}]`); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /martingale.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | baseBet: { label: "Base bet", type: "balance", value: 100 }, // how many satoshis to bet initially 3 | target: { label: "Target", type: "multiplier", value: 2 }, // target multiplier 4 | betMultiplier: { label: "Bet multiplier", type: "multiplier", value: 2 } // what to multiply the bet size by when we lose a wager 5 | } 6 | 7 | 8 | let lossCount = 0 9 | this.log(`Starting martingale with a base bet of ${config.baseBet.value} satoshis.`) 10 | 11 | while (true) { 12 | // make the bet and wait for the result 13 | const { multiplier } = await this.bet(betSize(lossCount), config.target.value) 14 | 15 | if (multiplier < config.target.value) { // loss 16 | lossCount++ 17 | this.log(`Lost bet. Multiplying bet size by ${config.betMultiplier.value} for new bet size of ${betSize(lossCount)} satoshis.`) 18 | } else { // win 19 | lossCount = 0 20 | this.log(`Won bet. Setting bet size to ${config.baseBet.value} satoshis.`) 21 | } 22 | } 23 | 24 | function betSize(lossCount) { 25 | const bet = config.baseBet.value * Math.pow(config.betMultiplier.value, lossCount) 26 | return Math.round(bet / 100) * 100 27 | } 28 | -------------------------------------------------------------------------------- /payout-martingale.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | bet: { label: "Bet", type: "balance", value: 100 }, // how many satoshis to bet 3 | baseTarget: { label: "Base target", type: "multiplier", value: 2 }, // target multiplier 4 | stop: { label: "Stop", type: "multiplier", value: 20 }, // how big the target can get before stopping the script 5 | 6 | resetTargetOnLoss: { label: "Reset target on loss", type: "checkbox", value: false }, // return to base target on loss? 7 | lossIncrease: { label: "Increase on loss", type: "multiplier", value: 1 }, // what to increase the target on loss. Ignored if resetTargetOnLoss is true 8 | 9 | resetTargetOnWin: { label: "Reset target on win", type: "checkbox", value: true }, // return to base target on win? 10 | winIncrease: { label: "Increase on win", type: "multiplier", value: 1 } // what to increase the target on win. Ignored if resetTargetOnWin is true 11 | } 12 | 13 | 14 | let currentTarget = config.baseTarget.value 15 | this.log(`Starting payout martingale with a base target of ${config.baseTarget.value}`) 16 | 17 | while (true) { 18 | // make the bet and wait for the result 19 | const { multiplier } = await this.bet(config.bet.value, currentTarget) 20 | 21 | if (multiplier < currentTarget) { // loss 22 | if (config.resetTargetOnLoss.value) { 23 | currentTarget = config.baseTarget.value 24 | this.log(`Lost bet, so resetting target to ${currentTarget}`) 25 | } else { 26 | increaseTarget(config.lossIncrease.value) 27 | this.log(`Lost bet, so increasing target to ${currentTarget}`) 28 | } 29 | } else { // win 30 | if (config.resetTargetOnWin.value) { 31 | currentTarget = config.baseTarget.value 32 | this.log(`Won bet, so resetting target to ${currentTarget}`) 33 | } else { 34 | increaseTarget(config.winIncrease.value) 35 | this.log(`Won bet, so increasing target to ${currentTarget}`) 36 | } 37 | } 38 | 39 | if (currentTarget > config.stop.value) { 40 | this.log(`Target got to ${currentTarget}. Stopping the script`) 41 | this.stop() 42 | } 43 | } 44 | 45 | function increaseTarget(increase) { 46 | currentTarget += increase 47 | currentTarget = Math.round(currentTarget * 100) / 100 48 | } -------------------------------------------------------------------------------- /random.js: -------------------------------------------------------------------------------- 1 | // This script repeats the following steps: 2 | // 1. Randomly determine a wager size that is permitted. 3 | // 2. For the given wager size, randomly determine a permitted target 4 | // multiplier. 5 | // 3. Place a bet with the determined parameters and wait for the result. 6 | 7 | 8 | while (true) { 9 | // assume a slightly smaller profit limit in case it has decreased by the time 10 | // our bet has reached the server 11 | const maxProfit = this.maxProfit * 0.99 12 | 13 | // the largest bet the profit limit will support or our balance, whichever is 14 | // smaller 15 | const maxBet = Math.floor(Math.min(this.balance, maxProfit / 0.01)) 16 | const betSize = Math.round((Math.random() * (maxBet - 100) + 100) / 100) * 100 17 | 18 | const maxTarget = Math.min( 19 | Math.floor((maxProfit / betSize + 1) * 100), 20 | 100000000 21 | ) 22 | const target = Math.round(Math.random() * (maxTarget - 101) + 101) / 100 23 | 24 | this.log(`Betting ${betSize/100} bits at ${target.toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}x.`) 25 | await this.bet(betSize, target) 26 | } 27 | -------------------------------------------------------------------------------- /seed.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | clientSeed: { label: "Client seed", type: "text", value: "This is my new client seed." } 3 | } 4 | 5 | 6 | // This script assumes that the current seed pair is already used, i.e. that bets have been placed 7 | // with it. It will fail if the current seed pair is unused. 8 | 9 | // request a new server seed using a random client seed 10 | const { server_seed_hash } = await this.newSeedPair() 11 | await this.log("The new server seed has the hash:", server_seed_hash) 12 | 13 | // set the client seed 14 | await this.setClientSeed(config.clientSeed.value) 15 | await this.log("The client seed was set to:", config.clientSeed.value) 16 | --------------------------------------------------------------------------------