// ==UserScript==
// @name Comchat-Mods
// @namespace http://tampermonkey.net/
// @version 2026-03-28
// @description Enhance Comchat functionality
// @author Anonymous
// @match %%HOST%%
// @icon https://www.google.com/s2/favicons?sz=64&domain=%%ICON_DOMAIN%%
// @grant none
// ==/UserScript==
// @ts-check
(function () {
'use strict';
console.log('Comchat-Mods loaded');
// A little type-checking deception below
const frames = document.querySelector('frame' + 'set');
const formFrame = /** @type {HTMLIFrameElement} */ (document.querySelector('frame[name="form"]'));
const logFrame = /** @type {HTMLIFrameElement} */ (document.querySelector('frame[name="log"]'));
let formDoc;
let form;
let formSrc;
if (!frames || !formFrame || !logFrame)
return;
frames.setAttribute('rows', '0,*');
frames.setAttribute('frameborder', '0');
frames.setAttribute('border', '0');
frames.setAttribute('framespacing', '0');
logFrame.src = '%%ENHANCED_CHAT_URL%%';
const hiddenFrame = document.createElement('iframe');
hiddenFrame.name = 'hidden_frame';
hiddenFrame.style.display = 'none';
document.body.appendChild(hiddenFrame);
function extractError(body) {
if (!body)
return 'Page not loaded';
const html = body.innerHTML;
if (html && !html.includes('<(form|div)')) // No form or div? Might be error page.
return (/
([3-5]\d\d\b.+)<\/h2>/.exec(html) || [])[1];
return null;
}
function documentCheck(frame, action, selector, tries = 0) {
const doc = frame.contentDocument;
const formError = extractError(doc?.body);
if (formError && formError !== 'Page not loaded')
logFrame.contentWindow.postMessage([action, formError], '*');
else if (doc.querySelector(selector))
logFrame.contentWindow.postMessage([action, null], '*');
else if (++tries < 50)
setTimeout(() => documentCheck(frame, action, selector, ++tries), 100);
else
logFrame.contentWindow.postMessage([action, 'Timed out'], '*');
}
function enterChatRoom(name, email, color) {
const nameField = formDoc.querySelector('input[name="name"]');
const emailField = formDoc.querySelector('input[name="email"]');
if (!nameField || !emailField) { // Already in chat?
logFrame.contentWindow.postMessage(['enterChatRoom', null], '*');
return;
}
if (!formSrc)
formSrc = formFrame.src;
let tripCode;
[name, tripCode] = name.split('#');
if (tripCode)
localStorage.setItem('password', tripCode);
const colorButton = formDoc.querySelector(`input[type="radio"][value="${color}"]`);
const submitButton = formDoc.querySelector('input[type="submit"]');
form.setAttribute('target', '_self');
nameField.value = name;
emailField.value = email || '';
colorButton?.click();
submitButton.click();
documentCheck(formFrame, 'enterChatRoom', 'input[name="comment"]');
}
function leaveChatRoom() {
const leaveButton = formDoc.querySelector('input[type="button"][value="Leave room"]') ||
formDoc.querySelector('input[type="button"]');
if (leaveButton) {
form.setAttribute('target', '_self');
leaveButton.click();
}
formFrame.src = formSrc;
documentCheck(formFrame, 'leaveChatRoom', 'input[name="name"]');
}
function sendChatMessage(comment, color, tripCode) {
let face = '';
const $ = /^(.*)(\u2000(.+)\u2000)\s*$/.exec(comment);
if ($) {
comment = $[1];
face = $[3];
}
formDoc.querySelector('select[name="color"]').value = color;
formDoc.querySelector('#face').value = face;
formDoc.querySelector('input[name="comment"]').value = comment;
formDoc.querySelector('input[name="password"]').value = tripCode;
formDoc.querySelector('form').setAttribute('target', 'hidden_frame');
hiddenFrame.contentDocument.body.innerHTML = '';
form.submit();
documentCheck(hiddenFrame, 'sendChatMessage', 'div[class="messageRow"]');
}
window.addEventListener('message', evt => {
formDoc = formFrame.contentDocument;
form = formDoc.querySelector('form');
switch (evt.data[0]) {
case 'enterChatRoom':
enterChatRoom(evt.data[1], evt.data[2], evt.data[3]);
break;
case 'leaveChatRoom':
leaveChatRoom();
break;
case 'sendChatMessage':
sendChatMessage(evt.data[1], evt.data[2], evt.data[3]);
break;
}
});
})();