Skip to content

Instantly share code, notes, and snippets.

@ravenbuilder934
Last active January 22, 2021 17:59
Show Gist options
  • Save ravenbuilder934/88ceb66a12435ae4e5f9c15177a897cc to your computer and use it in GitHub Desktop.
Save ravenbuilder934/88ceb66a12435ae4e5f9c15177a897cc to your computer and use it in GitHub Desktop.
CloseEnough™
/* "That's not my username." "Close enough!"
CloseEnough™ is a Discord console script that tweaks people's usernames just enough to make them funny.
To run it, copy this entire file, open Discord's Inspect Element, paste it into the console, and press enter.
It'll stick around until Discord is closed (not just minimized).
CloseEnough is c̶o̶p̶y̶p̶a̶s̶t̶a̶ ̶o̶f a fork of the excellent Bolbify script by AterAnimAvis.
Long live the bolb revolution! */
if (closeenough) closeenough.uninstall()
var closeenough = (() => {
var NULL_VALUE = "closeenough-unset"
/* ========================================================================================== Default Config ==== */
var default_config = {
effectMentions: true,
/* Note that there's no way to get the ID for the person mentioned so it will use the default_mapping */
effectReplies: true,
/* Note that there's no way to get the ID for the reply so it will use the default_mapping */
effectEmbedBot: true,
/* Should CloseEnough change usernames within embeds? */
applyActivity: false,
/* Changes statuses. Might crash. */
active: true,
/* Reapply CloseEnough when a change is detected? */
default_mapping: {
undefined
},
mappings: {
"315757273676906497": {
_name: "AshersLab",
name: ["AshersLab™ (NineColonies)", "Ashe", "Ashers"],
},
"240838510825701378": {
_name: "Raycoms",
name: "Wrong Way Ray",
},
"300194899188514816": {
_name: "Someaddons",
name: ["Sam", "Sam Adams", "Sam Ladders", "Ladders"],
},
"462008577071251467": {
_name: "Shindria",
name: "Gail",
},
"304594425286361088": {
_name: "OrionOnline",
name: "ConstellationInternet",
},
"607058472709652501": {
_name: "sciwhiz12",
name: ["sciquiz12", "scicurle"],
},
"145199140048076800": {
_name: "FinnT730",
name: ["Duck", "Village Idiot", "Stabby Curle", "Java Inquisitor"], // Nobody expects the Java Inquisition!
},
"718189714200330321": {
_name: "ravenbuilder934", // This is not my actual name atm, my name is something from the list below (I change it a lot). This is my original (boring) name
name: ["Raven. Probably.", "notravenbuilder934", "Raven Ladders", "Weighs the Same as a Duck", "swallowbuilder934", "Coco-Builder", "ravenbuilder934 (NineColonies)", "Ravenmas", "Ravencurle", "/(?i)(ravenCurle)/", "rAVENcURLE", "A Loyal Member of Curle Army", "Raven'); DROP TABLE users;--", "Raven (commit spam) builder934", "ㄣƐ6ɹǝplᴉnquǝʌɐɹ", "ERROR_LOADING_USERNAME", "My name is not User", "My name is still not User", "I'm Running Out of Names"],
activity: ["Help I\'m trapped in a Discord status factory", "Autometalogolex!", "leads back to Philosophy", "Crafting ladders", "Stapling bread to trees", "Blaming Wissi", "translaeting minecoloniez into lolcat (ɥsᴉlƃuƎ uʍop-ǝpᴉsdn puɐ)", "Internet killed the video star", "Are you suggesting coconuts migrate?", "Wiki wiki wiki wiki ptwang zoo-boinga!", "ERROR_LOADING_STATUS", "(And this isn't a status)", "(And this still isn't a status)", "𝘴𝘲𝘶𝘢𝘸𝘬𝘴 Nevermore!", "didn't start the fire", "Long live the bolb revolution!"],
},
"134321206642868225": {
_name: "reggaemuffin",
name: "A Reggae-Playing Muffin",
},
"104313475756466176": {
_name: "cpw",
name: "wpc",
},
"134030797756694528": {
_name: "Lex",
name: "Never Ping Me",
},
"581411867575189504": {
_name: "AterAnimAvis",
name: ["Atercurle", "Bolbify Creator"],
},
"233374394632634368": {
_name: "Cugo24",
name: "Cugo on the Go",
},
"199780752748052481": {
_name: "AravanFox",
name: "Fox in a Van",
},
"356950275044671499": {
_name: "UnbelievaBoat Premium",
name: "Pizza bot",
},
"739102509494304769": {
_name: "Assistant",
name: "Builder bot",
},
"437036817858953217": {
_name: "forge-bot",
name: "Blacksmith bot",
},
"152089053452304386": {
_name: "Nightenom",
name: "Night Eater",
},
"462617385157787648": {
_name: "Curle",
name: ["Bolb", "Bolb Woman", "Bolbmas", "Purpcurle", "Commander Purpcurle"]
}
}
};
/* =================================================================================================== Utils ==== */
// https://stackoverflow.com/a/37164538
var isObject = (item) => (item && typeof item === 'object' && !Array.isArray(item));
var merge = (target, source) => {
let output = Object.assign({}, target);
if (isObject(target) && isObject(source)) {
Object.keys(source).forEach(key => {
if (isObject(source[key])) {
if (!(key in target))
Object.assign(output, { [key]: source[key] });
else
output[key] = merge(target[key], source[key]);
} else {
Object.assign(output, { [key]: source[key] });
}
});
}
return output;
}
// https://stackoverflow.com/a/55671924
var weightedRandom = (items, weights) => {
var i;
for (i = 0; i < weights.length; i++)
weights[i] += weights[i - 1] || 0;
var random = Math.random() * weights[weights.length - 1];
for (i = 0; i < weights.length; i++)
if (weights[i] > random)
break;
return items[i];
}
// CloseEnough
function debounce(func, timeout) {
let timer;
return (...args) => {
const next = () => func(...args);
if (timer) clearTimeout(timer);
timer = setTimeout(next, timeout > 0 ? timeout : 300);
};
}
function min(a ,b) {
return a < b ? a : b;
}
function max(a ,b) {
return a > b ? a : b;
}
function wrap(handler, size) {
let timer;
return (array) => {
var pos = 0;
function iteration() {
var max = min(pos + size, array.length);
for (var i = pos; i < max; i++) handler(array[i])
pos += size;
if (pos < array.length) setTimeout(iteration, 10);
}
if (timer) clearTimeout(timer);
iteration();
};
}
function wrap_reverse(handler, size) {
let timer;
return (array) => {
var pos = array.length - 1;
function iteration() {
var min = max(pos - size, 0);
for (var i = pos; i >= min; i--) handler(array[i])
pos -= size;
if (pos > 0) setTimeout(iteration, 10);
}
if (timer) clearTimeout(timer);
iteration();
};
}
var getAvatarId = (src) => {
var loc = src.indexOf("avatars/") + "avatars/".length;
return src.substring(loc, src.indexOf("/", loc));
}
var getCache = (id, key) => (API.cache[id] || {})[key]
var setCache = (id, key, value) => {
if (!API.cache[id]) API.cache[id] = {}
API.cache[id][key] = value || NULL_VALUE;
}
var randomProperty = (id, key, value) => {
if (!Array.isArray(value)) return value;
if (value.length < 1) return undefined;
if (id) {
var cached = getCache(id, key)
if (cached === NULL_VALUE) return null;
if (cached) return cached;
}
function getRandomValue() {
if (Array.isArray(value[0])) {
return weightedRandom(value.map(v => v[0]), value.map(v => v[1]))
}
if (isObject(value[0]) && value[0]["value"] && value[0]["weight"]) {
return weightedRandom(value.map(v => v.value), value.map(v => v.weight))
}
return weightedRandom(value, value.map(v => 1));
}
var result = getRandomValue();
if (id) setCache(id, key, result)
return result;
}
var getMapping = (id, mapping) => randomProperty(id, "mapping", mapping);
var getName = (id, mapping) => randomProperty(id, "name", mapping.name)
var getImg = (id, mapping) => randomProperty(id, "img", mapping.img)
var getActivity = (id, mapping) => randomProperty(id, "activity", mapping.activity)
/* Api */
var API = {}
API.observers = []
API.config = {...default_config}
API._closeenough = function(config, node, name, avatar, subText) {
var saved = node.getAttribute("closeenough-saved-id");
var id = saved || getAvatarId(avatar.src);
var mapping = getCache(id, "mapping")
if (!mapping) {
mapping = {...getMapping(null, config.default_mapping), ...getMapping(null, config.mappings[id])}
setCache(id, "mapping", mapping)
}
if (!saved) node.setAttribute("closeenough-saved-id", id);
var m_name = getName(id, mapping);
if (m_name) {
if (name.innerText.startsWith("@")) m_name = "@" + m_name;
name.innerText = m_name;
}
var m_img = getImg(id, mapping);
if (m_img) avatar.src = m_img + "?size=128";
if (config.applyActivity && subText) {
var m_activity = getActivity(id, mapping);
if (m_activity) subText.innerHTML = '<div class="activity-2Gy-9S"><div class="activityText-yGKsKm">' + m_activity + '</div><div class="textRuler-wO-qHe activityText-yGKsKm" aria-hidden="true">sup</div></div>';
}
}
API.closeenough_member = function(member) {
var name = member.querySelector("div[class^='name'] > div > span");
var avatar = member.querySelector("div[class^='avatar'] > div > svg > foreignObject > img");
var subText = member.querySelector("div[class^='subText']");
API._closeenough(API.config, member, name, avatar, subText);
}
API.closeenough_mention = function(mention) {
if (!API.config.effectMentions) return;
var m_name = mention.getAttribute("closeenough-saved-name") || getName(null, getMapping(null, API.config.default_mapping));
if (m_name) {
mention.innerText = '@' + m_name;
mention.setAttribute("closeenough-saved-name", m_name);
}
}
API.closeenough_embed = function(embed) {
if (!API.config.effectEmbedBot) return;
var name = embed.querySelector("[class*='embedAuthorName']");
var avatar = embed.querySelector("img[class^='embedAuthorIcon-']");
if (!name || !avatar) return
API._closeenough(API.config, embed, name, avatar, undefined);
}
API.closeenough_reply = function(reply) {
if (!API.config.effectReplies) return;
var name = reply.querySelector("span[class^='username-']");
var avatar = reply.querySelector("img[class^='replyAvatar-']");
API._closeenough(API.config, reply, name, avatar, undefined);
}
API.closeenough_message = function(message) {
var name = message.querySelector("span[class^='headerText-'] > span[class^='username-']");
var avatar = message.querySelector("img[class^='avatar']");
if (API.config.effectMentions) API.closeenough_all_mentions_in_message(message);
if (!name) {
/* Join Message */
var isJoin = message.querySelector("div[class='iconContainer']")
var name = message.querySelector("a[class^='anchor-']");
if (isJoin && name) {
var m_name = name.getAttribute("closeenough-saved-name") || getName(null, getMapping(null, API.config.default_mapping));
if (m_name) {
name.innerText = m_name;
name.setAttribute("closeenough-saved-name", m_name);
}
}
/* Overwise a continuation */
return
}
/* Reply */
var reply = message.querySelector("div[class^='repliedMessage-']");
if (reply) API.closeenough_reply(reply);
/* Embed */
var embedWrapper = message.querySelector("div[class^='embedWrapper-']")
if (embedWrapper) API.closeenough_embed(embedWrapper)
API._closeenough(API.config, message, name, avatar, undefined);
}
API._closeenough_all_members = wrap(API.closeenough_member, 25);
API._closeenough_all_messages = wrap_reverse(API.closeenough_message, 25);
API.closeenough_all_members = () => API._closeenough_all_members(document.querySelectorAll("div[class^='member-']"));
API.closeenough_all_messages = () => API._closeenough_all_messages(document.querySelectorAll("div[class^='message-']"));
API.closeenough_all_mentions_in_message = (message) => message.querySelectorAll("span[class^='mention']").forEach(API.closeenough_mention);
API.run = () => {
API.closeenough_all_members();
API.closeenough_all_messages();
};
API.observe = (func, node, config) => {
if (!node) return null
var observer = new MutationObserver(func);
observer.observe(node, config);
API.observers.push(observer);
return observer
}
var debounced_closeenough_members = debounce(API.closeenough_all_members, 250)
API._observe_members_list = (events) => debounced_closeenough_members();
var debounced_closeenough_chat = debounce(API.closeenough_all_messages, 250)
API._observe_chat_messages = (events) => debounced_closeenough_chat();
API.install_channel = () => {
API._reset();
if (API.config.active) {
API.observe(API._observe_members_list, document.querySelector("div[class^='members-']"), { attributes: true, childList: true, subtree: false })
API.observe(API._observe_chat_messages, document.querySelector("div[class^='messagesWrapper-']"), { attributes: true, childList: true, subtree: false })
API.observe(API._observe_chat_messages, document.querySelector("div[class^='messagesWrapper-'] > div[class^='scroller-']"), { attributes: true, childList: true, subtree: false })
}
API.run();
}
var debounced_install_channel = debounce(API.install_channel, 250)
API._observe_channel_change = (events) => debounced_install_channel();
API._observe_server_change = (events) => debounced_install_channel();
API.install = (config) => {
API.uninstall();
API.cache = {};
API.config = {...default_config, ...config };
if (config && config.extra_mappings) API.config.mappings = merge(API.config.mappings, config.extra_mappings);
API.install_channel();
};
API._reset = () => {
API.observers.forEach((observer) => observer.disconnect());
API.observers = [];
if (API.config.active) {
var chat = document.querySelector("div[class^='chat-'] > div[class^='content-']");
if (chat) API.observe(API._observe_channel_change, chat, { attributes: true, childList: true, subtree: false });
API.observe(API._observe_server_change, document.querySelector("div[class^='sidebar-']"), { attributes: false, childList: true, subtree: false });
}
}
API.uninstall = () => {
API.observers.forEach((observer) => observer.disconnect());
API.observers = [];
}
return API;
})();
/* Config */
closeenough.install({
/* default_mapping: [
{
name: [ "Curle", "Curl-e" ],
img: "https://cdn.discordapp.com/avatars/462617385157787648/6548d9da1c1cdd266c96f001ec2950a4.png",
},
{
name: "stabbyCurle",
img: "https://media.discordapp.net/attachments/788606055822393397/788842541080248390/stabolb.png",
}
],
extra_mappings: {
// Extra mappings override the default mappings
"134030797756694528": {
// Change LexBolb -> LexBolbMas
img: "https://media.discordapp.net/attachments/788606055822393397/788838202378944603/lexbolbmas.png"
}
}, */
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment