NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name kbin Feed Curator
// @version 0.2
// @namespace https://greasyfork.org/users/19234
// @description Hides posts with blacklisted keywords from the main feed.
// @author fiofiofio
// @match https://kbin.social/*
// @match https://fedia.io/*
// @match https://karab.in/*
// @match https://www.kbin.cafe/*
// @match https://karab.in/*
// @match https://readit.buzz/*
// @match https://forum.fail/*
// @match https://fedi196.gay/*
// @match https://feddit.online/*
// @match https://kbin.run/*
// @match https://nadajnik.org/*
// @match https://kbin.cafe/*
// @match https://kbin.lol/*
// @match https://nerdbin.social/*
// @match https://kbin.lgbt/*
// @match https://kbin.place/*
// @match https://kopnij.in/*
// @match https://kbin.sh/*
// @match https://kayb.ee/*
// @match https://wiku.hu/*
// @match https://kbin.chat/*
// @match https://fediverse.boo/*
// @match https://tuna.cat/*
// @match https://kbin.dk/*
// @match https://sacredori.net/*
// @grant GM_log
// @require https://github.com/Oricul/kbin-scripts/raw/main/kbin-mod-options.js
// @license MIT
// ==/UserScript==
(function () {
var blacklist = JSON.parse(localStorage.getItem('blacklist')) || [];
const style = document.createElement('style');
style.textContent = `
.hide-post {padding:.5rem 2rem;}
.hidden-note {margin-top:1rem; font-style:italic}
`;
document.head.appendChild(style);
// Settings
const feedCuratorHeader = kmoAddHeader(
'kbin Feed Curator', {
author: 'fiofiofio',
version: '0.1',
license: 'MIT',
url: 'https://greasyfork.org/users/19234'
}
);
const placeholderToggle = kmoAddToggle(
feedCuratorHeader,
'Show post placeholder?',
true,
'Show or hide the placeholder for blocked posts.'
);
const blacklistButton = kmoAddButton(
feedCuratorHeader,
'Blacklist',
'Edit',
'Open blacklist editor.'
);
// Save toggle state when clicked
placeholderToggle.addEventListener('click', () => {
localStorage.setItem('placeholderToggle', placeholderToggle.checked);
});
var toggleValue = localStorage.getItem('placeholderToggle');
if (toggleValue !== null) {
placeholderToggle.checked = JSON.parse(toggleValue);
}
// Open the blacklist editor when the button is clicked
blacklistButton.addEventListener('click', function () {
createBlacklistDialog();
document.getElementById("blacklistDialog").showModal();
});
function createBlacklistDialog() {
const modalDiv = document.createElement('dialog');
modalDiv.id = "blacklistDialog";
// Generate a list of current blacklist words
const blacklistItems = blacklist.map(word => `<li>${word} <button class="remove-button" data-word="${word}">Remove</button></li>`).join('');
modalDiv.innerHTML = `
<form id="blacklistForm">
<p>Current blacklist:</p>
<ul id="blacklistItems">${blacklistItems}</ul>
<label for="blacklistInput">Enter words to add to the blacklist (comma-separated):</label>
<input type="text" id="blacklistInput" required>
<div class="buttons">
<button type="submit">Update Blacklist</button>
<button type="button" id="cancelButton">Cancel</button>
</div>
</form>`;
document.body.appendChild(modalDiv);
const form = modalDiv.querySelector('#blacklistForm');
const input = modalDiv.querySelector('#blacklistInput');
const buttons = modalDiv.querySelector('.buttons');
const cancelButton = modalDiv.querySelector('#cancelButton');
const removeButtons = modalDiv.querySelectorAll('.remove-button');
// Add CSS styles to the dialog element
modalDiv.style.backgroundColor = 'var(--kbin-primary-color)';
modalDiv.style.color = 'var(--kbin-text-color)';
modalDiv.style.fontFamily = 'var(--kbin-body-font-family)';
modalDiv.style.border = 'var(--kbin-section-border)';
if (removeButtons.length > 0) {
removeButtons.forEach(button => {
button.style.backgroundColor = 'var(--kbin-vote-bg)';
button.style.color = 'var(--kbin-vote-text-color)';
button.style.marginLeft = 'var(--kbin-entry-element-spacing)';
button.style.padding = '2px';
button.style.border = 'var(--kbin-section-border)';
button.addEventListener('click', function () {
const wordToRemove = button.dataset.word;
blacklist = blacklist.filter(word => word !== wordToRemove);
localStorage.setItem('blacklist', JSON.stringify(blacklist));
modalDiv.remove();
createBlacklistDialog();
document.getElementById("blacklistDialog").showModal();
});
});
}
buttons.querySelectorAll("button").forEach(button => {
button.style.backgroundColor = 'var(--kbin-vote-bg)';
button.style.color = 'var(--kbin-vote-text-color)';
button.style.margin = 'var(--kbin-entry-element-spacing)';
button.style.marginLeft = '0';
button.style.padding = '10px';
button.style.border = 'var(--kbin-section-border)'
});
form.addEventListener('submit', function (event) {
event.preventDefault();
var userInput = input.value;
var userWords = userInput.split(',').map(word => word.trim());
blacklist = blacklist.concat(userWords);
localStorage.setItem('blacklist', JSON.stringify(blacklist));
modalDiv.close();
location.reload();
});
cancelButton.addEventListener('click', function () {
modalDiv.close();
});
}
function hidePosts() {
var magazineName = getMagazineName();
document.querySelectorAll("article").forEach(article => {
const header = article.getElementsByTagName("header")[0]?.textContent;
const description = article.getElementsByClassName("short-desc")[0]?.textContent;
// If not on a magazine page, get magazine name from article
if (magazineName === undefined) {
magazineName = article.querySelector('.magazine-inline')?.getAttribute('title')?.toLowerCase();
}
if (checkText(header) || checkText(description) || checkMagazineName(magazineName)) {
if (placeholderToggle.checked) {
let keywords = checkText(header)?.join(", ") || checkText(description)?.join(", ") || magazineName;
const noteClassName = "hidden-note";
article.classList.add("hide-post");
article.classList.remove("entry");
article.innerHTML = `<p class="${noteClassName}">Post hidden containing keywords: ${keywords}</p>`;
}
else {
article.style.display = "none";
}
}
});
}
// If on a magazine page, get the magazine name from the sidebar
function getMagazineName() {
const pageURL = document.querySelector('meta[property="og:url"]').getAttribute("content");
const nameRegex = new RegExp(`/\\/m\\//`, 'i');
return nameRegex.test(pageURL) ?
document.querySelector(".magazine__name").textContent.toLowerCase() :
null;
}
// Checks given string for presence of blacklisted words
function checkText(string) {
const matchedWords = blacklist.filter(word => string?.toLowerCase().includes(word.toLowerCase()));
return matchedWords.length > 0 ? matchedWords : null;
}
// Checks magazine name for blacklisted words
function checkMagazineName(string) {
return blacklist.some(word => {
const wordRegex = new RegExp(`\\b${word}\\b`, 'i');
return wordRegex.test(string);
});
}
// Wait for the document to load
window.addEventListener('load', function () {
hidePosts();
});
})();