Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name CoolChat
- // @namespace greasyfork.org/en/users/1213777-logan-usw
- // @version 3.0.0a
- // @description Markdown, Previews, X/YT/Spotify/GIF Embeds, Emote Fix, Wikimedia optimization, and Custom gimmickCellar Emotes. TheNiceOne added support for ...network.
- // @author gimmickCellar, TheNiceOne
- // @match https://ourworldoftext.com/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=ourworldoftext.com
- // @grant none
- // @license MIT
- // ==/UserScript==
- (() => {
- // 1. Inject Twitter Widget Script
- if (!document.getElementById('twitter-wjs')) {
- const twScript = document.createElement('script');
- twScript.id = 'twitter-wjs';
- twScript.src = "https://platform.twitter.com/widgets.js";
- twScript.charset = "utf-8";
- twScript.async = true;
- document.head.appendChild(twScript);
- }
- const css = `
- .msg-md-bold { font-weight: bold; }
- .msg-md-italic { font-style: italic; }
- .msg-md-strike { text-decoration: line-through; }
- .msg-md-code { background: rgba(255,255,255,0.1); font-family: monospace; padding: 1px 4px; border-radius: 3px; color: #ffadad; }
- .msg-link { color: #3498db; text-decoration: underline; }
- /* Directional Colors */
- .msg-color-green { color: #789922 !important; }
- .msg-color-purple { color: #825fdb !important; }
- .msg-color-gold { color: #dbac5f !important; }
- .msg-color-blue { color: #5f94db !important; }
- /* Custom Emote Class (16px) */
- .coolchat-custom-emote {
- background-image: url('https://files.catbox.moe/hlimbl.png');
- background-size: auto 16px;
- height: 16px;
- width: 16px;
- vertical-align: middle;
- display: inline-block;
- margin: 0 1px;
- }
- /* Media Previews */
- .owot-media-container { display: block; margin-top: 8px; max-width: 500px; width: 100%; }
- .owot-preview-img {
- max-width: 100%; max-height: 300px; object-fit: contain;
- display: block; border-radius: 4px; border: 1px solid #333; background: #111;
- }
- .owot-embed-iframe { width: 100%; aspect-ratio: 16 / 9; border-radius: 8px; border: none; background: #000; overflow: hidden; margin-bottom: 5px; }
- .owot-embed-spotify { width: 100%; height: 80px; border-radius: 12px; border: none; margin-bottom: 5px; }
- .owot-embed-gif { max-width: 300px; aspect-ratio: 1 / 1; border-radius: 8px; border: none; }
- .tweet-loading { font-size: 0.8em; color: #888; font-style: italic; padding: 10px; border: 1px dashed #444; border-radius: 8px; }
- `;
- const styleSheet = document.createElement("style");
- styleSheet.innerHTML = css;
- document.head.appendChild(styleSheet);
- const customEmoteMap = {
- "gc": 0, "gimmickcellar": 0,
- "gcfuckyou": 1,
- "gcthinkaaa": 2,
- "gcermactually": 3
- };
- function optimizeUrl(url) {
- if (url.includes('upload.wikimedia.org') && !url.includes('/thumb/')) {
- const parts = url.split('/');
- const filename = parts[parts.length - 1];
- return url.replace('/commons/', '/commons/thumb/') + '/500px-' + filename;
- }
- return url;
- }
- async function embedX(url, container) {
- const loadingNote = document.createElement('div');
- loadingNote.className = 'tweet-loading';
- loadingNote.innerText = "Loading X post...";
- container.appendChild(loadingNote);
- try {
- const apiUrl = `https://publish.twitter.com/oembed?url=${encodeURIComponent(url)}&theme=dark&dnt=true&omit_script=true`;
- const response = await fetch(apiUrl);
- const data = await response.json();
- loadingNote.remove();
- const tweetWrapper = document.createElement('div');
- tweetWrapper.innerHTML = data.html;
- container.appendChild(tweetWrapper);
- if (window.twttr && window.twttr.widgets) window.twttr.widgets.load(tweetWrapper);
- } catch (err) { loadingNote.innerText = "Failed to load X post."; }
- }
- function processMessage(msgElem) {
- let rawText = msgElem.textContent.replace(/^\u00A0/, "");
- let workingHtml = msgElem.innerHTML;
- let emoteTokens = [];
- // Shield Native Emotes
- workingHtml = workingHtml.replace(/<div[^>]*class=["']chat_emote["'][^>]*><\/div>/g, (match) => {
- emoteTokens.push(match);
- return `__EMOTE_TOKEN_${emoteTokens.length - 1}__`;
- });
- // Shield Custom Emotes (Scaled to 16px)
- workingHtml = workingHtml.replace(/:([a-z0-9_]+):/gi, (match, name) => {
- const lowerName = name.toLowerCase();
- if (customEmoteMap.hasOwnProperty(lowerName)) {
- const index = customEmoteMap[lowerName];
- const xPos = -(index * 16);
- const customDiv = `<div title=":${name}:" class="coolchat-custom-emote" style="background-position: ${xPos}px 0px;"></div>`;
- emoteTokens.push(customDiv);
- return `__EMOTE_TOKEN_${emoteTokens.length - 1}__`;
- }
- return match;
- });
- const trimmed = rawText.trim();
- if (trimmed.startsWith(">")) msgElem.classList.add("msg-color-green");
- else if (trimmed.startsWith("<") && !trimmed.startsWith("<3") && !trimmed.includes(">")) msgElem.classList.add("msg-color-purple");
- else if (trimmed.startsWith("^")) msgElem.classList.add("msg-color-gold");
- else if (trimmed.startsWith("V ") || trimmed === "V" || trimmed.startsWith("v ") || trimmed === "v") msgElem.classList.add("msg-color-blue");
- let parserDiv = document.createElement('div');
- parserDiv.innerHTML = workingHtml;
- let html = parserDiv.innerHTML;
- html = html
- .replace(/\*\*(.*?)\*\*/g, '<span class="msg-md-bold">$1</span>')
- .replace(/\*(.*?)\*/g, '<span class="msg-md-italic">$1</span>')
- .replace(/~~(.*?)~~/g, '<span class="msg-md-strike">$1</span>')
- .replace(/`(.*?)`/g, '<code class="msg-md-code">$1</code>');
- const urlRegex = /(https?:\/\/[^\s<]+)/gi;
- const urls = rawText.match(urlRegex) || [];
- html = html.replace(urlRegex, '<a href="$1" target="_blank" class="msg-link">$1</a>');
- for (let i = 0; i < emoteTokens.length; i++) {
- html = html.replace(`__EMOTE_TOKEN_${i}__`, emoteTokens[i]);
- }
- msgElem.innerHTML = html;
- if (urls.length > 0) {
- const container = document.createElement('div');
- container.className = 'owot-media-container';
- msgElem.appendChild(container);
- urls.forEach(url => {
- const lowUrl = url.toLowerCase();
- if (lowUrl.includes('twitter.com') || lowUrl.includes('x.com')) {
- if (url.includes('/status/')) return embedX(url, container);
- }
- const tenorMatch = url.match(/tenor\.com\/view\/.*-(\d+)/i);
- if (tenorMatch) {
- const f = document.createElement('iframe');
- f.src = `https://tenor.com/embed/${tenorMatch[1]}`;
- f.className = 'owot-embed-gif'; f.scrolling = "no";
- return container.appendChild(f);
- }
- const giphyMatch = url.match(/giphy\.com\/gifs\/.*-([a-zA-Z0-9]+)/i) || url.match(/giphy\.com\/gifs\/([a-zA-Z0-9]+)/i);
- if (giphyMatch) {
- const f = document.createElement('iframe');
- f.src = `https://giphy.com/embed/${giphyMatch[1]}`;
- f.className = 'owot-embed-gif'; f.scrolling = "no";
- return container.appendChild(f);
- }
- let ytMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/shorts\/)([a-zA-Z0-9_-]{11})/i);
- if (ytMatch) {
- const f = document.createElement('iframe');
- f.src = `https://www.youtube.com/embed/${ytMatch[1]}`;
- f.className = 'owot-embed-iframe'; f.allowFullscreen = true;
- return container.appendChild(f);
- }
- if (lowUrl.includes('open.spotify.com')) {
- const sMatch = url.match(/spotify\.com\/(track|album|playlist)\/([a-zA-Z0-9]+)/i);
- if (sMatch) {
- const f = document.createElement('iframe');
- f.src = `https://open.spotify.com/embed/${sMatch[1]}/${sMatch[2]}`;
- f.className = 'owot-embed-spotify'; f.allow = "encrypted-media";
- return container.appendChild(f);
- }
- }
- const cleanUrl = url.split('?')[0].toLowerCase();
- if (cleanUrl.match(/\.(jpeg|jpg|gif|png|webp|avif|svg)$/)) {
- const img = document.createElement('img');
- img.src = optimizeUrl(url);
- img.className = 'owot-preview-img'; img.loading = "lazy";
- container.appendChild(img);
- } else if (cleanUrl.match(/\.(mp4|webm|ogg)$/)) {
- const vid = document.createElement('video');
- vid.src = url; vid.className = 'owot-embed-iframe'; vid.controls = true; vid.muted = true;
- container.appendChild(vid);
- } else if (cleanUrl.match(/\.(mp3|wav|ogg|m4a)$/)) {
- const aud = document.createElement('audio');
- aud.src = url; aud.controls = true;
- container.appendChild(aud);
- }
- });
- }
- }
- const chatWatcher = new MutationObserver((mutations) => {
- mutations.forEach((mutation) => {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType !== 1) return;
- const msgContent = node.querySelector('.message__content') ||
- (node.classList && node.classList.contains('message__content') ? node : node.lastChild);
- if (msgContent && msgContent.nodeType === 1 && !msgContent.dataset.parsed) {
- setTimeout(() => {
- if (msgContent.dataset.parsed) return;
- msgContent.dataset.parsed = "true";
- processMessage(msgContent);
- }, 0);
- }
- });
- });
- });
- const start = () => {
- const p = document.getElementById('page_chatfield') || (window.elm && elm.page_chatfield);
- const g = document.getElementById('global_chatfield') || (window.elm && elm.global_chatfield);
- if (p && g) {
- chatWatcher.observe(p, { childList: true, subtree: true });
- chatWatcher.observe(g, { childList: true, subtree: true });
- window.networkinterval = setInterval(() => {
- if(elm.network_chatfield || document.getElementById('network_chatfield')) {
- w.coolChatAdd("network_chatfield", "network_chatfield");
- clearInterval(window.networkinterval);
- }
- }, 500)
- } else { setTimeout(start, 500); }
- };
- window.w.coolChatAdd = (tabId, elmIndex) => {
- const t = document.getElementById(tabId) || (window.elm && elm[elmIndex]);
- chatWatcher.observe(t, { childList: true, subtree: true });
- }
- start();
- })();
Add Comment
Please, Sign In to add comment