- Joined
- Jan 4, 2021
Yesterday I made a thread that teaches you how to calculate your reaction score, the score that used to be displayed on user profiles that was basically a Reddit karma of sorts. One day, Null nuked both this score as well as the ability to receive notifications in your tray when you are given a sticker.
I, and many people, used the reaction notifications as a way of navigating the site. Null disagreed with this method of navigation, but after the notifications were disabled I started clicking the "reactions received" page like every 10 minutes when using the site. Having to click twice to see my reactions isn't horrible, but it triggers my autism enough to warrant building a fix for it.
So here is a little userscript I (and chatGPT) wrote that adds a new tray to the navigation menu on the site that displays a menu of your received reactions:

It's not perfect and might have some retarded bugs since I suck at javascript. If you encounter any please let me know here.
Features I want to add:
Automatic calculation of reaction score
Get the actual sticker images built into it
Get a better menu icon than the "R"
Add an option to put the notifications in your regular alert tray
I also want to make more general client improvements that don't have to do with stickers, but my sticker autism has been off the charts recently so I made this. I'll post any new scripts or updates I make here.
please dont ban me josh i just like my stickers
I, and many people, used the reaction notifications as a way of navigating the site. Null disagreed with this method of navigation, but after the notifications were disabled I started clicking the "reactions received" page like every 10 minutes when using the site. Having to click twice to see my reactions isn't horrible, but it triggers my autism enough to warrant building a fix for it.
So here is a little userscript I (and chatGPT) wrote that adds a new tray to the navigation menu on the site that displays a menu of your received reactions:

