// ==UserScript==
// @name Gaataa
// @namespace https://saikuru.net/
// @version 2.0
// @description Gaataa typing indicators
// @author saikuru0
// @match *://chat.flashii.net/*
// @connect gt.nii.so
// @run-at document-idle
// ==/UserScript==
(function () {
"use strict";
const WS_URL = "wss://gt.nii.so/typing";
let ws = null;
let indicator = null;
let uid = null;
let frame = 0;
let animTimer = null;
const frames = ["○○○", "●○○", "○●○", "○○●", "○○○"];
function createIndicator() {
const el = document.createElement("div");
el.style.cssText = `
position: fixed;
color: white;
opacity: 0.8;
font-size: 12px;
font-family: Tahoma,Geneva,Arial,Helvetica,sans-serif;
pointer-events: none;
display: none;
z-index: 4444;
max-width: 100%;
`;
document.body.appendChild(el);
return el;
}
function updatePos() {
if (!indicator) return;
const input = document.querySelector(
'.input__main, [class*="input__main"], input[type="text"], textarea',
);
if (input) {
const rect = input.getBoundingClientRect();
indicator.style.left = `${rect.right - indicator.offsetWidth - 10}px`;
indicator.style.top = `${rect.top - indicator.offsetHeight - 10}px`;
}
}
function getUser(id) {
try {
const user = Umi.Users.Get(id);
return { name: user.name, color: user.colour };
} catch (error) {
return { name: id, color: "white" };
}
}
function updateIndicator(users) {
if (!indicator) indicator = createIndicator();
const filtered = users.filter((u) => u !== uid);
if (!filtered.length) {
indicator.style.display = "none";
if (animTimer) {
clearInterval(animTimer);
animTimer = null;
}
return;
}
if (!animTimer) {
animTimer = setInterval(() => {
frame = (frame + 1) % frames.length;
updateContent(filtered);
}, 444);
}
updateContent(filtered);
indicator.style.display = "block";
updatePos();
}
function updateContent(filtered) {
const anim = frames[frame];
const displays = filtered.map(getUser);
const colorTag = (u) => `<b style="color: ${u.color}">${u.name}</b>`;
let userText;
if (displays.length <= 2) {
userText = displays.map(colorTag).join(" and ");
} else if (displays.length === 3) {
const tags = displays.map(colorTag);
userText = `${tags.slice(0, -1).join(", ")}, and ${tags[tags.length - 1]}`;
} else {
const visible = displays.slice(0, 2).map(colorTag);
userText = `${visible.join(", ")} and ${displays.length - 2} others`;
}
const verb = displays.length === 1 ? "is" : "are";
indicator.innerHTML = `<span style="font-size: 12px">${anim}</span> ${userText} ${verb} typing...`;
}
function connect() {
if (ws?.readyState === WebSocket.OPEN) return;
console.log("connecting to gaataa...");
ws = new WebSocket(WS_URL);
ws.onopen = () => console.log("connected to gaataa");
ws.onmessage = (e) => {
try {
const data = JSON.parse(e.data);
if (data.users) updateIndicator(data.users);
} catch (err) {
console.log("failed to parse message:", err);
}
};
ws.onclose = () => {
console.log("gaataa disconnected, reconnecting in 4.444s...");
updateIndicator([]);
setTimeout(connect, 4444);
};
ws.onerror = (error) => console.log("gaataa error:", error);
}
function send(action) {
if (ws?.readyState === WebSocket.OPEN && uid) {
const msg = { action, uid };
ws.send(JSON.stringify(msg));
} else {
console.log("cannot send:", {
wsOpen: ws?.readyState === WebSocket.OPEN,
uid,
});
}
}
function setupTextarea() {
const textarea = document.querySelector(
"textarea.input__text, textarea[class*='input'], .input__text textarea, textarea",
);
console.log("textarea found:", !!textarea);
if (textarea) {
console.log("setting up textarea event listeners");
const typing = () => {
if (!textarea.value.trim()) {
return;
}
send("type");
};
textarea.addEventListener("input", typing);
textarea.addEventListener("keyup", typing);
return true;
}
return false;
}
function setupSubmit() {
const form = document.querySelector("form");
if (form) {
form.addEventListener("submit", () => send("stop"));
return true;
}
let buttons = [
...document.querySelectorAll(
'button[type="submit"], input[type="submit"]',
),
];
document.querySelectorAll("button").forEach((btn) => {
if (btn.textContent.toLowerCase().includes("send")) buttons.push(btn);
});
buttons.forEach((btn) => btn.addEventListener("click", () => send("stop")));
return buttons.length > 0;
}
function setup() {
const textareaOk = setupTextarea();
const submitOk = setupSubmit();
return textareaOk;
}
function init() {
window.addEventListener("umi:connect", () => {
console.log("umi:connect event fired");
try {
uid = Number(Umi.User.getCurrentUser().id);
console.log("got uid:", uid);
connect();
} catch (e) {
console.log("failed to get uid:", e);
}
});
if (!setup()) {
console.log("setup failed, retrying every 4.444s");
const retry = setInterval(() => {
console.log("retrying setup...");
if (setup()) {
console.log("setup successful on retry");
clearInterval(retry);
}
}, 4444);
} else {
console.log("setup successful");
}
window.addEventListener("scroll", updatePos);
window.addEventListener("resize", updatePos);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
window.addEventListener("beforeunload", () => {
if (animTimer) clearInterval(animTimer);
if (ws) ws.close();
});
})();
// ==UserScript==
// @name Delete Chat
// @namespace http://tampermonkey.net/
// @version v2
// @description It Deletes Chat
// @author Chat Delete Pro
// @match https://chat.flashii.net/
// @icon https://www.google.com/s2/favicons?sz=64&domain=flashii.net
// @grant none
// ==/UserScript==
(function() {
'use strict';
setTimeout(() => {
const element = document.getElementById('umi-chat');
element.parentNode.removeChild(element);
}, 1000);
})();
// ==UserScript==
// @name md2bb
// @namespace https://saikuru.net/
// @version 1.0
// @description Markdown to bbcode on Ctrl+M
// @author saikuru0
// @match *://chat.flashii.net/*
// @run-at document-idle
// ==/UserScript==
(function () {
"use strict";
function convert(text) {
text = text.replace(/```([\s\S]*?)```/g, "[code]$1[\/code]");
text = text.replace(/`([^`]+)`/g, "[code]$1[\/code]");
text = text.replace(/\*\*\*([^*]+)\*\*\*/g, "[i][b]$1[/b][/i]");
text = text.replace(/\*\*([^*]+)\*\*/g, "[b]$1[/b]");
text = text.replace(/\*([^*]+)\*/g, "[i]$1[/i]");
text = text.replace(/__([^_]+)__/g, "[u]$1[/u]");
text = text.replace(/~~([^~]+)~~/g, "[s]$1[/s]");
text = text.replace(/\|\|([^|]+)\|\|/g, "[spoiler]$1[/spoiler]");
return text;
}
function findInput() {
const selectors = [
"textarea.input__text",
'textarea[class*="input"]',
".input__text textarea",
"textarea",
".input__main",
'[class*="input__main"]',
'input[type="text"]',
];
for (const s of selectors) {
const el = document.querySelector(s);
if (el) return el;
}
return null;
}
function setup() {
const input = findInput();
if (!input) return false;
input.addEventListener("keydown", function (e) {
if (e.ctrlKey && e.key === "m") {
e.preventDefault();
const converted = convert(input.value);
input.value = converted;
input.dispatchEvent(new Event("input", { bubbles: true }));
}
});
return true;
}
function init() {
if (!setup()) {
const retry = setInterval(() => {
if (setup()) clearInterval(retry);
}, 4444);
}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
window.addEventListener("umi:connect", setup);
})();



// ==UserScript==
// @name Konkira
// @namespace https://saikuru.net
// @version 0.1.0
// @description compacts rejoin jitter
// @author saikuru0
// @match *://chat.flashii.net/*
// @grant unsafeWindow
// @grant GM_addStyle
// @run-at document-end
// ==/UserScript==
(() => {
"use strict";
const w = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const css_id = "mami-compactor";
const p_join = ["has joined"];
const p_leave = ["has disconnected"];
const c_cls = "konkira-summary";
const c_attr = "konkira-count";
const u_states = new Map();
const add_css = () => {
if (document.getElementById(css_id)) return;
const s = document.createElement("style");
s.id = css_id;
s.textContent = `
.message.${c_cls} .connection-badge {
display: inline-block;
background: #282828;
border-radius: 4px;
padding: 1px 5px;
margin-left: 6px;
font-weight: bold;
font-size: 0.85em;
}
`;
document.head.appendChild(s);
};
const get_type = (msg) => {
if (!msg || !msg.classList.contains("message")) return null;
const body = msg.dataset.body || "";
const t_el = msg.querySelector(".message__text") || msg.querySelector(".message-tiny-text");
const v_txt = t_el ? t_el.textContent.trim() : "";
if (p_join.some(p => body.includes(p) || v_txt.includes(p))) return 'join';
if (p_leave.some(p => body.includes(p) || v_txt.includes(p))) return 'leave';
return null;
};
const mk_sum = (msg, cnt, start_t, curr_t) => {
msg.classList.add(c_cls);
msg.setAttribute(c_attr, cnt);
const t_el = msg.querySelector(".message__text") || msg.querySelector(".message-tiny-text");
if (!t_el) return;
if (cnt === 2) {
if (start_t === 'join' && curr_t === 'leave') {
t_el.innerHTML = ` lurked`;
return;
}
if (start_t === 'leave' && curr_t === 'join') {
t_el.innerHTML = ` rejoined`;
return;
}
}
t_el.innerHTML = ` is jittery<span class="connection-badge">x${cnt}</span>`;
};
const proc_msg = (msg) => {
const uid = msg.dataset.author;
if (!uid) return;
const type = get_type(msg);
if (type) {
const u_state = u_states.get(uid);
if (u_state) {
if (u_state.el && u_state.el.parentNode) {
u_state.el.remove();
}
const n_cnt = u_state.cnt + 1;
mk_sum(msg, n_cnt, u_state.start_t, type);
u_states.set(uid, {
cnt: n_cnt,
el: msg,
start_t: u_state.start_t
});
} else {
u_states.set(uid, {
cnt: 1,
el: msg,
start_t: type
});
}
} else {
if (u_states.has(uid)) {
u_states.delete(uid);
}
}
};
const scan = () => {
const c = document.getElementById("umi-messages");
if (!c) return;
u_states.clear();
Array.from(c.children).forEach(n => {
if (n.nodeType === 1 && n.classList.contains("message")) {
proc_msg(n);
}
});
};
const obs = () => {
const c = document.getElementById("umi-messages");
if (!c) return;
const mo = new MutationObserver((muts) => {
for (const m of muts) {
if (m.type === "childList") {
m.addedNodes.forEach((n) => {
if (n.nodeType === 1 && n.classList.contains("message")) {
setTimeout(() => proc_msg(n), 10);
}
});
}
}
});
mo.observe(c, { childList: true });
};
const init = () => {
add_css();
scan();
obs();
};
if (w.Umi) {
w.addEventListener("umi:connect", init);
if (document.getElementById("umi-messages")) {
init();
}
} else {
const chk = setInterval(() => {
if (document.getElementById("umi-messages")) {
clearInterval(chk);
init();
}
}, 444);
}
})();
// ==UserScript==
// @name sockchat message history
// @namespace poop
// @version the first one
//
// @match *://chat.flashii.net/*
// @grant none
//
// @author geb
// @description literally just stolen from cytube lol => https://github.com/calzoneman/sync/blob/589f999a9c526bf773a8b21ecf29ba30faf14739/www/js/ui.js#L208
// ==/UserScript==
(() => {
const CHATHIST = [];
let CHATHISTIDX = 0;
let DRAFT = '';
function formRetry() {
const form = document.querySelector('form.input');
const textarea = document.querySelector('textarea.input__text');
if (form && textarea) {
form.addEventListener('submit', () => {
const content = textarea.value;
if (content.trim().length > 0) {
CHATHIST.push(content);
CHATHISTIDX = CHATHIST.length;
DRAFT = '';
}
}, true);
textarea.addEventListener('keydown', (ev) => {
const content = textarea.value;
if (ev.keyCode == 38) { // Up arrow (input history)
if (CHATHISTIDX == CHATHIST.length) {
if (content.trim().length > 0) { // dont save empty ass messages lol
DRAFT = content;
}
}
if (CHATHISTIDX > 0) {
if (DRAFT && content.trim().length == 0) {
textarea.value = DRAFT;
} else {
CHATHISTIDX--;
textarea.value = CHATHIST[CHATHISTIDX];
}
}
ev.preventDefault();
return false;
}
else if (ev.keyCode == 40) { // Down arrow (input history)
if (CHATHISTIDX < CHATHIST.length - 1) {
CHATHISTIDX++;
textarea.value = CHATHIST[CHATHISTIDX];
} else if (CHATHISTIDX == CHATHIST.length - 1) {
textarea.value = DRAFT;
CHATHISTIDX = CHATHIST.length;
} else {
textarea.value = '';
}
ev.preventDefault();
return false;
}
});
} else { setTimeout(formRetry, 500); }
};
formRetry();
})();