mirror of
https://github.com/stronk-dev/RandomChad.git
synced 2025-07-05 18:35:10 +02:00
194 lines
7.2 KiB
JavaScript
194 lines
7.2 KiB
JavaScript
// ///////////////////////////////
|
|
// Helper functions
|
|
// ///////////////////////////////
|
|
exports.dev = !!process.env.development
|
|
const log = ( ...messages ) => {
|
|
if( process.env.development ) console.log( ...messages )
|
|
}
|
|
exports.log = log
|
|
|
|
// Wait in async
|
|
const wait = timeInMs => new Promise( resolve => setTimeout( resolve ), timeInMs )
|
|
exports.wait = wait
|
|
|
|
// Pick random item from an array
|
|
const pickRandomArrayEntry = array => array[ Math.floor( Math.random() * array.length ) ]
|
|
exports.pickRandomArrayEntry = pickRandomArrayEntry
|
|
|
|
// Generate random number between x and y
|
|
exports.randomNumberBetween = ( min, max ) => Math.floor( Math.random() * ( max - min + 1 ) + min )
|
|
|
|
// Random attribute picker
|
|
exports.pickRandomAttributes = ( attributes ) => {
|
|
|
|
// Decimal accuracy, if probabilities have the lowest 0.01 then 100 is enough, for 0.001 1000 is needed
|
|
const probabilityDecimals = 3
|
|
|
|
// Remap the trait so it has a 'lottery ticket box' based on probs
|
|
const attributeLottery = attributes.map( ( { values, ...attribute } ) => ( {
|
|
// Attribute meta stays the same
|
|
...attribute,
|
|
// Values are reduced from objects with probabilities to an array with elements
|
|
values: values.reduce( ( acc, val ) => {
|
|
|
|
const { probability, value } = val
|
|
|
|
// Map probabilities to a flat array of items
|
|
const amountToAdd = 10 * probabilityDecimals * probability
|
|
for ( let i = 0; i < amountToAdd; i++ ) acc.push( value )
|
|
return acc
|
|
|
|
}, [] )
|
|
} ) )
|
|
|
|
// Pick a random element from the lottery box array items
|
|
return attributeLottery.map( ( { values, ...attribute } ) => ( {
|
|
// Attribute meta stays the same
|
|
...attribute,
|
|
// Select random entry from array
|
|
value: pickRandomArrayEntry( values )
|
|
} ) )
|
|
|
|
}
|
|
|
|
const nameColor = require('color-namer')
|
|
const Color = require('color')
|
|
exports.getColorName = ( rgb ) => {
|
|
try {
|
|
return nameColor( rgb ).basic[0].name
|
|
} catch( e ) {
|
|
return rgb
|
|
}
|
|
}
|
|
exports.getRgbArrayFromColorName = name => {
|
|
|
|
const { hex } = nameColor( name ).basic[0]
|
|
const color = Color( hex )
|
|
return color.rgb().array()
|
|
|
|
}
|
|
|
|
// ///////////////////////////////
|
|
// Attribute sources
|
|
// ///////////////////////////////
|
|
exports.globalAttributes = [
|
|
{ trait_type: "helmet", values: [
|
|
{ value: 'classic', probability: .2 },
|
|
{ value: 'racer', probability: .1 },
|
|
{ value: 'punk', probability: .1 },
|
|
{ value: 'knight', probability: .2 },
|
|
{ value: 'geek', probability: .2 }
|
|
|
|
] },
|
|
{ trait_type: "patch", values: [
|
|
{ value: 'nimbus', probability: .1 },
|
|
{ value: 'teku', probability: .1 },
|
|
{ value: 'lighthouse', probability: .1 },
|
|
{ value: 'prysm', probability: .2 },
|
|
{ value: 'rocketpool', probability: .5 }
|
|
|
|
] },
|
|
{ trait_type: "backpack", values: [
|
|
{ value: 'yes', probability: .9 },
|
|
{ value: 'no', probability: .1 }
|
|
] },
|
|
{ trait_type: "panel", values: [
|
|
{ value: 'yes', probability: .9 },
|
|
{ value: 'no', probability: .1 }
|
|
] },
|
|
{ trait_type: "background", values: [
|
|
{ value: 'planets', probability: .2 },
|
|
{ value: 'system', probability: .2 },
|
|
{ value: 'playful', probability: .1 },
|
|
{ value: 'moon', probability: .05 },
|
|
{ value: 'galaxy', probability: .2 },
|
|
{ value: 'chip', probability: .05 }
|
|
|
|
] },
|
|
{ trait_type: "background complexity", values: [
|
|
{ value: 1, probability: .05 },
|
|
{ value: 2, probability: .10 },
|
|
{ value: 3, probability: .10 },
|
|
{ value: 4, probability: .75 }
|
|
] }
|
|
|
|
|
|
]
|
|
exports.heavenlyBodies = [ "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto", "the Moon", "the Sun" ]
|
|
exports.web2domain = 'https://rocketeer.fans'
|
|
exports.lorem = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
|
|
|
|
/* ///////////////////////////////
|
|
// Retryable & throttled async
|
|
// /////////////////////////////*/
|
|
const Throttle = require( 'promise-parallel-throttle' )
|
|
const Retrier = require( 'promise-retry' )
|
|
|
|
/**
|
|
* Make async function (promise) retryable
|
|
* @param { function } async_function The function to make retryable
|
|
* @param { string } logging_label The label to add to the log entries
|
|
* @param { number } retry_times The amount of times to retry before throwing
|
|
* @param { number } cooldown_in_s The amount of seconds to wait between retries
|
|
* @param { boolean } cooldown_entropy Whether to add entropy to the retry delay to prevent retries from clustering in time
|
|
* @returns { function } An async function (promise) that will retry retry_times before throwing
|
|
*/
|
|
function make_retryable( async_function, logging_label='unlabeled retry', retry_times=5, cooldown_in_s=10, cooldown_entropy=true ) {
|
|
|
|
// Formulate retry logic
|
|
const retryable_function = () => Retrier( ( do_retry, retry_counter ) => {
|
|
|
|
// Failure handling
|
|
return async_function().catch( async e => {
|
|
|
|
// If retry attempts exhausted, throw out
|
|
if( retry_counter >= retry_times ) {
|
|
log( `♻️🚨 ${ logging_label } retry failed after ${ retry_counter } attempts` )
|
|
throw e
|
|
}
|
|
|
|
// If retries left, retry with a progressive delay
|
|
const entropy = !cooldown_entropy ? 0 : ( .1 + Math.random() )
|
|
const cooldown_in_ms = ( cooldown_in_s + entropy ) * 1000
|
|
const cooldown = cooldown_in_ms + ( cooldown_in_ms * ( retry_counter - 1 ) )
|
|
log( `♻️ ${ logging_label } retry failed ${ retry_counter }x, waiting for ${ cooldown / 1000 }s` )
|
|
await wait( cooldown )
|
|
log( `♻️ ${ logging_label } cooldown complete, continuing...` )
|
|
return do_retry()
|
|
|
|
} )
|
|
|
|
} )
|
|
|
|
return retryable_function
|
|
|
|
}
|
|
|
|
/**
|
|
* Make async function (promise) retryable
|
|
* @param { array } async_function_array Array of async functions (promises) to run in throttled parallel
|
|
* @param { number } max_parallell The maximum amount of functions allowed to run at the same time
|
|
* @param { string } logging_label The label to add to the log entries
|
|
* @param { number } retry_times The amount of times to retry before throwing
|
|
* @param { number } cooldown_in_s The amount of seconds to wait between retries
|
|
* @returns { Promise } An async function (promise) that will retry retry_times before throwing
|
|
*/
|
|
async function throttle_and_retry( async_function_array=[], max_parallell=2, logging_label, retry_times, cooldown_in_s ) {
|
|
|
|
// Create array of retryable functions
|
|
const retryable_async_functions = async_function_array.map( async_function => {
|
|
const retryable_function = make_retryable( async_function, logging_label, retry_times, cooldown_in_s )
|
|
return retryable_function
|
|
} )
|
|
|
|
// Throttle configuration
|
|
const throttle_config = {
|
|
maxInProgress: max_parallell
|
|
}
|
|
|
|
// Return throttler
|
|
return Throttle.all( retryable_async_functions, throttle_config )
|
|
|
|
}
|
|
|
|
exports.throttle_and_retry = throttle_and_retry |