It's not perfect and might have some retarded bugs since I suck at javascript. If you encounter any please let me know here.
JavaScript:
// ==UserScript==
// @name Kiwifarms Reaction Tray
// @namespace https://kiwifarms.st/
// @version 2.6
// @description Add a reaction recieved tray to the kiwi farms nav bar
// @author gagabobo1997
// @match https://kiwifarms.st/*
// @grant none
// @sneed chuck
// ==/UserScript==
(function() {
'use strict';
const REACTIONS_URL = 'https://kiwifarms.st/account/reactions';
const ALL_REACTIONS_FINAL_PAGE_URL = 'https://kiwifarms.st/account/reactions?reaction_id=0&page=100000000';
const style = document.createElement('style');
style.innerHTML = `
.p-navgroup-link--reactions {
display: inline-flex;
align-items: center;
justify-content: center;
vertical-align: middle;
min-width: 38px;
min-height: 38px;
}
.p-navgroup-link--reactions i {
font-size: 1em;
font-style: normal;
font-weight: normal;
}
.js-reactionsMenuBody {
max-height: 600px;
overflow-y: auto;
}
.js-reactionsMenuBody .reaction-item {
padding: 8px 10px;
border-bottom: 1px solid #444;
}
.js-reactionsMenuBody .reaction-item a {
text-decoration: none;
}
`;
document.head.appendChild(style);
function createReactionsNav() {
const pAccountNavGroup = document.querySelector('.p-navgroup.p-account.p-navgroup--member');
if (!pAccountNavGroup) return;
const link = document.createElement('a');
link.href = '#';
link.classList.add(
'p-navgroup-link',
'p-navgroup-link--iconic',
'p-navgroup-link--reactions',
'js-badge--reactions',
'badgeContainer'
);
link.setAttribute('data-badge', '0');
link.setAttribute('data-xf-click', 'menu');
link.setAttribute('data-menu-pos-ref', '< .p-navgroup');
link.setAttribute('aria-label', 'Reactions');
link.setAttribute('aria-expanded', 'false');
link.setAttribute('aria-haspopup', 'true');
const icon = document.createElement('i');
icon.textContent = 'R';
link.appendChild(icon);
const span = document.createElement('span');
span.classList.add('p-navgroup-linkText');
link.appendChild(span);
const menuDiv = document.createElement('div');
menuDiv.classList.add('menu', 'menu--structural', 'menu--medium');
menuDiv.setAttribute('data-menu', 'menu');
menuDiv.setAttribute('aria-hidden', 'true');
menuDiv.setAttribute('data-nocache', 'true');
const menuContent = document.createElement('div');
menuContent.classList.add('menu-content');
const header = document.createElement('h3');
header.classList.add('menu-header');
header.textContent = 'Reactions';
menuContent.appendChild(header);
const body = document.createElement('div');
body.classList.add('js-reactionsMenuBody');
body.innerHTML = '<div class="menu-row">Loading…</div>';
menuContent.appendChild(body);
const menuFooter = document.createElement('div');
menuFooter.classList.add('menu-footer', 'menu-footer--split');
menuFooter.innerHTML = `
<div class="menu-footer-main">
<ul class="listInline listInline--bullet">
<li><a href="${REACTIONS_URL}" target="_blank">Show all</a></li>
<li class="js-reactionTotal">Total Reactions: (loading...)</li>
</ul>
</div>
`;
menuContent.appendChild(menuFooter);
menuDiv.appendChild(menuContent);
const inboxLink = pAccountNavGroup.querySelector('.p-navgroup-link--conversations');
if (inboxLink) {
pAccountNavGroup.insertBefore(link, inboxLink);
pAccountNavGroup.insertBefore(menuDiv, inboxLink);
} else {
pAccountNavGroup.appendChild(link);
pAccountNavGroup.appendChild(menuDiv);
}
}
async function calculateTotalReactions() {
try {
const response = await fetch(ALL_REACTIONS_FINAL_PAGE_URL);
if (!response.ok) {
throw new Error(`Status: ${response.status}`);
}
const finalUrl = response.url;
const match = finalUrl.match(/[?&]page=(\d+)/);
let pageNum = 1;
if (match && match[1]) {
pageNum = parseInt(match[1], 10);
}
const text = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(text, 'text/html');
const finalPageReactions = doc.querySelectorAll('.js-reactionList-0 .block-row').length;
return ((pageNum - 1) * 20) + finalPageReactions;
} catch (err) {
console.error('Failed to calculate total reactions:', err);
return null;
}
}
async function fetchAndDisplayReactions() {
const bodyContainer = document.querySelector('.js-reactionsMenuBody');
if (!bodyContainer) return;
try {
const response = await fetch(REACTIONS_URL);
if (!response.ok) {
throw new Error(`Status: ${response.status}`);
}
const text = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(text, 'text/html');
const reactionsList = doc.querySelectorAll('.js-reactionList-0 .block-row');
const reactions = Array.from(reactionsList).map((row) => {
const userElem = row.querySelector('.contentRow-title .username');
const userName = userElem?.textContent.trim() || 'Unknown';
const userProfileUrl = userElem?.getAttribute('href') || '#';
const threadElem = row.querySelector('.contentRow-title a[href^="/threads/"]');
const postElem = row.querySelector('.contentRow-title a[href^="/posts/"]');
const threadTitle = threadElem?.textContent.trim() || 'Unknown thread';
const postUrl = postElem?.getAttribute('href') || '#';
const reactionType = row.querySelector('.reaction-text bdi')?.textContent.trim() || '??';
const timeAttr = row.querySelector('.contentRow-minor time')?.getAttribute('title') || 'N/A';
return {
userName,
userProfileUrl,
threadTitle,
postUrl,
reactionType,
timeAttr
};
});
if (reactions.length === 0) {
bodyContainer.innerHTML = '<div class="menu-row">No reactions found.</div>';
} else {
let html = '';
reactions.forEach((r) => {
html += `
<div class="reaction-item">
<strong>
<a href="${r.userProfileUrl}" target="_blank" style="color: #ffdc00;">
${r.userName}
</a>
</strong> reacted with
<strong style="color: #00ff7f;">${r.reactionType}</strong>
<br>on your post in:
<a href="${r.postUrl}" target="_blank" style="color: #87ceeb;">
${r.threadTitle}
</a>
<br>
<small style="color: #b0b0b0;">${r.timeAttr}</small>
</div>
`;
});
bodyContainer.innerHTML = html;
}
const total = await calculateTotalReactions();
const totalEl = document.querySelector('.js-reactionTotal');
if (totalEl) {
totalEl.textContent = (total === null)
? 'Total Reactions: (error)'
: `Total Reactions: ${total}`;
}
} catch (error) {
console.error('Failed to fetch reactions:', error);
bodyContainer.innerHTML = '<div class="menu-row" style="color: red;">Error loading reactions.</div>';
}
}
function init() {
createReactionsNav();
fetchAndDisplayReactions();
setInterval(fetchAndDisplayReactions, 60_000);
}
init();
})();
Features I want to add:
Automatic calculation of reaction score
Get the actual sticker images built into it
Get a better menu icon than the "R"
Add an option to put the notifications in your regular alert tray
I also want to make more general client improvements that don't have to do with stickers, but my sticker autism has been off the charts recently so I made this. I'll post any new scripts or updates I make here.
please dont ban me josh i just like my stickers
Last edited: