Parcel #yofzg330psh6j3s
Created by Anonymous
Public
Created October 15, 2026 Expires in 26 days
Loading editor...
// YAP++
// the original yap++ was made by @salami on yapper.
// Feel free to learn from the code.
// If you wish to make a fork of yap++ Then make sure to include this:
// <h1>This tool is built on top of Yapp++ made by @salami on yapper.</h1>
// I'm a kid so the plugin might be a little immature.
// here you can read the source code of a function which enables a plugin.
console.log("wait 5 sec for it's ui to load (might freeze for 1-2 sec)")
const PluginHost = {
plugins: [],
listeners: [],
register(plugin) {
this.plugins.push(plugin);
if (localStorage.getItem(`yapplus.plugin.${plugin.id}`) === '1') {
try { plugin.start(); } catch (e) { console.error('[Yap++] start failed', plugin.id, e); }
}
this.listeners.forEach(fn => fn(plugin));
},
isEnabled(plugin) {
return localStorage.getItem(`yapplus.plugin.${plugin.id}`) === '1';
},
setEnabled(plugin, on) {
localStorage.setItem(`yapplus.plugin.${plugin.id}`, on ? '1' : '0');
try {
on ? plugin.start() : plugin.stop();
} catch (e) { console.error('[Yap++] toggle failed', plugin.id, e); }
this.listeners.forEach(fn => fn(plugin));
},
all() { return this.plugins.slice(); },
onChange(fn) { this.listeners.push(fn); },
};
// You can read the plugins here allong with their source code:
const SIXSEVEN = {
id: 'SIXSEVEN',
name: 'SIXSEVEN',
description: 'HIHI S-S-SIXSEVENNs (makes every message you send have 67)',
origFetch: null,
start() {
if (!this.origFetch) this.origFetch = window.fetch;
const origFetch = this.origFetch;
window.fetch = function (input, init) {
const url = typeof input === 'string' ? input : (input && input.url) || '';
const isInsert = url.indexOf('/rest/v1/messages') !== -1
&& init && init.method === 'POST';
if (isInsert && init.body) {
try {
const body = JSON.parse(init.body);
if (typeof body.content === 'string') {
body.content = body.content + ' 67';
init.body = JSON.stringify(body);
}
} catch (e) {}
}
return origFetch.call(this, input, init);
};
},
stop() {
if (this.origFetch) {
window.fetch = this.origFetch;
this.origFetch = null;
}
},
};
const imasigma = {
id: 'imasigma',
name: 'Im a sigma',
description: `Let's every one of your messages know that you're a sigma`,
origFetch: null,
start() {
if (!this.origFetch) this.origFetch = window.fetch;
const origFetch = this.origFetch;
window.fetch = function (input, init) {
const url = typeof input === 'string' ? input : (input && input.url) || '';
const isInsert = url.indexOf('/rest/v1/messages') !== -1
&& init && init.method === 'POST';
if (isInsert && init.body) {
try {
const body = JSON.parse(init.body);
if (typeof body.content === 'string') {
body.content = body.content + ` \n* (I'm a sigma)`;
init.body = JSON.stringify(body);
}
} catch (e) {}
}
return origFetch.call(this, input, init);
};
},
stop() {
if (this.origFetch) {
window.fetch = this.origFetch;
this.origFetch = null;
}
},
};
const messagelogger = {
id: 'messagelogger',
name: 'MessageLogger',
description: 'Keeps deleted messages visible in red. Work in progress.',
origFetch: null,
origAddEventListener: null,
STORAGE_KEY: 'yapplus.messagelog',
domObserver: null,
renderTimer: null,
start() {
const self = this;
const css = document.createElement('style');
css.id = 'yapplus-ghost-css';
css.textContent = `
[data-yp-ghost] .msg-text,
[data-yp-ghost] .msg-text * { color: #ff4b4b !important; }
[data-yp-ghost] .msg-author { color: #ff4b4b !important; }
`;
(document.head || document.documentElement).appendChild(css);
if (!this.origFetch) this.origFetch = window.fetch;
const origFetch = this.origFetch;
window.fetch = function (input, init) {
const url = typeof input === 'string' ? input : (input && input.url) || '';
const method = (init && init.method) || 'GET';
const isMessages = url.indexOf('/rest/v1/messages') !== -1;
if (isMessages && method === 'POST' && init && init.body) {
try {
const body = JSON.parse(init.body);
const tempKey = `local-${Date.now()}-${Math.random()}`;
self.recordWithKey(tempKey, 'outgoing', {
content: body.content,
channel_id: body.channel_id,
author_id: body.author_id,
created_at: new Date().toISOString(),
});
const p = origFetch.call(this, input, init);
p.then(r => {
if (!r.ok) return;
r.clone().json().then(row => {
if (row && row.id) self.rekey(tempKey, row.id);
}).catch(() => {});
}).catch(() => {});
return p;
} catch (e) {}
}
if (isMessages && method === 'DELETE') {
const m = url.match(/id=eq\.([0-9a-f-]+)/i);
if (m) self.markDeleted(m[1], true);
}
if (isMessages && method === 'PATCH' && init && init.body) {
const m = url.match(/id=eq\.([0-9a-f-]+)/i);
try {
const body = JSON.parse(init.body);
if (m && body.content) self.markEdited(m[1], body.content);
} catch (e) {}
}
if (isMessages && method === 'GET') {
return origFetch.call(this, input, init).then(async (res) => {
if (!res.ok) return res;
try {
const text = await res.text();
const rows = JSON.parse(text);
if (!Array.isArray(rows)) return new Response(text, res);
const channelMatch = url.match(/channel_id=eq\.([0-9a-f-]+)/i);
const channelId = channelMatch ? channelMatch[1] : null;
if (channelId) {
const log = self.load();
const extras = Object.values(log).filter(r =>
r.deleted && r.channel_id === channelId
);
for (const r of extras) {
if (!rows.find(x => x.id === r.id)) {
rows.push(self.toSupabaseRow(r));
}
}
rows.sort((a, b) =>
new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
);
}
return new Response(JSON.stringify(rows), {
status: res.status,
statusText: res.statusText,
headers: res.headers,
});
} catch (e) {
return res;
}
});
}
if (url.indexOf('/rest/v1/profiles') !== -1 ||
url.indexOf('/rest/v1/users') !== -1 ||
url.indexOf('who_online') !== -1) {
return origFetch.call(this, input, init).then(res => {
res.clone().json().then(rows => {
if (Array.isArray(rows)) self.cacheAuthors(rows);
else if (rows && typeof rows === 'object') self.cacheAuthors([rows]);
}).catch(() => {});
return res;
});
}
return origFetch.call(this, input, init);
};
if (!this.origAddEventListener) {
this.origAddEventListener = WebSocket.prototype.addEventListener;
}
const origAddEL = this.origAddEventListener;
WebSocket.prototype.addEventListener = function (type, listener, options) {
if (type !== 'message') return origAddEL.call(this, type, listener, options);
const wrapped = function (ev) {
try {
const frame = JSON.parse(ev.data);
const data = frame && frame.payload && frame.payload.data;
if (data && data.table === 'messages') {
const t = data.type || frame.event;
if (t === 'INSERT') {
self.record('incoming', data.record);
self.scheduleGhostPaint();
} else if (t === 'UPDATE') {
self.markEdited(data.record.id, data.record.content);
} else if (t === 'DELETE') {
const old = data.old_record || data.old;
if (old && old.id) self.markDeleted(old.id, true);
}
}
} catch (e) {}
return listener.call(this, ev);
};
return origAddEL.call(this, type, wrapped, options);
};
this.installGhostStrip();
this.scheduleGhostPaint();
console.log('[Messagelogger] armed');
},
stop() {
if (this.origFetch) { window.fetch = this.origFetch; this.origFetch = null; }
if (this.origAddEventListener) {
WebSocket.prototype.addEventListener = this.origAddEventListener;
this.origAddEventListener = null;
}
if (this.domObserver) { this.domObserver.disconnect(); this.domObserver = null; }
if (this.renderTimer) { clearInterval(this.renderTimer); this.renderTimer = null; }
clearTimeout(this._paintDebounce);
clearTimeout(this._paintDebounce2);
clearTimeout(this._paintDebounce3);
const strip = document.getElementById('yapplus-ghost-strip');
if (strip) strip.remove();
const css = document.getElementById('yapplus-ghost-css');
if (css) css.remove();
console.log('[Messagelogger] disarmed');
},
load() {
try { return JSON.parse(sessionStorage.getItem(this.STORAGE_KEY) || '{}'); }
catch (e) { return {}; }
},
save(data) {
const arr = Object.values(data).filter(r => r && r.created_at);
while (arr.length > 500) {
const oldest = arr.reduce((a, b) => a.created_at < b.created_at ? a : b);
delete data[oldest.id || oldest.created_at];
}
try { sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(data)); }
catch (e) {}
},
cacheAuthors(rows) {
const data = this.load();
let changed = false;
for (const r of rows) {
if (!r || typeof r !== 'object') continue;
const id = r.id || r.user_id || r.author_id;
if (!id) continue;
const username = r.username || r.display_name || r.name || r.handle;
const avatar = r.avatar_url || r.avatar || r.profile_picture || r.pfp;
if (!username && !avatar) continue;
data['__author__' + id] = { id, username, avatar };
changed = true;
}
if (changed) this.save(data);
},
getAuthor(id) {
const data = this.load();
return data['__author__' + id] || null;
},
record(direction, row) {
const key = row.id || `local-${Date.now()}-${Math.random()}`;
this.recordWithKey(key, direction, row);
},
recordWithKey(key, direction, row) {
const data = this.load();
data[key] = {
id: row.id || null,
direction,
content: row.content,
channel_id: row.channel_id,
author_id: row.author_id,
created_at: row.created_at || new Date().toISOString(),
deleted: false,
edited: false,
};
if (row.author_id) {
const cached = data['__author__' + row.author_id];
if (cached) {
data[key].author_username = cached.username;
data[key].author_avatar = cached.avatar;
data[key].author_avatar_style = cached.avatar_style;
}
}
this.save(data);
},
rekey(oldKey, newId) {
const data = this.load();
if (!data[oldKey]) return;
data[oldKey].id = newId;
data[newId] = data[oldKey];
delete data[oldKey];
this.save(data);
},
markDeleted(id, toast) {
const data = this.load();
if (!data[id]) return false;
if (data[id].deleted) return true;
data[id].deleted = true;
data[id].deleted_at = new Date().toISOString();
this.save(data);
if (toast) this.showToast(data[id]);
this.scheduleGhostPaint();
return true;
},
markEdited(id, newContent) {
const data = this.load();
if (!data[id]) return;
if (!data[id].edits) data[id].edits = [];
data[id].edits.push({ at: new Date().toISOString(), content: newContent });
data[id].edited = true;
this.save(data);
},
toSupabaseRow(r) {
return {
id: r.id,
channel_id: r.channel_id,
author_id: r.author_id,
content: r.content,
created_at: r.created_at,
edited_at: null,
attachment_path: null,
attachment_width: null,
attachment_height: null,
reply_to_id: null,
pinned_at: null,
pinned_by: null,
};
},
currentChannelId() {
try {
const url = location.href;
const m = url.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
if (m) return m[1];
} catch (e) {}
try {
const p = JSON.parse(localStorage.getItem('yapper:place') || '{}');
return p.c || null;
} catch (e) { return null; }
},
scheduleGhostPaint() {
const self = this;
clearTimeout(this._paintDebounce);
clearTimeout(this._paintDebounce2);
clearTimeout(this._paintDebounce3);
this._paintDebounce = setTimeout(() => self.paintGhosts(), 60);
this._paintDebounce2 = setTimeout(() => self.paintGhosts(), 250);
this._paintDebounce3 = setTimeout(() => self.paintGhosts(), 800);
},
paintGhosts() {
const list = document.querySelector('.messages');
if (!list) return;
const channelId = this.currentChannelId();
if (!channelId) return;
const log = this.load();
const ghosts = Object.values(log).filter(r =>
r && r.deleted && r.channel_id === channelId
);
list.querySelectorAll('[data-yp-ghost]').forEach(el => {
const gid = el.getAttribute('data-message-id');
if (!ghosts.find(g => g.id === gid)) el.remove();
});
for (const g of ghosts) {
if (list.querySelector(`.msg[data-message-id="${g.id}"]`)) continue;
const el = this.buildGhostEl(g);
if (!el) continue;
const realMsgs = [...list.querySelectorAll('.msg[data-message-id]')];
let insertBefore = null;
for (const real of realMsgs) {
const id = real.getAttribute('data-message-id');
const row = Object.values(log).find(r => r && r.id === id);
if (row && row.created_at > g.created_at) { insertBefore = real; break; }
}
if (insertBefore && insertBefore.parentNode === list) {
list.insertBefore(el, insertBefore);
} else {
list.appendChild(el);
}
}
let dirty = false;
const log2 = this.load();
for (const g of ghosts) {
if (g.author_username && g.author_avatar && g.author_avatar_style) continue;
const real = list.querySelector(`.msg[data-message-id="${g.id}"]`);
if (!real) continue;
const authorEl = real.querySelector('.msg-author');
const img = real.querySelector('.avatar img');
if (authorEl && !g.author_username) {
g.author_username = authorEl.textContent.trim();
dirty = true;
}
if (img && !g.author_avatar) {
g.author_avatar = img.getAttribute('src');
g.author_avatar_style = img.getAttribute('style') || '';
dirty = true;
}
if (g.author_id && (g.author_username || g.author_avatar)) {
log2['__author__' + g.author_id] = {
id: g.author_id,
username: g.author_username,
avatar: g.author_avatar,
avatar_style: g.author_avatar_style,
};
}
if (dirty) log2[g.id] = g;
}
if (dirty) this.save(log2);
try {
document.querySelectorAll('.msg[data-message-id]').forEach(el => {
const id = el.getAttribute('data-message-id');
const l = this.load();
if (l[id] && l[id].deleted) {
el.querySelectorAll('.msg-text, .msg-text *').forEach(n =>
n.style.setProperty('color', '#ff4b4b', 'important'));
}
});
} catch (e) {}
},
buildGhostEl(g) {
const data = this.load();
const cached = data['__author__' + g.author_id] || {};
const username = (g.author_username || cached.username || 'deleted user') + ' (deleted)';
const avatar = g.author_avatar || cached.avatar || null;
const avatarStyle = g.author_avatar_style || cached.avatar_style || 'object-fit:cover;';
const el = document.createElement('div');
el.className = 'msg';
el.setAttribute('data-yp-ghost', '1');
el.setAttribute('data-message-id', g.id);
el.style.background = 'transparent';
const avatarHtml = avatar
? `<span class="avatar-clip" style="position:absolute;inset:0;border-radius:50%;overflow:hidden;"><img alt="" draggable="false" src="${this.escapeHtml(avatar)}" style="${this.escapeHtml(avatarStyle)}"></span>`
: `<div style="width:40px;height:40px;border-radius:50%;background:#ff4b4b;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;">?</div>`;
el.innerHTML =
`<div class="msg-gutter">` +
`<button type="button" class="avatar" style="position:relative;width:40px;height:40px;background:transparent;padding:0;border:none;cursor:default;">${avatarHtml}</button>` +
`</div>` +
`<div class="msg-body">` +
`<div class="msg-head">` +
`<button class="msg-author" style="color:#ff4b4b;cursor:default;">${this.escapeHtml(username)}</button>` +
`<span class="msg-time">${new Date(g.created_at).toLocaleTimeString()}</span>` +
`</div>` +
`<div class="msg-text" style="color:#ff4b4b !important;">${this.escapeHtml(g.content)}</div>` +
`</div>` +
`<div class="msg-actions"></div>`;
return el;
},
escapeHtml(s) {
return String(s == null ? '' : s)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
},
installGhostStrip() {
if (document.getElementById('yapplus-ghost-strip')) return;
const self = this;
const strip = document.createElement('div');
strip.id = 'yapplus-ghost-strip';
strip.style.cssText = 'padding:6px 16px;font-size:12px;color:#ff4b4b;background:rgba(255,75,75,0.08);border-top:1px solid rgba(255,75,75,0.2);cursor:pointer;display:none;user-select:none;';
strip.addEventListener('click', () => {
const l = document.getElementById('yapplus-ghost-list');
if (l) l.style.display = l.style.display === 'none' ? 'block' : 'none';
});
const list = document.createElement('div');
list.id = 'yapplus-ghost-list';
list.style.cssText = 'display:none;max-height:200px;overflow-y:auto;padding:8px 16px;font-size:12px;background:rgba(255,75,75,0.04);border-top:1px solid rgba(255,75,75,0.15);color:#ffb3b3;';
const attach = () => {
const composer = document.querySelector('.composer');
if (!composer || !composer.parentNode) return false;
if (!document.getElementById('yapplus-ghost-strip')) {
composer.parentNode.insertBefore(list, composer);
composer.parentNode.insertBefore(strip, list);
}
return true;
};
if (!attach()) {
const obs = new MutationObserver(() => {
if (attach()) obs.disconnect();
});
obs.observe(document.body, { childList: true, subtree: true });
}
this.renderTimer = setInterval(() => {
const log = self.load();
const channelId = self.currentChannelId();
const items = Object.values(log).filter(r =>
r && r.deleted && r.channel_id === channelId
);
if (items.length === 0) {
strip.style.display = 'none';
list.style.display = 'none';
list.innerHTML = '';
return;
}
strip.style.display = 'block';
strip.textContent = `${items.length} deleted message${items.length === 1 ? '' : 's'} in this channel — click to view`;
list.innerHTML = items
.sort((a, b) => a.created_at < b.created_at ? 1 : -1)
.map(r => {
const cached = log['__author__' + r.author_id] || {};
const name = r.author_username || cached.username || 'deleted user';
return `<div style="margin-bottom:6px;"><span style="color:#ff8a8a;">${new Date(r.created_at).toLocaleString()}</span> · <b style="color:#ff6b6b;">${self.escapeHtml(name)}</b>: <span style="color:#ff4b4b;">${self.escapeHtml(r.content)}</span></div>`;
})
.join('');
}, 1000);
},
showToast(row) {
const t = document.createElement('div');
t.style.cssText = 'position:fixed;bottom:20px;right:20px;background:#1e1f22;border:1px solid rgba(255,75,75,0.4);border-left:3px solid #ff4b4b;color:#dbdee1;padding:10px 14px;border-radius:8px;font-size:13px;z-index:2147483647;max-width:320px;box-shadow:0 8px 24px rgba(0,0,0,.5);';
t.innerHTML = `<div style="color:#ff4b4b;font-weight:700;margin-bottom:4px;">message deleted</div><div style="color:#b5bac1;font-size:12px;margin-bottom:6px;">${new Date().toLocaleTimeString()}</div><div style="color:#ff4b4b;">${this.escapeHtml(row.content)}</div>`;
document.body.appendChild(t);
setTimeout(() => t.remove(), 8000);
},
};
// REGISTER SO IT LIKE ACTUALLY APPEARS IN PLUGINS YESSSSSS WE NEED 67 PLUGINNN YEASSSSS
// humongous amount of newlines so i can navigate to ts (phack you messagelogger)
const staffbadge = {
id: 'staffbadge',
name: 'Staff Badge',
description: 'Shows the Yap++ badge next to staff members (client-side only — only Yap++ users see it).',
BADGED: ['77ae11af', '1720334f', 'c5ccf56', 'c5ccf056'], // id prefixes of badged users
BADGE_SRC: 'data:image/png;base64,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',
msgAuthor: new Map(), // message id -> author id
unresolvable: new Set(), // ids that came back empty
observer: null,
origFetch: null,
origAddEventListener: null,
origOnMessage: null,
paintTimer: null,
healTimer: null,
healInterval: null,
token: null,
isBadged(authorId) {
if (!authorId) return false;
const a = String(authorId).toLowerCase();
return this.BADGED.some(p => a.startsWith(p));
},
remember(msgId, authorId) {
if (msgId && authorId) this.msgAuthor.set(msgId, String(authorId).toLowerCase());
},
sniffToken(input, init) {
try {
let h = (init && init.headers) || (input && input.headers);
if (!h) return;
let auth = null;
if (typeof Headers !== 'undefined' && h instanceof Headers) auth = h.get('authorization');
else if (Array.isArray(h)) {
const f = h.find(x => String(x[0]).toLowerCase() === 'authorization');
auth = f && f[1];
} else auth = h.authorization || h.Authorization;
if (auth && auth.indexOf('Bearer ') === 0) this.token = auth.slice(7);
} catch (e) {}
},
getToken() {
if (this.token) return this.token;
try {
for (let i = 0; i < localStorage.length; i++) {
const k = localStorage.key(i);
if (k && k.indexOf('sb-') === 0 && k.indexOf('-auth-token') !== -1) {
try {
let v = JSON.parse(localStorage.getItem(k));
if (typeof v === 'string') v = JSON.parse(v);
const t = v && (v.access_token || (v.currentSession && v.currentSession.access_token));
if (t) { this.token = t; return t; }
} catch (e) {}
}
}
} catch (e) {}
return null;
},
start() {
const self = this;
// map messages -> author from fetch (history) and websocket (live)
if (!this.origFetch) this.origFetch = window.fetch;
const origFetch = this.origFetch;
window.fetch = function (input, init) {
self.sniffToken(input, init);
const p = origFetch.call(this, input, init);
try {
const url = typeof input === 'string' ? input : (input && input.url) || '';
if (url.indexOf('/rest/v1/messages') !== -1) {
p.then(res => {
if (!res.ok) return;
res.clone().json().then(rows => {
if (Array.isArray(rows)) rows.forEach(r => self.remember(r.id, r.author_id));
else if (rows && rows.id) self.remember(rows.id, rows.author_id);
self.schedulePaint();
}).catch(() => {});
}).catch(() => {});
}
if (url.indexOf('/rest/v1/profiles') !== -1) self.schedulePaint();
} catch (e) {}
return p;
};
this.handleWs = function (raw) {
try {
const f = JSON.parse(raw);
const p = f && (f.payload || f);
const d = p && p.data;
const rec = p.record || (d && d.record);
const table = p.table || (d && d.table);
if (rec && rec.id && rec.author_id && (!table || table === 'messages')) {
self.remember(rec.id, rec.author_id);
self.schedulePaint();
}
} catch (e) {}
};
if (!this.origAddEventListener) this.origAddEventListener = WebSocket.prototype.addEventListener;
const origAddEL = this.origAddEventListener;
WebSocket.prototype.addEventListener = function (type, listener, options) {
if (type !== 'message') return origAddEL.call(this, type, listener, options);
const wrapped = function (ev) {
self.handleWs(ev.data);
return listener.call(this, ev);
};
return origAddEL.call(this, type, wrapped, options);
};
// also catch sites using ws.onmessage = ... instead of addEventListener
const onmsgDesc = Object.getOwnPropertyDescriptor(WebSocket.prototype, 'onmessage');
if (onmsgDesc && onmsgDesc.set && !this.origOnMessage) {
this.origOnMessage = onmsgDesc;
Object.defineProperty(WebSocket.prototype, 'onmessage', {
configurable: true,
get() { return onmsgDesc.get.call(this); },
set(fn) {
const wrapped = function (ev) {
self.handleWs(ev.data);
return fn.call(this, ev);
};
return onmsgDesc.set.call(this, wrapped);
},
});
}
this.observer = new MutationObserver(() => self.schedulePaint());
this.observer.observe(document.body, { childList: true, subtree: true });
this.schedulePaint();
this.backfill();
this.healInterval = setInterval(() => self.heal(), 4000);
console.log('[StaffBadge] armed');
},
async backfill() {
// fetch recent messages from current channel to learn author ids (uses your session)
try {
const loc = (location.href.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i) || [])[0];
if (!loc) return;
const token = this.getToken();
if (!token) return;
const anonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRuZmt1cXpkZHd2dGRvanpnbWRtIiwicm9sZSI6ImFub24iLCJpYXQiOjE3OTE2Mzk0ODgsImV4cCI6MjEwNzIxNTQ4OH0.kMZcUf8DgMrYFNGtMPW9SGja2m1cRdMq5WJv5XBNgCU';
const res = await (this.origFetch || window.fetch)(
`https://dnfkuqzddwvtdojzgmdm.supabase.co/rest/v1/messages?channel_id=eq.${loc}&select=id,author_id&order=created_at.desc&limit=200`,
{ headers: { apikey: anonKey, Authorization: `Bearer ${token}` } }
);
if (res.ok) {
const rows = await res.json();
if (Array.isArray(rows)) rows.forEach(r => this.remember(r.id, r.author_id));
this.schedulePaint();
}
} catch (e) {}
},
// self-healing: any visible message we don't know the author of gets fetched by id
async heal() {
if (this.healing) return;
const missing = [];
document.querySelectorAll('.msg[data-message-id]').forEach(el => {
const id = el.getAttribute('data-message-id');
if (id && !this.msgAuthor.has(id) && !this.unresolvable.has(id)) missing.push(id);
});
if (!missing.length) return;
const token = this.getToken();
if (!token) return;
this.healing = true;
try {
const anonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRuZmt1cXpkZHd2dGRvanpnbWRtIiwicm9sZSI6ImFub24iLCJpYXQiOjE3OTE2Mzk0ODgsImV4cCI6MjEwNzIxNTQ4OH0.kMZcUf8DgMrYFNGtMPW9SGja2m1cRdMq5WJv5XBNgCU';
const batch = missing.slice(0, 150);
const res = await (this.origFetch || window.fetch)(
`https://dnfkuqzddwvtdojzgmdm.supabase.co/rest/v1/messages?id=in.(${batch.join(',')})&select=id,author_id`,
{ headers: { apikey: anonKey, Authorization: `Bearer ${token}` } }
);
if (res.ok) {
const rows = await res.json();
const found = new Set();
if (Array.isArray(rows)) rows.forEach(r => { this.remember(r.id, r.author_id); found.add(r.id); });
batch.forEach(id => { if (!found.has(id)) this.unresolvable.add(id); });
this.schedulePaint();
}
} catch (e) {}
this.healing = false;
},
stop() {
if (this.origFetch) { window.fetch = this.origFetch; this.origFetch = null; }
if (this.origAddEventListener) {
WebSocket.prototype.addEventListener = this.origAddEventListener;
this.origAddEventListener = null;
}
if (this.origOnMessage) {
Object.defineProperty(WebSocket.prototype, 'onmessage', this.origOnMessage);
this.origOnMessage = null;
}
if (this.observer) { this.observer.disconnect(); this.observer = null; }
clearInterval(this.healInterval);
clearTimeout(this.paintTimer);
document.querySelectorAll('.yp-staff-badge').forEach(el => el.remove());
console.log('[StaffBadge] disarmed');
},
schedulePaint() {
clearTimeout(this.paintTimer);
this.paintTimer = setTimeout(() => this.paint(), 80);
},
injectCss() {
if (document.getElementById('yp-staff-badge-css')) return;
const css = document.createElement('style');
css.id = 'yp-staff-badge-css';
css.textContent = `
.yp-staff-badge {
height: 16px;
width: 16px;
border-radius: 50%;
margin-left: 6px;
vertical-align: -3px;
align-self: center;
object-fit: cover;
box-shadow: 0 0 5px rgba(255, 0, 255, .55);
pointer-events: none;
user-select: none;
}
`;
(document.head || document.documentElement).appendChild(css);
},
paint() {
this.injectCss();
document.querySelectorAll('.msg[data-message-id]').forEach(el => {
const mid = el.getAttribute('data-message-id');
const author = this.msgAuthor.get(mid);
const name = el.querySelector('.msg-head .msg-author');
if (!name) return;
const head = name.parentNode;
let existing = head.querySelector(':scope > .yp-staff-badge');
if (author && this.isBadged(author)) {
if (existing && existing.previousElementSibling === name) return;
if (existing) existing.remove();
const img = document.createElement('img');
img.className = 'yp-staff-badge';
img.src = this.BADGE_SRC;
img.alt = 'Yap++';
img.title = 'Yap++ Client';
img.draggable = false;
head.insertBefore(img, name.nextSibling);
} else if (existing) {
existing.remove();
}
});
},
};
// badge on by default
if (localStorage.getItem('yapplus.plugin.staffbadge') === null) {
localStorage.setItem('yapplus.plugin.staffbadge', '1');
}
const autoedit = {
id: 'autoedit',
name: 'Auto Edit',
description: 'Send your command message, then instantly edits it to the replacement text.',
origFetch: null,
getCmd() {
try { return (localStorage.getItem('yapplus.autoedit.cmd') || '').trim(); } catch (e) { return ''; }
},
getText() {
try { return localStorage.getItem('yapplus.autoedit.text') || ''; } catch (e) { return ''; }
},
start() {
const self = this;
if (!this.origFetch) this.origFetch = window.fetch;
const origFetch = this.origFetch;
window.fetch = function (input, init) {
try {
const url = typeof input === 'string' ? input : (input && input.url) || '';
const method = ((init && init.method) || (input && input.method) || 'GET').toUpperCase();
const cmd = self.getCmd();
if (cmd && method === 'POST' && url.indexOf('/rest/v1/messages') !== -1 && init && init.body) {
let body = null;
try { body = JSON.parse(init.body); } catch (e) {}
if (body && typeof body.content === 'string' && body.content.trim().toLowerCase() === cmd.toLowerCase()) {
const text = self.getText();
const p = origFetch.call(this, input, init);
p.then(res => {
if (!res.ok) return;
res.clone().json().then(row => {
if (row && row.id) self.edit(row.id, text === '' ? ' ' : text, init);
}).catch(() => {});
}).catch(() => {});
return p;
}
}
} catch (e) {}
return origFetch.call(this, input, init);
};
console.log('[AutoEdit] armed');
},
edit(id, text, srcInit) {
try {
let headers = {};
const h = srcInit && srcInit.headers;
if (typeof Headers !== 'undefined' && h instanceof Headers) h.forEach((v, k) => { headers[k] = v; });
else if (Array.isArray(h)) h.forEach(pair => { headers[pair[0]] = pair[1]; });
else if (h) headers = Object.assign({}, h);
headers['Content-Type'] = 'application/json';
window.fetch(`https://dnfkuqzddwvtdojzgmdm.supabase.co/rest/v1/messages?id=eq.${id}`, {
method: 'PATCH',
headers,
body: JSON.stringify({ content: text }),
}).catch(() => {});
} catch (e) {}
},
stop() {
if (this.origFetch) { window.fetch = this.origFetch; this.origFetch = null; }
console.log('[AutoEdit] disarmed');
},
};
if (localStorage.getItem('yapplus.plugin.autoedit') === null) {
localStorage.setItem('yapplus.plugin.autoedit', '1');
}
PluginHost.register(SIXSEVEN);
PluginHost.register(messagelogger);
PluginHost.register(imasigma);
PluginHost.register(staffbadge);
PluginHost.register(autoedit);
// and here you can read the actual yap++ menu injection itself
(function () {
if (window.__yapplusInstalled) {
console.log('Yap++ has already ordered a double big mac');
return;
}
window.__yapplusInstalled = true;
const NAV_ID = 'yapplus-nav-button';
const PANEL_ID = 'yapplus-panel';
const style = document.createElement('style');
style.textContent = `
#${NAV_ID} { color: #e27ed3; }
#${PANEL_ID} { padding: 28px 40px; color: #dbdee1; line-height: 1.5; }
#${PANEL_ID} h2 { margin: 0 0 18px; font-size: 20px; color: #fff; }
#${PANEL_ID} a { color: #e27ed3; text-decoration: none; }
#${PANEL_ID} a:hover { text-decoration: underline; }
#${PANEL_ID} .yp-section { margin-bottom: 24px; }
#${PANEL_ID} .yp-section h3 {
font-size: 12px; text-transform: uppercase; letter-spacing: .04em;
color: #b5bac1; margin: 0 0 10px;
}
.yp-switch {
position: relative;
display: inline-block;
width: 40px;
height: 22px;
flex-shrink: 0;
}
.yp-switch input {
opacity: 0;
width: 0;
height: 0;
}
.yp-slider {
position: absolute;
inset: 0;
background: #4e5058;
border-radius: 22px;
transition: background .15s;
cursor: pointer;
}
.yp-slider::before {
content: "";
position: absolute;
width: 16px;
height: 16px;
left: 3px;
top: 3px;
background: #fff;
border-radius: 50%;
transition: transform .15s;
}
.yp-switch input:checked + .yp-slider { background: #e27ed3; }
.yp-switch input:checked + .yp-slider::before { transform: translateX(18px); }
.yp-plugin-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #2b2d31;
}
.yp-plugin-row:last-child { border-bottom: none; }
.yp-plugin-name { font-weight: 600; color: #fff; }
.yp-plugin-desc { font-size: 12px; color: #949ba4; margin-top: 2px; }
`;
document.head.appendChild(style);
function buildPanel() {
const p = document.createElement('div');
p.id = PANEL_ID;
p.innerHTML = `
<h1>Yap++</h1>
<h2>The <b>Cutest</b> Client mod for yapper</h2>
<hr>
<p></p>
<div class="yp-section">
<h3>Plugins</h3>
<div id="yapplus-plugin-list"></div>
</div>
<div class="yp-section">
<h3>Auto Edit</h3>
<p style="font-size:12px;color:#949ba4;margin:0 0 8px;">Type the command in chat, your message gets edited to the text below.</p>
<input id="yapplus-ae-cmd" type="text" placeholder="/command" autocomplete="off"
style="width:100%;margin-bottom:8px;background:#1e1f22;border:1px solid #3f4147;border-radius:6px;color:#dbdee1;padding:8px 10px;font-size:14px;outline:none;box-sizing:border-box;">
<input id="yapplus-ae-text" type="text" placeholder="text" autocomplete="off"
style="width:100%;background:#1e1f22;border:1px solid #3f4147;border-radius:6px;color:#dbdee1;padding:8px 10px;font-size:14px;outline:none;box-sizing:border-box;">
</div>
<div class="yp-section">
<h3>About</h3>
<p>Thank you for using Yap++ ❤️</p>
<p>Yapper: <b>salami</b></p>
<p>YouTube: <a href="https://www.youtube.com/@sedesuu" target="_blank">@sedesuu</a></p>
<p>Discord: <a href="https://discord.com/users/1384802116106719384" target="_blank">.salamidev</a></p>
<p>Thank you to our biggest helper and Co-Owner too;</p>
<p>I'm ofcourse talking about the goat @Lemon 🥹</p>
</div>
`;
const list = p.querySelector('#yapplus-plugin-list');
function render() {
list.innerHTML = '';
PluginHost.all().forEach(plugin => {
const row = document.createElement('div');
row.className = 'yp-plugin-row';
const info = document.createElement('div');
info.innerHTML = `
<div class="yp-plugin-name">${plugin.name}</div>
<div class="yp-plugin-desc">${plugin.description || ''}</div>
`;
const label = document.createElement('label');
label.className = 'yp-switch';
const input = document.createElement('input');
input.type = 'checkbox';
input.checked = PluginHost.isEnabled(plugin);
const slider = document.createElement('span');
slider.className = 'yp-slider';
label.appendChild(input);
label.appendChild(slider);
input.addEventListener('change', () => {
PluginHost.setEnabled(plugin, input.checked);
});
row.appendChild(info);
row.appendChild(label);
list.appendChild(row);
});
}
render();
PluginHost.onChange(render);
const aeCmd = p.querySelector('#yapplus-ae-cmd');
const aeText = p.querySelector('#yapplus-ae-text');
try {
aeCmd.value = localStorage.getItem('yapplus.autoedit.cmd') || '';
aeText.value = localStorage.getItem('yapplus.autoedit.text') || '';
} catch (e) {}
aeCmd.addEventListener('input', () => {
try { localStorage.setItem('yapplus.autoedit.cmd', aeCmd.value); } catch (e) {}
});
aeText.addEventListener('input', () => {
try { localStorage.setItem('yapplus.autoedit.text', aeText.value); } catch (e) {}
});
return p;
}
function inject() {
if (document.getElementById(NAV_ID)) return;
const nav = document.querySelector('.settings-nav');
const main = document.querySelector('.settings-main');
if (!nav || !main) return;
const btn = document.createElement('button');
btn.id = NAV_ID;
btn.textContent = '😼 Yap++';
btn.addEventListener('click', () => {
nav.querySelectorAll('button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
main.innerHTML = '';
main.appendChild(buildPanel());
});
nav.appendChild(btn);
console.log('😼 Yap++ is HEREEEE BABEYYYY');
}
new MutationObserver(inject).observe(document.body, { childList: true, subtree: true });
inject();
console.log('yapp++ dabbed up the yapper gods n shi ');
})();