-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathstreamerbot-client.js
More file actions
1 lines (1 loc) · 29.1 KB
/
Copy pathstreamerbot-client.js
File metadata and controls
1 lines (1 loc) · 29.1 KB
1
"use strict";var Streamerbot=(()=>{var W=Object.create;var p=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var D=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty;var O=(n,e)=>()=>(e||n((e={exports:{}}).exports,e),e.exports),q=(n,e)=>{for(var t in e)p(n,t,{get:e[t],enumerable:!0})},E=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of U(e))!H.call(n,o)&&o!==t&&p(n,o,{get:()=>e[o],enumerable:!(r=L(e,o))||r.enumerable});return n};var M=(n,e,t)=>(t=n!=null?W(D(n)):{},E(e||!n||!n.__esModule?p(t,"default",{value:n,enumerable:!0}):t,n)),B=n=>E(p({},"__esModule",{value:!0}),n);var G=O((Q,P)=>{"use strict";P.exports=function(){throw new Error("ws does not work in the browser. Browser clients must use the native WebSocket object")}});var _={};q(_,{Client:()=>l});var m=class{constructor(e={}){this.logLevels={verbose:0,debug:1,info:2,warn:3,error:4,none:5};this.level=e.level||"info",this.customLogger=e.customLogger}setLevel(e){this.level=e}setCustomLogger(e){this.customLogger=e}verbose(...e){this.log("verbose",...e)}debug(...e){this.log("debug",...e)}info(...e){this.log("info",...e)}warn(...e){this.log("warn",...e)}error(...e){this.log("error",...e)}log(e,...t){if(!(this.logLevels[e]<this.logLevels[this.level]))if(this.customLogger)this.customLogger(e,...t);else switch(e){default:case"verbose":console.log(...t);break;case"debug":console.debug(...t);break;case"info":console.info(...t);break;case"warn":console.warn(...t);break;case"error":console.error(...t);break}}},w=new m;var g={General:["Custom"],Twitch:["Follow","Cheer","Sub","ReSub","GiftSub","GiftBomb","Raid","HypeTrainStart","HypeTrainUpdate","HypeTrainLevelUp","HypeTrainEnd","RewardRedemption","RewardCreated","RewardUpdated","RewardDeleted","CommunityGoalContribution","CommunityGoalEnded","StreamUpdate","Whisper","FirstWord","SubCounterRollover","BroadcastUpdate","StreamUpdateGameOnConnect","PresentViewers","PollCreated","PollUpdated","PollCompleted","PredictionCreated","PredictionUpdated","PredictionCompleted","PredictionCanceled","PredictionLocked","ChatMessage","ChatMessageDeleted","UserTimedOut","UserBanned","Announcement","AdRun","BotWhisper","CharityDonation","CharityCompleted","CoinCheer","ShoutoutCreated","UserUntimedOut","CharityStarted","CharityProgress","GoalBegin","GoalProgress","GoalEnd","ShieldModeBegin","ShieldModeEnd","StreamOnline","StreamOffline","ShoutoutReceived","ChatCleared","RaidStart","RaidSend","RaidCancelled","PollTerminated","PyramidSuccess","PyramidBroken","ViewerCountUpdate","GuestStarSessionBegin","GuestStarSessionEnd","GuestStarGuestUpdate","GuestStarSlotUpdate","GuestStarSettingsUpdate","HypeChat","RewardRedemptionUpdated","HypeChatLevel","BroadcasterAuthenticated","BroadcasterChatConnected","BroadcasterChatDisconnected","BroadcasterEventSubConnected","BroadcasterEventSubDisconnected","SevenTVEmoteAdded","SevenTVEmoteRemoved","BetterTTVEmoteAdded","BetterTTVEmoteRemoved","BotEventSubConnected","BotEventSubDisconnected","UpcomingAd","ModeratorAdded","ModeratorRemoved","VipAdded","VipRemoved","UserUnbanned","UnbanRequestApproved","UnbanRequestDenied","AutomaticRewardRedemption","UnbanRequestCreated","PowerUp","ChatEmoteModeOn","ChatEmoteModeOff","ChatFollowerModeOn","ChatFollowerModeOff","ChatFollowerModeChanged","ChatSlowModeOn","ChatSlowModeOff","ChatSlowModeChanged","ChatSubscriberModeOn","ChatSubscriberModeOff","ChatUniqueModeOn","ChatUniqueModeOff","AutoModMessageHeld","AutoModMessageUpdate","BlockedTermsAdded","BlockedTermsDeleted","WarnedUser","SuspiciousUserUpdate","PermittedTermsAdded","PermittedTermsDeleted","WarningAcknowledged","WatchStreak","PollArchived","SharedChatSessionBegin","SharedChatSessionUpdate","SharedChatSessionEnd","PrimePaidUpgrade","PayItForward","GiftPaidUpgrade","BitsBadgeTier","SharedChatAnnouncement","SharedChatRaid","SharedChatPrimePaidUpgrade","SharedChatGiftPaidUpgrade","SharedChatPayItForward","SharedChatSub","SharedChatResub","SharedChatSubGift","SharedChatCommunitySubGift","SharedChatUserBanned","SharedChatUserUnbanned","SharedChatUserTimedout","SharedChatUserUntimedout","SharedChatMessageDeleted"],Streamlabs:["Donation","Merchandise","Connected","Disconnected","CharityDonation","Authenticated"],SpeechToText:["Dictation","Command"],Command:["Triggered","Cooldown"],FileWatcher:["Changed","Created","Deleted","Renamed"],FileTail:["Changed"],Quote:["Added","Show"],Misc:["TimedAction","Test","ProcessStarted","ProcessStopped","ChatWindowAction","StreamerbotStarted","StreamerbotExiting","ToastActivation","GlobalVariableUpdated","UserGlobalVariableUpdated","GlobalVariableCreated","GlobalVariableDeleted","ApplicationImport"],Raw:["Action","SubAction","ActionCompleted"],WebsocketClient:["Open","Close","Message"],StreamElements:["Tip","Merch","Connected","Disconnected","Authenticated"],WebsocketCustomServer:["Open","Close","Message"],DonorDrive:["Donation","ProfileUpdated","Incentive"],YouTube:["BroadcastStarted","BroadcastEnded","Message","MessageDeleted","UserBanned","SuperChat","SuperSticker","NewSponsor","MemberMileStone","NewSponsorOnlyStarted","NewSponsorOnlyEnded","StatisticsUpdated","BroadcastUpdated","MembershipGift","GiftMembershipReceived","FirstWords","PresentViewers","NewSubscriber","BroadcastMonitoringStarted","BroadcastMonitoringEnded","BroadcastAdded","BroadcastRemoved","SevenTVEmoteAdded","SevenTVEmoteRemoved","BetterTTVEmoteAdded","BetterTTVEmoteRemoved","PollClosed","PollStarted","PollUpdated"],Pulsoid:["HeartRatePulse"],HypeRate:["HeartRatePulse","TwitchClipCreated","Connected","Disconnected"],Kofi:["Donation","Subscription","Resubscription","ShopOrder","Commission"],Patreon:["FollowCreated","FollowDeleted","PledgeCreated","PledgeUpdated","PledgeDeleted"],Application:["ActionAdded","ActionUpdated","ActionDeleted","CommandAdded","CommandUpdated","CommandDeleted"],TipeeeStream:["Donation"],TreatStream:["Treat","Authenticated","Connected","Disconnected"],Shopify:["OrderCreated","OrderPaid"],Obs:["Connected","Disconnected","Event","SceneChanged","StreamingStarted","StreamingStopped","RecordingStarted","RecordingStopped","VendorEvent"],Midi:["Message"],Inputs:["InputKeyPress","InputMouseClick"],StreamDeck:["Action","Connected","Disconnected","Info"],Custom:["Event","CodeEvent"],VTubeStudio:["ModelLoaded","ModelUnloaded","BackgroundChanged","ModelConfigChanged","HotkeyTriggered","ModelAnimation","Connected","Disconnected","TrackingStatusChanged","ItemEvent","ModelClicked"],CrowdControl:["GameSessionStart","GameSessionEnd","EffectRequest","EffectSuccess","EffectFailure","TimedEffectStarted","TimedEffectEnded","TimedEffectUpdated","CoinExchange"],Elgato:["WaveLinkOutputSwitched","WaveLinkOutputVolumeChanged","WaveLinkOutputMuteChanged","WaveLinkSelectedOutputChanged","WaveLinkInputVolumeChanged","WaveLinkInputMuteChanged","WaveLinkInputNameChanged","WaveLinkMicrophoneGainChanged","WaveLinkMicrophoneOutputVolumeChanged","WaveLinkMicrophoneBalanceChanged","WaveLinkMicrophoneMuteChanged","WaveLinkMicrophoneSettingChanged","WaveLinkFilterAdded","WaveLinkFilterChanged","WaveLinkFilterDeleted","WaveLinkFilterBypassStateChanged","WaveLinkConnected","WaveLinkDisconnected","WaveLinkInputLevelMeterChanged","WaveLinkOutputLevelMeterChanged","CameraHubConnected","CameraHubDisconnected","CameraHubWebcamConnected","CameraHubWebcamrDisconnected","CameraHubWebcamActivated","CameraHubWebcamDeactivated","CameraHubSelectedWebcamChanged","CameraHubWebcamMirrored","CameraHubWebcamFlipped","CameraHubWebcamDeviceOrientationChanged","CameraHubWebcamExposureAutoLockEnabled","CameraHubWebcamExposureAutoLockDisabled","CameraHubWebcamSnapshotTaken","CameraHubWebcamZoomChanged","CameraHubWebcamContrastChanged","CameraHubWebcamWhiteBalanceChanged","CameraHubWebcamAutoExposureEnabled","CameraHubWebcamAutoExposureDisabled","CameraHubWebcamAutoWhiteBalanceEnabled","CameraHubWebcamAutoWhiteBalanceDisabled","CameraHubWebcamNoiseReductionEnabled","CameraHubWebcamNoiseReductionDisabled","CameraHubWebcamISOChanged","CameraHubWebcamShutterSpeedChanged","CameraHubWebcamSharpnessChanged","CameraHubWebcamAntiFlickerChanged","CameraHubWebcamLensChanged","CameraHubWebcamARLensChanged","CameraHubWebcamBitrateChanged","CameraHubWebcamFlashEnabled","CameraHubWebcamFlashDisabled","CameraHubWebcamPanChanged","CameraHubTiltChanged","CameraHubWebcamOverscanChanged","CameraHubWebcamAutoFocusEnabled","CameraHubWebcamAutoFocusDisabled","CameraHubWebcamFocusChanged","CameraHubWebcamWhiteBalanceTintChanged","CameraHubWebcamBrightnessChanged","CameraHubWebcamSaturationChanged","CameraHubWebcamLiveISOChanged","CameraHubWebcamLiveShutterSpeedChanged","CameraHubWebcamLiveWhiteBalanceChanged","CameraHubWebcamLiveWhiteBalanceTintChanged","CameraHubPrompterConnected","CameraHubPrompterDisconnected","CameraHubPrompterModeChanged","CameraHubPrompterBrightnessChanged","CameraHubPrompterFontChanged","CameraHubPrompterFontSizeChanged","CameraHubPrompterAutoScrollEnabled","CameraHubPrompterAutoScrollDisabled","CameraHubPrompterAutoScrollChapterEnabled","CameraHubPrompterAutoScrollChapterDisabled","CameraHubPrompterScrollSpeedChanged","CameraHubPrompterOpacityChanged","CameraHubPrompterHorizontalMarginChanged","CameraHubPrompterVerticalMarginChanged","CameraHubPrompterLineSpacingChanged","CameraHubPrompterFontColorChanged","CameraHubPrompterBackgroundColorChanged","CameraHubPrompterSelectedChapterChanged","CameraHubPrompterChannelsChanged","CameraHubPrompterSelectedChannelChanged","CameraHubPrompterContrastChanged","CameraHubPrompterCrosshairEnabled","CameraHubPrompterCrosshairDisabled","CameraHubPrompterCrosshairImageChanged","CameraHubPrompterCrosshairColorChanged","CameraHubPrompterSelectedScriptChanged"],StreamlabsDesktop:["Connected","Disconnected","SceneChanged","StreamingStarted","StreamingStopped","RecordingStarted","RecordingStopped"],SpeakerBot:["Connected","Disconnected"],Fourthwall:["ProductCreated","ProductUpdated","GiftPurchase","OrderPlaced","OrderUpdated","Donation","SubscriptionPurchased","SubscriptionExpired","SubscriptionChanged","ThankYouSent","NewsletterSubscribed","GiftDrawStarted","GiftDrawEnded"],Trovo:["BroadcasterAuthenticated","BroadcasterChatConnected","BroadcasterChatDisconnected","FirstWords","PresentViewers","ChatMessage","Follow","SpellCast","CustomSpellCast","Raid","Subscription","Resubscription","GiftSubscription","MassGiftSubscription","StreamOnline","StreamOffline"],ThrowingSystem:["Connected","WebsocketConnected","WebsocketDisconnected","EventsConnected","EventsDisconnected","ItemHit","TriggerActivated","TriggerEnded"],Pallygg:["Connected","Disconnected","CampaignTip"],StreamerBotRemote:["InstanceConnected","InstanceDisconnected","InstanceTrigger","InstanceSignal"],VoiceMod:["Connected","Disconnected","VoiceLoaded","SoundboardChanged"],Group:["Added","Removed","Cleared","UsersAdded","UsersRemoved"],MeldStudio:["Connected","Disconnected","StreamingStarted","StreamingStopped","RecordingStarted","RecordingStopped","SceneChanged","LayerVisbilityChanged","LEffectEnabledStateChanged","TrackMonitoringStateChanged","TrackMustedStateChanged","Event"],StreamerBot:["CustomWebook"],Kick:["BroadcasterAuthenticated","BroadcasterChatConnected","BroadcasterChatDisconnected","FirstWords","PresentViewers","ChatMessage","Follow","StreamOnline","StreamOffline","Subscription","Resubscription","GiftSubscription","MassGiftSubscription","UserTimedOut","UserBanned","ChannelUpdate","SevenTVEmoteAdded","SevenTVEmoteRemoved","ViewerCountUpdate"],System:["ClipboardChanged"]};var y=globalThis.crypto,T=y.subtle;var R=n=>y.getRandomValues(n);function C(n="req"){return`sb:client:${n}:${Date.now()}-${R(new Uint32Array(12))[0]}`}function k(n){let e;return n.code==1e3?e="Connection closed.":n.code==1001?e='Endpoint is "going away".':n.code==1002?e="Connection closed due to a protocol error.":n.code==1003||n.code==1007||n.code==1008||n.code==1010?e="Bad request.":n.code==1004?e="Reserved":n.code==1005?e="Missing status code.":n.code==1006?e="The connection was closed abnormally.":n.code==1009?e="Message size limit exceeded.":n.code==1011?e="Server terminated connection because due to unexpected condition.":n.code==1015?e="TLS handshake failure":e="Unknown error",e}async function h(n,e){let{timeout:t,message:r="Operation timed out.",controller:o}=e,s;return await Promise.race([new Promise((i,a)=>{s=setTimeout(()=>(o.abort(),console.debug("[withTimeout] timeout reached",e),a(new Error(r))),t),e.signal?.addEventListener("abort",()=>{clearTimeout(s),o?.abort(),a(new Error("Operation aborted."))},{once:!0})}),n]).finally(()=>{clearTimeout(s),o.abort()})}async function S(n){let e=new TextEncoder().encode(n),t=await T.digest("SHA-256",e),o=Array.from(new Uint8Array(t)).map(s=>s.toString(16).padStart(2,"0")).join("");return I(o)}function I(n){let e=new Uint8Array(n.match(/.{1,2}/g).map(r=>parseInt(r,16)));return btoa(String.fromCharCode.apply(null,Array.from(e)))}function v({timeout:n=1e4,addEventListener:e,removeEventListener:t}){let r=C("res"),o=new AbortController,s=o.signal,i=new Promise((a,c)=>{let d=b=>{let f=b?.data;f?.eventName===r&&(o.abort(),a(f?.args))};e("Custom.Event",d);let u=setTimeout(()=>{o.abort(),c(new Error("Timed out waiting for Custom Event"))},n);s.addEventListener("abort",()=>{clearTimeout(u),t(b=>b.events?.includes("Custom.Event")&&b.callback===d)},{once:!0})});return{responseId:r,promise:i,controller:o}}var A={scheme:"ws",host:"127.0.0.1",port:8080,endpoint:"/",immediate:!0,autoReconnect:!0,retries:-1,subscribe:{},logger:w,logLevel:"info"},l=class{constructor(e=A){this._authEnabled=!1;this._authenticated=!1;this.listeners=[];this.subscriptions={};this._explicitlyClosed=!1;this._retried=0;this._connectController=new AbortController;this._reconnectTimeout=void 0;this.options={...A,...e},this.logger=this.options.logger||null,this.logger&&this.options.logLevel&&this.logger.setLevel(this.options.logLevel),this.options.immediate===!0&&this.connect().catch(t=>this.logger?.warn("Failed to connect:",t))}get authenticated(){return!!this.socket&&this.socket.readyState===this.socket.OPEN&&this._authenticated}get ready(){return!this.socket||this.socket.readyState!==this.socket.OPEN||this._authEnabled&&!this._authenticated?!1:!!this.info&&!!this.version}async connect(e=1e4){if(this.socket?.readyState!==this.socket?.CLOSED)try{await this.disconnect()}catch{}this._explicitlyClosed=!1,this._connectController.abort(),this._connectController=new AbortController;let t=new AbortController;return this._connectController.signal.addEventListener("abort",()=>{t.abort()},{once:!0}),await h(new Promise(async(r,o)=>{try{this.options.password&&(this._authEnabled=!0);let s=`${this.options.scheme}://${this.options.host}:${this.options.port}${this.options.endpoint}`;this.logger?.debug("Connecting to Streamer.bot WebSocket server at",s,this._authEnabled?"with authentication":""),this.socket=globalThis?.process?.versions?.node?new(await Promise.resolve().then(()=>M(G(),1))).WebSocket(s):new WebSocket(s),this.socket.onmessage=this.onMessage.bind(this),this.socket.onopen=this.onOpen.bind(this),this.socket.onclose=this.onClose.bind(this),this.socket.onerror=this.onError.bind(this),this.socket.addEventListener("open",()=>{if(!this.socket)return o(new Error("WebSocket not initialized"));r()},{signal:t.signal}),this.socket.addEventListener("close",()=>o(new Error("WebSocket closed")),{once:!0})}catch(s){try{await this.disconnect(),this?.options?.onError?.(s)}catch(i){this.logger?.warn("Error invoking onError handler",i)}o(s)}}),{timeout:e,message:"WebSocket connection timeout exceeded",controller:t})}async disconnect(e=1e3,t=1e3){if(this._explicitlyClosed=!0,this._connectController.abort(),this._reconnectTimeout&&clearTimeout(this._reconnectTimeout),!this.socket||this.socket.readyState===this.socket.CLOSED)return;let r=new AbortController,o=r.signal;return await h(new Promise((s,i)=>{if(this.socket?.addEventListener("close",()=>{this.logger?.debug("Disconnected from Streamer.bot WebSocket server"),s()},{signal:o}),this.socket?.readyState!==this.socket?.CLOSING)try{this.socket?.close(e)}catch(a){i(a)}}),{timeout:t,message:"Timeout exceeded while closing connection",controller:r})}async handshake(){if(!this.socket)throw new Error("WebSocket not initialized");let e=new AbortController,{signal:t}=e;this._connectController.signal.addEventListener("abort",()=>{e.abort()},{once:!0,signal:t});let r=await h(new Promise((o,s)=>{this.socket?.addEventListener("message",async i=>{if(!("data"in i)||!i.data||typeof i.data!="string"){this.logger?.debug("Unknown message received",i);return}try{let a=JSON.parse(i.data);a&&"info"in a&&o(a)}catch(a){this.logger?.warn("Invalid JSON payload received",i.data),s(a)}},{signal:t})}),{timeout:5e3,message:"Handshake timeout exceeded",controller:e});if(!r||!("info"in r))throw new Error("Handshake failed (invalid payload)");if("request"in r&&r?.request==="Hello"&&r.authentication)return await this.authenticate(r);if(r.info&&!r.authentication){this.logger?.debug("Connected to Streamer.bot WebSocket server",r.info),this.info=r.info,this.version=r.info.version;return}throw new Error("Handshake failed (unknown)")}async authenticate(e){if(!this._authEnabled||!this.options.password){if(this.logger?.debug("No password provided for authentication. Checking if auth is enforced for all requests..."),(await this.getInfo()).status==="ok"){this._authenticated=!1,this.version=e.info.version,this.info=e.info;return}throw await this.disconnect(),new Error("Authentication required")}if(!e.authentication)throw this.logger?.debug("Missing authentication payload"),await this.disconnect(),new Error("Invalid authentication payload");this.logger?.debug("Authenticating with Streamer.bot WebSocket server...");let{salt:t,challenge:r}=e?.authentication,o=await S(`${this.options.password}${t}`),s=await S(`${o}${r}`);if((await this.request({request:"Authenticate",authentication:s})).status==="ok")this._authenticated=!0,this.version=e.info.version,this.info=e.info;else throw await this.disconnect(),new Error("Authentication failed")}async onOpen(){this._retried=0,this._reconnectTimeout&&clearTimeout(this._reconnectTimeout);try{this._authEnabled||this.getInfo().catch(()=>this.logger?.debug("Failed to fetch Streamer.bot instance info")),await this.handshake(),this.version&&this.info&&(this.logger?.debug(`Connected to Streamer.bot: v${this.version} (${this.info.name})`),await this.updateSupportedEvents(),this?.options?.onConnect?.(this.info))}catch(e){return this.logger?.warn("Failed handshake with Streamer.bot",e),this.options?.onError?.(e instanceof Error?e:new Error("Failed handshake with Streamer.bot")),await this.disconnect()}try{if(this.options.subscribe==="*"||typeof this.options.subscribe=="object"&&!Array.isArray(this.options.subscribe)&&Object.keys(this.options.subscribe??{}).length)this.logger?.debug("Subscribing to initial events from options:",this.options.subscribe),await this.subscribe(this.options.subscribe);else if(typeof this.options.subscribe=="string"||Array.isArray(this.options.subscribe)){this.logger?.debug("Subscribing to initial events from options:",this.options.subscribe);let e=await this.getSubscriptionsFromEventStrings(this.options.subscribe);this.logger?.debug("Parsed subscriptions from options:",e),e&&await this.subscribe(e)}if(this.listeners.length){let e=await this.getSubscriptionsFromListeners();await this.subscribe(e)}this.logger?.verbose("Subscribed to requested events",this.subscriptions,this.listeners)}catch(e){this.logger?.warn("Error subscribing to requested events",e)}}onClose(e){this._connectController.abort();try{(e.type==="error"||!e.wasClean)&&this.options.onError&&this?.options?.onError(new Error(k(e))),this?.options?.onDisconnect?.()}catch(t){this.logger?.warn("Error invoking user-provided onDisconnect handler",t)}if(this._explicitlyClosed||!this.options.autoReconnect)return this.logger?.debug("Cleaning up..."),this.cleanup();this._retried+=1,typeof this.options.retries=="number"&&(this.options.retries<0||this._retried<this.options.retries)?(this._reconnectTimeout&&clearTimeout(this._reconnectTimeout),this._reconnectTimeout=setTimeout(async()=>{if(!(this.socket&&this.socket.readyState!==this.socket.CLOSED)){this.logger?.debug(`Reconnecting... (attempt ${this._retried})`);try{await this.connect(1e4)}catch(t){this._retried&&this.logger?.warn(`Failed to reconnect (attempt ${this._retried-1})`,t)}}},Math.min(3e4,this._retried*1e3))):(this.logger?.debug("Auto-reconnect limit reached. Cleaning up..."),this.cleanup())}async onMessage(e){if(!e.data||typeof e.data!="string"){this.logger?.debug("Unknown message received",e);return}let t;try{t=JSON.parse(e.data)}catch(r){this.logger?.warn("Invalid JSON payload received",e.data,r);return}this.logger?.verbose("RECV",t);try{this.options.onData&&this?.options?.onData(t)}catch(r){this.logger?.warn("Error occurred within user-provided onData callback",r)}if(t?.event?.source&&t?.event?.type){for(let r of this.listeners)if(r.events?.length&&r.events.find(o=>o==="*"||o===`${t?.event?.source}.${t?.event?.type}`||o.split(".",2)?.[1]==="*"&&o.split(".",2)?.[0]===t?.event?.source))try{r.callback(t)}catch(o){this.logger?.warn(`Error occurred within user-provided event callback (${r.events})`,o)}}}onError(e){this.logger?.debug("WebSocket onError",e),this.socket&&this.socket.readyState!==this.socket.OPEN&&this._connectController.abort();try{this?.options?.onError?.(new Error("WebSocket Error"))}catch(t){this.logger?.warn("Error occurred within user-provided onError callback",t)}}cleanup(){this.socket&&(this.socket.onopen=null,this.socket.onclose=null,this.socket.onerror=null,this.socket.onmessage=null,this.socket=void 0),this.listeners=[],this._retried=0,this._connectController.abort(),this._reconnectTimeout&&clearTimeout(this._reconnectTimeout)}send(e){this.socket?.send(JSON.stringify(e))}async request(e,t="",r=1e4){if(!this.socket||this.socket.readyState!==this.socket.OPEN)throw new Error("WebSocket is not connected");t||(t=C());let o=new AbortController,s=o.signal;this._connectController.signal.addEventListener("abort",()=>{o.abort()},{once:!0,signal:s});let i=await h(new Promise((a,c)=>{this.socket?.addEventListener("message",d=>{if(!("data"in d)||!d.data||typeof d.data!="string"){this.logger?.debug("Unknown message received",d.data);return}try{let u=JSON.parse(d?.data);if(u?.id===t)return this.logger?.verbose(`RECV :: ${e.request}`,u),a(u)}catch(u){this.logger?.warn("Invalid JSON payload received",d.data),c(u)}},{signal:s}),this.logger?.verbose(`SEND :: ${e.request}`,{...e,id:t}),this.send({...e,id:t})}),{timeout:r,message:"Request timed out",controller:o,signal:s});if(i?.status==="ok"){try{this.options.onData&&this?.options?.onData(i)}catch(a){this.logger?.warn("Error invoking onData handler",a)}return{event:{source:"Request",type:e.request??"Unknown"},...i}}throw new Error("Request failed")}async on(e,t){try{if(!e)return;let r={events:[e],callback:t};if(this.listeners.push(r),this.ready){let o=await this.getSubscriptionsFromListeners([r]);await this.subscribe(o)}this.logger?.debug(`Added event listener for "${e}"`)}catch(r){this.logger?.warn(`Failed adding event listener for "${e}"`,r)}}async updateSupportedEvents(){if(this.ready)try{let e=await this.getEvents();if(e.status!=="ok"||!e.events)throw new Error(e.status);this.supportedEvents=e.events,this.logger?.debug(`Successfully fetched supported event types for Streamer.bot v${this.version}`)}catch(e){this.logger?.warn("Failed to fetch supported events from Streamer.bot, falling back to stored events type.",e),this.supportedEvents=g}}async getSupportedEvents(){return this.supportedEvents||(this.logger?.warn("Supported event types not yet initialized, fetching from Streamer.bot instance..."),await this.updateSupportedEvents()),this.supportedEvents??g}getEventsFromListeners(e){return(e??this.listeners).reduce((t,r)=>(r.events.forEach(o=>{t[o]||(t[o]=[]),t[o].push(r.callback)}),t),{})}async getSubscriptionsFromListeners(e){let t=this.getEventsFromListeners(e);return this.getSubscriptionsFromEventStrings(Object.keys(t))}async getSubscriptionsFromEventStrings(e){let t={};typeof e=="string"&&(e=[e]);for(let r of e){let o=await this.parseEventString(r);if(o)for(let s of o){let{source:i,eventTypes:a}=s,c=new Set([...t[i]??[],...a]);t[i]=[...c]}}return t}async parseEventString(e){let t=await this.getSupportedEvents();if(!e||typeof e!="string"){this.logger?.warn(`Invalid event subscription requested "${e}"`);return}if(e==="*")return Object.keys(t).map(r=>{let o=r,s=t[o]??[];return{source:o,eventTypes:s}});{let[r,o]=e.split(".",2);if(!r||!o||!(r in t)){this.logger?.warn(`Invalid event subscription requested "${e}"`);return}let s=r,i=o;if(i)return[{source:s,eventTypes:i==="*"?t[s]:[i]}];this.logger?.warn(`Invalid event type requested "${e}"`);return}}async subscribe(e){let t=await this.getSupportedEvents();e==="*"&&(e=t);for(let r in e){if(!r||r==="err")continue;if(!(r in t)){this.logger?.warn(`Attempted to subscribe to empty or unknown event source: "${r}"`,Object.keys(e));continue}let o=r,s=e[o]??[];if(s&&s.length){let i=new Set([...this.subscriptions[o]??[],...s]);this.subscriptions[o]=[...i]}}return Object.keys(this.subscriptions).length===0?(this.logger?.warn("No valid events to subscribe to. Please provide valid event sources and types."),{id:"invalid",status:"error",error:"No valid events to subscribe to"}):await this.request({request:"Subscribe",events:this.subscriptions})}async unsubscribe(e){let t=await this.getSupportedEvents();e==="*"&&(e=t);for(let r in e){if(r===void 0||!Object.keys(t).includes(r))continue;let o=r,s=e[o];if(s&&s.length)for(let i of s)i&&this.subscriptions[o]?.filter&&(this.subscriptions[o]=this.subscriptions[o])?.filter(a=>i!==a)}return await this.request({request:"UnSubscribe",events:e})}async getEvents(){return await this.request({request:"GetEvents"})}async getActions(){return await this.request({request:"GetActions"})}async doAction(e,t,r){if(r?.customEventResponse)return this.doActionWithCustomEventResponse(e,t);let o,s;return typeof e=="string"?o=e:(o=e.id,s=e.name),await this.request({request:"DoAction",action:{id:o,name:s},args:t})}async doActionWithCustomEventResponse(e,t={},r=1e4){if(!this.socket||this.socket.readyState!==this.socket.OPEN)throw new Error("WebSocket is not connected");let{responseId:o,promise:s,controller:i}=v({timeout:r,addEventListener:(c,d)=>this.on(c,d),removeEventListener:c=>{this.listeners=this.listeners.filter(d=>!c(d))}}),a={...t,sbClientResponse:o};try{let c=await this.doAction(e,a),d=await s;return{...c,customEventResponseArgs:d}}catch(c){throw i.signal.aborted||i.abort(),c}}async getBroadcaster(){return await this.request({request:"GetBroadcaster"})}async getMonitoredYouTubeBroadcasts(){return await this.request({request:"GetMonitoredYouTubeBroadcasts"})}async getCredits(){return await this.request({request:"GetCredits"})}async testCredits(){return await this.request({request:"TestCredits"})}async clearCredits(){return await this.request({request:"ClearCredits"})}async getInfo(){return await this.request({request:"GetInfo"})}async getActiveViewers(){return await this.request({request:"GetActiveViewers"})}async executeCodeTrigger(e,t,r){return r?.customEventResponse?this.executeCodeTriggerWithCustomEventResponse(e,t):await this.request({request:"ExecuteCodeTrigger",triggerName:e,args:t})}async executeCodeTriggerWithCustomEventResponse(e,t={},r=1e4){if(!this.socket||this.socket.readyState!==this.socket.OPEN)throw new Error("WebSocket is not connected");let{responseId:o,promise:s,controller:i}=v({timeout:r,addEventListener:(c,d)=>this.on(c,d),removeEventListener:c=>{this.listeners=this.listeners.filter(d=>!c(d))}}),a={...t,sbClientResponse:o};try{let c=await this.executeCodeTrigger(e,a),d=await s;return{...c,customEventResponseArgs:d}}catch(c){throw i.signal.aborted||i.abort(),c}}async getCodeTriggers(){return await this.request({request:"GetCodeTriggers"})}async getCommands(){return await this.request({request:"GetCommands"})}async getEmotes(e){switch(e){case"twitch":return await this.request({request:"TwitchGetEmotes"});case"youtube":return await this.request({request:"YouTubeGetEmotes"});default:throw new Error("Invalid platform")}}async getGlobals(e=!0){return await this.request({request:"GetGlobals",persisted:e})}async getGlobal(e,t=!0){let r=await this.request({request:"GetGlobal",variable:e,persisted:t});return r.status==="ok"?r.variables[e]?{id:r.id,status:r.status,variable:r.variables[e]}:{status:"error",error:"Variable not found"}:r}async getUserGlobals(e,t=null,r=!0){let s={twitch:"TwitchGetUserGlobals",youtube:"YouTubeGetUserGlobals",trovo:"TrovoGetUserGlobals",kick:"KickGetUserGlobals"}[e];if(!s)throw new Error("Invalid platform");return await this.request({request:s,variable:t,persisted:r})}async getUserGlobal(e,t,r=null,o=!0){let i={twitch:"TwitchGetUserGlobal",youtube:"YouTubeGetUserGlobal",trovo:"TrovoGetUserGlobal",kick:"KickGetUserGlobal"}[e];if(!i)throw new Error("Invalid platform");let a=await this.request({request:i,userId:t,variable:r||null,persisted:o});if(a.status==="ok"&&t&&r){let c=a.variables.find(d=>d.name===r);return c?{id:a.id,status:a.status,variable:c}:{status:"error",error:"Variable not found"}}return a}async sendMessage(e,t,{bot:r=!1,internal:o=!0,...s}={}){if(!this._authenticated)return{status:"error",error:"Authentication required"};let i={platform:e,message:t,bot:r,internal:o};return["twitch","kick"].includes(e)&&s.replyId&&Object.assign(i,{replyId:s.replyId}),["youtube"].includes(e)&&s.broadcastId&&Object.assign(i,{broadcastId:s.broadcastId}),await this.request({...i,request:"SendMessage"})}async getUserPronouns(e,t){return await this.request({request:"GetUserPronouns",platform:e,userLogin:t})}};Object.assign(globalThis,{StreamerbotClient:l});return B(_);})();