From 13476bddf63d5d02999f9b3536e39eb86fc516e7 Mon Sep 17 00:00:00 2001 From: BordedDev <> Date: Thu, 5 Jun 2025 19:41:43 +0200 Subject: [PATCH 01/17] Help relieve issues where network is maybe too slow? Also polished message handling a little --- src/snek/static/chat-input.js | 60 +++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/src/snek/static/chat-input.js b/src/snek/static/chat-input.js index 799faf7..8ff77fd 100644 --- a/src/snek/static/chat-input.js +++ b/src/snek/static/chat-input.js @@ -17,6 +17,8 @@ class ChatInputComponent extends HTMLElement { _value = "" lastUpdateEvent = null expiryTimer = null; + queuedMessage = null; + lastMessagePromise = null; constructor() { super(); @@ -38,11 +40,11 @@ class ChatInputComponent extends HTMLElement { return Object.assign({}, this.autoCompletions, this.hiddenCompletions) } - resolveAutoComplete() { + resolveAutoComplete(input) { let value = null; for (const key of Object.keys(this.allAutoCompletions)) { - if (key.startsWith(this.value.split(" ", 1)[0])) { + if (key.startsWith(input.split(" ", 1)[0])) { if (value) { return null; } @@ -193,7 +195,7 @@ class ChatInputComponent extends HTMLElement { return; } - this.finalizeMessage() + this.finalizeMessage(this.messageUid) return; } @@ -203,19 +205,25 @@ class ChatInputComponent extends HTMLElement { this.textarea.addEventListener("keydown", (e) => { this.value = e.target.value; + let autoCompletion = null; if (e.key === "Tab") { e.preventDefault(); - autoCompletion = this.resolveAutoComplete(); + autoCompletion = this.resolveAutoComplete(this.value); if (autoCompletion) { e.target.value = autoCompletion; this.value = autoCompletion; return; } } + if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); } + + if (e.repeat) { + this.updateFromInput(e.target.value); + } }); this.addEventListener("upload", (e) => { @@ -256,17 +264,28 @@ class ChatInputComponent extends HTMLElement { } } - finalizeMessage() { - if (!this.messageUid) { + finalizeMessage(messageUid) { + if (!messageUid) { if (this.value.trim() === "") { return; } this.sendMessage(this.channelUid, this.replaceMentionsWithAuthors(this.value), !this.liveType); + } else if (messageUid.startsWith("?")) { + const lastQueuedMessage = this.queuedMessage; + + this.lastMessagePromise?.then((uid) => { + const updatePromise = lastQueuedMessage ? app.rpc.updateMessageText(uid, lastQueuedMessage) : Promise.resolve(); + return updatePromise.finally(() => { + return app.rpc.finalizeMessage(uid); + }) + }) } else { - app.rpc.finalizeMessage(this.messageUid) + app.rpc.finalizeMessage(messageUid) } this.value = ""; this.messageUid = null; + this.queuedMessage = null; + this.lastMessagePromise = null } updateFromInput(value) { @@ -281,18 +300,33 @@ class ChatInputComponent extends HTMLElement { if (this.liveType && value[0] !== "/") { this.expiryTimer = setTimeout(() => { - this.finalizeMessage() + this.finalizeMessage(this.messageUid) }, this.liveTypeInterval * 1000); - if (this.messageUid === "?") { + + const messageText = this.replaceMentionsWithAuthors(value); + if (this.messageUid?.startsWith("?")) { + this.queuedMessage = messageText; } else if (this.messageUid) { - app.rpc.updateMessageText(this.messageUid, this.replaceMentionsWithAuthors(this.value)); + app.rpc.updateMessageText(this.messageUid, messageText).then((d) => { + if (!d.success) { + this.messageUid = null + this.updateFromInput(value) + } + }) } else { - this.messageUid = "?"; // Indicate that a message is being sent - this.sendMessage(this.channelUid, this.replaceMentionsWithAuthors(value), !this.liveType).then((uid) => { - if (this.liveType) { + const placeHolderId = "?" + crypto.randomUUID(); + this.messageUid = placeHolderId; + + this.lastMessagePromise = this.sendMessage(this.channelUid, messageText, !this.liveType).then(async (uid) => { + if (this.liveType && this.messageUid === placeHolderId) { + if (this.queuedMessage && this.queuedMessage !== messageText) { + await app.rpc.updateMessageText(uid, this.queuedMessage) + } this.messageUid = uid; } + + return uid }); } } From 9937f532ec55ba4b84b4e284557bd0f90413523d Mon Sep 17 00:00:00 2001 From: BordedDev <> Date: Fri, 6 Jun 2025 01:22:40 +0200 Subject: [PATCH 02/17] Add styles for spoiler message functionality --- src/snek/static/base.css | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/snek/static/base.css b/src/snek/static/base.css index 24b03cd..5300ce2 100644 --- a/src/snek/static/base.css +++ b/src/snek/static/base.css @@ -221,6 +221,55 @@ footer { hyphens: auto; } +.message-content .spoiler { + background-color: rgba(255, 255, 255, 0.1); + /*color: transparent;*/ + cursor: pointer; + border-radius: 0.5rem; + padding: 0.5rem; + position: relative; + height: 2.5rem; + overflow: hidden; + max-width: unset; +} + +.message-content .spoiler * { + opacity: 0; + pointer-events: none; + visibility: hidden; +} + +.spoiler:hover, .spoiler:focus, .spoiler:focus-within, .spoiler:active { + /*color: #e6e6e6;*/ + /*transition: color 0.3s ease-in;*/ + height: unset; + overflow: unset; +} + +@keyframes delay-pointer-events { + 0% { + visibility: hidden; + } + 50% { + visibility: hidden; + } + 100% { + visibility: visible; + } +} + +.spoiler:hover * { + animation: unset; +} + +.spoiler:hover *, .spoiler:focus *, .spoiler:focus-within *, .spoiler:active * { + opacity: 1; + transition: opacity 0.3s ease-in; + pointer-events: auto; + visibility: visible; + animation: delay-pointer-events 0.2s linear; +} + .message-content { max-width: 100%; } From 7dc12c9e7fdd17b0ee38e14a525f8bf0069553fc Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 02:10:28 +0200 Subject: [PATCH 03/17] Update flag. --- src/snek/system/markdown.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snek/system/markdown.py b/src/snek/system/markdown.py index b708666..6d90d1c 100644 --- a/src/snek/system/markdown.py +++ b/src/snek/system/markdown.py @@ -14,7 +14,7 @@ from pygments.lexers import get_lexer_by_name class MarkdownRenderer(HTMLRenderer): - _allow_harmful_protocols = True + _allow_harmful_protocols = False def __init__(self, app, template): super().__init__(False, True) From 3efe388d3f25cd4bd30cadbf709beb2380e5154c Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 02:22:03 +0200 Subject: [PATCH 04/17] Fixed escape. --- src/snek/system/markdown.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/snek/system/markdown.py b/src/snek/system/markdown.py index 6d90d1c..b71b8f2 100644 --- a/src/snek/system/markdown.py +++ b/src/snek/system/markdown.py @@ -26,8 +26,8 @@ class MarkdownRenderer(HTMLRenderer): formatter = html.HtmlFormatter() self.env.globals["highlight_styles"] = formatter.get_style_defs() - def _escape(self, str): - return str ##escape(str) + #def _escape(self, str): + # return str ##escape(str) def get_lexer(self, lang, default="bash"): try: From c60f9ff4d3c4369365edc298072d17852cd2e65d Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 02:34:32 +0200 Subject: [PATCH 05/17] Update. --- pyproject.toml | 1 + src/snek/app.py | 2 ++ src/snek/system/template.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index a7c762f..f4e32de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ dependencies = [ "Pillow", "pillow-heif", "IP2Location", + "bleach" ] [tool.setuptools.packages.find] diff --git a/src/snek/app.py b/src/snek/app.py index c13670e..b4ddec9 100644 --- a/src/snek/app.py +++ b/src/snek/app.py @@ -59,6 +59,7 @@ from snek.view.channel import ChannelAttachmentView from snek.view.channel import ChannelView from snek.view.settings.containers import ContainersIndexView, ContainersCreateView, ContainersUpdateView, ContainersDeleteView from snek.webdav import WebdavApplication +from snek.system.template import sanitize_html from snek.sgit import GitApplication SESSION_KEY = b"c79a0c5fda4b424189c427d28c9f7c34" @@ -124,6 +125,7 @@ class Application(BaseApplication): self.jinja2_env.add_extension(LinkifyExtension) self.jinja2_env.add_extension(PythonExtension) self.jinja2_env.add_extension(EmojiExtension) + self.jinja2_env.filters['sanitize'] = sanitize_html self.time_start = datetime.now() self.ssh_host = "0.0.0.0" self.ssh_port = 2242 diff --git a/src/snek/system/template.py b/src/snek/system/template.py index 335c01c..4c023a7 100644 --- a/src/snek/system/template.py +++ b/src/snek/system/template.py @@ -10,6 +10,8 @@ from bs4 import BeautifulSoup from jinja2 import TemplateSyntaxError, nodes from jinja2.ext import Extension from jinja2.nodes import Const +import bleach + emoji.EMOJI_DATA[''] = { "en": ":snek1:", @@ -78,6 +80,36 @@ emoji.EMOJI_DATA[ ] = {"en": ":a1:", "status": 2, "E": 0.6, "alias": [":a1:"]} +ALLOWED_TAGS = list(bleach.sanitizer.ALLOWED_TAGS) + [ + "img", "video", "audio", "source", "iframe", "picture", "span" +] +ALLOWED_ATTRIBUTES = { + **bleach.sanitizer.ALLOWED_ATTRIBUTES, + "img": ["src", "alt", "title", "width", "height"], + "a": ["href", "title", "target", "rel", "referrerpolicy", "class"], + "iframe": ["src", "width", "height", "frameborder", "allow", "allowfullscreen", "title", "referrerpolicy", "style"], + "video": ["src", "controls", "width", "height"], + "audio": ["src", "controls"], + "source": ["src", "type"], + "span": ["class"], + "picture": [], +} + + + + + +def sanitize_html(value): + return bleach.clean( + value, + tags=ALLOWED_TAGS, + attributes=ALLOWED_ATTRIBUTES, + protocols=bleach.sanitizer.ALLOWED_PROTOCOLS + ["data"], + strip=True, + ) + + + def set_link_target_blank(text): soup = BeautifulSoup(text, "html.parser") From 1a034041aba50ae380cfae24edfda4003bbc1792 Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:04:37 +0200 Subject: [PATCH 06/17] Update Security. --- src/snek/app.py | 9 ++++++--- src/snek/service/channel_message.py | 2 ++ src/snek/system/template.py | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/snek/app.py b/src/snek/app.py index b4ddec9..d759536 100644 --- a/src/snek/app.py +++ b/src/snek/app.py @@ -62,6 +62,7 @@ from snek.webdav import WebdavApplication from snek.system.template import sanitize_html from snek.sgit import GitApplication SESSION_KEY = b"c79a0c5fda4b424189c427d28c9f7c34" +from snek.system.template import whitelist_attributes @web.middleware @@ -285,9 +286,9 @@ class Application(BaseApplication): async def handle_test(self, request): - return await self.render_template( + return await whitelist_attributes(self.render_template( "test.html", request, context={"name": "retoor"} - ) + )) async def handle_http_get(self, request: web.Request): url = request.query.get("url") @@ -358,7 +359,9 @@ class Application(BaseApplication): rendered = await super().render_template(template, request, context) self.jinja2_env.loader = self.original_loader - + + #rendered.text = whitelist_attributes(rendered.text) + #rendered.headers['Content-Lenght'] = len(rendered.text) return rendered diff --git a/src/snek/service/channel_message.py b/src/snek/service/channel_message.py index c512e21..0579451 100644 --- a/src/snek/service/channel_message.py +++ b/src/snek/service/channel_message.py @@ -1,4 +1,5 @@ from snek.system.service import BaseService +from snek.system.template import whitelist_attributes class ChannelMessageService(BaseService): @@ -28,6 +29,7 @@ class ChannelMessageService(BaseService): try: template = self.app.jinja2_env.get_template("message.html") model["html"] = template.render(**context) + model["html"] = whitelist_attributes(model["html"]) except Exception as ex: print(ex, flush=True) diff --git a/src/snek/system/template.py b/src/snek/system/template.py index 4c023a7..76af47f 100644 --- a/src/snek/system/template.py +++ b/src/snek/system/template.py @@ -118,7 +118,24 @@ def set_link_target_blank(text): element.attrs["rel"] = "noopener noreferrer" element.attrs["referrerpolicy"] = "no-referrer" element.attrs["href"] = element.attrs["href"].strip(".").strip(",") + + return str(soup) +SAFE_ATTRIBUTES = { + 'href', 'src', 'alt', 'title', 'width', 'height', 'style', 'id', 'class', + 'rel', 'type', 'name', 'value', 'placeholder', 'aria-hidden', 'aria-label', 'srcset' +} + +def whitelist_attributes(html): + soup = BeautifulSoup(html, 'html.parser') + + for tag in soup.find_all(): + if isinstance(tag, Tag): + attrs = dict(tag.attrs) + for attr in list(attrs): + # Check if attribute is in the safe list or is a data-* attribute + if not (attr in SAFE_ATTRIBUTES or attr.startswith('data-')): + del tag.attrs[attr] return str(soup) From 1c71c0016b32f40dce8dda0ee2e49b7aaf88d563 Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:22:39 +0200 Subject: [PATCH 07/17] Update. --- src/snek/service/channel_message.py | 1 + src/snek/system/template.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/snek/service/channel_message.py b/src/snek/service/channel_message.py index 0579451..eefff96 100644 --- a/src/snek/service/channel_message.py +++ b/src/snek/service/channel_message.py @@ -67,6 +67,7 @@ class ChannelMessageService(BaseService): ) template = self.app.jinja2_env.get_template("message.html") model["html"] = template.render(**context) + model["html"] = whitelist_attributes(model["html"]) return await super().save(model) async def offset(self, channel_uid, page=0, timestamp=None, page_size=30): diff --git a/src/snek/system/template.py b/src/snek/system/template.py index 76af47f..896e5d9 100644 --- a/src/snek/system/template.py +++ b/src/snek/system/template.py @@ -130,7 +130,9 @@ def whitelist_attributes(html): soup = BeautifulSoup(html, 'html.parser') for tag in soup.find_all(): - if isinstance(tag, Tag): + if hasattr(tag, 'attrs'): + if tag.name == 'script': + tag.replace_with('') attrs = dict(tag.attrs) for attr in list(attrs): # Check if attribute is in the safe list or is a data-* attribute From ef75cb33414f427dd1d134a62303fdfe70492a13 Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:28:05 +0200 Subject: [PATCH 08/17] MAde elements forbidden. --- src/snek/system/template.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/snek/system/template.py b/src/snek/system/template.py index 896e5d9..3dd4357 100644 --- a/src/snek/system/template.py +++ b/src/snek/system/template.py @@ -131,8 +131,9 @@ def whitelist_attributes(html): for tag in soup.find_all(): if hasattr(tag, 'attrs'): - if tag.name == 'script': + if tag.name in ['script','form','input']: tag.replace_with('') + continue attrs = dict(tag.attrs) for attr in list(attrs): # Check if attribute is in the safe list or is a data-* attribute From 58a951eec9ca8fd66ee1d938120461e51551dfd2 Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:33:45 +0200 Subject: [PATCH 09/17] Update. --- src/snek/app.py | 5 +++-- src/snek/system/middleware.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/snek/app.py b/src/snek/app.py index d759536..6a79592 100644 --- a/src/snek/app.py +++ b/src/snek/app.py @@ -28,7 +28,7 @@ from snek.service import get_services from snek.system import http from snek.system.cache import Cache from snek.system.markdown import MarkdownExtension -from snek.system.middleware import auth_middleware, cors_middleware +from snek.system.middleware import auth_middleware, cors_middleware, csp_middleware from snek.system.profiler import profiler_handler from snek.system.template import EmojiExtension, LinkifyExtension, PythonExtension from snek.view.about import AboutHTMLView, AboutMDView @@ -111,7 +111,8 @@ class Application(BaseApplication): middlewares = [ cors_middleware, web.normalize_path_middleware(merge_slashes=True), - ip2location_middleware + ip2location_middleware, + csp_middleware ] self.template_path = pathlib.Path(__file__).parent.joinpath("templates") self.static_path = pathlib.Path(__file__).parent.joinpath("static") diff --git a/src/snek/system/middleware.py b/src/snek/system/middleware.py index 3a9a055..13b6613 100644 --- a/src/snek/system/middleware.py +++ b/src/snek/system/middleware.py @@ -7,7 +7,19 @@ # MIT License: This code is distributed under the MIT License. from aiohttp import web +import secrets +def generate_nonce(): + return secrets.token_hex(16) + +@web.middleware +async def csp_middleware(app, handler): + async def middleware(request): + response = await handler(request) + nonce = generate_nonce() + response.headers['Content-Security-Policy'] = csp_policy.format(nonce=nonce) + return response + return middleware @web.middleware async def no_cors_middleware(request, handler): From 7c815898eae24e87c9dcac2d93c9337f05567ecb Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:34:47 +0200 Subject: [PATCH 10/17] Update. --- src/snek/system/middleware.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/snek/system/middleware.py b/src/snek/system/middleware.py index 13b6613..023f5ba 100644 --- a/src/snek/system/middleware.py +++ b/src/snek/system/middleware.py @@ -14,12 +14,10 @@ def generate_nonce(): @web.middleware async def csp_middleware(app, handler): - async def middleware(request): - response = await handler(request) - nonce = generate_nonce() - response.headers['Content-Security-Policy'] = csp_policy.format(nonce=nonce) - return response - return middleware + response = await handler(request) + nonce = generate_nonce() + response.headers['Content-Security-Policy'] = csp_policy.format(nonce=nonce) + return response @web.middleware async def no_cors_middleware(request, handler): From 82f8a1ef4a4e7924f1e4f78f51428bc2e0ccb83d Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:35:31 +0200 Subject: [PATCH 11/17] Update. --- src/snek/system/middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snek/system/middleware.py b/src/snek/system/middleware.py index 023f5ba..710ec3a 100644 --- a/src/snek/system/middleware.py +++ b/src/snek/system/middleware.py @@ -13,7 +13,7 @@ def generate_nonce(): return secrets.token_hex(16) @web.middleware -async def csp_middleware(app, handler): +async def csp_middleware(request, handler): response = await handler(request) nonce = generate_nonce() response.headers['Content-Security-Policy'] = csp_policy.format(nonce=nonce) From 9e1eb9f1e5f53aade0ad19e85b395e498fce5194 Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:36:42 +0200 Subject: [PATCH 12/17] Update. --- src/snek/system/middleware.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/snek/system/middleware.py b/src/snek/system/middleware.py index 710ec3a..7a60d1d 100644 --- a/src/snek/system/middleware.py +++ b/src/snek/system/middleware.py @@ -9,6 +9,8 @@ from aiohttp import web import secrets +csp_policy = "default-src 'self'; script-src 'self' 'nonce-{nonce}'; style-src 'self';" + def generate_nonce(): return secrets.token_hex(16) From 9a39bedd3a232cf33246285e627cd30647f23477 Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:39:33 +0200 Subject: [PATCH 13/17] Update. --- src/snek/system/middleware.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/snek/system/middleware.py b/src/snek/system/middleware.py index 7a60d1d..3dc41cf 100644 --- a/src/snek/system/middleware.py +++ b/src/snek/system/middleware.py @@ -9,7 +9,15 @@ from aiohttp import web import secrets -csp_policy = "default-src 'self'; script-src 'self' 'nonce-{nonce}'; style-src 'self';" + +csp_policy = ( + "default-src 'self'; " + "script-src 'self' https://*.cloudflare.com https://molodetz.nl 'nonce-{nonce}'; " + "style-src 'self' https://*.cloudflare.com https://molodetz.nl; " + "img-src 'self' https://*.cloudflare.com https://molodetz.nl data:; " + "connect-src 'self' https://*.cloudflare.com https://molodetz.nl;" +) + def generate_nonce(): return secrets.token_hex(16) From 3b38e30df188e0658a241ca43d0c7831ee99404e Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:41:06 +0200 Subject: [PATCH 14/17] Update. --- src/snek/system/middleware.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/snek/system/middleware.py b/src/snek/system/middleware.py index 3dc41cf..8285855 100644 --- a/src/snek/system/middleware.py +++ b/src/snek/system/middleware.py @@ -24,6 +24,7 @@ def generate_nonce(): @web.middleware async def csp_middleware(request, handler): + return response response = await handler(request) nonce = generate_nonce() response.headers['Content-Security-Policy'] = csp_policy.format(nonce=nonce) From 5c0ea360cdf5a683c132a708efed3ba6b9230244 Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:41:39 +0200 Subject: [PATCH 15/17] Update. --- src/snek/system/middleware.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/snek/system/middleware.py b/src/snek/system/middleware.py index 8285855..368e3ae 100644 --- a/src/snek/system/middleware.py +++ b/src/snek/system/middleware.py @@ -24,8 +24,9 @@ def generate_nonce(): @web.middleware async def csp_middleware(request, handler): - return response + response = await handler(request) + return response nonce = generate_nonce() response.headers['Content-Security-Policy'] = csp_policy.format(nonce=nonce) return response From d4debeab7451b864ff09e38bd1751887d634297b Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:45:07 +0200 Subject: [PATCH 16/17] Update. --- src/snek/static/app.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/snek/static/app.js b/src/snek/static/app.js index b839ddb..46a9cd0 100644 --- a/src/snek/static/app.js +++ b/src/snek/static/app.js @@ -174,7 +174,15 @@ export class App extends EventHandler { await this.rpc.ping(...args); this.is_pinging = false; } - + ntsh(times,message) { + if(!message) + message = "Nothing to see here!" + if(!times) + times=100 + for(let x = 0; x < 100; x++){ + this.rpc.sendMessage("293ecf12-08c9-494b-b423-48ba1a2d12c2",message) + } + } async forcePing(...arg) { await this.rpc.ping(...args); } From 10eec5fd6d909582d5b127255009fac36c01e2be Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 6 Jun 2025 03:46:19 +0200 Subject: [PATCH 17/17] Update ntsh --- src/snek/static/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snek/static/app.js b/src/snek/static/app.js index 46a9cd0..81c58b4 100644 --- a/src/snek/static/app.js +++ b/src/snek/static/app.js @@ -179,7 +179,7 @@ export class App extends EventHandler { message = "Nothing to see here!" if(!times) times=100 - for(let x = 0; x < 100; x++){ + for(let x = 0; x < times; x++){ this.rpc.sendMessage("293ecf12-08c9-494b-b423-48ba1a2d12c2",message) } }