From ebe8889adff7018ad7e0969077cff71ce38107c1 Mon Sep 17 00:00:00 2001 From: Lemon <165233560+BludIsAnLemon@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:03:59 +0800 Subject: [PATCH 01/10] Create websockets-plus.js --- extensions/Cheddarphanie/websockets-plus.js | 587 ++++++++++++++++++++ 1 file changed, 587 insertions(+) create mode 100644 extensions/Cheddarphanie/websockets-plus.js diff --git a/extensions/Cheddarphanie/websockets-plus.js b/extensions/Cheddarphanie/websockets-plus.js new file mode 100644 index 0000000000..c63ef73e47 --- /dev/null +++ b/extensions/Cheddarphanie/websockets-plus.js @@ -0,0 +1,587 @@ +// Name: WebSockets+ +// ID: lemonWebSocketsPlus +// Description: Connect to more than one WebSockets. +// By: Cheddarphanie +// License: Apache-2.0 + +(function(Scratch) { + 'use strict'; + + if(!Scratch.extensions.unsandboxed) { + throw new Error('The WebSockets+ Extension must run unsandboxed.') + } + + const vm = Scratch.vm + const extManager = vm.extensionManager + const runtime = vm.runtime + const Cast = Scratch.Cast + + const regenReporters = ["lemonWebSocketsPlus_socketMessage"] + + if(Scratch.gui) Scratch.gui.getBlockly().then(SB => { + const ogCheck = SB.scratchBlocksUtils.isShadowArgumentReporter + SB.scratchBlocksUtils.isShadowArgumentReporter = function(block) { + const result = ogCheck(block) + if(result) return true + return block.isShadow() && regenReporters.includes(block.type) + } + }) + + const createLabel = txt => { + return { + blockType: Scratch.BlockType.LABEL, + text: txt + } + } + + class WebsocketsPlusExt { + constructor() { + this.debugging = false + + this.sockets = {} + + this.lastMessages = {} + + this.socketStatuses = {} + + this.socketCloseCodes = {} + + this.socketCloseReasons = {} + + this.fetchables = {} + + this.WebSocketStates = { + 0: 'CONNECTING', + 1: 'OPEN', + 2: 'CLOSING', + 3: 'CLOSED' + } + /** + * + * @param {string} socket + * @returns {Function} + */ + this.listener = function(socket='') { + return function({ data }) { + runtime.startHats('lemonWebSocketsPlus_socketMessageReceived', { + SOCKET: socket + }).forEach(thread => { + thread.socketMessage = data + }) + } + } + } + getInfo() { + return { + id: 'lemonWebSocketsPlus', + name: Scratch.translate('WebSockets+'), + color1: "#307eff", + color2: "#2c5eb0", + blocks: [ + { + func: 'toggleDebugging', + blockType: Scratch.BlockType.BUTTON, + text: Scratch.translate('Toggle Debugging') + }, + + createLabel('Variables'), + + { + opcode: 'websockets', + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate('websockets') + }, + { + opcode: 'socketState', + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate('state of socket [SOCKET]'), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'socket' + } + } + }, + { + opcode: 'socketLastMessage', + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate('last message received from socket [SOCKET]'), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'socket' + } + } + }, + { + opcode: 'socketCloseReason', + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate('reason of socket [SOCKET] closing'), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'socket' + } + } + }, + { + opcode: 'socketCloseCode', + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate('code of socket [SOCKET] closing'), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'socket' + } + } + }, + + '---', + + createLabel(Scratch.translate('Blocks')), + + { + opcode: 'connect', + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate('connect to [URL] with id [ID]'), + arguments: { + URL: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'wss://echo.websocket.org/' + }, + ID: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'socket' + } + } + }, + { + opcode: 'disconnect', + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate('close connection with socket [ID] with code [C] and reason [R]'), + arguments: { + ID: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'socket' + }, + C: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: 1000 + }, + R: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'fulfilled' + } + } + }, + { + opcode: 'sendMessage', + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate('send message [MESSAGE] to socket [SOCKET]'), + arguments: { + MESSAGE: { + type: Scratch.ArgumentType.STRING, + defaultValue: Scratch.translate('Hello :)') + }, + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'socket' + } + } + }, + + '---', + + createLabel(Scratch.translate('Booleans')), + + { + opcode: 'socketExists', + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate('socket [SOCKET] exists?'), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'socket' + } + } + }, + { + opcode: 'socketConnected', + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate('connected to socket [SOCKET]?'), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'socket' + } + } + }, + { + opcode: 'socketClosed', + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate('closed connection with [SOCKET]?'), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'socket' + } + } + }, + + '---', + + createLabel(Scratch.translate('Events')), + + { + opcode: 'socketMessageReceived', + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate('when i receive a message from [SOCKET] [MESSAGE]'), + isEdgeActivated: false, + hideFromPalette: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: 'socketMenu' + }, + MESSAGE: {} + } + }, + { + opcode: 'socketMessage', + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate('message'), + hideFromPalette: true, + disableMonitor: true + }, + { + opcode: 'socketOpensConnection', + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate('when connection with [SOCKET] opens'), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: 'socketMenu' + } + } + }, + { + blockType: Scratch.BlockType.XML, + xml: ` + + + + ` + }, + { + opcode: 'socketErrored', + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate('when socket [SOCKET] errors'), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: 'socketMenu' + }, + } + }, + { + opcode: 'socketClosedConnection', + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate('when connection with [SOCKET] closes'), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: 'socketMenu' + }, + } + }, + ], + menus: { + socketMenu: { + items: 'getSockets', + } + } + } + } + + toggleDebugging() { + this.debugging = !this.debugging + window.alert(Scratch.translate('Toggled Debugging! :)')) + } + + async connect({ ID, URL }) { + const id = Cast.toString(ID) + const url = Cast.toString(URL) + + if(this.sockets[id] instanceof WebSocket) { + try { + this.sockets[id].removeEventListener('message', this.listener(id)) + this.sockets[id].removeEventListener('error', () => { + runtime.startHats('lemonWebSocketsPlus_socketErrored', { + SOCKET: id + }) + }) + this.sockets[id].removeEventListener('message', ({data}) => { + this.lastMessages[id] = data + }) + this.sockets[id].removeEventListener('close', ({reason, code}) => { + runtime.startHats('lemonWebSocketsPlus_socketClosedConnection', { + SOCKET: id + }) + this.socketCloseReasons[id] = reason + this.socketCloseCodes[id] = code + }) + this.sockets[id].removeEventListener('open', () => { + runtime.startHats('lemonWebSocketsPlus_socketOpensConnection', { + SOCKET: id + }) + }) + this.sockets[id].close() + } catch(err) { + console.error(err) + } + } + + if(this.debugging) console.groupCollapsed('WebSockets+ Connecting') + + if(this.debugging) console.log(`[WebSockets+] Attempting to connect to '${url}'..`) + + if(!this.fetchables[url]) this.fetchables[url] = await Scratch.canFetch(url) + + if(!this.fetchables[url]) { + this.socketStatuses[id] = 'failed to connect' + console.log(`[Websockets+] Connection to '${url}' denied!`) + if(this.debugging) console.groupEnd() + return + } + + try { + + this.sockets[id] = new WebSocket(url) + this.socketStatuses[id] = 'connected' + this.socketCloseCodes[id] = 0 + this.socketCloseReasons[id] = '' + + /** + * @type {WebSocket} + */ + const socket = this.sockets[id] + + socket.addEventListener('message', this.listener(id)) + socket.addEventListener('error', () => { + runtime.startHats('lemonWebSocketsPlus_socketErrored', { + SOCKET: id + }) + }) + socket.addEventListener('message', ({data}) => { + this.lastMessages[id] = data + }) + socket.addEventListener('close', ({reason, code}) => { + runtime.startHats('lemonWebSocketsPlus_socketClosedConnection', { + SOCKET: id + }) + this.socketCloseReasons[id] = reason + this.socketCloseCodes[id] = code + }) + socket.addEventListener('open', () => { + runtime.startHats('lemonWebSocketsPlus_socketOpensConnection', { + SOCKET: id + }) + }) + + extManager.refreshBlocks('lemonWebSocketsPlus') + vm.refreshWorkspace() + + if(this.debugging) console.log(`[WebSockets+] Successfully connected to '${url}'.`) + } catch(err) { + console.error(err) + this.socketStatuses[id] = 'failed to connect' + } + + if(this.debugging) console.groupEnd() + } + + disconnect({ ID, C, R }) { + const id = Cast.toString(ID) + const Code = Cast.toNumber(C) + const Reason = Cast.toString(R) + + if(this.debugging) console.groupCollapsed('WebSockets+ Closing Connection') + + if(this.debugging) console.log(`[WebSockets+] Attemping to close connection with '${id}'..`) + + const socket = this.sockets[id] + + if(socket instanceof WebSocket) { + + socket.removeEventListener('message', this.listener(id)) + socket.removeEventListener('error', () => { + runtime.startHats('lemonWebSocketsPlus_socketErrored', { + SOCKET: id + }) + }) + socket.removeEventListener('message', ({data}) => { + this.lastMessages[id] = data + }) + socket.removeEventListener('close', ({reason, code}) => { + runtime.startHats('lemonWebSocketsPlus_socketClosedConnection', { + SOCKET: id + }) + this.socketCloseReasons[id] = reason + this.socketCloseCodes[id] = code + }) + socket.removeEventListener('open', () => { + runtime.startHats('lemonWebSocketsPlus_socketOpensConnection', { + SOCKET: id + }) + }) + + socket.close(Code, Reason) + + delete this.sockets[id] + this.socketCloseCodes[id] = Code + this.socketCloseReasons[id] = Reason + this.socketStatuses[id] = 'closed' + + extManager.refreshBlocks('lemonWebSocketsPlus') + vm.refreshWorkspace() + + if(this.debugging) console.log(`[WebSockets+] Successfully closed connection with '${id}'!`) + } else { + if(this.debugging) console.warn(`[WebSockets+] WebSocket '${id}' is not a WebSocket!`) + } + + if(this.debugging) console.groupEnd() + + return + } + + sendMessage({ MESSAGE, SOCKET }) { + if(this.debugging) { + console.groupCollapsed('WebSockets+ Message Sending') + + console.log(`[WebSockets+] Attempting to send a message to '${SOCKET}'..`) + } + SOCKET = Cast.toString(SOCKET) + const socket = this.sockets[SOCKET] + + if(!socket) { + if(this.debugging) { + console.warn(`[WebSockets+] '${SOCKET}' doesn't exist!`) + console.groupEnd() + } + return + } + + if(socket instanceof WebSocket) { + try { + socket.send(MESSAGE) + if(this.debugging) console.log(`[WebSockets+] Successfully sent a message to '${SOCKET}'!`) + } catch(err) { + console.error(err) + if(this.debugging) console.groupEnd() + } + } else { + if(this.debugging) console.warn(`[WebSockets+] '${SOCKET}' isn't a WebSocket!`) + } + if(this.debugging) console.groupEnd() + } + + socketExists({ SOCKET }) { + return Cast.toBoolean(this.sockets[Cast.toString(SOCKET)]) + } + + socketConnected({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)] + + if(socket instanceof WebSocket) { + return socket.readyState === WebSocket.OPEN + } + + return false + } + + socketClosed({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)] + + if(socket instanceof WebSocket) { + return socket.readyState === WebSocket.CLOSED + } + + return true + } + + websockets() { + return JSON.stringify(Object.keys(this.sockets)) + } + + socketState({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)] + if(socket instanceof WebSocket) { + return this.WebSocketStates[socket.readyState] ?? 'UNKNOWN' + } + return 'UNKNOWN' + } + + socketLastMessage({ SOCKET }) { + const socket = this.lastMessages[Cast.toString(SOCKET)] + return socket ?? '' + } + + socketCloseReason({ SOCKET }) { + return this.socketCloseReasons[Cast.toString(SOCKET)] ?? '' + } + + socketCloseCode({ SOCKET }) { + return this.socketCloseCodes[Cast.toString(SOCKET)] ?? 0 + } + + getSockets() { + const Sockets = Object.keys(this.sockets) + return Sockets.length > 0 ? Sockets.map((socket) => { + return { + value: socket, + text: socket + } + }) : [{ + value: Scratch.translate('None yet :('), + text: Scratch.translate('None yet :(') + }] + } + + /** + * @param {{}} args + * @param {VM.BlockUtility} util + */ + socketMessage(args, util) { + return util.thread.socketMessage ?? '' + } + + socketOpensConnection() { + return + } + + socketMessageReceived() { + return + } + + socketErrored() { + return + } + + socketClosedConnection() { + return + } + } + + Scratch.extensions.register(new WebsocketsPlusExt()) +})(Scratch) From 13192ce9ca13f1869a9ee2b2d4bdc0335fe81136 Mon Sep 17 00:00:00 2001 From: Lemon <165233560+BludIsAnLemon@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:05:49 +0800 Subject: [PATCH 02/10] Update extensions.json --- extensions/extensions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/extensions.json b/extensions/extensions.json index d8ed71e33d..2a79f01fc7 100644 --- a/extensions/extensions.json +++ b/extensions/extensions.json @@ -89,6 +89,7 @@ "vercte/dictionaries", "godslayerakp/http", "godslayerakp/ws", + "Cheddarphanie/websockets-plus", "Lily/CommentBlocks", "veggiecan/LongmanDictionary", "CubesterYT/TurboHook", From 3d1a23356a766a5e2430b116569d320d2636feec Mon Sep 17 00:00:00 2001 From: "DangoCat[bot]" Date: Wed, 12 Mar 2025 11:10:44 +0000 Subject: [PATCH 03/10] [Automated] Format code --- extensions/Cheddarphanie/websockets-plus.js | 1132 ++++++++++--------- 1 file changed, 581 insertions(+), 551 deletions(-) diff --git a/extensions/Cheddarphanie/websockets-plus.js b/extensions/Cheddarphanie/websockets-plus.js index c63ef73e47..a64817696d 100644 --- a/extensions/Cheddarphanie/websockets-plus.js +++ b/extensions/Cheddarphanie/websockets-plus.js @@ -4,584 +4,614 @@ // By: Cheddarphanie // License: Apache-2.0 -(function(Scratch) { - 'use strict'; +(function (Scratch) { + "use strict"; + + if (!Scratch.extensions.unsandboxed) { + throw new Error("The WebSockets+ Extension must run unsandboxed."); + } + + const vm = Scratch.vm; + const extManager = vm.extensionManager; + const runtime = vm.runtime; + const Cast = Scratch.Cast; + + const regenReporters = ["lemonWebSocketsPlus_socketMessage"]; + + if (Scratch.gui) + Scratch.gui.getBlockly().then((SB) => { + const ogCheck = SB.scratchBlocksUtils.isShadowArgumentReporter; + SB.scratchBlocksUtils.isShadowArgumentReporter = function (block) { + const result = ogCheck(block); + if (result) return true; + return block.isShadow() && regenReporters.includes(block.type); + }; + }); + + const createLabel = (txt) => { + return { + blockType: Scratch.BlockType.LABEL, + text: txt, + }; + }; + + class WebsocketsPlusExt { + constructor() { + this.debugging = false; + + this.sockets = {}; + + this.lastMessages = {}; + + this.socketStatuses = {}; + + this.socketCloseCodes = {}; + + this.socketCloseReasons = {}; + + this.fetchables = {}; + + this.WebSocketStates = { + 0: "CONNECTING", + 1: "OPEN", + 2: "CLOSING", + 3: "CLOSED", + }; + /** + * + * @param {string} socket + * @returns {Function} + */ + this.listener = function (socket = "") { + return function ({ data }) { + runtime + .startHats("lemonWebSocketsPlus_socketMessageReceived", { + SOCKET: socket, + }) + .forEach((thread) => { + thread.socketMessage = data; + }); + }; + }; + } + getInfo() { + return { + id: "lemonWebSocketsPlus", + name: Scratch.translate("WebSockets+"), + color1: "#307eff", + color2: "#2c5eb0", + blocks: [ + { + func: "toggleDebugging", + blockType: Scratch.BlockType.BUTTON, + text: Scratch.translate("Toggle Debugging"), + }, + + createLabel("Variables"), + + { + opcode: "websockets", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("websockets"), + }, + { + opcode: "socketState", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("state of socket [SOCKET]"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + }, + }, + { + opcode: "socketLastMessage", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate( + "last message received from socket [SOCKET]" + ), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + }, + }, + { + opcode: "socketCloseReason", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("reason of socket [SOCKET] closing"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + }, + }, + { + opcode: "socketCloseCode", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("code of socket [SOCKET] closing"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + }, + }, + + "---", + + createLabel(Scratch.translate("Blocks")), + + { + opcode: "connect", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("connect to [URL] with id [ID]"), + arguments: { + URL: { + type: Scratch.ArgumentType.STRING, + defaultValue: "wss://echo.websocket.org/", + }, + ID: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + }, + }, + { + opcode: "disconnect", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate( + "close connection with socket [ID] with code [C] and reason [R]" + ), + arguments: { + ID: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + C: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: 1000, + }, + R: { + type: Scratch.ArgumentType.STRING, + defaultValue: "fulfilled", + }, + }, + }, + { + opcode: "sendMessage", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate( + "send message [MESSAGE] to socket [SOCKET]" + ), + arguments: { + MESSAGE: { + type: Scratch.ArgumentType.STRING, + defaultValue: Scratch.translate("Hello :)"), + }, + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + }, + }, + + "---", + + createLabel(Scratch.translate("Booleans")), + + { + opcode: "socketExists", + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate("socket [SOCKET] exists?"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + }, + }, + { + opcode: "socketConnected", + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate("connected to socket [SOCKET]?"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + }, + }, + { + opcode: "socketClosed", + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate("closed connection with [SOCKET]?"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + }, + }, + + "---", + + createLabel(Scratch.translate("Events")), + + { + opcode: "socketMessageReceived", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate( + "when i receive a message from [SOCKET] [MESSAGE]" + ), + isEdgeActivated: false, + hideFromPalette: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", + }, + MESSAGE: {}, + }, + }, + { + opcode: "socketMessage", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("message"), + hideFromPalette: true, + disableMonitor: true, + }, + { + opcode: "socketOpensConnection", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when connection with [SOCKET] opens"), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", + }, + }, + }, + { + blockType: Scratch.BlockType.XML, + xml: ` + + + + `, + }, + { + opcode: "socketErrored", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when socket [SOCKET] errors"), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", + }, + }, + }, + { + opcode: "socketClosedConnection", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when connection with [SOCKET] closes"), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", + }, + }, + }, + ], + menus: { + socketMenu: { + items: "getSockets", + }, + }, + }; + } - if(!Scratch.extensions.unsandboxed) { - throw new Error('The WebSockets+ Extension must run unsandboxed.') + toggleDebugging() { + this.debugging = !this.debugging; + window.alert(Scratch.translate("Toggled Debugging! :)")); } - const vm = Scratch.vm - const extManager = vm.extensionManager - const runtime = vm.runtime - const Cast = Scratch.Cast + async connect({ ID, URL }) { + const id = Cast.toString(ID); + const url = Cast.toString(URL); + + if (this.sockets[id] instanceof WebSocket) { + try { + this.sockets[id].removeEventListener("message", this.listener(id)); + this.sockets[id].removeEventListener("error", () => { + runtime.startHats("lemonWebSocketsPlus_socketErrored", { + SOCKET: id, + }); + }); + this.sockets[id].removeEventListener("message", ({ data }) => { + this.lastMessages[id] = data; + }); + this.sockets[id].removeEventListener("close", ({ reason, code }) => { + runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { + SOCKET: id, + }); + this.socketCloseReasons[id] = reason; + this.socketCloseCodes[id] = code; + }); + this.sockets[id].removeEventListener("open", () => { + runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { + SOCKET: id, + }); + }); + this.sockets[id].close(); + } catch (err) { + console.error(err); + } + } - const regenReporters = ["lemonWebSocketsPlus_socketMessage"] + if (this.debugging) console.groupCollapsed("WebSockets+ Connecting"); - if(Scratch.gui) Scratch.gui.getBlockly().then(SB => { - const ogCheck = SB.scratchBlocksUtils.isShadowArgumentReporter - SB.scratchBlocksUtils.isShadowArgumentReporter = function(block) { - const result = ogCheck(block) - if(result) return true - return block.isShadow() && regenReporters.includes(block.type) - } - }) + if (this.debugging) + console.log(`[WebSockets+] Attempting to connect to '${url}'..`); - const createLabel = txt => { - return { - blockType: Scratch.BlockType.LABEL, - text: txt - } + if (!this.fetchables[url]) + this.fetchables[url] = await Scratch.canFetch(url); + + if (!this.fetchables[url]) { + this.socketStatuses[id] = "failed to connect"; + console.log(`[Websockets+] Connection to '${url}' denied!`); + if (this.debugging) console.groupEnd(); + return; + } + + try { + this.sockets[id] = new WebSocket(url); + this.socketStatuses[id] = "connected"; + this.socketCloseCodes[id] = 0; + this.socketCloseReasons[id] = ""; + + /** + * @type {WebSocket} + */ + const socket = this.sockets[id]; + + socket.addEventListener("message", this.listener(id)); + socket.addEventListener("error", () => { + runtime.startHats("lemonWebSocketsPlus_socketErrored", { + SOCKET: id, + }); + }); + socket.addEventListener("message", ({ data }) => { + this.lastMessages[id] = data; + }); + socket.addEventListener("close", ({ reason, code }) => { + runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { + SOCKET: id, + }); + this.socketCloseReasons[id] = reason; + this.socketCloseCodes[id] = code; + }); + socket.addEventListener("open", () => { + runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { + SOCKET: id, + }); + }); + + extManager.refreshBlocks("lemonWebSocketsPlus"); + vm.refreshWorkspace(); + + if (this.debugging) + console.log(`[WebSockets+] Successfully connected to '${url}'.`); + } catch (err) { + console.error(err); + this.socketStatuses[id] = "failed to connect"; + } + + if (this.debugging) console.groupEnd(); } - class WebsocketsPlusExt { - constructor() { - this.debugging = false - - this.sockets = {} - - this.lastMessages = {} - - this.socketStatuses = {} - - this.socketCloseCodes = {} - - this.socketCloseReasons = {} - - this.fetchables = {} - - this.WebSocketStates = { - 0: 'CONNECTING', - 1: 'OPEN', - 2: 'CLOSING', - 3: 'CLOSED' - } - /** - * - * @param {string} socket - * @returns {Function} - */ - this.listener = function(socket='') { - return function({ data }) { - runtime.startHats('lemonWebSocketsPlus_socketMessageReceived', { - SOCKET: socket - }).forEach(thread => { - thread.socketMessage = data - }) - } - } + disconnect({ ID, C, R }) { + const id = Cast.toString(ID); + const Code = Cast.toNumber(C); + const Reason = Cast.toString(R); + + if (this.debugging) + console.groupCollapsed("WebSockets+ Closing Connection"); + + if (this.debugging) + console.log( + `[WebSockets+] Attemping to close connection with '${id}'..` + ); + + const socket = this.sockets[id]; + + if (socket instanceof WebSocket) { + socket.removeEventListener("message", this.listener(id)); + socket.removeEventListener("error", () => { + runtime.startHats("lemonWebSocketsPlus_socketErrored", { + SOCKET: id, + }); + }); + socket.removeEventListener("message", ({ data }) => { + this.lastMessages[id] = data; + }); + socket.removeEventListener("close", ({ reason, code }) => { + runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { + SOCKET: id, + }); + this.socketCloseReasons[id] = reason; + this.socketCloseCodes[id] = code; + }); + socket.removeEventListener("open", () => { + runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { + SOCKET: id, + }); + }); + + socket.close(Code, Reason); + + delete this.sockets[id]; + this.socketCloseCodes[id] = Code; + this.socketCloseReasons[id] = Reason; + this.socketStatuses[id] = "closed"; + + extManager.refreshBlocks("lemonWebSocketsPlus"); + vm.refreshWorkspace(); + + if (this.debugging) + console.log( + `[WebSockets+] Successfully closed connection with '${id}'!` + ); + } else { + if (this.debugging) + console.warn(`[WebSockets+] WebSocket '${id}' is not a WebSocket!`); + } + + if (this.debugging) console.groupEnd(); + + return; + } + + sendMessage({ MESSAGE, SOCKET }) { + if (this.debugging) { + console.groupCollapsed("WebSockets+ Message Sending"); + + console.log( + `[WebSockets+] Attempting to send a message to '${SOCKET}'..` + ); + } + SOCKET = Cast.toString(SOCKET); + const socket = this.sockets[SOCKET]; + + if (!socket) { + if (this.debugging) { + console.warn(`[WebSockets+] '${SOCKET}' doesn't exist!`); + console.groupEnd(); } - getInfo() { - return { - id: 'lemonWebSocketsPlus', - name: Scratch.translate('WebSockets+'), - color1: "#307eff", - color2: "#2c5eb0", - blocks: [ - { - func: 'toggleDebugging', - blockType: Scratch.BlockType.BUTTON, - text: Scratch.translate('Toggle Debugging') - }, - - createLabel('Variables'), - - { - opcode: 'websockets', - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate('websockets') - }, - { - opcode: 'socketState', - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate('state of socket [SOCKET]'), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'socket' - } - } - }, - { - opcode: 'socketLastMessage', - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate('last message received from socket [SOCKET]'), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'socket' - } - } - }, - { - opcode: 'socketCloseReason', - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate('reason of socket [SOCKET] closing'), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'socket' - } - } - }, - { - opcode: 'socketCloseCode', - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate('code of socket [SOCKET] closing'), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'socket' - } - } - }, - - '---', - - createLabel(Scratch.translate('Blocks')), - - { - opcode: 'connect', - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate('connect to [URL] with id [ID]'), - arguments: { - URL: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'wss://echo.websocket.org/' - }, - ID: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'socket' - } - } - }, - { - opcode: 'disconnect', - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate('close connection with socket [ID] with code [C] and reason [R]'), - arguments: { - ID: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'socket' - }, - C: { - type: Scratch.ArgumentType.NUMBER, - defaultValue: 1000 - }, - R: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'fulfilled' - } - } - }, - { - opcode: 'sendMessage', - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate('send message [MESSAGE] to socket [SOCKET]'), - arguments: { - MESSAGE: { - type: Scratch.ArgumentType.STRING, - defaultValue: Scratch.translate('Hello :)') - }, - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'socket' - } - } - }, - - '---', - - createLabel(Scratch.translate('Booleans')), - - { - opcode: 'socketExists', - blockType: Scratch.BlockType.BOOLEAN, - text: Scratch.translate('socket [SOCKET] exists?'), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'socket' - } - } - }, - { - opcode: 'socketConnected', - blockType: Scratch.BlockType.BOOLEAN, - text: Scratch.translate('connected to socket [SOCKET]?'), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'socket' - } - } - }, - { - opcode: 'socketClosed', - blockType: Scratch.BlockType.BOOLEAN, - text: Scratch.translate('closed connection with [SOCKET]?'), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'socket' - } - } - }, - - '---', - - createLabel(Scratch.translate('Events')), - - { - opcode: 'socketMessageReceived', - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate('when i receive a message from [SOCKET] [MESSAGE]'), - isEdgeActivated: false, - hideFromPalette: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: 'socketMenu' - }, - MESSAGE: {} - } - }, - { - opcode: 'socketMessage', - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate('message'), - hideFromPalette: true, - disableMonitor: true - }, - { - opcode: 'socketOpensConnection', - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate('when connection with [SOCKET] opens'), - isEdgeActivated: false, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: 'socketMenu' - } - } - }, - { - blockType: Scratch.BlockType.XML, - xml: ` - - - - ` - }, - { - opcode: 'socketErrored', - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate('when socket [SOCKET] errors'), - isEdgeActivated: false, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: 'socketMenu' - }, - } - }, - { - opcode: 'socketClosedConnection', - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate('when connection with [SOCKET] closes'), - isEdgeActivated: false, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: 'socketMenu' - }, - } - }, - ], - menus: { - socketMenu: { - items: 'getSockets', - } - } - } + return; + } + + if (socket instanceof WebSocket) { + try { + socket.send(MESSAGE); + if (this.debugging) + console.log( + `[WebSockets+] Successfully sent a message to '${SOCKET}'!` + ); + } catch (err) { + console.error(err); + if (this.debugging) console.groupEnd(); } + } else { + if (this.debugging) + console.warn(`[WebSockets+] '${SOCKET}' isn't a WebSocket!`); + } + if (this.debugging) console.groupEnd(); + } - toggleDebugging() { - this.debugging = !this.debugging - window.alert(Scratch.translate('Toggled Debugging! :)')) - } + socketExists({ SOCKET }) { + return Cast.toBoolean(this.sockets[Cast.toString(SOCKET)]); + } - async connect({ ID, URL }) { - const id = Cast.toString(ID) - const url = Cast.toString(URL) - - if(this.sockets[id] instanceof WebSocket) { - try { - this.sockets[id].removeEventListener('message', this.listener(id)) - this.sockets[id].removeEventListener('error', () => { - runtime.startHats('lemonWebSocketsPlus_socketErrored', { - SOCKET: id - }) - }) - this.sockets[id].removeEventListener('message', ({data}) => { - this.lastMessages[id] = data - }) - this.sockets[id].removeEventListener('close', ({reason, code}) => { - runtime.startHats('lemonWebSocketsPlus_socketClosedConnection', { - SOCKET: id - }) - this.socketCloseReasons[id] = reason - this.socketCloseCodes[id] = code - }) - this.sockets[id].removeEventListener('open', () => { - runtime.startHats('lemonWebSocketsPlus_socketOpensConnection', { - SOCKET: id - }) - }) - this.sockets[id].close() - } catch(err) { - console.error(err) - } - } - - if(this.debugging) console.groupCollapsed('WebSockets+ Connecting') - - if(this.debugging) console.log(`[WebSockets+] Attempting to connect to '${url}'..`) - - if(!this.fetchables[url]) this.fetchables[url] = await Scratch.canFetch(url) - - if(!this.fetchables[url]) { - this.socketStatuses[id] = 'failed to connect' - console.log(`[Websockets+] Connection to '${url}' denied!`) - if(this.debugging) console.groupEnd() - return - } - - try { - - this.sockets[id] = new WebSocket(url) - this.socketStatuses[id] = 'connected' - this.socketCloseCodes[id] = 0 - this.socketCloseReasons[id] = '' - - /** - * @type {WebSocket} - */ - const socket = this.sockets[id] - - socket.addEventListener('message', this.listener(id)) - socket.addEventListener('error', () => { - runtime.startHats('lemonWebSocketsPlus_socketErrored', { - SOCKET: id - }) - }) - socket.addEventListener('message', ({data}) => { - this.lastMessages[id] = data - }) - socket.addEventListener('close', ({reason, code}) => { - runtime.startHats('lemonWebSocketsPlus_socketClosedConnection', { - SOCKET: id - }) - this.socketCloseReasons[id] = reason - this.socketCloseCodes[id] = code - }) - socket.addEventListener('open', () => { - runtime.startHats('lemonWebSocketsPlus_socketOpensConnection', { - SOCKET: id - }) - }) - - extManager.refreshBlocks('lemonWebSocketsPlus') - vm.refreshWorkspace() - - if(this.debugging) console.log(`[WebSockets+] Successfully connected to '${url}'.`) - } catch(err) { - console.error(err) - this.socketStatuses[id] = 'failed to connect' - } - - if(this.debugging) console.groupEnd() - } + socketConnected({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)]; - disconnect({ ID, C, R }) { - const id = Cast.toString(ID) - const Code = Cast.toNumber(C) - const Reason = Cast.toString(R) - - if(this.debugging) console.groupCollapsed('WebSockets+ Closing Connection') - - if(this.debugging) console.log(`[WebSockets+] Attemping to close connection with '${id}'..`) - - const socket = this.sockets[id] - - if(socket instanceof WebSocket) { - - socket.removeEventListener('message', this.listener(id)) - socket.removeEventListener('error', () => { - runtime.startHats('lemonWebSocketsPlus_socketErrored', { - SOCKET: id - }) - }) - socket.removeEventListener('message', ({data}) => { - this.lastMessages[id] = data - }) - socket.removeEventListener('close', ({reason, code}) => { - runtime.startHats('lemonWebSocketsPlus_socketClosedConnection', { - SOCKET: id - }) - this.socketCloseReasons[id] = reason - this.socketCloseCodes[id] = code - }) - socket.removeEventListener('open', () => { - runtime.startHats('lemonWebSocketsPlus_socketOpensConnection', { - SOCKET: id - }) - }) - - socket.close(Code, Reason) - - delete this.sockets[id] - this.socketCloseCodes[id] = Code - this.socketCloseReasons[id] = Reason - this.socketStatuses[id] = 'closed' - - extManager.refreshBlocks('lemonWebSocketsPlus') - vm.refreshWorkspace() - - if(this.debugging) console.log(`[WebSockets+] Successfully closed connection with '${id}'!`) - } else { - if(this.debugging) console.warn(`[WebSockets+] WebSocket '${id}' is not a WebSocket!`) - } - - if(this.debugging) console.groupEnd() - - return - } - - sendMessage({ MESSAGE, SOCKET }) { - if(this.debugging) { - console.groupCollapsed('WebSockets+ Message Sending') - - console.log(`[WebSockets+] Attempting to send a message to '${SOCKET}'..`) - } - SOCKET = Cast.toString(SOCKET) - const socket = this.sockets[SOCKET] - - if(!socket) { - if(this.debugging) { - console.warn(`[WebSockets+] '${SOCKET}' doesn't exist!`) - console.groupEnd() - } - return - } - - if(socket instanceof WebSocket) { - try { - socket.send(MESSAGE) - if(this.debugging) console.log(`[WebSockets+] Successfully sent a message to '${SOCKET}'!`) - } catch(err) { - console.error(err) - if(this.debugging) console.groupEnd() - } - } else { - if(this.debugging) console.warn(`[WebSockets+] '${SOCKET}' isn't a WebSocket!`) - } - if(this.debugging) console.groupEnd() - } + if (socket instanceof WebSocket) { + return socket.readyState === WebSocket.OPEN; + } - socketExists({ SOCKET }) { - return Cast.toBoolean(this.sockets[Cast.toString(SOCKET)]) - } + return false; + } - socketConnected({ SOCKET }) { - const socket = this.sockets[Cast.toString(SOCKET)] + socketClosed({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)]; - if(socket instanceof WebSocket) { - return socket.readyState === WebSocket.OPEN - } - - return false - } + if (socket instanceof WebSocket) { + return socket.readyState === WebSocket.CLOSED; + } - socketClosed({ SOCKET }) { - const socket = this.sockets[Cast.toString(SOCKET)] + return true; + } - if(socket instanceof WebSocket) { - return socket.readyState === WebSocket.CLOSED - } - - return true - } + websockets() { + return JSON.stringify(Object.keys(this.sockets)); + } - websockets() { - return JSON.stringify(Object.keys(this.sockets)) - } + socketState({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)]; + if (socket instanceof WebSocket) { + return this.WebSocketStates[socket.readyState] ?? "UNKNOWN"; + } + return "UNKNOWN"; + } - socketState({ SOCKET }) { - const socket = this.sockets[Cast.toString(SOCKET)] - if(socket instanceof WebSocket) { - return this.WebSocketStates[socket.readyState] ?? 'UNKNOWN' - } - return 'UNKNOWN' - } + socketLastMessage({ SOCKET }) { + const socket = this.lastMessages[Cast.toString(SOCKET)]; + return socket ?? ""; + } - socketLastMessage({ SOCKET }) { - const socket = this.lastMessages[Cast.toString(SOCKET)] - return socket ?? '' - } + socketCloseReason({ SOCKET }) { + return this.socketCloseReasons[Cast.toString(SOCKET)] ?? ""; + } - socketCloseReason({ SOCKET }) { - return this.socketCloseReasons[Cast.toString(SOCKET)] ?? '' - } + socketCloseCode({ SOCKET }) { + return this.socketCloseCodes[Cast.toString(SOCKET)] ?? 0; + } - socketCloseCode({ SOCKET }) { - return this.socketCloseCodes[Cast.toString(SOCKET)] ?? 0 - } + getSockets() { + const Sockets = Object.keys(this.sockets); + return Sockets.length > 0 + ? Sockets.map((socket) => { + return { + value: socket, + text: socket, + }; + }) + : [ + { + value: Scratch.translate("None yet :("), + text: Scratch.translate("None yet :("), + }, + ]; + } - getSockets() { - const Sockets = Object.keys(this.sockets) - return Sockets.length > 0 ? Sockets.map((socket) => { - return { - value: socket, - text: socket - } - }) : [{ - value: Scratch.translate('None yet :('), - text: Scratch.translate('None yet :(') - }] - } + /** + * @param {{}} args + * @param {VM.BlockUtility} util + */ + socketMessage(args, util) { + return util.thread.socketMessage ?? ""; + } - /** - * @param {{}} args - * @param {VM.BlockUtility} util - */ - socketMessage(args, util) { - return util.thread.socketMessage ?? '' - } + socketOpensConnection() { + return; + } - socketOpensConnection() { - return - } + socketMessageReceived() { + return; + } - socketMessageReceived() { - return - } - - socketErrored() { - return - } + socketErrored() { + return; + } - socketClosedConnection() { - return - } + socketClosedConnection() { + return; } + } - Scratch.extensions.register(new WebsocketsPlusExt()) -})(Scratch) + Scratch.extensions.register(new WebsocketsPlusExt()); +})(Scratch); From 3a0657e0250498dd2b18cf970d77a55a01972b5b Mon Sep 17 00:00:00 2001 From: Lemon <165233560+BludIsAnLemon@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:12:10 +0800 Subject: [PATCH 04/10] will this work --- extensions/Cheddarphanie/websockets-plus.js | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/Cheddarphanie/websockets-plus.js b/extensions/Cheddarphanie/websockets-plus.js index a64817696d..6df939384c 100644 --- a/extensions/Cheddarphanie/websockets-plus.js +++ b/extensions/Cheddarphanie/websockets-plus.js @@ -368,6 +368,7 @@ console.log(`[WebSockets+] Attempting to connect to '${url}'..`); if (!this.fetchables[url]) + // eslint-disable-next-line extension/check-can-fetch this.fetchables[url] = await Scratch.canFetch(url); if (!this.fetchables[url]) { From f58cdd367cafbbccd7645d7cf843ebf5f510fa2d Mon Sep 17 00:00:00 2001 From: Lemon <165233560+BludIsAnLemon@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:15:45 +0800 Subject: [PATCH 05/10] checking again if this works --- extensions/Cheddarphanie/websockets-plus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/Cheddarphanie/websockets-plus.js b/extensions/Cheddarphanie/websockets-plus.js index 6df939384c..719939201d 100644 --- a/extensions/Cheddarphanie/websockets-plus.js +++ b/extensions/Cheddarphanie/websockets-plus.js @@ -368,7 +368,7 @@ console.log(`[WebSockets+] Attempting to connect to '${url}'..`); if (!this.fetchables[url]) - // eslint-disable-next-line extension/check-can-fetch + // eslint-disable-next-line this.fetchables[url] = await Scratch.canFetch(url); if (!this.fetchables[url]) { From 9ac80fc359943d79cd83970b6a865c96e2e1bf64 Mon Sep 17 00:00:00 2001 From: Lemon <165233560+BludIsAnLemon@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:17:33 +0800 Subject: [PATCH 06/10] might have to brute force --- extensions/Cheddarphanie/websockets-plus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/Cheddarphanie/websockets-plus.js b/extensions/Cheddarphanie/websockets-plus.js index 719939201d..3158d6578c 100644 --- a/extensions/Cheddarphanie/websockets-plus.js +++ b/extensions/Cheddarphanie/websockets-plus.js @@ -368,7 +368,7 @@ console.log(`[WebSockets+] Attempting to connect to '${url}'..`); if (!this.fetchables[url]) - // eslint-disable-next-line + // eslint-disable-next-line extension/check-can-fetch extension/check-can-fetch this.fetchables[url] = await Scratch.canFetch(url); if (!this.fetchables[url]) { From 8a0b6df9198d98e8964f0a75cbaf3cac7a65b582 Mon Sep 17 00:00:00 2001 From: Lemon <165233560+BludIsAnLemon@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:19:39 +0800 Subject: [PATCH 07/10] i swear if its a missing space --- extensions/Cheddarphanie/websockets-plus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/Cheddarphanie/websockets-plus.js b/extensions/Cheddarphanie/websockets-plus.js index 3158d6578c..b8a885830a 100644 --- a/extensions/Cheddarphanie/websockets-plus.js +++ b/extensions/Cheddarphanie/websockets-plus.js @@ -368,7 +368,7 @@ console.log(`[WebSockets+] Attempting to connect to '${url}'..`); if (!this.fetchables[url]) - // eslint-disable-next-line extension/check-can-fetch extension/check-can-fetch + // eslint-disable-next-line extension/check-can-fetch extension/check-can-fetch this.fetchables[url] = await Scratch.canFetch(url); if (!this.fetchables[url]) { From a711da40a1fe4877920b1c3a59462182650a1d64 Mon Sep 17 00:00:00 2001 From: Lemon <165233560+BludIsAnLemon@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:25:17 +0800 Subject: [PATCH 08/10] icl ts pmo sm rn --- extensions/Cheddarphanie/websockets-plus.js | 1083 ++++++++++--------- 1 file changed, 543 insertions(+), 540 deletions(-) diff --git a/extensions/Cheddarphanie/websockets-plus.js b/extensions/Cheddarphanie/websockets-plus.js index b8a885830a..7e8abf1053 100644 --- a/extensions/Cheddarphanie/websockets-plus.js +++ b/extensions/Cheddarphanie/websockets-plus.js @@ -4,615 +4,618 @@ // By: Cheddarphanie // License: Apache-2.0 -(function (Scratch) { - "use strict"; - - if (!Scratch.extensions.unsandboxed) { - throw new Error("The WebSockets+ Extension must run unsandboxed."); - } - - const vm = Scratch.vm; - const extManager = vm.extensionManager; - const runtime = vm.runtime; - const Cast = Scratch.Cast; - - const regenReporters = ["lemonWebSocketsPlus_socketMessage"]; +// eslint-ignore - if (Scratch.gui) - Scratch.gui.getBlockly().then((SB) => { - const ogCheck = SB.scratchBlocksUtils.isShadowArgumentReporter; - SB.scratchBlocksUtils.isShadowArgumentReporter = function (block) { - const result = ogCheck(block); - if (result) return true; - return block.isShadow() && regenReporters.includes(block.type); +(function (Scratch) { + "use strict"; + + if (!Scratch.extensions.unsandboxed) { + throw new Error("The WebSockets+ Extension must run unsandboxed."); + } + + const vm = Scratch.vm; + const extManager = vm.extensionManager; + const runtime = vm.runtime; + const Cast = Scratch.Cast; + + const regenReporters = ["lemonWebSocketsPlus_socketMessage"]; + + if (Scratch.gui) + Scratch.gui.getBlockly().then((SB) => { + const ogCheck = SB.scratchBlocksUtils.isShadowArgumentReporter; + SB.scratchBlocksUtils.isShadowArgumentReporter = function (block) { + const result = ogCheck(block); + if (result) return true; + return block.isShadow() && regenReporters.includes(block.type); + }; + }); + + const createLabel = (txt) => { + return { + blockType: Scratch.BlockType.LABEL, + text: txt, }; - }); - - const createLabel = (txt) => { - return { - blockType: Scratch.BlockType.LABEL, - text: txt, }; - }; - - class WebsocketsPlusExt { - constructor() { - this.debugging = false; - - this.sockets = {}; - - this.lastMessages = {}; - - this.socketStatuses = {}; - - this.socketCloseCodes = {}; - - this.socketCloseReasons = {}; - - this.fetchables = {}; - - this.WebSocketStates = { - 0: "CONNECTING", - 1: "OPEN", - 2: "CLOSING", - 3: "CLOSED", - }; - /** - * - * @param {string} socket - * @returns {Function} - */ - this.listener = function (socket = "") { - return function ({ data }) { - runtime - .startHats("lemonWebSocketsPlus_socketMessageReceived", { - SOCKET: socket, - }) - .forEach((thread) => { - thread.socketMessage = data; - }); + + class WebsocketsPlusExt { + constructor() { + this.debugging = false; + + this.sockets = {}; + + this.lastMessages = {}; + + this.socketStatuses = {}; + + this.socketCloseCodes = {}; + + this.socketCloseReasons = {}; + + this.fetchables = {}; + + this.WebSocketStates = { + 0: "CONNECTING", + 1: "OPEN", + 2: "CLOSING", + 3: "CLOSED", }; - }; - } - getInfo() { - return { - id: "lemonWebSocketsPlus", - name: Scratch.translate("WebSockets+"), - color1: "#307eff", - color2: "#2c5eb0", - blocks: [ - { - func: "toggleDebugging", - blockType: Scratch.BlockType.BUTTON, - text: Scratch.translate("Toggle Debugging"), - }, - - createLabel("Variables"), - - { - opcode: "websockets", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate("websockets"), - }, - { - opcode: "socketState", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate("state of socket [SOCKET]"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, + /** + * + * @param {string} socket + * @returns {Function} + */ + this.listener = function (socket = "") { + return function ({ data }) { + runtime + .startHats("lemonWebSocketsPlus_socketMessageReceived", { + SOCKET: socket, + }) + .forEach((thread) => { + thread.socketMessage = data; + }); + }; + }; + } + getInfo() { + return { + id: "lemonWebSocketsPlus", + name: Scratch.translate("WebSockets+"), + color1: "#307eff", + color2: "#2c5eb0", + blocks: [ + { + func: "toggleDebugging", + blockType: Scratch.BlockType.BUTTON, + text: Scratch.translate("Toggle Debugging"), }, - }, - { - opcode: "socketLastMessage", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate( - "last message received from socket [SOCKET]" - ), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, + + createLabel("Variables"), + + { + opcode: "websockets", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("websockets"), }, - }, - { - opcode: "socketCloseReason", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate("reason of socket [SOCKET] closing"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", + { + opcode: "socketState", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("state of socket [SOCKET]"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, }, }, - }, - { - opcode: "socketCloseCode", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate("code of socket [SOCKET] closing"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", + { + opcode: "socketLastMessage", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate( + "last message received from socket [SOCKET]" + ), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, }, }, - }, - - "---", - - createLabel(Scratch.translate("Blocks")), - - { - opcode: "connect", - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate("connect to [URL] with id [ID]"), - arguments: { - URL: { - type: Scratch.ArgumentType.STRING, - defaultValue: "wss://echo.websocket.org/", - }, - ID: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", + { + opcode: "socketCloseReason", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("reason of socket [SOCKET] closing"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, }, }, - }, - { - opcode: "disconnect", - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate( - "close connection with socket [ID] with code [C] and reason [R]" - ), - arguments: { - ID: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, - C: { - type: Scratch.ArgumentType.NUMBER, - defaultValue: 1000, + { + opcode: "socketCloseCode", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("code of socket [SOCKET] closing"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, }, - R: { - type: Scratch.ArgumentType.STRING, - defaultValue: "fulfilled", + }, + + "---", + + createLabel(Scratch.translate("Blocks")), + + { + opcode: "connect", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("connect to [URL] with id [ID]"), + arguments: { + URL: { + type: Scratch.ArgumentType.STRING, + defaultValue: "wss://echo.websocket.org/", + }, + ID: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, }, }, - }, - { - opcode: "sendMessage", - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate( - "send message [MESSAGE] to socket [SOCKET]" - ), - arguments: { - MESSAGE: { - type: Scratch.ArgumentType.STRING, - defaultValue: Scratch.translate("Hello :)"), + { + opcode: "disconnect", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate( + "close connection with socket [ID] with code [C] and reason [R]" + ), + arguments: { + ID: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + C: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: 1000, + }, + R: { + type: Scratch.ArgumentType.STRING, + defaultValue: "fulfilled", + }, }, - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", + }, + { + opcode: "sendMessage", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate( + "send message [MESSAGE] to socket [SOCKET]" + ), + arguments: { + MESSAGE: { + type: Scratch.ArgumentType.STRING, + defaultValue: Scratch.translate("Hello :)"), + }, + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, }, }, - }, - - "---", - - createLabel(Scratch.translate("Booleans")), - - { - opcode: "socketExists", - blockType: Scratch.BlockType.BOOLEAN, - text: Scratch.translate("socket [SOCKET] exists?"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", + + "---", + + createLabel(Scratch.translate("Booleans")), + + { + opcode: "socketExists", + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate("socket [SOCKET] exists?"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, }, }, - }, - { - opcode: "socketConnected", - blockType: Scratch.BlockType.BOOLEAN, - text: Scratch.translate("connected to socket [SOCKET]?"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", + { + opcode: "socketConnected", + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate("connected to socket [SOCKET]?"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, }, }, - }, - { - opcode: "socketClosed", - blockType: Scratch.BlockType.BOOLEAN, - text: Scratch.translate("closed connection with [SOCKET]?"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", + { + opcode: "socketClosed", + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate("closed connection with [SOCKET]?"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, }, }, - }, - - "---", - - createLabel(Scratch.translate("Events")), - - { - opcode: "socketMessageReceived", - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate( - "when i receive a message from [SOCKET] [MESSAGE]" - ), - isEdgeActivated: false, - hideFromPalette: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: "socketMenu", + + "---", + + createLabel(Scratch.translate("Events")), + + { + opcode: "socketMessageReceived", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate( + "when i receive a message from [SOCKET] [MESSAGE]" + ), + isEdgeActivated: false, + hideFromPalette: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", + }, + MESSAGE: {}, }, - MESSAGE: {}, }, - }, - { - opcode: "socketMessage", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate("message"), - hideFromPalette: true, - disableMonitor: true, - }, - { - opcode: "socketOpensConnection", - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate("when connection with [SOCKET] opens"), - isEdgeActivated: false, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: "socketMenu", + { + opcode: "socketMessage", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("message"), + hideFromPalette: true, + disableMonitor: true, + }, + { + opcode: "socketOpensConnection", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when connection with [SOCKET] opens"), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", + }, }, }, - }, - { - blockType: Scratch.BlockType.XML, - xml: ` - - - - `, - }, - { - opcode: "socketErrored", - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate("when socket [SOCKET] errors"), - isEdgeActivated: false, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: "socketMenu", + { + blockType: Scratch.BlockType.XML, + xml: ` + + + + `, + }, + { + opcode: "socketErrored", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when socket [SOCKET] errors"), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", + }, }, }, - }, - { - opcode: "socketClosedConnection", - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate("when connection with [SOCKET] closes"), - isEdgeActivated: false, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: "socketMenu", + { + opcode: "socketClosedConnection", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when connection with [SOCKET] closes"), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", + }, }, }, + ], + menus: { + socketMenu: { + items: "getSockets", + }, }, - ], - menus: { - socketMenu: { - items: "getSockets", - }, - }, - }; - } - - toggleDebugging() { - this.debugging = !this.debugging; - window.alert(Scratch.translate("Toggled Debugging! :)")); - } - - async connect({ ID, URL }) { - const id = Cast.toString(ID); - const url = Cast.toString(URL); - - if (this.sockets[id] instanceof WebSocket) { + }; + } + + toggleDebugging() { + this.debugging = !this.debugging; + window.alert(Scratch.translate("Toggled Debugging! :)")); + } + + async connect({ ID, URL }) { + const id = Cast.toString(ID); + const url = Cast.toString(URL); + + if (this.sockets[id] instanceof WebSocket) { + try { + this.sockets[id].removeEventListener("message", this.listener(id)); + this.sockets[id].removeEventListener("error", () => { + runtime.startHats("lemonWebSocketsPlus_socketErrored", { + SOCKET: id, + }); + }); + this.sockets[id].removeEventListener("message", ({ data }) => { + this.lastMessages[id] = data; + }); + this.sockets[id].removeEventListener("close", ({ reason, code }) => { + runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { + SOCKET: id, + }); + this.socketCloseReasons[id] = reason; + this.socketCloseCodes[id] = code; + }); + this.sockets[id].removeEventListener("open", () => { + runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { + SOCKET: id, + }); + }); + this.sockets[id].close(); + } catch (err) { + console.error(err); + } + } + + if (this.debugging) console.groupCollapsed("WebSockets+ Connecting"); + + if (this.debugging) + console.log(`[WebSockets+] Attempting to connect to '${url}'..`); + + if (!this.fetchables[url]) + this.fetchables[url] = await Scratch.canFetch(url); + + if (!this.fetchables[url]) { + this.socketStatuses[id] = "failed to connect"; + if (this.debugging) { + console.log(`[Websockets+] Connection to '${url}' denied!`); + console.groupEnd(); + } + return; + } + try { - this.sockets[id].removeEventListener("message", this.listener(id)); - this.sockets[id].removeEventListener("error", () => { + this.sockets[id] = new WebSocket(url); + this.socketStatuses[id] = "connected"; + this.socketCloseCodes[id] = 0; + this.socketCloseReasons[id] = ""; + + /** + * @type {WebSocket} + */ + const socket = this.sockets[id]; + + socket.addEventListener("message", this.listener(id)); + socket.addEventListener("error", () => { runtime.startHats("lemonWebSocketsPlus_socketErrored", { SOCKET: id, }); }); - this.sockets[id].removeEventListener("message", ({ data }) => { + socket.addEventListener("message", ({ data }) => { this.lastMessages[id] = data; }); - this.sockets[id].removeEventListener("close", ({ reason, code }) => { + socket.addEventListener("close", ({ reason, code }) => { runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { SOCKET: id, }); this.socketCloseReasons[id] = reason; this.socketCloseCodes[id] = code; }); - this.sockets[id].removeEventListener("open", () => { + socket.addEventListener("open", () => { runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { SOCKET: id, }); }); - this.sockets[id].close(); + + extManager.refreshBlocks("lemonWebSocketsPlus"); + vm.refreshWorkspace(); + + if (this.debugging) + console.log(`[WebSockets+] Successfully connected to '${url}'.`); } catch (err) { console.error(err); + this.socketStatuses[id] = "failed to connect"; } - } - - if (this.debugging) console.groupCollapsed("WebSockets+ Connecting"); - - if (this.debugging) - console.log(`[WebSockets+] Attempting to connect to '${url}'..`); - - if (!this.fetchables[url]) - // eslint-disable-next-line extension/check-can-fetch extension/check-can-fetch - this.fetchables[url] = await Scratch.canFetch(url); - - if (!this.fetchables[url]) { - this.socketStatuses[id] = "failed to connect"; - console.log(`[Websockets+] Connection to '${url}' denied!`); + if (this.debugging) console.groupEnd(); - return; } - - try { - this.sockets[id] = new WebSocket(url); - this.socketStatuses[id] = "connected"; - this.socketCloseCodes[id] = 0; - this.socketCloseReasons[id] = ""; - - /** - * @type {WebSocket} - */ + + disconnect({ ID, C, R }) { + const id = Cast.toString(ID); + const Code = Cast.toNumber(C); + const Reason = Cast.toString(R); + + if (this.debugging) + console.groupCollapsed("WebSockets+ Closing Connection"); + + if (this.debugging) + console.log( + `[WebSockets+] Attemping to close connection with '${id}'..` + ); + const socket = this.sockets[id]; - - socket.addEventListener("message", this.listener(id)); - socket.addEventListener("error", () => { - runtime.startHats("lemonWebSocketsPlus_socketErrored", { - SOCKET: id, - }); - }); - socket.addEventListener("message", ({ data }) => { - this.lastMessages[id] = data; - }); - socket.addEventListener("close", ({ reason, code }) => { - runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { - SOCKET: id, - }); - this.socketCloseReasons[id] = reason; - this.socketCloseCodes[id] = code; - }); - socket.addEventListener("open", () => { - runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { - SOCKET: id, + + if (socket instanceof WebSocket) { + socket.removeEventListener("message", this.listener(id)); + socket.removeEventListener("error", () => { + runtime.startHats("lemonWebSocketsPlus_socketErrored", { + SOCKET: id, + }); }); - }); - - extManager.refreshBlocks("lemonWebSocketsPlus"); - vm.refreshWorkspace(); - - if (this.debugging) - console.log(`[WebSockets+] Successfully connected to '${url}'.`); - } catch (err) { - console.error(err); - this.socketStatuses[id] = "failed to connect"; - } - - if (this.debugging) console.groupEnd(); - } - - disconnect({ ID, C, R }) { - const id = Cast.toString(ID); - const Code = Cast.toNumber(C); - const Reason = Cast.toString(R); - - if (this.debugging) - console.groupCollapsed("WebSockets+ Closing Connection"); - - if (this.debugging) - console.log( - `[WebSockets+] Attemping to close connection with '${id}'..` - ); - - const socket = this.sockets[id]; - - if (socket instanceof WebSocket) { - socket.removeEventListener("message", this.listener(id)); - socket.removeEventListener("error", () => { - runtime.startHats("lemonWebSocketsPlus_socketErrored", { - SOCKET: id, + socket.removeEventListener("message", ({ data }) => { + this.lastMessages[id] = data; }); - }); - socket.removeEventListener("message", ({ data }) => { - this.lastMessages[id] = data; - }); - socket.removeEventListener("close", ({ reason, code }) => { - runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { - SOCKET: id, + socket.removeEventListener("close", ({ reason, code }) => { + runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { + SOCKET: id, + }); + this.socketCloseReasons[id] = reason; + this.socketCloseCodes[id] = code; }); - this.socketCloseReasons[id] = reason; - this.socketCloseCodes[id] = code; - }); - socket.removeEventListener("open", () => { - runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { - SOCKET: id, + socket.removeEventListener("open", () => { + runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { + SOCKET: id, + }); }); - }); - - socket.close(Code, Reason); - - delete this.sockets[id]; - this.socketCloseCodes[id] = Code; - this.socketCloseReasons[id] = Reason; - this.socketStatuses[id] = "closed"; - - extManager.refreshBlocks("lemonWebSocketsPlus"); - vm.refreshWorkspace(); - - if (this.debugging) + + socket.close(Code, Reason); + + delete this.sockets[id]; + this.socketCloseCodes[id] = Code; + this.socketCloseReasons[id] = Reason; + this.socketStatuses[id] = "closed"; + + extManager.refreshBlocks("lemonWebSocketsPlus"); + vm.refreshWorkspace(); + + if (this.debugging) + console.log( + `[WebSockets+] Successfully closed connection with '${id}'!` + ); + } else { + if (this.debugging) + console.warn(`[WebSockets+] WebSocket '${id}' is not a WebSocket!`); + } + + if (this.debugging) console.groupEnd(); + + return; + } + + sendMessage({ MESSAGE, SOCKET }) { + if (this.debugging) { + console.groupCollapsed("WebSockets+ Message Sending"); + console.log( - `[WebSockets+] Successfully closed connection with '${id}'!` + `[WebSockets+] Attempting to send a message to '${SOCKET}'..` ); - } else { - if (this.debugging) - console.warn(`[WebSockets+] WebSocket '${id}' is not a WebSocket!`); + } + SOCKET = Cast.toString(SOCKET); + const socket = this.sockets[SOCKET]; + + if (!socket) { + if (this.debugging) { + console.warn(`[WebSockets+] '${SOCKET}' doesn't exist!`); + console.groupEnd(); + } + return; + } + + if (socket instanceof WebSocket) { + try { + socket.send(MESSAGE); + if (this.debugging) + console.log( + `[WebSockets+] Successfully sent a message to '${SOCKET}'!` + ); + } catch (err) { + console.error(err); + if (this.debugging) console.groupEnd(); + } + } else { + if (this.debugging) + console.warn(`[WebSockets+] '${SOCKET}' isn't a WebSocket!`); + } + if (this.debugging) console.groupEnd(); } - - if (this.debugging) console.groupEnd(); - - return; - } - - sendMessage({ MESSAGE, SOCKET }) { - if (this.debugging) { - console.groupCollapsed("WebSockets+ Message Sending"); - - console.log( - `[WebSockets+] Attempting to send a message to '${SOCKET}'..` - ); + + socketExists({ SOCKET }) { + return Cast.toBoolean(this.sockets[Cast.toString(SOCKET)]); } - SOCKET = Cast.toString(SOCKET); - const socket = this.sockets[SOCKET]; - - if (!socket) { - if (this.debugging) { - console.warn(`[WebSockets+] '${SOCKET}' doesn't exist!`); - console.groupEnd(); + + socketConnected({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)]; + + if (socket instanceof WebSocket) { + return socket.readyState === WebSocket.OPEN; } - return; + + return false; } - - if (socket instanceof WebSocket) { - try { - socket.send(MESSAGE); - if (this.debugging) - console.log( - `[WebSockets+] Successfully sent a message to '${SOCKET}'!` - ); - } catch (err) { - console.error(err); - if (this.debugging) console.groupEnd(); + + socketClosed({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)]; + + if (socket instanceof WebSocket) { + return socket.readyState === WebSocket.CLOSED; } - } else { - if (this.debugging) - console.warn(`[WebSockets+] '${SOCKET}' isn't a WebSocket!`); + + return true; } - if (this.debugging) console.groupEnd(); - } - - socketExists({ SOCKET }) { - return Cast.toBoolean(this.sockets[Cast.toString(SOCKET)]); - } - - socketConnected({ SOCKET }) { - const socket = this.sockets[Cast.toString(SOCKET)]; - - if (socket instanceof WebSocket) { - return socket.readyState === WebSocket.OPEN; + + websockets() { + return JSON.stringify(Object.keys(this.sockets)); } - - return false; - } - - socketClosed({ SOCKET }) { - const socket = this.sockets[Cast.toString(SOCKET)]; - - if (socket instanceof WebSocket) { - return socket.readyState === WebSocket.CLOSED; + + socketState({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)]; + if (socket instanceof WebSocket) { + return this.WebSocketStates[socket.readyState] ?? "UNKNOWN"; + } + return "UNKNOWN"; } - - return true; - } - - websockets() { - return JSON.stringify(Object.keys(this.sockets)); - } - - socketState({ SOCKET }) { - const socket = this.sockets[Cast.toString(SOCKET)]; - if (socket instanceof WebSocket) { - return this.WebSocketStates[socket.readyState] ?? "UNKNOWN"; + + socketLastMessage({ SOCKET }) { + const socket = this.lastMessages[Cast.toString(SOCKET)]; + return socket ?? ""; + } + + socketCloseReason({ SOCKET }) { + return this.socketCloseReasons[Cast.toString(SOCKET)] ?? ""; + } + + socketCloseCode({ SOCKET }) { + return this.socketCloseCodes[Cast.toString(SOCKET)] ?? 0; + } + + getSockets() { + const Sockets = Object.keys(this.sockets); + return Sockets.length > 0 + ? Sockets.map((socket) => { + return { + value: socket, + text: socket, + }; + }) + : [ + { + value: Scratch.translate("None yet :("), + text: Scratch.translate("None yet :("), + }, + ]; + } + + /** + * @param {{}} args + * @param {VM.BlockUtility} util + */ + socketMessage(args, util) { + return util.thread.socketMessage ?? ""; + } + + socketOpensConnection() { + return; + } + + socketMessageReceived() { + return; + } + + socketErrored() { + return; + } + + socketClosedConnection() { + return; } - return "UNKNOWN"; - } - - socketLastMessage({ SOCKET }) { - const socket = this.lastMessages[Cast.toString(SOCKET)]; - return socket ?? ""; - } - - socketCloseReason({ SOCKET }) { - return this.socketCloseReasons[Cast.toString(SOCKET)] ?? ""; - } - - socketCloseCode({ SOCKET }) { - return this.socketCloseCodes[Cast.toString(SOCKET)] ?? 0; - } - - getSockets() { - const Sockets = Object.keys(this.sockets); - return Sockets.length > 0 - ? Sockets.map((socket) => { - return { - value: socket, - text: socket, - }; - }) - : [ - { - value: Scratch.translate("None yet :("), - text: Scratch.translate("None yet :("), - }, - ]; - } - - /** - * @param {{}} args - * @param {VM.BlockUtility} util - */ - socketMessage(args, util) { - return util.thread.socketMessage ?? ""; - } - - socketOpensConnection() { - return; - } - - socketMessageReceived() { - return; - } - - socketErrored() { - return; - } - - socketClosedConnection() { - return; } - } - - Scratch.extensions.register(new WebsocketsPlusExt()); -})(Scratch); + + Scratch.extensions.register(new WebsocketsPlusExt()); + })(Scratch); From acff2935461f95d13a8fd7b4028027ef77bf1e70 Mon Sep 17 00:00:00 2001 From: "DangoCat[bot]" Date: Wed, 12 Mar 2025 11:27:08 +0000 Subject: [PATCH 09/10] [Automated] Format code --- extensions/Cheddarphanie/websockets-plus.js | 1076 +++++++++---------- 1 file changed, 538 insertions(+), 538 deletions(-) diff --git a/extensions/Cheddarphanie/websockets-plus.js b/extensions/Cheddarphanie/websockets-plus.js index 7e8abf1053..1eab3c13a7 100644 --- a/extensions/Cheddarphanie/websockets-plus.js +++ b/extensions/Cheddarphanie/websockets-plus.js @@ -7,615 +7,615 @@ // eslint-ignore (function (Scratch) { - "use strict"; - - if (!Scratch.extensions.unsandboxed) { - throw new Error("The WebSockets+ Extension must run unsandboxed."); - } - - const vm = Scratch.vm; - const extManager = vm.extensionManager; - const runtime = vm.runtime; - const Cast = Scratch.Cast; - - const regenReporters = ["lemonWebSocketsPlus_socketMessage"]; - - if (Scratch.gui) - Scratch.gui.getBlockly().then((SB) => { - const ogCheck = SB.scratchBlocksUtils.isShadowArgumentReporter; - SB.scratchBlocksUtils.isShadowArgumentReporter = function (block) { - const result = ogCheck(block); - if (result) return true; - return block.isShadow() && regenReporters.includes(block.type); - }; - }); - - const createLabel = (txt) => { - return { - blockType: Scratch.BlockType.LABEL, - text: txt, + "use strict"; + + if (!Scratch.extensions.unsandboxed) { + throw new Error("The WebSockets+ Extension must run unsandboxed."); + } + + const vm = Scratch.vm; + const extManager = vm.extensionManager; + const runtime = vm.runtime; + const Cast = Scratch.Cast; + + const regenReporters = ["lemonWebSocketsPlus_socketMessage"]; + + if (Scratch.gui) + Scratch.gui.getBlockly().then((SB) => { + const ogCheck = SB.scratchBlocksUtils.isShadowArgumentReporter; + SB.scratchBlocksUtils.isShadowArgumentReporter = function (block) { + const result = ogCheck(block); + if (result) return true; + return block.isShadow() && regenReporters.includes(block.type); }; + }); + + const createLabel = (txt) => { + return { + blockType: Scratch.BlockType.LABEL, + text: txt, }; - - class WebsocketsPlusExt { - constructor() { - this.debugging = false; - - this.sockets = {}; - - this.lastMessages = {}; - - this.socketStatuses = {}; - - this.socketCloseCodes = {}; - - this.socketCloseReasons = {}; - - this.fetchables = {}; - - this.WebSocketStates = { - 0: "CONNECTING", - 1: "OPEN", - 2: "CLOSING", - 3: "CLOSED", - }; - /** - * - * @param {string} socket - * @returns {Function} - */ - this.listener = function (socket = "") { - return function ({ data }) { - runtime - .startHats("lemonWebSocketsPlus_socketMessageReceived", { - SOCKET: socket, - }) - .forEach((thread) => { - thread.socketMessage = data; - }); - }; + }; + + class WebsocketsPlusExt { + constructor() { + this.debugging = false; + + this.sockets = {}; + + this.lastMessages = {}; + + this.socketStatuses = {}; + + this.socketCloseCodes = {}; + + this.socketCloseReasons = {}; + + this.fetchables = {}; + + this.WebSocketStates = { + 0: "CONNECTING", + 1: "OPEN", + 2: "CLOSING", + 3: "CLOSED", + }; + /** + * + * @param {string} socket + * @returns {Function} + */ + this.listener = function (socket = "") { + return function ({ data }) { + runtime + .startHats("lemonWebSocketsPlus_socketMessageReceived", { + SOCKET: socket, + }) + .forEach((thread) => { + thread.socketMessage = data; + }); }; - } - getInfo() { - return { - id: "lemonWebSocketsPlus", - name: Scratch.translate("WebSockets+"), - color1: "#307eff", - color2: "#2c5eb0", - blocks: [ - { - func: "toggleDebugging", - blockType: Scratch.BlockType.BUTTON, - text: Scratch.translate("Toggle Debugging"), - }, - - createLabel("Variables"), - - { - opcode: "websockets", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate("websockets"), - }, - { - opcode: "socketState", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate("state of socket [SOCKET]"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, + }; + } + getInfo() { + return { + id: "lemonWebSocketsPlus", + name: Scratch.translate("WebSockets+"), + color1: "#307eff", + color2: "#2c5eb0", + blocks: [ + { + func: "toggleDebugging", + blockType: Scratch.BlockType.BUTTON, + text: Scratch.translate("Toggle Debugging"), + }, + + createLabel("Variables"), + + { + opcode: "websockets", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("websockets"), + }, + { + opcode: "socketState", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("state of socket [SOCKET]"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", }, }, - { - opcode: "socketLastMessage", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate( - "last message received from socket [SOCKET]" - ), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, + }, + { + opcode: "socketLastMessage", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate( + "last message received from socket [SOCKET]" + ), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", }, }, - { - opcode: "socketCloseReason", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate("reason of socket [SOCKET] closing"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, + }, + { + opcode: "socketCloseReason", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("reason of socket [SOCKET] closing"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", }, }, - { - opcode: "socketCloseCode", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate("code of socket [SOCKET] closing"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, + }, + { + opcode: "socketCloseCode", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("code of socket [SOCKET] closing"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", }, }, - - "---", - - createLabel(Scratch.translate("Blocks")), - - { - opcode: "connect", - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate("connect to [URL] with id [ID]"), - arguments: { - URL: { - type: Scratch.ArgumentType.STRING, - defaultValue: "wss://echo.websocket.org/", - }, - ID: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, + }, + + "---", + + createLabel(Scratch.translate("Blocks")), + + { + opcode: "connect", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("connect to [URL] with id [ID]"), + arguments: { + URL: { + type: Scratch.ArgumentType.STRING, + defaultValue: "wss://echo.websocket.org/", }, - }, - { - opcode: "disconnect", - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate( - "close connection with socket [ID] with code [C] and reason [R]" - ), - arguments: { - ID: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, - C: { - type: Scratch.ArgumentType.NUMBER, - defaultValue: 1000, - }, - R: { - type: Scratch.ArgumentType.STRING, - defaultValue: "fulfilled", - }, + ID: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", }, }, - { - opcode: "sendMessage", - blockType: Scratch.BlockType.COMMAND, - text: Scratch.translate( - "send message [MESSAGE] to socket [SOCKET]" - ), - arguments: { - MESSAGE: { - type: Scratch.ArgumentType.STRING, - defaultValue: Scratch.translate("Hello :)"), - }, - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, + }, + { + opcode: "disconnect", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate( + "close connection with socket [ID] with code [C] and reason [R]" + ), + arguments: { + ID: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", + }, + C: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: 1000, + }, + R: { + type: Scratch.ArgumentType.STRING, + defaultValue: "fulfilled", }, }, - - "---", - - createLabel(Scratch.translate("Booleans")), - - { - opcode: "socketExists", - blockType: Scratch.BlockType.BOOLEAN, - text: Scratch.translate("socket [SOCKET] exists?"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, + }, + { + opcode: "sendMessage", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate( + "send message [MESSAGE] to socket [SOCKET]" + ), + arguments: { + MESSAGE: { + type: Scratch.ArgumentType.STRING, + defaultValue: Scratch.translate("Hello :)"), + }, + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", }, }, - { - opcode: "socketConnected", - blockType: Scratch.BlockType.BOOLEAN, - text: Scratch.translate("connected to socket [SOCKET]?"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, + }, + + "---", + + createLabel(Scratch.translate("Booleans")), + + { + opcode: "socketExists", + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate("socket [SOCKET] exists?"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", }, }, - { - opcode: "socketClosed", - blockType: Scratch.BlockType.BOOLEAN, - text: Scratch.translate("closed connection with [SOCKET]?"), - disableMonitor: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - defaultValue: "socket", - }, + }, + { + opcode: "socketConnected", + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate("connected to socket [SOCKET]?"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", }, }, - - "---", - - createLabel(Scratch.translate("Events")), - - { - opcode: "socketMessageReceived", - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate( - "when i receive a message from [SOCKET] [MESSAGE]" - ), - isEdgeActivated: false, - hideFromPalette: true, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: "socketMenu", - }, - MESSAGE: {}, + }, + { + opcode: "socketClosed", + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate("closed connection with [SOCKET]?"), + disableMonitor: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + defaultValue: "socket", }, }, - { - opcode: "socketMessage", - blockType: Scratch.BlockType.REPORTER, - text: Scratch.translate("message"), - hideFromPalette: true, - disableMonitor: true, + }, + + "---", + + createLabel(Scratch.translate("Events")), + + { + opcode: "socketMessageReceived", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate( + "when i receive a message from [SOCKET] [MESSAGE]" + ), + isEdgeActivated: false, + hideFromPalette: true, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", + }, + MESSAGE: {}, }, - { - opcode: "socketOpensConnection", - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate("when connection with [SOCKET] opens"), - isEdgeActivated: false, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: "socketMenu", - }, + }, + { + opcode: "socketMessage", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("message"), + hideFromPalette: true, + disableMonitor: true, + }, + { + opcode: "socketOpensConnection", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when connection with [SOCKET] opens"), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", }, }, - { - blockType: Scratch.BlockType.XML, - xml: ` + }, + { + blockType: Scratch.BlockType.XML, + xml: ` `, - }, - { - opcode: "socketErrored", - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate("when socket [SOCKET] errors"), - isEdgeActivated: false, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: "socketMenu", - }, + }, + { + opcode: "socketErrored", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when socket [SOCKET] errors"), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", }, }, - { - opcode: "socketClosedConnection", - blockType: Scratch.BlockType.EVENT, - text: Scratch.translate("when connection with [SOCKET] closes"), - isEdgeActivated: false, - arguments: { - SOCKET: { - type: Scratch.ArgumentType.STRING, - menu: "socketMenu", - }, + }, + { + opcode: "socketClosedConnection", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when connection with [SOCKET] closes"), + isEdgeActivated: false, + arguments: { + SOCKET: { + type: Scratch.ArgumentType.STRING, + menu: "socketMenu", }, }, - ], - menus: { - socketMenu: { - items: "getSockets", - }, }, - }; - } - - toggleDebugging() { - this.debugging = !this.debugging; - window.alert(Scratch.translate("Toggled Debugging! :)")); - } - - async connect({ ID, URL }) { - const id = Cast.toString(ID); - const url = Cast.toString(URL); - - if (this.sockets[id] instanceof WebSocket) { - try { - this.sockets[id].removeEventListener("message", this.listener(id)); - this.sockets[id].removeEventListener("error", () => { - runtime.startHats("lemonWebSocketsPlus_socketErrored", { - SOCKET: id, - }); - }); - this.sockets[id].removeEventListener("message", ({ data }) => { - this.lastMessages[id] = data; - }); - this.sockets[id].removeEventListener("close", ({ reason, code }) => { - runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { - SOCKET: id, - }); - this.socketCloseReasons[id] = reason; - this.socketCloseCodes[id] = code; - }); - this.sockets[id].removeEventListener("open", () => { - runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { - SOCKET: id, - }); - }); - this.sockets[id].close(); - } catch (err) { - console.error(err); - } - } - - if (this.debugging) console.groupCollapsed("WebSockets+ Connecting"); - - if (this.debugging) - console.log(`[WebSockets+] Attempting to connect to '${url}'..`); - - if (!this.fetchables[url]) - this.fetchables[url] = await Scratch.canFetch(url); - - if (!this.fetchables[url]) { - this.socketStatuses[id] = "failed to connect"; - if (this.debugging) { - console.log(`[Websockets+] Connection to '${url}' denied!`); - console.groupEnd(); - } - return; - } - + ], + menus: { + socketMenu: { + items: "getSockets", + }, + }, + }; + } + + toggleDebugging() { + this.debugging = !this.debugging; + window.alert(Scratch.translate("Toggled Debugging! :)")); + } + + async connect({ ID, URL }) { + const id = Cast.toString(ID); + const url = Cast.toString(URL); + + if (this.sockets[id] instanceof WebSocket) { try { - this.sockets[id] = new WebSocket(url); - this.socketStatuses[id] = "connected"; - this.socketCloseCodes[id] = 0; - this.socketCloseReasons[id] = ""; - - /** - * @type {WebSocket} - */ - const socket = this.sockets[id]; - - socket.addEventListener("message", this.listener(id)); - socket.addEventListener("error", () => { + this.sockets[id].removeEventListener("message", this.listener(id)); + this.sockets[id].removeEventListener("error", () => { runtime.startHats("lemonWebSocketsPlus_socketErrored", { SOCKET: id, }); }); - socket.addEventListener("message", ({ data }) => { + this.sockets[id].removeEventListener("message", ({ data }) => { this.lastMessages[id] = data; }); - socket.addEventListener("close", ({ reason, code }) => { + this.sockets[id].removeEventListener("close", ({ reason, code }) => { runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { SOCKET: id, }); this.socketCloseReasons[id] = reason; this.socketCloseCodes[id] = code; }); - socket.addEventListener("open", () => { + this.sockets[id].removeEventListener("open", () => { runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { SOCKET: id, }); }); - - extManager.refreshBlocks("lemonWebSocketsPlus"); - vm.refreshWorkspace(); - - if (this.debugging) - console.log(`[WebSockets+] Successfully connected to '${url}'.`); + this.sockets[id].close(); } catch (err) { console.error(err); - this.socketStatuses[id] = "failed to connect"; } - - if (this.debugging) console.groupEnd(); } - - disconnect({ ID, C, R }) { - const id = Cast.toString(ID); - const Code = Cast.toNumber(C); - const Reason = Cast.toString(R); - - if (this.debugging) - console.groupCollapsed("WebSockets+ Closing Connection"); - - if (this.debugging) - console.log( - `[WebSockets+] Attemping to close connection with '${id}'..` - ); - + + if (this.debugging) console.groupCollapsed("WebSockets+ Connecting"); + + if (this.debugging) + console.log(`[WebSockets+] Attempting to connect to '${url}'..`); + + if (!this.fetchables[url]) + this.fetchables[url] = await Scratch.canFetch(url); + + if (!this.fetchables[url]) { + this.socketStatuses[id] = "failed to connect"; + if (this.debugging) { + console.log(`[Websockets+] Connection to '${url}' denied!`); + console.groupEnd(); + } + return; + } + + try { + this.sockets[id] = new WebSocket(url); + this.socketStatuses[id] = "connected"; + this.socketCloseCodes[id] = 0; + this.socketCloseReasons[id] = ""; + + /** + * @type {WebSocket} + */ const socket = this.sockets[id]; - - if (socket instanceof WebSocket) { - socket.removeEventListener("message", this.listener(id)); - socket.removeEventListener("error", () => { - runtime.startHats("lemonWebSocketsPlus_socketErrored", { - SOCKET: id, - }); + + socket.addEventListener("message", this.listener(id)); + socket.addEventListener("error", () => { + runtime.startHats("lemonWebSocketsPlus_socketErrored", { + SOCKET: id, }); - socket.removeEventListener("message", ({ data }) => { - this.lastMessages[id] = data; + }); + socket.addEventListener("message", ({ data }) => { + this.lastMessages[id] = data; + }); + socket.addEventListener("close", ({ reason, code }) => { + runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { + SOCKET: id, }); - socket.removeEventListener("close", ({ reason, code }) => { - runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { - SOCKET: id, - }); - this.socketCloseReasons[id] = reason; - this.socketCloseCodes[id] = code; + this.socketCloseReasons[id] = reason; + this.socketCloseCodes[id] = code; + }); + socket.addEventListener("open", () => { + runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { + SOCKET: id, }); - socket.removeEventListener("open", () => { - runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { - SOCKET: id, - }); - }); - - socket.close(Code, Reason); - - delete this.sockets[id]; - this.socketCloseCodes[id] = Code; - this.socketCloseReasons[id] = Reason; - this.socketStatuses[id] = "closed"; - - extManager.refreshBlocks("lemonWebSocketsPlus"); - vm.refreshWorkspace(); - - if (this.debugging) - console.log( - `[WebSockets+] Successfully closed connection with '${id}'!` - ); - } else { - if (this.debugging) - console.warn(`[WebSockets+] WebSocket '${id}' is not a WebSocket!`); - } - - if (this.debugging) console.groupEnd(); - - return; + }); + + extManager.refreshBlocks("lemonWebSocketsPlus"); + vm.refreshWorkspace(); + + if (this.debugging) + console.log(`[WebSockets+] Successfully connected to '${url}'.`); + } catch (err) { + console.error(err); + this.socketStatuses[id] = "failed to connect"; } - - sendMessage({ MESSAGE, SOCKET }) { - if (this.debugging) { - console.groupCollapsed("WebSockets+ Message Sending"); - + + if (this.debugging) console.groupEnd(); + } + + disconnect({ ID, C, R }) { + const id = Cast.toString(ID); + const Code = Cast.toNumber(C); + const Reason = Cast.toString(R); + + if (this.debugging) + console.groupCollapsed("WebSockets+ Closing Connection"); + + if (this.debugging) + console.log( + `[WebSockets+] Attemping to close connection with '${id}'..` + ); + + const socket = this.sockets[id]; + + if (socket instanceof WebSocket) { + socket.removeEventListener("message", this.listener(id)); + socket.removeEventListener("error", () => { + runtime.startHats("lemonWebSocketsPlus_socketErrored", { + SOCKET: id, + }); + }); + socket.removeEventListener("message", ({ data }) => { + this.lastMessages[id] = data; + }); + socket.removeEventListener("close", ({ reason, code }) => { + runtime.startHats("lemonWebSocketsPlus_socketClosedConnection", { + SOCKET: id, + }); + this.socketCloseReasons[id] = reason; + this.socketCloseCodes[id] = code; + }); + socket.removeEventListener("open", () => { + runtime.startHats("lemonWebSocketsPlus_socketOpensConnection", { + SOCKET: id, + }); + }); + + socket.close(Code, Reason); + + delete this.sockets[id]; + this.socketCloseCodes[id] = Code; + this.socketCloseReasons[id] = Reason; + this.socketStatuses[id] = "closed"; + + extManager.refreshBlocks("lemonWebSocketsPlus"); + vm.refreshWorkspace(); + + if (this.debugging) console.log( - `[WebSockets+] Attempting to send a message to '${SOCKET}'..` + `[WebSockets+] Successfully closed connection with '${id}'!` ); - } - SOCKET = Cast.toString(SOCKET); - const socket = this.sockets[SOCKET]; - - if (!socket) { - if (this.debugging) { - console.warn(`[WebSockets+] '${SOCKET}' doesn't exist!`); - console.groupEnd(); - } - return; - } - - if (socket instanceof WebSocket) { - try { - socket.send(MESSAGE); - if (this.debugging) - console.log( - `[WebSockets+] Successfully sent a message to '${SOCKET}'!` - ); - } catch (err) { - console.error(err); - if (this.debugging) console.groupEnd(); - } - } else { - if (this.debugging) - console.warn(`[WebSockets+] '${SOCKET}' isn't a WebSocket!`); - } - if (this.debugging) console.groupEnd(); - } - - socketExists({ SOCKET }) { - return Cast.toBoolean(this.sockets[Cast.toString(SOCKET)]); + } else { + if (this.debugging) + console.warn(`[WebSockets+] WebSocket '${id}' is not a WebSocket!`); } - - socketConnected({ SOCKET }) { - const socket = this.sockets[Cast.toString(SOCKET)]; - - if (socket instanceof WebSocket) { - return socket.readyState === WebSocket.OPEN; - } - - return false; + + if (this.debugging) console.groupEnd(); + + return; + } + + sendMessage({ MESSAGE, SOCKET }) { + if (this.debugging) { + console.groupCollapsed("WebSockets+ Message Sending"); + + console.log( + `[WebSockets+] Attempting to send a message to '${SOCKET}'..` + ); } - - socketClosed({ SOCKET }) { - const socket = this.sockets[Cast.toString(SOCKET)]; - - if (socket instanceof WebSocket) { - return socket.readyState === WebSocket.CLOSED; + SOCKET = Cast.toString(SOCKET); + const socket = this.sockets[SOCKET]; + + if (!socket) { + if (this.debugging) { + console.warn(`[WebSockets+] '${SOCKET}' doesn't exist!`); + console.groupEnd(); } - - return true; - } - - websockets() { - return JSON.stringify(Object.keys(this.sockets)); + return; } - - socketState({ SOCKET }) { - const socket = this.sockets[Cast.toString(SOCKET)]; - if (socket instanceof WebSocket) { - return this.WebSocketStates[socket.readyState] ?? "UNKNOWN"; + + if (socket instanceof WebSocket) { + try { + socket.send(MESSAGE); + if (this.debugging) + console.log( + `[WebSockets+] Successfully sent a message to '${SOCKET}'!` + ); + } catch (err) { + console.error(err); + if (this.debugging) console.groupEnd(); } - return "UNKNOWN"; - } - - socketLastMessage({ SOCKET }) { - const socket = this.lastMessages[Cast.toString(SOCKET)]; - return socket ?? ""; - } - - socketCloseReason({ SOCKET }) { - return this.socketCloseReasons[Cast.toString(SOCKET)] ?? ""; - } - - socketCloseCode({ SOCKET }) { - return this.socketCloseCodes[Cast.toString(SOCKET)] ?? 0; - } - - getSockets() { - const Sockets = Object.keys(this.sockets); - return Sockets.length > 0 - ? Sockets.map((socket) => { - return { - value: socket, - text: socket, - }; - }) - : [ - { - value: Scratch.translate("None yet :("), - text: Scratch.translate("None yet :("), - }, - ]; - } - - /** - * @param {{}} args - * @param {VM.BlockUtility} util - */ - socketMessage(args, util) { - return util.thread.socketMessage ?? ""; - } - - socketOpensConnection() { - return; + } else { + if (this.debugging) + console.warn(`[WebSockets+] '${SOCKET}' isn't a WebSocket!`); } - - socketMessageReceived() { - return; + if (this.debugging) console.groupEnd(); + } + + socketExists({ SOCKET }) { + return Cast.toBoolean(this.sockets[Cast.toString(SOCKET)]); + } + + socketConnected({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)]; + + if (socket instanceof WebSocket) { + return socket.readyState === WebSocket.OPEN; } - - socketErrored() { - return; + + return false; + } + + socketClosed({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)]; + + if (socket instanceof WebSocket) { + return socket.readyState === WebSocket.CLOSED; } - - socketClosedConnection() { - return; + + return true; + } + + websockets() { + return JSON.stringify(Object.keys(this.sockets)); + } + + socketState({ SOCKET }) { + const socket = this.sockets[Cast.toString(SOCKET)]; + if (socket instanceof WebSocket) { + return this.WebSocketStates[socket.readyState] ?? "UNKNOWN"; } + return "UNKNOWN"; + } + + socketLastMessage({ SOCKET }) { + const socket = this.lastMessages[Cast.toString(SOCKET)]; + return socket ?? ""; + } + + socketCloseReason({ SOCKET }) { + return this.socketCloseReasons[Cast.toString(SOCKET)] ?? ""; + } + + socketCloseCode({ SOCKET }) { + return this.socketCloseCodes[Cast.toString(SOCKET)] ?? 0; + } + + getSockets() { + const Sockets = Object.keys(this.sockets); + return Sockets.length > 0 + ? Sockets.map((socket) => { + return { + value: socket, + text: socket, + }; + }) + : [ + { + value: Scratch.translate("None yet :("), + text: Scratch.translate("None yet :("), + }, + ]; + } + + /** + * @param {{}} args + * @param {VM.BlockUtility} util + */ + socketMessage(args, util) { + return util.thread.socketMessage ?? ""; + } + + socketOpensConnection() { + return; } - - Scratch.extensions.register(new WebsocketsPlusExt()); - })(Scratch); + + socketMessageReceived() { + return; + } + + socketErrored() { + return; + } + + socketClosedConnection() { + return; + } + } + + Scratch.extensions.register(new WebsocketsPlusExt()); +})(Scratch); From 82cae42a2631be629abd111893b6cabe66d9d9bf Mon Sep 17 00:00:00 2001 From: Lemon <165233560+BludIsAnLemon@users.noreply.github.com> Date: Thu, 13 Mar 2025 16:26:31 +0800 Subject: [PATCH 10/10] Changed name to WebSocket V2 --- extensions/Cheddarphanie/websockets-plus.js | 34 ++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/extensions/Cheddarphanie/websockets-plus.js b/extensions/Cheddarphanie/websockets-plus.js index 1eab3c13a7..8a90f5712b 100644 --- a/extensions/Cheddarphanie/websockets-plus.js +++ b/extensions/Cheddarphanie/websockets-plus.js @@ -1,4 +1,4 @@ -// Name: WebSockets+ +// Name: WebSocket V2 // ID: lemonWebSocketsPlus // Description: Connect to more than one WebSockets. // By: Cheddarphanie @@ -10,7 +10,7 @@ "use strict"; if (!Scratch.extensions.unsandboxed) { - throw new Error("The WebSockets+ Extension must run unsandboxed."); + throw new Error("The WebSocket V2 Extension must run unsandboxed."); } const vm = Scratch.vm; @@ -37,7 +37,7 @@ }; }; - class WebsocketsPlusExt { + class WebsocketV2Ext { constructor() { this.debugging = false; @@ -79,7 +79,7 @@ getInfo() { return { id: "lemonWebSocketsPlus", - name: Scratch.translate("WebSockets+"), + name: Scratch.translate("WebSocket V2"), color1: "#307eff", color2: "#2c5eb0", blocks: [ @@ -364,10 +364,10 @@ } } - if (this.debugging) console.groupCollapsed("WebSockets+ Connecting"); + if (this.debugging) console.groupCollapsed("WebSocket V2 Connecting"); if (this.debugging) - console.log(`[WebSockets+] Attempting to connect to '${url}'..`); + console.log(`[WebSockets V2] Attempting to connect to '${url}'..`); if (!this.fetchables[url]) this.fetchables[url] = await Scratch.canFetch(url); @@ -375,7 +375,7 @@ if (!this.fetchables[url]) { this.socketStatuses[id] = "failed to connect"; if (this.debugging) { - console.log(`[Websockets+] Connection to '${url}' denied!`); + console.log(`[Websocket V2] Connection to '${url}' denied!`); console.groupEnd(); } return; @@ -418,7 +418,7 @@ vm.refreshWorkspace(); if (this.debugging) - console.log(`[WebSockets+] Successfully connected to '${url}'.`); + console.log(`[WebSocket V2] Successfully connected to '${url}'.`); } catch (err) { console.error(err); this.socketStatuses[id] = "failed to connect"; @@ -433,11 +433,11 @@ const Reason = Cast.toString(R); if (this.debugging) - console.groupCollapsed("WebSockets+ Closing Connection"); + console.groupCollapsed("WebSocket V2 Closing Connection"); if (this.debugging) console.log( - `[WebSockets+] Attemping to close connection with '${id}'..` + `[WebSocket V2] Attemping to close connection with '${id}'..` ); const socket = this.sockets[id]; @@ -477,11 +477,11 @@ if (this.debugging) console.log( - `[WebSockets+] Successfully closed connection with '${id}'!` + `[WebSocket V2] Successfully closed connection with '${id}'!` ); } else { if (this.debugging) - console.warn(`[WebSockets+] WebSocket '${id}' is not a WebSocket!`); + console.warn(`[WebSocket V2] WebSocket '${id}' is not a WebSocket!`); } if (this.debugging) console.groupEnd(); @@ -491,10 +491,10 @@ sendMessage({ MESSAGE, SOCKET }) { if (this.debugging) { - console.groupCollapsed("WebSockets+ Message Sending"); + console.groupCollapsed("WebSocket V2 Message Sending"); console.log( - `[WebSockets+] Attempting to send a message to '${SOCKET}'..` + `[WebSocket V2] Attempting to send a message to '${SOCKET}'..` ); } SOCKET = Cast.toString(SOCKET); @@ -513,7 +513,7 @@ socket.send(MESSAGE); if (this.debugging) console.log( - `[WebSockets+] Successfully sent a message to '${SOCKET}'!` + `[WebSocket V2] Successfully sent a message to '${SOCKET}'!` ); } catch (err) { console.error(err); @@ -521,7 +521,7 @@ } } else { if (this.debugging) - console.warn(`[WebSockets+] '${SOCKET}' isn't a WebSocket!`); + console.warn(`[WebSocket V2] '${SOCKET}' isn't a WebSocket!`); } if (this.debugging) console.groupEnd(); } @@ -617,5 +617,5 @@ } } - Scratch.extensions.register(new WebsocketsPlusExt()); + Scratch.extensions.register(new WebsocketV2Ext()); })(Scratch);