import { AuxGlobalContext , AsyncTask , BotTimer , TimeoutOrIntervalTimer , DEBUG_STRING , debugStringifyFunction , } from './AuxGlobalContext' ; import { hasValue , trimTag , isBot , BotTags , Bot , BOT_SPACE_TAG , toast as toastMessage , showJoinCode as calcShowJoinCode , requestFullscreen , exitFullscreen , html as htmlMessage , hideHtml as hideHtmlMessage , setClipboard as calcSetClipboard , tweenTo as calcTweenTo , showChat as calcShowChat , hideChat as calcHideChat , ShowChatOptions , runScript , enableAR as calcEnableAR , disableAR as calcDisableAR , enableVR as calcEnableVR , disableVR as calcDisableVR , showUploadAuxFile as calcShowUploadAuxFile , openQRCodeScanner as calcOpenQRCodeScanner , showQRCode as calcShowQRCode , openBarcodeScanner as calcOpenBarcodeScanner , showBarcode as calcShowBarcode , importAUX as calcImportAUX , showInputForTag as calcShowInputForTag , showInput as calcShowInput , replaceDragBot as calcReplaceDragBot , goToDimension as calcGoToDimension , goToURL as calcGoToURL , openURL as calcOpenURL , checkout as calcCheckout , playSound as calcPlaySound , bufferSound as calcBufferSound , cancelSound as calcCancelSound , setupServer as calcSetupServer , shell as calcShell , backupToGithub as calcBackupToGithub , backupAsDownload as calcBackupAsDownload , finishCheckout as calcFinishCheckout , markHistory as calcMarkHistory , browseHistory as calcBrowseHistory , restoreHistoryMark as calcRestoreHistoryMark , loadFile as calcLoadFile , saveFile as calcSaveFile , reject as calcReject , localFormAnimation as calcLocalFormAnimation , webhook as calcWebhook , superShout as calcSuperShout , share as calcShare , registerPrefix as calcRegisterPrefix , createCertificate as calcCreateCertificate , signTag as calcSignTag , revokeCertificate as calcRevokeCertificate , localPositionTween as calcLocalPositionTween , localRotationTween as calcLocalRotationTween , animateTag as calcAnimateTag , showUploadFiles as calcShowUploadFiles , BotAction , download , BotsState , CameraType , BarcodeFormat , loadSimulation , unloadSimulation , getUploadState , addState , PortalType , getPortalTag , ShowInputOptions , KNOWN_PORTALS , openConsole , StartCheckoutOptions , tagsOnBot , getOriginalObject , getBotSpace , trimEvent , CREATE_ACTION_NAME , CREATE_ANY_ACTION_NAME , DESTROY_ACTION_NAME , LocalFormAnimationAction , ORIGINAL_OBJECT , AsyncActions , ShareOptions , unlockSpace , getRemoteCount , getServers , getRemotes , action , getServerStatuses , setSpacePassword , exportGpioPin , unexportGpioPin , setGpioPin , getGpioPin , rpioInitPin , rpioExitPin , rpioOpenPin , rpioModePin , rpioReadPin , rpioReadSequencePin , rpioWritePin , rpioWriteSequencePin , rpioReadpadPin , rpioWritepadPin , rpioPudPin , rpioPollPin , rpioClosePin , rpioI2CBeginPin , rpioI2CSetSlaveAddressPin , rpioI2CSetBaudRatePin , rpioI2CSetClockDividerPin , rpioI2CReadPin , rpioI2CWritePin , rpioI2CEndPin , rpioPWMSetClockDividerPin , rpioPWMSetRangePin , rpioPWMSetDataPin , rpioSPIBeginPin , rpioSPIChipSelectPin , rpioSPISetCSPolarityPin , rpioSPISetClockDividerPin , rpioSPISetDataModePin , rpioSPITransferPin , rpioSPIWritePin , rpioSPIEndPin , serialConnectPin , serialOpenPin , serialStreamPin , serialUpdatePin , serialWritePin , serialReadPin , serialClosePin , serialFlushPin , serialDrainPin , serialPausePin , serialResumePin , Easing , LocalPositionTweenAction , LocalRotationTweenAction , BotAnchorPoint , calculateAnchorPoint , calculateAnchorPointOffset , getBotPosition as calcGetBotPosition , RuntimeBot , isRuntimeBot , SET_TAG_MASK_SYMBOL , CLEAR_TAG_MASKS_SYMBOL , getBotScale , EDIT_TAG_SYMBOL , BotSpace , EDIT_TAG_MASK_SYMBOL , AnimateTagOptions , EaseType , RegisterPrefixOptions , OpenCircleWipeOptions , circleWipe , addDropSnap as calcAddDropSnap , SuperShoutAction , ShowToastAction , ShowJoinCodeAction , RequestFullscreenAction , ExitFullscreenAction , ShowHtmlAction , HideHtmlAction , SetClipboardAction , FocusOnBotAction , ShowChatBarAction , EnableARAction , EnableVRAction , DownloadAction , ShowUploadAuxFileAction , OpenQRCodeScannerAction , ShowQRCodeAction , OpenBarcodeScannerAction , ShowBarcodeAction , LoadServerAction , UnloadServerAction , ImportAUXAction , ReplaceDragBotAction , ShowInputForTagAction , GoToDimensionAction , GoToURLAction , OpenURLAction , OpenConsoleAction , StartCheckoutAction , FinishCheckoutAction , ShowUploadFilesAction , ApplyStateAction , RejectAction , FocusOnOptions , animateToPosition , AsyncAction , beginAudioRecording as calcBeginAudioRecording , endAudioRecording as calcEndAudioRecording , beginRecording as calcBeginRecording , endRecording as calcEndRecording , speakText as calcSpeakText , getVoices as calcGetVoices , getGeolocation as calcGetGeolocation , cancelAnimation , SnapTarget , AddDropSnapTargetsAction , RecordingOptions , Recording , SyntheticVoice , SpeakTextOptions , EnablePOVAction , disablePOV , enablePOV , EnableCustomDraggingAction , enableCustomDragging as calcEnableCustomDragging , MINI_PORTAL , registerCustomApp , setAppOutput , SetAppOutputAction , unregisterCustomApp , requestAuthData as calcRequestAuthData , AuthData , createBot , defineGlobalBot as calcDefineGlobalBot , TEMPORARY_BOT_PARTITION_ID , Record , RecordReference , convertToString , GET_TAG_MASKS_SYMBOL , PartialBotsState , PartialBot , isBotLink , parseBotLink , createBotLink , ParsedBotLink , convertGeolocationToWhat3Words as calcConvertGeolocationToWhat3Words , ConvertGeolocationToWhat3WordsOptions , getPublicRecordKey as calcGetPublicRecordKey , recordData as calcRecordData , getRecordData , recordFile as calcRecordFile , BeginAudioRecordingAction , } from '../bots' ; import { sortBy , every , cloneDeep , union , isEqual , flatMap } from 'lodash' ; import { remote as calcRemote , DeviceSelector , RemoteAction , } from '@casual-simulation/causal-trees' ; import { RanOutOfEnergyError } from './AuxResults' ; import '../polyfill/Array.first.polyfill' ; import '../polyfill/Array.last.polyfill' ; import { convertToCopiableValue , embedBase64InPdf , formatAuthToken , getEasing , getEmbeddedBase64FromPdf , } from './Utils' ; import { sha256 as hashSha256 , sha512 as hashSha512 , hmac } from 'hash.js' ; import stableStringify from '@casual-simulation/fast-json-stable-stringify' ; import { encrypt as realEncrypt , decrypt as realDecrypt , keypair as realKeypair , sign as realSign , verify as realVerify , asymmetricKeypair as realAsymmetricKeypair , asymmetricEncrypt as realAsymmetricEncrypt , asymmetricDecrypt as realAsymmetricDecrypt , isAsymmetricKeypair , isAsymmetricEncrypted , isEncrypted , } from '@casual-simulation/crypto' ; import { tagValueHash } from '../aux-format-2/AuxOpTypes' ; import { apply , del , insert , isTagEdit , preserve } from '../aux-format-2' ; import { Euler , Vector3 , Plane , Ray , RGBA_ASTC_10x10_Format , } from '@casual-simulation/three' ; import mime from 'mime' ; import TWEEN from '@tweenjs/tween.js' ; import './PerformanceNowPolyfill' ; import './BlobPolyfill' ; import { AuxDevice } from './AuxDevice' ; import { AuxVersion } from './AuxVersion' ; import { Fragment , h } from 'preact' ; import htm from 'htm' ; import { fromByteArray , toByteArray } from 'base64-js' ; import expect , { iterableEquality , Tester } from '@casual-simulation/expect' ; import { CreatePublicRecordKeyResult , GetDataResult , isRecordKey , parseRecordKey , RecordDataResult , RecordFileFailure , RecordFileResult , isRecordKey as calcIsRecordKey , } from '@casual-simulation/aux-records' ; const _html = htm . bind ( h ) as any ; const html = ( ( ... args ) => { return _html ( ... args ) ; } ) as any ; ( < any > html ) . h = h ; ( < any > html ) . f = Fragment ; /**
 * Defines an interface for a function that provides HTML VDOM capabilities to bots.
 */ export interface HtmlFunction { ( ... args ) : any ; h : ( name : string | Function , props : any , ... children : any [ ] ) => any ; f : any ; } /**
 * Defines an interface for a library of functions and values that can be used by formulas and listeners.
 */ export interface AuxLibrary { /**
     * The functions that are part of the general API.
     */ api : { whisper ( bot , eventName , arg ? ) : any [ ] ; shout ( name , arg ? ) : any [ ] ; __energyCheck ( ) : void ; [ key ] : any ; } ; /**
     * The functions that are part of the bot-specific API.
     */ tagSpecificApi : { [ key ] : ( options : TagSpecificApiOptions ) => any ; } ; typeDefinitions ? : string ; } type TagFilter = ( ( value : any ) => boolean ) | string | number | boolean | null | undefined ; /**
 * Defines a type that represents a mod.
 * That is, a set of tags that can be applied to another bot.
 */ type Mod = BotTags | Bot ; /**
 * An interface that is used to say which user/device/session an event should be sent to.
 */ export interface SessionSelector { username ? : string ; device ? : string ; session ? : string ; broadcast ? : boolean ; } /**
 * Defines an interface for options that complete payment for a product.
 */ interface FinishCheckoutOptions { /**
     * The secret API key that should be used to checkout with stripe.
     */ secretKey : string ; /**
     * The token that authorized payment from the user.
     */ token : string ; /**
     * The amount that should be charged in the currency's smallest unit. (cents, etc.)
     */ amount : number ; /**
     * The three character currency code.
     */ currency : string ; /**
     * The description for the charge.
     */ description : string ; /**
     * Any extra info that should be included in the onPaymentSuccessful() or onPaymentFailed() events for this checkout.
     */ extra ? : any ; } /**
 * Defines an interface for options that mark a specific time in history.
 */ interface MarkHistoryOptions { /**
     * The message that the mark should contain.
     */ message : string ; } /**
 * Options for loading a file.
 */ interface LoadFileOptions { /**
     * The shout that should be made when the request finishes.
     */ callbackShout ? : string ; } /**
 * Options for saving a file.
 */ interface SaveFileOptions { /**
     * The shout that should be made when the request finishes.
     */ callbackShout ? : string ; /**
     * Whether to overwrite an existing file.
     */ overwriteExistingFile ? : boolean ; } /**
 * The status codes that should be used to retry web requests.
 */ const DEFUALT_RETRY_STATUS_CODES = [ 408 , 429 , 500 , 502 , 503 , 504 , 0 , ] ; /**
 * The time to wait until another web request retry unless specified by the webhook options.
 * Defaults to 3 seconds.
 */ const DEFAULT_RETRY_AFTER_MS = 3 * 1000 ; /**
 * The maximum amount of time to wait before giving up on a set of requests.
 * Defaults to 1 minute.
 */ const MAX_RETRY_AFTER_MS = 60 * 60 * 1000 ; /**
 * The maximum number of times that a web request should be retried for.
 */ const MAX_RETRY_COUNT = 10 ; /**
 * Defines a set of options for a webhook.
 */ export interface WebhookOptions { /**
     * The HTTP Method that the request should use.
     */ method ? : string ; /**
     * The URL that the request should be made to.
     */ url ? : string ; /**
     * The headers to include in the request.
     */ headers ? : { [ key ] : string ; } ; /**
     * The data to send with the request.
     */ data ? : any ; /**
     * The shout that should be made when the request finishes.
     */ responseShout ? : string ; /**
     * The number of retries that should be attempted for the webhook.
     */ retryCount ? : number ; /**
     * The HTTP response status codes that should allow the web request to be retried.
     */ retryStatusCodes ? : number [ ] ; /**
     * The number of miliseconds to wait between retry requests.
     */ retryAfterMs ? : number ; } /**
 * Defines a set of options for animateTag().
 */ export interface AnimateTagFunctionOptions { /**
     * The value that should be animated from.
     * If not specified then the current tag value will be used.
     */ fromValue ? : any ; /**
     * The value that should be animated to.
     */ toValue : any ; /**
     * The duration of the animation in seconds.
     */ duration : number ; /**
     * The type of easing to use.
     * If not specified then "linear" "inout" will be used.
     */ easing ? : EaseType | Easing ; /**
     * The space that the tag should be animated in.
     * If not specified then "tempLocal" will be used.
     * If false, then the bot will be edited instead of using tag masks.
     */ tagMaskSpace ? : BotSpace | false ; } export interface BotFilterFunction { ( bot ) : boolean ; sort ? : ( bot : Bot ) => any ; [ DEBUG_STRING ] ? : string ; } export interface RecordFilter { recordFilter : true ; [ DEBUG_STRING ] ? : string ; } export interface AuthIdRecordFilter extends RecordFilter { authID : string ; } export interface SpaceFilter extends BotFilterFunction , RecordFilter { space : string ; toJSON : ( ) => RecordFilter ; } export interface AddressRecordFilter extends RecordFilter { address : string ; } export interface IDRecordFilter extends BotFilterFunction , RecordFilter { id : string ; toJSON : ( ) => RecordFilter ; } export type RecordFilters = AuthIdRecordFilter | SpaceFilter | AddressRecordFilter | IDRecordFilter | RecordReference ; /**
 * Defines a set of options for a tween.
 */ export interface TweenOptions { /**
     * The easing for the tween.
     */ easing ? : Easing ; /**
     * The duration of the tween in seconds.
     */ duration ? : number ; } /**
 * Defines an interface that contains performance statistics about a inst.
 */ export interface PerformanceStats { /**
     * The number of bots in the inst.
     */ numberOfBots : number ; /**
     * A list of listen tags and the amount of time spent executing them (in miliseconds).
     * Useful to guage if a listen tag is causing the inst to slow down.
     */ shoutTimes : { tag : string ; timeMs : number ; } [ ] ; /**
     * The total number of active setTimeout() and setInterval() timers that are active.
     */ numberOfActiveTimers : number ; } /**
 * Options needed for the Bot-specific API.
 */ export interface TagSpecificApiOptions { /**
     * The Bot that the API is for.
     */ bot : Bot ; /**
     * The tag that the API is for.
     */ tag : string ; /**
     * The bot that is set as the creator of the current bot.
     */ creator : RuntimeBot ; /**
     * The bot that is set as the config of the current bot.
     */ config : RuntimeBot ; } /**
 * Defines an interface that represents a set of records that were retrieved.
 */ export interface GetRecordsResult { /**
     * The set of records that were retrieved.
     */ records : Record [ ] ; /**
     * The total number of records that the query would have returned.
     */ totalCount : number ; /**
     * Whether there are more records available to retrieve for the query.
     */ hasMoreRecords : boolean ; /**
     * Gets the set page of records.
     */ getMoreRecords ( ) : Promise < GetRecordsResult > ; } /**
 * Defines an interface that contains options for an aux debugger.
 */ export interface AuxDebuggerOptions { /**
     * Whether to use "real" UUIDs instead of predictable ones.
     */ useRealUUIDs : boolean ; /**
     * Whether to allow scripts to be asynchronous.
     * If false, then all scripts will be forced to be synchronous.
     * Defaults to false.
     */ allowAsynchronousScripts : boolean ; /**
     * The data that the configBot should be created from.
     * Can be a mod or another bot.
     */ configBot : Bot | BotTags ; } export interface MaskableFunction { mask ( ... args ) : MaskedFunction ; } export interface MaskedFunction { returns ( value ) : void ; } export interface WebhookInterface extends MaskableFunction { ( options ) : void ; post : ( ( url : string , data ? : any , options ? : WebhookOptions ) => Promise < any > ) & MaskableFunction ; } /**
 * Defines an interface that represents the result of a webhook.
 */ export interface WebhookResult { /**
     * The data that was returned from the webhook.
     */ data : any ; /**
     * The HTTP Status Code that was returned from the webhook.
     * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status for more information.
     */ status : number ; /**
     * The HTTP Headers that were included in the response.
     * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers for more information.
     */ headers : { [ name ] : string ; } ; } export type RecordFileApiResult = RecordFileApiSuccess | RecordFileApiFailure ; export interface RecordFileApiSuccess { success : true ; /**
     * The URL that the file can be accessed at.
     */ url : string ; /**
     * The SHA-256 hash of the file.
     * When downloading the URL, the resulting data is guaranteed to have a SHA-256 hash that matches this value.
     */ sha256Hash : string ; } export interface RecordFileApiFailure { success : false ; errorCode : RecordFileFailure [ 'errorCode' ] | 'file_already_exists' | 'invalid_file_data' ; errorMessage : string ; } const botsEquality = function ( first , second ) { if ( isRuntimeBot ( first ) && isRuntimeBot ( second ) ) { expect ( getBotSnapshot ( first ) ) . toEqual ( getBotSnapshot ( second ) ) ; return true ; } return undefined ; } ; expect . extend ( { toEqual ( received , expected ) { const matcherName = 'toEqual' ; const options = { comment : 'deep equality' , isNot : this . isNot , promise : this . promise , } ; const pass = this . equals ( received , expected , [ botsEquality , iterableEquality , ] ) ; const message = pass ? ( ) => this . utils . matcherHint ( matcherName , undefined , undefined , options ) + '\n\n' + `Expected: not ${ this . utils . printExpected ( expected ) }\n` + ( this . utils . stringify ( expected ) !== this . utils . stringify ( received ) ? `Received:     ${ this . utils . printReceived ( received ) }` : '' ) : ( ) => this . utils . matcherHint ( matcherName , undefined , undefined , options ) + '\n\n' + this . utils . printDiffOrStringify ( expected , received , 'Expected' , 'Received' , this . expand !== false ) ; return { actual : received , expected , message , name : matcherName , pass } ; } , } ) ; function getBotSnapshot ( bot ) { let b = { id : bot . id , space : bot . space , tags : typeof bot . tags . toJSON === 'function' ? bot . tags . toJSON ( ) : bot . tags , } as Bot ; let masks = isRuntimeBot ( bot ) ? bot [ GET_TAG_MASKS_SYMBOL ] ( ) : cloneDeep ( bot . masks ?? { } ) ; if ( Object . keys ( masks ) . length > 0 ) { b . masks = masks ; } return b ; } /**
 * Defines an interface that represents the set of additional options that can be provided when recording a file.
 */ export interface RecordFileOptions { /**
     * The description of the file.
     */ description ? : string ; /**
     * The MIME type of the file.
     * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types for more information.
     */ mimeType ? : string ; } param context export function createDefaultLibrary ( context ) { webhook . post = function ( url , data ? , options ? ) { return webhook ( { ... options , method : 'POST' , url : url , data : data , } ) ; } ; const webhookFunc = makeMockableFunction ( webhook , 'webhook' ) ; webhookFunc . post = makeMockableFunction ( webhook . post , 'webhook.post' ) ; const shoutImpl = shout as any ; const shoutProxy = new Proxy ( shoutImpl , { get ( target , name , reciever ) { return ( arg ? ) => { return shout ( name , arg ) ; } ; } , } ) ; return { api : { getBots , getBot , getBotTagValues , getMod , getBotPosition , getID , getJSON , getFormattedJSON , getSnapshot , diffSnapshots , applyDiffToSnapshot , getTag , setTag , setTagMask , clearTagMasks , insertTagText , insertTagMaskText , deleteTagText , deleteTagMaskText , removeTags , renameTag , applyMod , subtractMods , destroy , changeState , getLink : createBotLinkApi , getBotLinks , updateBotLinks , superShout , priorityShout , shout : shoutProxy , whisper , byTag , byID , byMod , inDimension , atPosition , inStack , neighboring , bySpace , byCreator , either , not , remote , sendRemoteData : remoteWhisper , remoteWhisper , remoteShout , uuid , animateTag , clearAnimations , webhook : < WebhookInterface > ( < any > webhookFunc ) , sleep , __energyCheck , clearTimeout , clearInterval , clearWatchBot , clearWatchPortal , assert , assertEqual , expect , html , os : { sleep , toast , showJoinCode , requestFullscreenMode , exitFullscreenMode , showHtml , hideHtml , setClipboard , tweenTo , moveTo , focusOn , showChat , hideChat , run , version , device , isCollaborative , getAB1BootstrapURL , enableAR , disableAR , enableVR , disableVR , enablePointOfView , disablePointOfView , download : downloadData , downloadBots , downloadServer , downloadInst : downloadServer , showUploadAuxFile , showUploadFiles , openQRCodeScanner , closeQRCodeScanner , showQRCode , hideQRCode , openBarcodeScanner , closeBarcodeScanner , showBarcode , hideBarcode , loadServer , unloadServer , loadInst : loadServer , unloadInst : unloadServer , importAUX , parseBotsFromData , replaceDragBot , isInDimension , getCurrentDimension , getCurrentServer , getCurrentInst : getCurrentServer , getMenuDimension , getMiniPortalDimension , getPortalDimension , getDimensionalDepth , showInputForTag , showInput : makeMockableFunction ( showInput , 'os.showInput' ) , goToDimension , goToURL , openURL , openDevConsole , checkout , playSound , bufferSound , cancelSound , hasBotInMiniPortal , share , closeCircleWipe , openCircleWipe , addDropSnap , addBotDropSnap , enableCustomDragging , log , getGeolocation , inSheet , getCameraPosition , getCameraRotation , getFocusPoint , getPointerPosition , getPointerRotation , getPointerDirection , getInputState , getInputList , registerTagPrefix : registerPrefix , registerApp : registerApp , unregisterApp , compileApp : setAppContent , requestAuthBot , getPublicRecordKey , isRecordKey , recordData , getData , recordFile , getFile , convertGeolocationToWhat3Words , setupInst : setupServer , remotes , instances : servers , remoteCount : serverRemoteCount , totalRemoteCount : totalRemoteCount , instStatuses : serverStatuses , beginAudioRecording , endAudioRecording , get vars ( ) { return context . global ; } , } , portal : { registerPrefix , } , server : { setupServer , exportGpio , unexportGpio , setGpio , getGpio , rpioInit , rpioExit , rpioOpen , rpioMode , rpioRead , rpioReadSequence , rpioWrite , rpioWriteSequence , rpioReadpad , rpioWritepad , rpioPud , rpioPoll , rpioClose , rpioI2CBegin , rpioI2CSetSlaveAddress , rpioI2CSetBaudRate , rpioI2CSetClockDivider , rpioI2CRead , rpioI2CWrite , rpioI2CEnd , rpioPWMSetClockDivider , rpioPWMSetRange , rpioPWMSetData , rpioSPIBegin , rpioSPIChipSelect , rpioSPISetCSPolarity , rpioSPISetClockDivider , rpioSPISetDataMode , rpioSPITransfer , rpioSPIWrite , rpioSPIEnd , serialConnect , serialStream , serialOpen , serialUpdate , serialWrite , serialRead , serialClose , serialFlush , serialDrain , serialPause , serialResume , shell , backupToGithub , backupAsDownload , finishCheckout , markHistory , browseHistory , restoreHistoryMark , restoreHistoryMarkToServer , restoreHistoryMarkToInst : restoreHistoryMarkToServer , loadFile , saveFile , serverRemoteCount , totalRemoteCount , serverStatuses , remotes , servers , stories : servers , players : remotes , serverPlayerCount : serverRemoteCount , totalPlayerCount : totalRemoteCount , } , action : { perform , reject , } , adminSpace : { unlock : unlockAdminSpace , setPassword : setAdminSpacePassword , } , experiment : { localFormAnimation , localPositionTween , localRotationTween , getAnchorPointPosition , beginAudioRecording , endAudioRecording , beginRecording , endRecording , speakText , getVoices , } , math : { sum , avg , sqrt , abs , stdDev , randomInt , random , getForwardDirection , intersectPlane , getAnchorPointOffset , addVectors , subtractVectors , negateVector , normalizeVector , vectorLength , scaleVector , areClose , } , mod : { cameraPositionOffset , cameraRotationOffset , } , crypto : { sha256 , sha512 , hmacSha256 , hmacSha512 , encrypt , decrypt , isEncrypted , asymmetric : { keypair : asymmetricKeypair , isKeypair : isAsymmetricKeypair , encrypt : asymmetricEncrypt , decrypt : asymmetricDecrypt , isEncrypted : isAsymmetricEncrypted , } , keypair , sign , verify , createCertificate , signTag , verifyTag , revokeCertificate , } , perf : { getStats , } , web : { get : makeMockableFunction ( webGet , 'web.get' ) , post : makeMockableFunction ( webPost , 'web.post' ) , hook : makeMockableFunction ( webhook , 'web.hook' ) , } , } , tagSpecificApi : { create : ( options ) => ( ... args ) => create ( options . bot ?. id , ... args ) , setTimeout : botTimer ( 'timeout' , setTimeout , true ) , setInterval : botTimer ( 'interval' , setInterval , false ) , watchPortal : watchPortalBots ( ) , watchBot : watchBot ( ) , } , } ; function botTimer ( type , func , clearAfterHandlerIsRun ) { return ( options ) => function ( handler , timeout ? , ... args ) { if ( ! options . bot ) { throw new Error ( `Timers are not supported when there is no current bot.` ) ; } let timer ; if ( clearAfterHandlerIsRun ) { timer = func ( function ( ) { try { handler ( ... arguments ) ; } finally { context . removeBotTimer ( options . bot . id , type , timer ) ; } } , timeout , ... args ) ; } else { timer = func ( handler , timeout , ... args ) ; } context . recordBotTimer ( options . bot . id , { timerId : timer , type : type , } ) ; return timer ; } ; } function clearTimeout ( id ) { context . cancelAndRemoveTimers ( id ) ; } function clearInterval ( id ) { context . cancelAndRemoveTimers ( id ) ; } function watchPortalBots ( ) { let timerId = 0 ; return ( options ) => function ( portalId , handler ) { let id = timerId ++ ; context . recordBotTimer ( options . bot . id , { type : 'watch_portal' , timerId : id , portalId , tag : options . tag , handler , } ) ; return id ; } ; } function watchBot ( ) { let timerId = 0 ; return ( options ) => function ( bot , handler ) { let id = timerId ++ ; let botIds = Array . isArray ( bot ) ? bot . map ( ( b ) => getID ( b ) ) : [ getID ( bot ) ] ; const finalHandler = ( ) => { try { return handler ( ) ; } catch ( err ) { context . enqueueError ( err ) ; } } ; for ( let botId of botIds ) { context . recordBotTimer ( options . bot . id , { type : 'watch_bot' , timerId : id , botId : botId , tag : options . tag , handler : finalHandler , } ) ; } return id ; } ; } function clearWatchBot ( id ) { context . cancelAndRemoveTimers ( id , 'watch_bot' ) ; } function clearWatchPortal ( id ) { context . cancelAndRemoveTimers ( id , 'watch_portal' ) ; } param condition param message function assert ( condition , message ? ) { if ( ! condition ) { if ( hasValue ( message ) ) { throw new Error ( 'Assertion failed. ' + message ) ; } else { throw new Error ( 'Assertion failed.' ) ; } } } function getAssertionValue ( value ) { if ( value instanceof Error ) { return value . toString ( ) ; } return value ; } param first param second function assertEqual ( first , second ) { expect ( first ) . toEqual ( second ) ; } example function getBots ( ... args ) { if ( args . length > 0 && typeof args [ 0 ] === 'function' ) { const filtered = context . bots . filter ( ( b ) => args . every ( ( f ) => f ( b ) ) ) ; const sortFuncs = args . filter ( ( f ) => typeof f . sort === 'function' ) . map ( ( f ) => f . sort ) ; const sorted = sortFuncs . length > 0 ? sortBy ( filtered , ... sortFuncs ) : filtered ; return sorted ; } let tag = args [ 0 ] ; if ( typeof tag === 'undefined' ) { return context . bots . slice ( ) ; } else if ( ! tag ) { return [ ] ; } tag = trimTag ( tag ) ; const filter = arguments [ 1 ] ; if ( hasValue ( filter ) ) { if ( typeof filter === 'function' ) { return context . bots . filter ( ( b ) => filter ( b . tags [ tag ] ) ) ; } else if ( tag === 'id' && typeof filter === 'string' ) { const bot = context . state [ filter ] ; return bot ? [ bot ] : [ ] ; } else { return context . bots . filter ( ( b ) => b . tags [ tag ] === filter ) ; } } else { return context . bots . filter ( ( b ) => hasValue ( b . tags [ tag ] ) ) ; } } returns example function getBot ( ... args ) { const bots = getBots ( ... args ) ; return bots . first ( ) ; } param tag param filter function getBotTagValues ( tag , filter ? ) { const values = context . bots . map ( ( b ) => getTag ( b , tag ) ) . filter ( ( t ) => hasValue ( t ) ) ; if ( hasValue ( filter ) ) { if ( typeof filter === 'function' ) { return values . filter ( ( val ) => filter ( val ) ) ; } else { return values . filter ( ( val ) => val === filter ) ; } } else { return values ; } } param bot param tags returns function getMod ( bot , ... tags ) { if ( typeof bot === 'string' ) { bot = JSON . parse ( bot ) ; } let diff = { } ; let tagsObj ; let botTags ; if ( isBot ( bot ) ) { tagsObj = bot . tags ; botTags = tagsOnBot ( bot ) ; } else if ( hasValue ( bot [ ORIGINAL_OBJECT ] ) ) { tagsObj = bot [ ORIGINAL_OBJECT ] ; botTags = Object . keys ( tagsObj ) ; } else { tagsObj = bot ; botTags = Object . keys ( tagsObj ) ; } for ( let botTag of botTags ) { let add = false ; if ( tags . length > 0 ) { for ( let tag of tags ) { if ( tag instanceof RegExp ) { if ( tag . test ( botTag ) ) { add = true ; break ; } } else { if ( tag === botTag ) { add = true ; break ; } } } } else { add = true ; } if ( add ) { diff [ botTag ] = tagsObj [ botTag ] ; } } return diff ; } param bot param dimension function getBotPosition ( bot , dimension ) { if ( ! bot ) { throw new Error ( 'The given bot must not be null.' ) ; } const finalBot = typeof bot === 'string' ? context . state [ bot ] : bot ; if ( ! finalBot ) { throw new Error ( `Could not find the bot with the given ID (${ bot }).` ) ; } return calcGetBotPosition ( null , finalBot , dimension ) ; } param tag param filter example example example function byTag ( tag , filter ? ) { tag = trimTag ( tag ) ; if ( filter && typeof filter === 'function' ) { return ( bot ) => { let val = bot . tags [ tag ] ; return hasValue ( val ) && filter ( val ) ; } ; } else if ( hasValue ( filter ) ) { if ( isBotLink ( filter ) ) { const ids = parseBotLink ( filter ) ; if ( ids . length === 0 ) { return ( bot ) => { let val = bot . tags [ tag ] ; return val === filter ; } ; } else if ( ids . length === 1 ) { return ( bot ) => { let val = bot . tags [ tag ] ; return ( ids [ 0 ] === val || ( isBotLink ( val ) && parseBotLink ( val ) . some ( ( id ) => id === ids [ 0 ] ) ) ) ; } ; } else { return ( bot ) => { let val = bot . tags [ tag ] ; const valIds = parseBotLink ( val ) ; return ( ! ! valIds && ids . every ( ( id1 ) => valIds . some ( ( id2 ) => id1 === id2 ) ) ) ; } ; } } else { return ( bot ) => { let val = bot . tags [ tag ] ; return hasValue ( val ) && filter === val ; } ; } } else if ( filter === null ) { return ( bot ) => { let val = bot . tags [ tag ] ; return ! hasValue ( val ) ; } ; } else { return ( bot ) => { let val = bot . tags [ tag ] ; return hasValue ( val ) ; } ; } } param id example function byID ( id ) { let filter = ( ( bot ) => { return bot . id === id ; } ) as any ; filter . recordFilter = true ; filter . id = id ; filter . toJSON = ( ) => { return { recordFilter : true , id : id , } ; } ; filter [ DEBUG_STRING ] = debugStringifyFunction ( 'byID' , [ id ] ) ; return filter ; } param mod example function byMod ( mod ) { let tags = isBot ( mod ) ? mod . tags : mod ; let filters = Object . keys ( tags ) . map ( ( k ) => byTag ( k , tags [ k ] ) ) ; return ( bot ) => filters . every ( ( f ) => f ( bot ) ) ; } param dimension returns example function inDimension ( dimension ) { return byTag ( dimension , true ) ; } param dimension param x param y returns example function atPosition ( dimension , x , y ) { const inCtx = inDimension ( dimension ) ; const atX = byTag ( `${ dimension }X` , ( bx ) => areClose ( bx , x ) ) ; const atY = byTag ( `${ dimension }Y` , ( by ) => areClose ( by , y ) ) ; const filter = ( b ) => inCtx ( b ) && atX ( b ) && atY ( b ) ; filter . sort = ( b ) => getTag ( b , `${ dimension }SortOrder` ) || 0 ; return filter ; } param bot param dimension returns example function inStack ( bot , dimension ) { return atPosition ( dimension , getTag ( bot , `${ dimension }X` ) , getTag ( bot , `${ dimension }Y` ) ) ; } param bot param dimension param direction returns example function neighboring ( bot , dimension , direction ? ) { if ( ! hasValue ( direction ) ) { return either ( neighboring ( bot , dimension , 'front' ) , neighboring ( bot , dimension , 'right' ) , neighboring ( bot , dimension , 'back' ) , neighboring ( bot , dimension , 'left' ) ) ; } else if ( direction !== 'left' && direction !== 'right' && direction !== 'front' && direction !== 'back' ) { return ( ) => false ; } const offsetX = direction === 'left' ? 1 : direction === 'right' ? - 1 : 0 ; const offsetY = direction === 'back' ? 1 : direction === 'front' ? - 1 : 0 ; const x = getTag ( bot , `${ dimension }X` ) ; const y = getTag ( bot , `${ dimension }Y` ) ; return atPosition ( dimension , x + offsetX , y + offsetY ) ; } param space function bySpace ( space ) { let func = byTag ( BOT_SPACE_TAG , space ) as SpaceFilter ; func . recordFilter = true ; func . space = space ; func . toJSON = ( ) => { return { recordFilter : true , space : space , } ; } ; func [ DEBUG_STRING ] = debugStringifyFunction ( 'bySpace' , [ space ] ) ; return func ; } param bot returns example function byCreator ( bot ) { const id = getID ( bot ) ; return byTag ( 'creator' , id ) ; } param filters example function either ( ... filters ) { return ( bot ) => filters . some ( ( f ) => f ( bot ) ) ; } param filter example function not ( filter ) { return ( bot ) => ! filter ( bot ) ; } param bot param tag example function getTag ( bot , ... tags ) { let current = bot ; for ( let i = 0 ; i < tags . length ; i ++ ) { const tag = trimTag ( tags [ i ] . toString ( ) ) ; if ( isBot ( current ) ) { current = current . tags [ tag ] ; } else { return current ; } } return current ; } param bot function getID ( bot ) { if ( typeof bot === 'string' ) { return bot || null ; } else if ( bot ) { return bot . id || null ; } return null ; } param data function getJSON ( data ) { if ( hasValue ( data ?. [ ORIGINAL_OBJECT ] ) ) { return stableStringify ( data [ ORIGINAL_OBJECT ] ) ; } return stableStringify ( data ) ; } param data function getFormattedJSON ( data ) { if ( hasValue ( data ?. [ ORIGINAL_OBJECT ] ) ) { return stableStringify ( data [ ORIGINAL_OBJECT ] , { space : 2 } ) ; } return stableStringify ( data , { space : 2 } ) ; } param bots function getSnapshot ( bots ) { if ( ! Array . isArray ( bots ) ) { return getSnapshot ( [ bots ] ) ; } let state = { } as BotsState ; for ( let bot of bots ) { let b = ( state [ bot . id ] = { id : bot . id , tags : { ... ( typeof bot . tags . toJSON === 'function' ? bot . tags . toJSON ( ) : bot . tags ) , } , } as Bot ) ; if ( bot . space ) { b . space = bot . space ; } let masks = isRuntimeBot ( bot ) ? bot [ GET_TAG_MASKS_SYMBOL ] ( ) : cloneDeep ( bot . masks ?? { } ) ; if ( Object . keys ( masks ) . length > 0 ) { b . masks = masks ; } } return state ; } param first param second function diffSnapshots ( first , second ) { const allIds = union ( Object . keys ( first ) , Object . keys ( second ) ) ; let diff = { } ; for ( let id of allIds ) { const inFirst = id in first ; const inSecond = id in second ; if ( inFirst && inSecond ) { const firstBot = first [ id ] ; const secondBot = second [ id ] ; if ( firstBot && secondBot ) { let botDiff = { } as PartialBot ; let tagsDiff = diffTags ( firstBot . tags , secondBot . tags ) ; if ( ! ! tagsDiff ) { botDiff . tags = tagsDiff ; } const firstBotMasks = firstBot . masks || { } ; const secondBotMasks = secondBot . masks || { } ; let masksDiff = { } as PartialBot [ 'masks' ] ; let hasMasksDiff = false ; const allMaskSpaces = union ( Object . keys ( firstBotMasks ) , Object . keys ( secondBotMasks ) ) ; for ( let space of allMaskSpaces ) { const firstMasks = firstBotMasks [ space ] || { } ; const secondMasks = secondBotMasks [ space ] || { } ; let tagsDiff = diffTags ( firstMasks , secondMasks ) ; if ( ! ! tagsDiff ) { hasMasksDiff = true ; masksDiff [ space ] = tagsDiff ; } } if ( hasMasksDiff ) { botDiff . masks = masksDiff ; } if ( ! ! tagsDiff || hasMasksDiff ) { diff [ id ] = botDiff ; } } } else if ( inFirst ) { diff [ id ] = null ; } else if ( inSecond ) { diff [ id ] = second [ id ] ; } } return diff ; function diffTags ( firstTags , secondTags ) { let tagsDiff = { } as BotTags ; let hasTagsDiff = false ; const allTags = union ( Object . keys ( firstTags ) , Object . keys ( secondTags ) ) ; for ( let tag of allTags ) { const firstValue = firstTags [ tag ] ; const secondValue = secondTags [ tag ] ; if ( ! isEqual ( firstValue , secondValue ) ) { hasTagsDiff = true ; tagsDiff [ tag ] = hasValue ( secondValue ) ? secondValue : null ; } } return hasTagsDiff ? tagsDiff : null ; } } param snapshot param diff function applyDiffToSnapshot ( snapshot , diff ) { return apply ( snapshot , diff ) ; } param message param duration function toast ( message , duration = 2 ) { return addAction ( toastMessage ( convertToCopiableValue ( message ) , duration ) ) ; } param inst param dimension function showJoinCode ( inst ? , dimension ? ) { return addAction ( calcShowJoinCode ( inst , dimension ) ) ; } /**
     * Requests that AUX enters fullscreen mode.
     * Depending on the web browser, this may ask the player for permission.
     */ function requestFullscreenMode ( ) { return addAction ( requestFullscreen ( ) ) ; } /**
     * Exits fullscreen mode.
     */ function exitFullscreenMode ( ) { return addAction ( exitFullscreen ( ) ) ; } param html function showHtml ( html ) { return addAction ( htmlMessage ( html ) ) ; } /**
     * Hides the HTML from the user.
     */ function hideHtml ( ) { return addAction ( hideHtmlMessage ( ) ) ; } param text function setClipboard ( text ) { return addAction ( calcSetClipboard ( text ) ) ; } param bot param zoomValue param rotX param rotY function tweenTo ( bot , zoomValue ? , rotX ? , rotY ? , duration ? ) { return addAction ( calcTweenTo ( getID ( bot ) , { zoom : zoomValue , rotation : hasValue ( rotX ) || hasValue ( rotY ) ? { x : hasValue ( rotX ) ? rotX * ( Math . PI / 180 ) : null , y : hasValue ( rotY ) ? rotY * ( Math . PI / 180 ) : null , } : undefined , duration , } ) ) ; } param bot param zoomValue param rotX param rotY function moveTo ( bot , zoomValue ? , rotX ? , rotY ? ) { return tweenTo ( bot , zoomValue , rotX , rotY , 0 ) ; } param botOrPosition param options function focusOn ( botOrPosition , options = { } ) { const task = context . createTask ( ) ; const finalOptions = { duration : 1 , easing : 'quadratic' , ... ( options ?? { } ) , } ; let action ; if ( botOrPosition === null ) { action = cancelAnimation ( task . taskId ) ; } else if ( botOrPosition === undefined ) { throw new Error ( 'Cannot focus on an undefined bot. Maybe a getBot() is returning undefined?' ) ; } else if ( typeof botOrPosition === 'string' || isBot ( botOrPosition ) ) { action = calcTweenTo ( getID ( botOrPosition ) , finalOptions , task . taskId ) ; } else { action = animateToPosition ( botOrPosition , finalOptions , task . taskId ) ; } return addAsyncAction ( task , action ) ; } param placeholderOrOptions function showChat ( placeholderOrOptions ? ) { const action = typeof placeholderOrOptions === 'string' ? calcShowChat ( { placeholder : placeholderOrOptions , } ) : calcShowChat ( placeholderOrOptions ) ; return addAction ( action ) ; } /**
     * Hides the run bar.
     */ function hideChat ( ) { return addAction ( calcHideChat ( ) ) ; } param script function run ( script ) { const task = context . createTask ( ) ; const event = runScript ( script , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     * Gets information about the version of AUX that is running.
     */ function version ( ) { return context . version ; } /**
     * Gets information about the device that the player is using.
     */ function device ( ) { if ( context . device ) { return context . device ; } return { supportsAR : null as boolean , supportsVR : null as boolean , isCollaborative : null as boolean , ab1BootstrapUrl : null as string , } ; } /**
     * Gets whether this device has enabled collaborative features.
     */ function isCollaborative ( ) { if ( context . device ) { return context . device . isCollaborative ; } return true ; } /**
     * Gets the URL that AB1 should be bootstrapped from.
     */ function getAB1BootstrapURL ( ) { if ( context . device ) { return context . device . ab1BootstrapUrl ; } return 'https://bootstrap.casualos.com/ab1.aux' ; } /**
     * Enables Augmented Reality features.
     */ function enableAR ( ) { return addAction ( calcEnableAR ( ) ) ; } /**
     * Disables Augmented Reality features.
     */ function disableAR ( ) { return addAction ( calcDisableAR ( ) ) ; } /**
     * Enables Virtual Reality features.
     */ function enableVR ( ) { return addAction ( calcEnableVR ( ) ) ; } /**
     * Disables Virtual Reality features.
     */ function disableVR ( ) { return addAction ( calcDisableVR ( ) ) ; } /**
     * Enables Point-of-View mode.
     */ function enablePointOfView ( center = { x : 0 , y : 0 , z : 0 } , imu ? ) { return addAction ( enablePOV ( center , imu ) ) ; } /**
     * Disables Point-of-View mode.
     */ function disablePointOfView ( ) { return addAction ( disablePOV ( ) ) ; } param data param filename param mimeType function downloadData ( data , filename , mimeType = mime . getType ( filename ) || 'text/plain' ) { if ( typeof filename !== 'string' ) { throw new Error ( 'The filename must be a string.' ) ; } if ( typeof mimeType !== 'string' ) { throw new Error ( 'The mimeType must be a string.' ) ; } if ( data instanceof Blob ) { mimeType = data . type ; } if ( ! hasExtension ( filename ) ) { const extension = mime . getExtension ( mimeType ) ; if ( hasValue ( extension ) ) { filename = `${ filename }.${ extension }` ; } } if ( typeof data === 'string' ) { return addAction ( download ( data , filename , mimeType ) ) ; } else if ( data instanceof ArrayBuffer ) { return addAction ( download ( data , filename , mimeType ) ) ; } else if ( data instanceof Blob ) { return addAction ( download ( data , filename , mimeType ) ) ; } else if ( typeof data === 'object' ) { return addAction ( download ( JSON . stringify ( data ) , filename , mimeType ) ) ; } throw new Error ( 'The data must be either a string, object, or ArrayBuffer.' ) ; } param filename function hasExtension ( filename ) { const dot = filename . lastIndexOf ( '.' ) ; return dot >= 0 ; } param bots param filename function downloadBots ( bots , filename ) { let state = { } ; for ( let bot of bots ) { state [ bot . id ] = bot ; } let data = JSON . stringify ( getDownloadState ( state ) ) ; if ( isPdf ( filename ) ) { const encoder = new TextEncoder ( ) ; const bytes = encoder . encode ( data ) ; const base64 = fromByteArray ( bytes ) ; data = embedBase64InPdf ( base64 ) ; } const downloadedFilename = formatAuxFilename ( filename ) ; return addAction ( download ( data , downloadedFilename , mime . getType ( downloadedFilename ) || 'application/json' ) ) ; } /**
     * Downloads all the shared bots in the inst.
     */ function downloadServer ( ) { return downloadBots ( getBots ( bySpace ( 'shared' ) ) , `${ getCurrentServer ( ) }.aux` ) ; } /**
     * Shows the "Upload AUX File" dialog.
     */ function showUploadAuxFile ( ) { return addAction ( calcShowUploadAuxFile ( ) ) ; } /**
     * Shows the "Upload File" dialog.
     */ function showUploadFiles ( ) { const task = context . createTask ( ) ; const action = calcShowUploadFiles ( task . taskId ) ; return addAsyncAction ( task , action ) ; } param camera function openQRCodeScanner ( camera ? ) { const event = calcOpenQRCodeScanner ( true , camera ) ; return addAction ( event ) ; } /**
     * Closes the QR Code Scanner.
     */ function closeQRCodeScanner ( ) { const event = calcOpenQRCodeScanner ( false ) ; return addAction ( event ) ; } param code function showQRCode ( code ) { const event = calcShowQRCode ( true , code ) ; return addAction ( event ) ; } /**
     * Hides the QR Code.
     */ function hideQRCode ( ) { const event = calcShowQRCode ( false ) ; return addAction ( event ) ; } param camera function openBarcodeScanner ( camera ? ) { const event = calcOpenBarcodeScanner ( true , camera ) ; return addAction ( event ) ; } /**
     * Closes the barcode scanner.
     */ function closeBarcodeScanner ( ) { const event = calcOpenBarcodeScanner ( false ) ; return addAction ( event ) ; } param code param format function showBarcode ( code , format ? ) { const event = calcShowBarcode ( true , code , format ) ; return addAction ( event ) ; } /**
     * Hides the barcode.
     */ function hideBarcode ( ) { const event = calcShowBarcode ( false ) ; return addAction ( event ) ; } param id function loadServer ( id ) { const event = loadSimulation ( id ) ; return addAction ( event ) ; } param id function unloadServer ( id ) { const event = unloadSimulation ( id ) ; return addAction ( event ) ; } param urlOrJSON function importAUX ( urlOrJSON ) { try { const bots = parseBotsFromData ( urlOrJSON ) ; if ( bots ) { let state = { } ; for ( let bot of bots ) { state [ bot . id ] = bot ; } const uploaded = getUploadState ( state ) ; const event = addState ( uploaded ) ; return addAction ( event ) ; } } catch { } const event = calcImportAUX ( urlOrJSON ) ; return addAction ( event ) ; } param jsonOrPdf function parseBotsFromData ( jsonOrPdf ) { let data ; try { data = JSON . parse ( jsonOrPdf ) ; } catch ( e ) { try { data = getEmbeddedBase64FromPdf ( jsonOrPdf ) ; const bytes = toByteArray ( data ) ; const decoder = new TextDecoder ( ) ; const text = decoder . decode ( bytes ) ; data = JSON . parse ( text ) ; } catch ( err ) { data = null ; } } if ( ! hasValue ( data ) ) { return null ; } const state = getUploadState ( data ) ; let bots = [ ] as Bot [ ] ; for ( let bot in state ) { const b = state [ bot ] ; if ( hasValue ( b ) ) { bots . push ( b ) ; } } return bots ; } param bot function replaceDragBot ( bot ) { const event = calcReplaceDragBot ( context . unwrapBot ( bot ) ) ; return addAction ( event ) ; } param dimension function isInDimension ( dimension ) { return ( getCurrentDimension ( ) === dimension && getCurrentDimension ( ) != undefined ) ; } /**
     * Gets the dimension that the player is currently viewing.
     */ function getCurrentDimension ( ) { const user = context . playerBot ; if ( user ) { const dimension = getTag ( user , 'gridPortal' ) ; if ( hasValue ( dimension ) ) { return dimension . toString ( ) ; } return undefined ; } return undefined ; } /**
     * Gets the instance that the player is currently in.
     */ function getCurrentServer ( ) { const user = context . playerBot ; if ( user ) { let inst = getTag ( user , 'inst' ) ; if ( hasValue ( inst ) ) { return inst . toString ( ) ; } return undefined ; } return undefined ; } /**
     * Gets the name of the dimension that is used for the current user's miniGridPortal.
     */ function getMiniPortalDimension ( ) { const user = context . playerBot ; if ( user ) { const miniGridPortal = getTag ( user , MINI_PORTAL ) ; if ( hasValue ( miniGridPortal ) ) { return miniGridPortal . toString ( ) ; } return null ; } else { return null ; } } /**
     * Gets the name of the dimension that is used for the current user's menu.
     */ function getMenuDimension ( ) { const user = context . playerBot ; if ( user ) { const menu = getTag ( user , 'menuPortal' ) ; if ( hasValue ( menu ) ) { return menu . toString ( ) ; } return null ; } else { return null ; } } param portal function getPortalDimension ( portal ) { const user = context . playerBot ; if ( ! user ) { return null ; } const portalTag = getPortalTag ( portal ) ; const dimension = getTag ( user , portalTag ) ; if ( ! hasValue ( dimension ) ) { return null ; } return dimension . toString ( ) ; } param dimension function getDimensionalDepth ( dimension ) { const bot = context . playerBot ; if ( getTag ( bot , dimension ) === true ) { return 0 ; } else if ( KNOWN_PORTALS . some ( ( portal ) => getTag ( bot , portal ) === dimension ) ) { return 1 ; } return - 1 ; } param bot param tag param options example example function showInputForTag ( bot , tag , options ? ) { const id = typeof bot === 'string' ? bot : bot . id ; const event = calcShowInputForTag ( id , trimTag ( tag ) , options ) ; return addAction ( event ) ; } param currentValue param options example function showInput ( currentValue ? , options ? ) { const task = context . createTask ( ) ; const event = calcShowInput ( currentValue , options , task . taskId ) ; return addAsyncAction ( task , event ) ; } param dimension example function goToDimension ( dimension ) { const event = calcGoToDimension ( dimension ) ; return addAction ( event ) ; } param url example function goToURL ( url ) { const event = calcGoToURL ( url ) ; return addAction ( event ) ; } param url example function openURL ( url ) { const event = calcOpenURL ( url ) ; return addAction ( event ) ; } /**
     * Instructs CasualOS to open the built-in developer console.
     * The dev console provides easy access to error messages and debug logs for formulas and actions.
     */ function openDevConsole ( ) { const event = openConsole ( ) ; return addAction ( event ) ; } param options example function checkout ( options ) { const event = calcCheckout ( options ) ; return addAction ( event ) ; } example function playSound ( url ) { const task = context . createTask ( ) ; const event = calcPlaySound ( url , task . taskId , task . taskId ) ; return addAsyncAction ( task , event ) ; } param url example function bufferSound ( url ) { const task = context . createTask ( ) ; const event = calcBufferSound ( url , task . taskId ) ; return addAsyncAction ( task , event ) ; } param soundId example function cancelSound ( soundId ) { const task = context . createTask ( ) ; const id = typeof soundId === 'object' ? getOriginalObject ( soundId ) . soundID : soundId ; const event = calcCancelSound ( id , task . taskId ) ; return addAsyncAction ( task , event ) ; } param bots function hasBotInMiniPortal ( bots ) { if ( ! Array . isArray ( bots ) ) { bots = [ bots ] ; } let miniGridPortal = getMiniPortalDimension ( ) ; if ( ! hasValue ( miniGridPortal ) ) { return false ; } return every ( bots , ( f ) => getTag ( f , miniGridPortal ) === true ) ; } param options function share ( options ) { const task = context . createTask ( ) ; const event = calcShare ( options , task . taskId ) ; return addAsyncAction ( task , event ) ; } param options function closeCircleWipe ( options ? ) { const task = context . createTask ( ) ; const event = circleWipe ( false , { color : options ?. color || 'black' , duration : options ?. duration || 1 , } , task . taskId ) ; return addAsyncAction ( task , event ) ; } param options function openCircleWipe ( options ? ) { const task = context . createTask ( ) ; const event = circleWipe ( true , { color : options ?. color || 'black' , duration : options ?. duration || 1 , } , task . taskId ) ; return addAsyncAction ( task , event ) ; } param targets function addDropSnap ( ... targets ) { return addAction ( calcAddDropSnap ( null , targets ) ) ; } param bot param targets function addBotDropSnap ( bot , ... targets ) { return addAction ( calcAddDropSnap ( getID ( bot ) , targets ) ) ; } /**
     * Enables custom dragging for the current drag operation.
     * This will disable the built-in logic that moves the bot(s) and
     * enables the "onDragging" and "onAnyBotDragging" listen tags.
     */ function enableCustomDragging ( ) { return addAction ( calcEnableCustomDragging ( ) ) ; } param args function log ( ... args ) { console . log ( ... args ) ; } /**
     * Gets the geolocation of the device.
     * Returns a promise that resolves with the location.
     */ function getGeolocation ( ) { const task = context . createTask ( ) ; const event = calcGetGeolocation ( task . taskId ) ; return addAsyncAction ( task , event ) ; } param prefix param options function registerPrefix ( prefix , options = { } ) { if ( typeof prefix !== 'string' ) { throw new Error ( 'A prefix must be provided.' ) ; } const task = context . createTask ( ) ; const event = calcRegisterPrefix ( prefix , { language : options ?. language || 'javascript' , } , task . taskId ) ; return addAsyncAction ( task , event ) ; } param portalId param bot function registerApp ( portalId , bot ) { const task = context . createTask ( ) ; const event = registerCustomApp ( portalId , getID ( bot ) , task . taskId ) ; return addAsyncAction ( task , event ) ; } param portalId function unregisterApp ( portalId ) { const task = context . createTask ( ) ; const event = unregisterCustomApp ( portalId , task . taskId ) ; return addAsyncAction ( task , event ) ; } param portalId param output function setAppContent ( portalId , output ) { const event = setAppOutput ( portalId , output ) ; return addAction ( event ) ; } /**
     * Requests an Auth Bot for the current session.
     */ async function requestAuthBot ( ) { const data = await requestAuthData ( ) ; if ( ! data ) { return null ; } let bot = getBot ( 'id' , data . userId ) ; if ( ! bot ) { bot = context . createBot ( createBot ( data . userId , { avatarAddress : data . avatarUrl , avatarPortraitAddress : data . avatarPortraitUrl , name : data . name , } , TEMPORARY_BOT_PARTITION_ID ) ) ; } await defineGlobalBot ( 'auth' , bot . id ) ; return bot ; } function requestAuthData ( ) { const task = context . createTask ( ) ; const event = calcRequestAuthData ( task . taskId ) ; return addAsyncAction ( task , event ) ; } function defineGlobalBot ( name , botId ) { const task = context . createTask ( ) ; const event = calcDefineGlobalBot ( name , botId , task . taskId ) ; return addAsyncAction ( task , event ) ; } param name function getPublicRecordKey ( name ) { const task = context . createTask ( ) ; const event = calcGetPublicRecordKey ( name , task . taskId ) ; return addAsyncAction ( task , event ) ; } param key function isRecordKey ( key ) { return calcIsRecordKey ( key ) ; } param recordKey param address param data function recordData ( recordKey , address , data ) { const task = context . createTask ( ) ; const event = calcRecordData ( recordKey , address , data , task . taskId ) ; return addAsyncAction ( task , event ) ; } param recordKeyOrName param address function getData ( recordKeyOrName , address ) { let recordName = isRecordKey ( recordKeyOrName ) ? parseRecordKey ( recordKeyOrName ) [ 0 ] : recordKeyOrName ; const task = context . createTask ( ) ; const event = getRecordData ( recordName , address , task . taskId ) ; return addAsyncAction ( task , event ) ; } param recordKey param data param options function recordFile ( recordKey , data , options ? ) { if ( ! hasValue ( recordKey ) ) { throw new Error ( 'A recordKey must be provided.' ) ; } else if ( typeof recordKey !== 'string' ) { throw new Error ( 'recordKey must be a string.' ) ; } if ( ! hasValue ( data ) ) { throw new Error ( 'data must be provided.' ) ; } const task = context . createTask ( ) ; const event = calcRecordFile ( recordKey , data , options ?. description , options ?. mimeType , task . taskId ) ; return addAsyncAction ( task , event ) ; } param result function getFile ( result ) ; param url function getFile ( url ) ; param urlOrRecordFileResult function getFile ( urlOrRecordFileResult ) { if ( ! hasValue ( urlOrRecordFileResult ) ) { throw new Error ( 'A url or successful os.recordFile() result must be provided.' ) ; } let url ; if ( typeof urlOrRecordFileResult === 'string' ) { url = urlOrRecordFileResult ; } else { if ( ! urlOrRecordFileResult . success ) { throw new Error ( 'The result must be a successful os.recordFile() result.' ) ; } url = urlOrRecordFileResult . url ; } let promise = webGet ( url ) ; let action = ( promise as any ) [ ORIGINAL_OBJECT ] ; let final = promise . then ( ( result ) => { return result . data ; } ) ; ( final as any ) [ ORIGINAL_OBJECT ] = action ; return final ; } param location function convertGeolocationToWhat3Words ( location ) { const task = context . createTask ( ) ; const event = calcConvertGeolocationToWhat3Words ( location , task . taskId ) ; return addAsyncAction ( task , event ) ; } param inst param botOrMod function setupServer ( inst , botOrMod ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( calcSetupServer ( inst , convertToCopiableValue ( botOrMod ) ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param mode function exportGpio ( pin , mode ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( exportGpioPin ( pin , mode ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin function unexportGpio ( pin ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( unexportGpioPin ( pin ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param value function setGpio ( pin , value ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( setGpioPin ( pin , value ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin function getGpio ( pin ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( getGpioPin ( pin ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param options example function rpioInit ( options ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioInitPin ( options ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     * Shuts down rpio, unmaps, and clears everything.
     */ function rpioExit ( ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioExitPin ( ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param mode param options function rpioOpen ( pin , mode , options ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioOpenPin ( pin , mode , options ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param mode param options function rpioMode ( pin , mode , options ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioModePin ( pin , mode , options ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin function rpioRead ( pin ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioReadPin ( pin ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param length function rpioReadSequence ( pin , length ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioReadSequencePin ( pin , length ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param value function rpioWrite ( pin , value ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioWritePin ( pin , value ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param buffer function rpioWriteSequence ( pin , buffer ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioWriteSequencePin ( pin , buffer ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param group param bitmask function rpioReadpad ( group , bitmask ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioReadpadPin ( group , bitmask ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param group param slew param hysteresis param current function rpioWritepad ( group , slew ? , hysteresis ? , current ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioWritepadPin ( group , slew , hysteresis , current ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param state function rpioPud ( pin , state ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioPudPin ( pin , state ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param cb param options function rpioPoll ( pin , cb , options ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioPollPin ( pin , cb , options ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param options function rpioClose ( pin , options ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioClosePin ( pin , options ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     * Initializes i2c for use.
     */ function rpioI2CBegin ( ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioI2CBeginPin ( ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param address function rpioI2CSetSlaveAddress ( address ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioI2CSetSlaveAddressPin ( address ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param rate function rpioI2CSetBaudRate ( rate ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioI2CSetBaudRatePin ( rate ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param rate function rpioI2CSetClockDivider ( rate ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioI2CSetClockDividerPin ( rate ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param rx param length function rpioI2CRead ( rx , length ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioI2CReadPin ( rx , length ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param tx param length function rpioI2CWrite ( tx , length ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioI2CWritePin ( tx , length ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     *
     */ /**
     *
     */ /**
     * Turn off the i²c interface and return the pins to GPIO.
     */ function rpioI2CEnd ( ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioI2CEndPin ( ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param rate function rpioPWMSetClockDivider ( rate ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioPWMSetClockDividerPin ( rate ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param range function rpioPWMSetRange ( pin , range ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioPWMSetRangePin ( pin , range ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param pin param width function rpioPWMSetData ( pin , width ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioPWMSetDataPin ( pin , width ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     * Initiate SPI mode.
     */ function rpioSPIBegin ( ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioSPIBeginPin ( ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param value function rpioSPIChipSelect ( value ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioSPIChipSelectPin ( value ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param value param polarity function rpioSPISetCSPolarity ( value , polarity ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioSPISetCSPolarityPin ( value , polarity ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param rate function rpioSPISetClockDivider ( rate ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioSPISetClockDividerPin ( rate ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param mode function rpioSPISetDataMode ( mode ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioSPISetDataModePin ( mode ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     *
     */ function rpioSPITransfer ( tx ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioSPITransferPin ( tx ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     *
     */ function rpioSPIWrite ( tx ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioSPIWritePin ( tx ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     * Release the pins back to general purpose use.
     */ function rpioSPIEnd ( ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( rpioSPIEndPin ( ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param name param device param mac param channel param options { boolean } function serialConnect ( name , device , mac , channel , options ? , cb ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( serialConnectPin ( name , device , mac , channel , options , cb ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param bot param name function serialStream ( bot , name ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( serialStreamPin ( bot , name ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param name function serialOpen ( name ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( serialOpenPin ( name ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param name param options { number = } param cb function serialUpdate ( name , options , cb ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( serialUpdatePin ( name , options , cb ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param name param data param encoding param cb param taskId function serialWrite ( name , data , encoding ? , cb ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( serialWritePin ( name , data , encoding , cb ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param name param size param taskId function serialRead ( name , size ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( serialReadPin ( name , size ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param name param cb param device param taskId function serialClose ( name , device , cb ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( serialClosePin ( name , device , cb ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param name function serialFlush ( name ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( serialFlushPin ( name ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param name function serialDrain ( name ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( serialDrainPin ( name ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param name function serialPause ( name ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( serialPausePin ( name ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param name function serialResume ( name ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( serialResumePin ( name ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param script function shell ( script ) { return remote ( calcShell ( script ) ) ; } param auth function backupToGithub ( auth ) { return remote ( calcBackupToGithub ( auth ) ) ; } /**
     * Backs up all the AUX instances to a zip bot.
     */ function backupAsDownload ( target ) { return remote ( calcBackupAsDownload ( convertSessionSelector ( target ) ) ) ; } param options example function finishCheckout ( options ) { const event = calcFinishCheckout ( options . secretKey , options . token , options . amount , options . currency , options . description , options . extra ) ; return addAction ( event ) ; } param options example function markHistory ( options ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( calcMarkHistory ( options ) , undefined , false , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     * Loads the "history" space into the inst.
     */ function browseHistory ( ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( calcBrowseHistory ( ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param mark function restoreHistoryMark ( mark ) { const id = getID ( mark ) ; const task = context . createTask ( true , true ) ; const event = calcRemote ( calcRestoreHistoryMark ( id ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param mark param inst function restoreHistoryMarkToServer ( mark , inst ) { const id = getID ( mark ) ; const task = context . createTask ( true , true ) ; const event = calcRemote ( calcRestoreHistoryMark ( id , inst ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param path param options function loadFile ( path , options ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( calcLoadFile ( { path : path , ... ( options || { } ) , } ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param path param data param options function saveFile ( path , data , options ? ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( calcSaveFile ( { path : path , data : data , ... ( options || { } ) , } ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param inst function serverRemoteCount ( inst ? ) { const task = context . createTask ( true , true ) ; const actualServer = hasValue ( inst ) ? inst : getCurrentServer ( ) ; const event = calcRemote ( getRemoteCount ( actualServer ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     * Gets the total number of remotes that are connected to the server.
     */ function totalRemoteCount ( ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( getRemoteCount ( ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     * Gets the list of instances that are on the server.
     */ function servers ( ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( getServers ( ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     * Gets the list of instances that are on the server.
     */ function serverStatuses ( ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( getServerStatuses ( ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     * Gets the list of remote IDs that are connected to the instance.
     */ function remotes ( ) { const task = context . createTask ( true , true ) ; const event = calcRemote ( getRemotes ( ) , undefined , undefined , task . taskId ) ; return addAsyncAction ( task , event ) ; } param event param selector param allowBatching example function remote ( event , selector ? , allowBatching ? ) { if ( ! event ) { return ; } let actions = [ ] ; let selectors = Array . isArray ( selector ) ? selector : [ selector ] ; for ( let s of selectors ) { const r = calcRemote ( event , convertSessionSelector ( s ) , allowBatching ) ; actions . push ( addAction ( r ) ) ; } if ( Array . isArray ( selector ) ) { return actions ; } else { return actions [ 0 ] ; } } param remoteId param name param arg function remoteWhisper ( remoteId , name , arg ? ) { return remote ( action ( name , null , null , arg ) , remoteId ) ; } param name param arg function remoteShout ( name , arg ? ) { return remote ( action ( name , null , null , arg ) , { broadcast : true , } ) ; } param url param options function webGet ( url , options = { } ) { return webhook ( { ... options , method : 'GET' , url , } ) ; } param url param data param options function webPost ( url , data ? , options ? ) { return webhook ( { ... options , method : 'POST' , url , data , } ) ; } param func param functionName returns function makeMockableFunction < T > ( func , functionName ) { if ( context . mockAsyncActions ) { let mock = ( ... args ) => { return context . getNextMockReturn ( func , functionName , args ) ; } ; mock . mask = ( ... args ) => ( { returns ( value ) { context . setMockReturn ( func , args , value ) ; } , } ) ; mock [ ORIGINAL_OBJECT ] = func ; return mock ; } else { return func as any ; } } param options function webhook ( options ) { if ( options . retryCount > 0 ) { return _retryWebhook ( options ) ; } else { return _webhook ( options ) ; } } async function _retryWebhook ( options ) { const retryCount = Math . min ( options . retryCount , MAX_RETRY_COUNT ) ; const timeToWait = Math . max ( 0 , Math . min ( options . retryAfterMs ?? DEFAULT_RETRY_AFTER_MS , MAX_RETRY_AFTER_MS ) ) ; const statusCodes = options . retryStatusCodes ?? DEFUALT_RETRY_STATUS_CODES ; let retries = 0 ; while ( true ) { try { return await _webhook ( options ) ; } catch ( err ) { if ( retries >= retryCount ) { throw err ; } else if ( ! statusCodes . includes ( err . response ?. status ?? 0 ) ) { throw err ; } await sleep ( timeToWait ) ; retries += 1 ; } } } function _webhook ( options ) { const task = context . createTask ( ) ; const event = calcWebhook ( { method : options . method , url : options . url , responseShout : options . responseShout , data : options . data , headers : options . headers , } , task . taskId ) ; return addAsyncAction ( task , event ) ; } /**
     * Creates a Universally Unique IDentifier (UUID).
     */ function uuid ( ) { return context . uuid ( ) ; } param time function sleep ( time ) { let sleepy = new Promise < void > ( ( resolve ) => setTimeout ( resolve , time ) ) ; return sleepy ; } param bot param tag param options function animateTag ( bot , tag , options ) ; param bot param options function animateTag ( bot , options ) ; param bot param tag param options function animateTag ( bot , tagOrOptions , options ? ) { if ( Array . isArray ( bot ) ) { const bots = bot . map ( ( b ) => ( typeof b === 'string' ? getBot ( 'id' , b ) : b ) ) . filter ( ( b ) => ! ! b ) ; const promises = bots . map ( ( b ) => animateBotTag ( b , tagOrOptions , options ) ) ; const allPromises = Promise . all ( promises ) ; return < Promise < void > > ( < any > allPromises ) ; } else if ( typeof bot === 'string' ) { const finalBot = getBot ( 'id' , bot ) ; if ( finalBot ) { return animateBotTag ( finalBot , tagOrOptions , options ) ; } else { return Promise . resolve ( ) ; } } else { return animateBotTag ( bot , tagOrOptions , options ) ; } } function animateBotTag ( bot , tagOrOptions , options ) { if ( typeof tagOrOptions === 'string' ) { return animateSingleTag ( bot , tagOrOptions , options ) ; } else { return animateMultipleTags ( bot , tagOrOptions ) ; } } async function animateMultipleTags ( bot , options ) { if ( typeof options . fromValue !== 'object' ) { throw new Error ( 'You must provide an object as fromValue when not specifying a tag.' ) ; } if ( typeof options . toValue !== 'object' ) { throw new Error ( 'You must provide an object as toValue when not specifying a tag.' ) ; } if ( typeof options . duration !== 'number' ) { throw new Error ( 'You must provide a duration.' ) ; } const keys = Object . keys ( options . fromValue ) ; const groupId = uuid ( ) ; await Promise . all ( keys . map ( ( k ) => animateSingleTag ( bot , k , { ... options , fromValue : options . fromValue [ k ] , toValue : options . toValue [ k ] , } , groupId ) ) ) ; } function animateSingleTag ( bot , tag , options , groupId ? ) { if ( ! options ) { clearAnimations ( bot , tag ) ; return Promise . resolve ( ) ; } if ( typeof options . duration !== 'number' ) { throw new Error ( 'You must provide a duration.' ) ; } return new Promise < void > ( ( resolve , reject ) => { let valueHolder = { [ tag ] : hasValue ( options . fromValue ) ? options . fromValue : bot . tags [ tag ] , } ; const easing = getEasing ( options . easing ) ; const tween = new TWEEN . Tween < any > ( valueHolder ) . to ( { [ tag ] : options . toValue , } ) . duration ( options . duration * 1000 ) . easing ( easing ) . onUpdate ( ( ) => { if ( options . tagMaskSpace === false || options . tagMaskSpace === getBotSpace ( bot ) ) { setTag ( bot , tag , valueHolder [ tag ] ) ; } else { setTagMask ( bot , tag , valueHolder [ tag ] , options . tagMaskSpace || 'tempLocal' ) ; } } ) . onComplete ( ( ) => { context . removeBotTimer ( bot . id , 'animation' , tween . getId ( ) ) ; resolve ( ) ; } ) . start ( context . localTime ) ; context . recordBotTimer ( bot . id , { type : 'animation' , timerId : tween . getId ( ) , tag : tag , groupId , cancel : ( ) => { tween . stop ( ) ; reject ( new Error ( 'The animation was canceled.' ) ) ; } , } ) ; } ) ; } param bot param tag function clearAnimations ( bot , tag ? ) { const bots = Array . isArray ( bot ) ? bot . map ( ( b ) => ( typeof b === 'string' ? getBot ( 'id' , b ) : b ) ) . filter ( ( b ) => ! ! b ) : typeof bot === 'string' ? getBots ( 'id' , bot ) : [ bot ] ; let tags = ( ! hasValue ( tag ) ? null : Array . isArray ( tag ) ? tag : [ tag ] ) as string [ ] ; let groups = [ ] as string [ ] ; for ( let bot of bots ) { if ( ! bot ) { continue ; } const timers = context . getBotTimers ( bot . id ) ; for ( let timer of timers ) { if ( timer . type === 'animation' && timer . cancel ) { if ( ! hasValue ( tag ) || tags . indexOf ( timer . tag ) >= 0 ) { if ( hasValue ( timer . groupId ) ) { groups . push ( timer . groupId ) ; } timer . cancel ( ) ; context . removeBotTimer ( bot . id , timer . type , timer . timerId ) ; } } } } clearGroupAnimations ( bots , groups ) ; } function clearGroupAnimations ( bots , groups ) { if ( groups . length <= 0 ) { return ; } for ( let bot of bots ) { const timers = context . getBotTimers ( bot . id ) ; for ( let timer of timers ) { if ( timer . type === 'animation' && timer . cancel ) { if ( hasValue ( timer . groupId ) && groups . indexOf ( timer . groupId ) >= 0 ) { timer . cancel ( ) ; context . removeBotTimer ( bot . id , timer . type , timer . timerId ) ; } } } } } param action function perform ( action ) { const event = action ; if ( event . type === 'update_bot' ) { if ( event . update . tags ) { for ( let tag in event . update . tags ) { const val = event . update . tags [ tag ] ; if ( isTagEdit ( val ) ) { val . isRemote = true ; } } } if ( event . update . masks ) { for ( let space in event . update . masks ) { const tags = event . update . masks [ space ] ; if ( tags ) { for ( let tag in tags ) { const val = tags [ tag ] ; if ( isTagEdit ( val ) ) { val . isRemote = true ; } } } } } } return addAction ( action ) ; } param action function reject ( action ) { const original = getOriginalObject ( action ) ; const event = Array . isArray ( original ) ? calcReject ( ... original ) : calcReject ( original ) ; return addAction ( event ) ; } param password function unlockAdminSpace ( password ) { const task = context . createTask ( ) ; const event = unlockSpace ( 'admin' , password , task . taskId ) ; return addAsyncAction ( task , event ) ; } param oldPassword param newPassword function setAdminSpacePassword ( oldPassword , newPassword ) { const task = context . createTask ( ) ; const event = setSpacePassword ( 'admin' , oldPassword , newPassword , task . taskId ) ; return addAsyncAction ( task , event ) ; } param bot param animation function localFormAnimation ( bot , animation ) { return addAction ( calcLocalFormAnimation ( getID ( bot ) , animation ) ) ; } param bot param dimension param position param options function localPositionTween ( bot , dimension , position , options ? ) { const task = context . createTask ( ) ; const action = calcLocalPositionTween ( getID ( bot ) , dimension , position , options ? options . easing : undefined , options ? options . duration : undefined , task . taskId ) ; return addAsyncAction ( task , action ) ; } param bot param dimension param rotation param options function localRotationTween ( bot , dimension , rotation , options ? ) { const task = context . createTask ( ) ; const action = calcLocalRotationTween ( getID ( bot ) , dimension , rotation , options ? options . easing : undefined , options ? options . duration : undefined , task . taskId ) ; return addAsyncAction ( task , action ) ; } param bot param dimension param anchorPoint function getAnchorPointPosition ( bot , dimension , anchorPoint ) { const offset = getAnchorPointOffset ( anchorPoint ) ; const scale = getBotScale ( null , bot , 1 ) ; const position = calcGetBotPosition ( null , bot , dimension ) ; return { x : position . x + offset . x * scale . x , y : position . y + offset . y * scale . y , z : position . z + offset . z * scale . z , } ; } /**
     * Starts a new audio recording.
     */ function beginAudioRecording ( options ? ) { const task = context . createTask ( ) ; const action = calcBeginAudioRecording ( options ?? { } , task . taskId ) ; return addAsyncAction ( task , action ) ; } /**
     * Finishes an audio recording.
     * Returns a promise that resolves with the recorded blob.
     */ function endAudioRecording ( ) { const task = context . createTask ( ) ; const action = calcEndAudioRecording ( task . taskId ) ; return addAsyncAction ( task , action ) ; } param options returns function beginRecording ( options = { audio : true , video : true , screen : false } ) { const task = context . createTask ( ) ; const action = calcBeginRecording ( options , task . taskId ) ; return addAsyncAction ( task , action ) ; } /**
     * Finishes a recording.
     * Returns a promise that resolves with the recorded data.
     */ function endRecording ( ) { const task = context . createTask ( ) ; const action = calcEndRecording ( task . taskId ) ; return addAsyncAction ( task , action ) ; } param text param options function speakText ( text , options = { } ) { const task = context . createTask ( ) ; const voice = typeof options . voice === 'object' ? options . voice ?. name : options . voice ; const action = calcSpeakText ( text , { ... options , voice , } , task . taskId ) ; return addAsyncAction ( task , action ) ; } /**
     * Gets the list of synthetic voices that are supported by the system.
     * Returns a promise that resolves with the voices.
     */ function getVoices ( ) { const task = context . createTask ( ) ; const action = calcGetVoices ( task . taskId ) ; return addAsyncAction ( task , action ) ; } param list function sum ( list ) { if ( ! Array . isArray ( list ) ) { return parseFloat ( list ) ; } let carry = 0 ; for ( let i = 0 ; i < list . length ; i ++ ) { const l = list [ i ] ; if ( ! Array . isArray ( l ) ) { carry += parseFloat ( l ) ; } else { carry += sum ( l ) ; } } return carry ; } param list function avg ( list ) { if ( ! Array . isArray ( list ) ) { return parseFloat ( list ) ; } let total = sum ( list ) ; let count = list . length ; return total / count ; } param value function sqrt ( value ) { return Math . sqrt ( parseFloat ( value ) ) ; } param number function abs ( number ) { return Math . abs ( parseFloat ( number ) ) ; } param list function stdDev ( list ) { if ( ! Array . isArray ( list ) ) { list = [ parseFloat ( list ) ] ; } let mean = avg ( list ) ; let numbersMinusMean = list . map ( ( l ) => ( l - mean ) * ( l - mean ) ) ; let standardMean = avg ( numbersMinusMean ) ; return sqrt ( standardMean ) ; } param min param max function randomInt ( min = 0 , max ? ) { min = Math . ceil ( min ) ; max = Math . floor ( max ) ; const rand = Math . random ( ) ; if ( max ) { return Math . floor ( rand * ( max - min ) ) + min ; } else { return Math . floor ( rand ) + min ; } } param min param max function random ( min = 0 , max ? ) { const rand = Math . random ( ) ; if ( max ) { return rand * ( max - min ) + min ; } else { return rand + min ; } } param pointerRotation function getForwardDirection ( pointerRotation ) { let euler = new Euler ( pointerRotation . x , pointerRotation . z , pointerRotation . y , 'XYZ' ) ; let direction = new Vector3 ( 0 , 0 , - 1 ) ; direction . applyEuler ( euler ) ; return { x : direction . x , y : - direction . z , z : direction . y , } ; } param origin param direction function intersectPlane ( origin , direction ) { let plane = new Plane ( new Vector3 ( 0 , 0 , 1 ) ) ; let final = new Vector3 ( ) ; let ray = new Ray ( new Vector3 ( origin . x , origin . y , origin . z ) , new Vector3 ( direction . x , direction . y , direction . z ) ) ; let result = ray . intersectPlane ( plane , final ) ; if ( result ) { return { x : result . x , y : result . y , z : result . z , } ; } else { return null ; } } param anchorPoint function getAnchorPointOffset ( anchorPoint ) { const value = calculateAnchorPoint ( anchorPoint ) ; const offset = calculateAnchorPointOffset ( value ) ; return { x : offset . x , y : - offset . y , z : offset . z , } ; } param vectors function addVectors < T > ( ... vectors ) { if ( vectors . length <= 0 ) { return { } as T ; } let result = { } as any ; for ( let i = 0 ; i < vectors . length ; i ++ ) { const v = vectors [ i ] as any ; if ( ! hasValue ( v ) ) { continue ; } const keys = Object . keys ( v ) ; for ( let key of keys ) { if ( key in result ) { result [ key ] += v [ key ] ; } else { result [ key ] = v [ key ] ; } } } return result ; } param vectors function subtractVectors < T > ( ... vectors ) { if ( vectors . length <= 0 ) { return { } as T ; } let result = { } as any ; for ( let i = 0 ; i < vectors . length ; i ++ ) { const v = vectors [ i ] as any ; if ( ! hasValue ( v ) ) { continue ; } const keys = Object . keys ( v ) ; for ( let key of keys ) { if ( key in result ) { result [ key ] -= v [ key ] ; } else { result [ key ] = v [ key ] ; } } } return result ; } param vector function negateVector < T > ( vector ) { if ( ! hasValue ( vector ) ) { return vector ; } let result = { } as any ; const keys = Object . keys ( vector ) ; for ( let key of keys ) { result [ key ] = - ( vector as any ) [ key ] ; } return result ; } param vector function normalizeVector < T > ( vector ) { if ( ! hasValue ( vector ) ) { return vector ; } let result = { } as any ; const length = vectorLength ( vector ) ; if ( length === 0 ) { return vector ; } const keys = Object . keys ( vector ) ; for ( let key of keys ) { const val = ( vector as any ) [ key ] ; result [ key ] = val / length ; } return result ; } param vector function vectorLength < T > ( vector ) { if ( ! hasValue ( vector ) ) { return null ; } let result = 0 ; const keys = Object . keys ( vector ) ; for ( let key of keys ) { const val = ( vector as any ) [ key ] ; result += val * val ; } return Math . sqrt ( result ) ; } param vector param scale function scaleVector < T > ( vector , scale ) { if ( ! hasValue ( vector ) ) { return vector ; } let result = { } as any ; const keys = Object . keys ( vector ) ; for ( let key of keys ) { result [ key ] = ( vector as any ) [ key ] * scale ; } return result ; } param first param second function areClose ( first , second ) { const maxDelta = 0.005 ; const delta = Math . abs ( first - second ) ; return delta < maxDelta ; } param point function cameraPositionOffset ( point ) { let result = { } as any ; if ( 'x' in point ) { result . cameraPositionOffsetX = point . x ; } if ( 'y' in point ) { result . cameraPositionOffsetY = point . y ; } if ( 'z' in point ) { result . cameraPositionOffsetZ = point . z ; } return result ; } param rotation function cameraRotationOffset ( rotation ) { let result = { } as any ; if ( 'x' in rotation ) { result . cameraRotationOffsetX = rotation . x ; } if ( 'y' in rotation ) { result . cameraRotationOffsetY = rotation . y ; } if ( 'z' in rotation ) { result . cameraRotationOffsetZ = rotation . z ; } return result ; } param data function sha256 ( ... data ) { let sha = hashSha256 ( ) ; return _hash ( sha , data ) ; } param data function sha512 ( ... data ) { let sha = hashSha512 ( ) ; return _hash ( sha , data ) ; } param key param data function hmacSha256 ( key , ... data ) { if ( ! hasValue ( key ) ) { throw new Error ( 'The key must not be empty, null, or undefined' ) ; } if ( typeof key !== 'string' ) { throw new Error ( 'The key must be a string' ) ; } let sha = hmac ( < any > hashSha256 , key ) ; return _hash ( sha , data ) ; } param key param data function hmacSha512 ( key , ... data ) { if ( ! hasValue ( key ) ) { throw new Error ( 'The key must not be empty, null, or undefined' ) ; } if ( typeof key !== 'string' ) { throw new Error ( 'The key must be a string' ) ; } let sha = hmac ( < any > hashSha512 , key ) ; return _hash ( sha , data ) ; } description param secret param data function encrypt ( secret , data ) { if ( typeof data === 'string' ) { const encoder = new TextEncoder ( ) ; const bytes = encoder . encode ( data ) ; return realEncrypt ( secret , bytes ) ; } else { throw new Error ( 'The data to encrypt must be a string.' ) ; } } param secret param data function decrypt ( secret , data ) { if ( typeof data === 'string' ) { const bytes = realDecrypt ( secret , data ) ; if ( ! bytes ) { return null ; } const decoder = new TextDecoder ( ) ; return decoder . decode ( bytes ) ; } else { throw new Error ( 'The data to encrypt must be a string.' ) ; } } description param secret function asymmetricKeypair ( secret ) { return realAsymmetricKeypair ( secret ) ; } description param keypair param data function asymmetricEncrypt ( keypair , data ) { if ( typeof data === 'string' ) { const encoder = new TextEncoder ( ) ; const bytes = encoder . encode ( data ) ; return realAsymmetricEncrypt ( keypair , bytes ) ; } else { throw new Error ( 'The data to encrypt must be a string.' ) ; } } param keypair param secret param data function asymmetricDecrypt ( keypair , secret , data ) { if ( typeof data === 'string' ) { const bytes = realAsymmetricDecrypt ( keypair , secret , data ) ; if ( ! bytes ) { return null ; } const decoder = new TextDecoder ( ) ; return decoder . decode ( bytes ) ; } else { throw new Error ( 'The data to encrypt must be a string.' ) ; } } description param secret function keypair ( secret ) { return realKeypair ( secret ) ; } description param keypair param secret param data function sign ( keypair , secret , data ) { if ( typeof data === 'string' ) { const encoder = new TextEncoder ( ) ; const bytes = encoder . encode ( data ) ; return realSign ( keypair , secret , bytes ) ; } else { throw new Error ( 'The data to encrypt must be a string.' ) ; } } param keypair param signature param data function verify ( keypair , signature , data ) { if ( typeof data === 'string' ) { const encoder = new TextEncoder ( ) ; const bytes = encoder . encode ( data ) ; return realVerify ( keypair , signature , bytes ) ; } else { throw new Error ( 'The data to encrypt must be a string.' ) ; } } param certificate param secret param keypair function createCertificate ( certificate , secret , keypair ) { const signingBotId = getID ( certificate ) ; const task = context . createTask ( ) ; const action = hasValue ( signingBotId ) ? calcCreateCertificate ( { keypair : keypair , signingBotId : signingBotId , signingPassword : secret , } , task . taskId ) : calcCreateCertificate ( { keypair : keypair , signingPassword : secret , } , task . taskId ) ; return addAsyncAction ( task , action ) ; } param certificate param secret param bot param tag function signTag ( certificate , secret , bot , tag ) { tag = trimTag ( tag ) ; const signingBotId = getID ( certificate ) ; const realBot = getBot ( 'id' , getID ( bot ) ) ; const value = realBot . raw [ tag ] ; const task = context . createTask ( ) ; const action = calcSignTag ( signingBotId , secret , realBot . id , tag , value , task . taskId ) ; return addAsyncAction ( task , action ) ; } param bot param tag function verifyTag ( bot , tag ) { tag = trimTag ( tag ) ; const id = getID ( bot ) ; const realBot = isRuntimeBot ( bot ) ? bot : getBot ( 'id' , id ) ; if ( ! realBot . signatures ) { return false ; } const value = realBot . raw [ tag ] ; const sig = tagValueHash ( id , tag , value ) ; return realBot . signatures [ sig ] === tag ; } param certificate param secret param signer function revokeCertificate ( certificate , secret , signer ? ) { const certId = getID ( certificate ) ; const signerId = getID ( signer || certificate ) ; const task = context . createTask ( ) ; const action = calcRevokeCertificate ( signerId , secret , certId , task . taskId ) ; return addAsyncAction ( task , action ) ; } /**
     * Gets performance stats from the runtime.
     */ function getStats ( ) { return { numberOfBots : context . bots . length , shoutTimes : context . getShoutTimers ( ) , numberOfActiveTimers : context . getNumberOfActiveTimers ( ) , } ; } function _hash ( hash , data ) { for ( let d of data ) { if ( ! hasValue ( d ) ) { d = '' ; } else if ( typeof d === 'object' ) { d = stableStringify ( d ) ; } else if ( typeof d !== 'string' ) { d = d . toString ( ) ; } hash . update ( d ) ; } return hash . digest ( 'hex' ) ; } param bot param tag param value example function setTag ( bot , tag , value ) { tag = trimTag ( tag ) ; if ( tag === 'id' || tag === BOT_SPACE_TAG ) { return value ; } if ( isRuntimeBot ( value ) ) { throw new Error ( `It is not possible to save bots as tag values. (Setting '${ tag }')` ) ; } if ( Array . isArray ( bot ) && bot . length > 0 ) { for ( let b of bot ) { setTag ( b , tag , value ) ; } return value ; } else if ( bot && isBot ( bot ) ) { bot . tags [ tag ] = value ; return value ; } else if ( bot ) { ( < BotTags > bot ) [ tag ] = value ; return value ; } } param bot param tag param value param space example function setTagMask ( bot , tag , value , space ? ) { tag = trimTag ( tag ) ; if ( tag === 'id' || tag === BOT_SPACE_TAG ) { return value ; } if ( Array . isArray ( bot ) && bot . length > 0 ) { for ( let b of bot ) { setTagMask ( b , tag , value , space ) ; } return value ; } else if ( bot && isRuntimeBot ( bot ) ) { bot [ SET_TAG_MASK_SYMBOL ] ( tag , value , space ) ; return value ; } } param bot param space function clearTagMasks ( bot , space ? ) { if ( Array . isArray ( bot ) && bot . length > 0 ) { for ( let b of bot ) { clearTagMasks ( b , space ) ; } } else if ( bot && isRuntimeBot ( bot ) ) { bot [ CLEAR_TAG_MASKS_SYMBOL ] ( space ) ; } } param bot param tag param index param text function insertTagText ( bot , tag , index , text ) { const currentValue = convertToString ( bot . raw [ tag ] ) ; if ( index < 0 ) { index += currentValue . length ; } index = Math . max ( 0 , Math . min ( index , currentValue . length ) ) ; bot [ EDIT_TAG_SYMBOL ] ( tag , [ preserve ( index ) , insert ( text ) ] ) ; return bot . raw [ tag ] ; } param bot param tag param index param text param space function insertTagMaskText ( bot , tag , index , text , space ? ) { const currentValue = convertToString ( bot . masks [ tag ] ) ; if ( index < 0 ) { index += currentValue . length ; } index = Math . max ( 0 , Math . min ( index , currentValue . length ) ) ; bot [ EDIT_TAG_MASK_SYMBOL ] ( tag , [ preserve ( index ) , insert ( text ) ] , space ) ; return bot . masks [ tag ] ; } param bot param tag param index param count function deleteTagText ( bot , tag , index , count ) { const currentValue = convertToString ( bot . raw [ tag ] ) ; if ( index < 0 ) { index += currentValue . length ; } index = Math . max ( 0 , Math . min ( index , currentValue . length ) ) ; count = Math . min ( count , currentValue . length - index ) ; if ( count > 0 ) { bot [ EDIT_TAG_SYMBOL ] ( tag , [ preserve ( index ) , del ( count ) ] ) ; } return bot . raw [ tag ] || '' ; } param bot param tag param index param count param space function deleteTagMaskText ( bot , tag , index , count , space ? ) { const currentValue = convertToString ( bot . masks [ tag ] ) ; if ( index < 0 ) { index += currentValue . length ; } index = Math . max ( 0 , Math . min ( index , currentValue . length ) ) ; count = Math . min ( count , currentValue . length - index ) ; if ( count > 0 ) { bot [ EDIT_TAG_MASK_SYMBOL ] ( tag , [ preserve ( index ) , del ( count ) ] , space ) ; } return bot . masks [ tag ] || '' ; } param bot param tagSection example example function removeTags ( bot , tagSection ) { if ( typeof bot === 'object' && Array . isArray ( bot ) ) { let botList = bot ; for ( let h = 0 ; h < bot . length ; h ++ ) { let currentBot = botList [ h ] ; let tags = tagsOnBot ( currentBot ) ; for ( let i = tags . length - 1 ; i >= 0 ; i -- ) { if ( tagSection instanceof RegExp ) { if ( tagSection . test ( tags [ i ] ) ) { setTag ( currentBot , tags [ i ] , null ) ; } } else if ( tags [ i ] . indexOf ( tagSection ) === 0 ) { setTag ( currentBot , tags [ i ] , null ) ; } } } } else { let tags = tagsOnBot ( bot ) ; for ( let i = tags . length - 1 ; i >= 0 ; i -- ) { if ( tagSection instanceof RegExp ) { if ( tagSection . test ( tags [ i ] ) ) { setTag ( bot , tags [ i ] , null ) ; } } else if ( tags [ i ] . indexOf ( tagSection ) === 0 ) { setTag ( bot , tags [ i ] , null ) ; } } } } param bot param originalTag param newTag function renameTag ( bot , originalTag , newTag ) { if ( Array . isArray ( bot ) ) { for ( let b of bot ) { renameTag ( b , originalTag , newTag ) ; } } else { if ( originalTag in bot . tags ) { const original = bot . tags [ originalTag ] ; delete bot . tags [ originalTag ] ; bot . tags [ newTag ] = original ; } } } param bot param diffs function applyMod ( bot , ... diffs ) { let appliedDiffs = [ ] ; for ( let diff of diffs ) { if ( ! diff ) { continue ; } let tags ; if ( isRuntimeBot ( diff ) ) { tags = diff . raw ; } else if ( isBot ( diff ) ) { tags = diff . tags ; } else { tags = diff ; } appliedDiffs . push ( tags ) ; for ( let key in tags ) { setTag ( bot , key , tags [ key ] ) ; } } } param bot param diff function subtractMods ( bot , ... diffs ) { let subtractedDiffs = [ ] ; for ( let diff of diffs ) { if ( ! diff ) { continue ; } let tags ; if ( isBot ( diff ) ) { tags = diff . tags ; } else { tags = diff ; } subtractedDiffs . push ( tags ) ; for ( let key in tags ) { setTag ( bot , key , null ) ; } } } param parent param mods returns example example function create ( botId , ... mods ) { return createBase ( botId , ( ) => context . uuid ( ) , ... mods ) ; } function createBase ( botId , idFactory , ... datas ) { let parentDiff = botId ? { creator : botId } : { } ; return createFromMods ( idFactory , parentDiff , ... datas ) ; } param mods function createFromMods ( idFactory , ... mods ) { let variants = new Array < Mod [ ] > ( 1 ) ; variants [ 0 ] = [ ] ; for ( let i = 0 ; i < mods . length ; i ++ ) { let diff = mods [ i ] ; if ( Array . isArray ( diff ) ) { let newVariants = new Array < Mod [ ] > ( variants . length * diff . length ) ; for ( let b = 0 ; b < newVariants . length ; b ++ ) { let diffIdx = Math . floor ( b / variants . length ) ; let d = diff [ diffIdx ] ; let variantIdx = b % variants . length ; let newVariant = variants [ variantIdx ] . slice ( ) ; newVariant . push ( d ) ; newVariants [ b ] = newVariant ; } variants = newVariants ; } else if ( typeof diff === 'object' ) { for ( let b = 0 ; b < variants . length ; b ++ ) { variants [ b ] . push ( diff ) ; } } } let bots = variants . map ( ( v ) => { let bot = { id : idFactory ( ) , tags : { } , } ; for ( let i = v . length - 1 ; i >= 0 ; i -- ) { const mod = v [ i ] ; if ( mod && BOT_SPACE_TAG in mod ) { const space = mod [ BOT_SPACE_TAG ] ; if ( hasValue ( space ) ) { bot . space = space ; } break ; } } applyMod ( bot . tags , ... v ) ; if ( 'creator' in bot . tags ) { let clearCreator = false ; const creatorId = bot . tags [ 'creator' ] ; if ( ! creatorId ) { clearCreator = true ; } else { const creator = getBot ( 'id' , creatorId ) ; if ( ! creator ) { clearCreator = true ; } else { const creatorSpace = getBotSpace ( creator ) ; const currentSpace = getBotSpace ( bot ) ; if ( creatorSpace !== currentSpace ) { clearCreator = true ; } } } if ( clearCreator ) { delete bot . tags [ 'creator' ] ; } } return bot ; } ) ; if ( bots . some ( ( b ) => Object . keys ( b . tags ) . length <= 0 ) ) { throw new Error ( 'Cannot create a bot with zero tags.' ) ; } let ret = new Array < RuntimeBot > ( bots . length ) ; for ( let i = 0 ; i < bots . length ; i ++ ) { ret [ i ] = context . createBot ( bots [ i ] ) ; } event ( CREATE_ACTION_NAME , ret ) ; for ( let bot of ret ) { event ( CREATE_ANY_ACTION_NAME , null , { bot : bot , } ) ; } if ( ret . length === 1 ) { return ret [ 0 ] ; } else { return ret ; } } param bot function destroy ( bot ) { if ( typeof bot === 'object' && Array . isArray ( bot ) ) { bot . forEach ( ( f ) => destroyBot ( f ) ) ; } else { destroyBot ( bot ) ; } } param bot function destroyBot ( bot ) { let realBot ; let id ; if ( ! hasValue ( bot ) ) { return ; } if ( typeof bot === 'object' ) { if ( isRuntimeBot ( bot ) ) { id = bot . id ; realBot = bot ; } else if ( isBot ( bot ) ) { id = bot . id ; realBot = getBot ( 'id' , id ) ; } else { return ; } } else if ( typeof bot === 'string' ) { if ( ! hasValue ( bot ) ) { return ; } id = bot ; realBot = getBot ( 'id' , id ) ; } if ( ! realBot || ! isRuntimeBot ( realBot ) || ! hasValue ( id ) ) { return ; } let destroyable = realBot . tags . auxDestroyable ; if ( hasValue ( destroyable ) && destroyable !== true ) { return ; } destroyable = realBot . tags . destroyable ; if ( hasValue ( destroyable ) && destroyable !== true ) { return ; } if ( id ) { event ( DESTROY_ACTION_NAME , [ id ] ) ; context . destroyBot ( realBot ) ; } destroyChildren ( id ) ; } function destroyChildren ( id ) { const children = getBots ( byTag ( 'creator' , createBotLink ( [ id ] ) ) ) ; for ( let child of children ) { destroyBot ( child ) ; } } param bot param stateName param groupName function changeState ( bot , stateName , groupName = 'state' ) { const previousState = getTag ( bot , groupName ) ; if ( previousState === stateName ) { return ; } setTag ( bot , groupName , stateName ) ; const arg = { to : stateName , from : previousState , } ; if ( hasValue ( previousState ) ) { whisper ( bot , `${ groupName }${ previousState }OnExit` , arg ) ; } whisper ( bot , `${ groupName }${ stateName }OnEnter` , arg ) ; } param bots function createBotLinkApi ( ... bots ) { let targets = flatMap ( bots ) ; let result = [ ] as string [ ] ; for ( let t of targets ) { if ( isBot ( t ) ) { result . push ( t . id ) ; } else { let links = parseBotLink ( t ) ; if ( links ) { result . push ( ... links ) ; } else { result . push ( t ) ; } } } return createBotLink ( result ) ; } param bot function getBotLinks ( bot ) { let links = [ ] as ParsedBotLink [ ] ; for ( let tag of Object . keys ( bot . tags ) ) { const val = bot . tags [ tag ] ; const ids = parseBotLink ( val ) ; if ( ids ) { links . push ( { tag , botIDs : ids , } ) ; } } return links ; } param bot param idMap function updateBotLinks ( bot , idMap ) { let map ; if ( idMap instanceof Map ) { map = idMap ; } else if ( typeof idMap === 'object' ) { map = new Map ( ) ; for ( let key in idMap ) { const newId = idMap [ key ] ; if ( typeof newId === 'string' ) { map . set ( key , newId ) ; } else if ( isBot ( newId ) ) { map . set ( key , newId . id ) ; } } } else { return ; } for ( let tag of Object . keys ( bot . tags ) ) { const val = bot . tags [ tag ] ; const ids = parseBotLink ( val ) ; if ( ids ) { const mapped = ids . map ( ( id ) => { if ( map . has ( id ) ) { const newId = map . get ( id ) ; if ( typeof newId === 'string' ) { return newId ; } else if ( isBot ( newId ) ) { return newId . id ; } } return id ; } ) ; bot . tags [ tag ] = createBotLink ( mapped ) ; } } } param eventName param arg function superShout ( eventName , arg ? ) { const event = calcSuperShout ( trimEvent ( eventName ) , arg ) ; return addAction ( event ) ; } param eventNames param arg function priorityShout ( eventNames , arg ? ) { for ( let name of eventNames ) { let results = event ( name , null , arg , undefined , true ) ; if ( results . hasResult ) { return results [ results . length - 1 ] ; } } return undefined ; } param name param arg returns example example example function shout ( name , arg ? ) { return event ( name , null , arg ) ; } param bot param eventName param arg returns example example example function whisper ( bot , eventName , arg ? ) { let bots ; if ( Array . isArray ( bot ) ) { bots = bot ; } else if ( hasValue ( bot ) ) { bots = [ bot ] ; } else { return [ ] ; } return event ( eventName , bots , arg ) ; } /**
     * Gets whether the player is in the sheet dimension.
     */ function inSheet ( ) { return getPortalDimension ( 'sheet' ) !== null ; } param portal function getCameraPosition ( portal = 'grid' ) { const bot = context . global [ `${ portal }PortalBot` ] ; if ( ! bot ) { return { x : NaN , y : NaN , z : NaN , } ; } return { x : bot . tags [ `cameraPositionX` ] , y : bot . tags [ `cameraPositionY` ] , z : bot . tags [ `cameraPositionZ` ] , } ; } param portal function getCameraRotation ( portal = 'grid' ) { const bot = context . global [ `${ portal }PortalBot` ] ; if ( ! bot ) { return { x : NaN , y : NaN , z : NaN , } ; } return { x : bot . tags [ `cameraRotationX` ] , y : bot . tags [ `cameraRotationY` ] , z : bot . tags [ `cameraRotationZ` ] , } ; } param portal function getFocusPoint ( portal = 'grid' ) { const bot = context . global [ `${ portal }PortalBot` ] ; if ( ! bot ) { return { x : NaN , y : NaN , z : NaN , } ; } return { x : bot . tags [ `cameraFocusX` ] , y : bot . tags [ `cameraFocusY` ] , z : bot . tags [ `cameraFocusZ` ] , } ; } param pointer function getPointerPosition ( pointer = 'mouse' ) { const user = context . playerBot ; if ( ! user ) { return { x : NaN , y : NaN , z : NaN , } ; } return { x : user . tags [ `${ pointer }PointerPositionX` ] , y : user . tags [ `${ pointer }PointerPositionY` ] , z : user . tags [ `${ pointer }PointerPositionZ` ] , } ; } param pointer function getPointerRotation ( pointer = 'mouse' ) { const user = context . playerBot ; if ( ! user ) { return { x : NaN , y : NaN , z : NaN , } ; } return { x : user . tags [ `${ pointer }PointerRotationX` ] , y : user . tags [ `${ pointer }PointerRotationY` ] , z : user . tags [ `${ pointer }PointerRotationZ` ] , } ; } param pointer function getPointerDirection ( pointer = 'mouse' ) { const rotation = getPointerRotation ( pointer ) ; if ( isNaN ( rotation . x ) || isNaN ( rotation . y ) || isNaN ( rotation . z ) ) { return { x : NaN , y : NaN , z : NaN , } ; } return getForwardDirection ( rotation ) ; } param controller param button function getInputState ( controller , button ) { const user = context . playerBot ; if ( ! user ) { return null ; } return user . tags [ `${ controller }_${ button }` ] || null ; } /**
     * Gets the list of inputs that are currently available.
     */ function getInputList ( ) { const user = context . playerBot ; if ( ! user ) { return [ ] ; } return user . tags . inputList || [ ] ; } param name param bots param arg param sort param shortCircuit function event ( name , bots , arg ? , sendListenEvents = true , shortCircuit = false ) { const startTime = globalThis . performance . now ( ) ; let tag = trimEvent ( name ) ; let ids = ! ! bots ? bots . map ( ( bot ) => { return ! ! bot ? typeof bot === 'string' ? bot : bot . id : null ; } ) : context . getBotIdsWithListener ( tag ) ; let results = [ ] as any [ ] ; let targets = [ ] as RuntimeBot [ ] ; let listeners = [ ] as RuntimeBot [ ] ; let checkedEnergy = false ; let stop = false ; for ( let id of ids ) { if ( stop ) { break ; } if ( ! id ) { continue ; } const bot = context . state [ id ] ; if ( bot ) { targets . push ( bot ) ; } if ( ! bot || bot . tags . auxListening === false || bot . tags . listening === false ) { continue ; } let listener = bot . listeners [ tag ] ; if ( listener ) { if ( ! checkedEnergy ) { checkedEnergy = true ; __energyCheck ( ) ; } try { const result = listener ( arg ) ; if ( result instanceof Promise ) { result . catch ( ( ex ) => { context . enqueueError ( ex ) ; } ) ; } results . push ( result ) ; if ( shortCircuit && result !== undefined ) { stop = true ; } } catch ( ex ) { context . enqueueError ( ex ) ; results . push ( undefined ) ; } listeners . push ( bot ) ; } } const endTime = globalThis . performance . now ( ) ; const delta = endTime - startTime ; context . addShoutTime ( name , delta ) ; if ( sendListenEvents ) { const listenArg = { name : name , that : arg , responses : results , targets , listeners , } ; event ( 'onListen' , listeners , listenArg , false ) ; event ( 'onAnyListen' , null , listenArg , false ) ; } if ( shortCircuit && stop ) { ( < any > results ) . hasResult = true ; } return results ; } function __energyCheck ( ) { let current = context . energy ; current -= 1 ; context . energy = current ; if ( current <= 0 ) { throw new RanOutOfEnergyError ( ) ; } } function addAction < T extends BotAction > ( action ) { context . enqueueAction ( action ) ; return action ; } function addAsyncAction < T extends AsyncActions > ( task , action ) { addAction ( action ) ; let promise = task . promise ; ( < any > promise ) [ ORIGINAL_OBJECT ] = action ; return promise ; } function getDownloadState ( state ) { return { version : 1 , state , } ; } function isPdf ( filename ) { return filename . endsWith ( '.pdf' ) ; } function formatAuxFilename ( filename ) { if ( filename . endsWith ( '.aux' ) ) { return filename ; } else if ( isPdf ( filename ) ) { return filename ; } return filename + '.aux' ; } function convertSessionSelector ( selector ) { if ( typeof selector === 'string' ) { return { sessionId : selector , } ; } return selector ? { sessionId : selector . session , username : selector . username , deviceId : selector . device , broadcast : selector . broadcast , } : undefined ; } }