Skip to content

Commit 7783a8a

Browse files
steveseguinactions-user
authored andcommitted
feat(chat): Add configurable word blocking for messages
- Introduce a new "blockedwords" URL parameter and associated settings in `popup.html`. - Implement client-side logic in `dock.html` to filter incoming chat messages and usernames against the configured list of blocked words. - Messages or names containing blocked words are either blurred (if a blurring mode is active) or completely hidden from view. - Provide an intuitive user interface in `popup.html` with an input field and dynamic tags for adding and removing words/phrases to be blocked. - Develop `popup.js` functions (`updateBlockedWordsList`, `addBlockedWord`, `removeBlockedWord`, `setupBlockedWordsInput`) to manage this interactive tag system for blocked words. - This feature enhances content moderation capabilities, giving users more control over displayed chat content. [auto-enhanced]
1 parent e794b64 commit 7783a8a

File tree

3 files changed

+139
-2
lines changed

3 files changed

+139
-2
lines changed

dock.html

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3160,7 +3160,12 @@ <h3>Status</h3>
31603160
if (urlParams.get("hidefrom") || urlParams.get("exclude")) {
31613161
hideFrom = (urlParams.get("hidefrom") || urlParams.get("exclude")).toLowerCase().trim().split(",");
31623162
}
3163-
3163+
3164+
var blockedWords = [];
3165+
if (urlParams.get("blockedwords")) {
3166+
blockedWords = urlParams.get("blockedwords").toLowerCase().split(",").map(w => w.trim()).filter(w => w);
3167+
}
3168+
31643169
var trivialevents = false;
31653170
if (urlParams.has("trivialevents")) {
31663171
trivialevents = true;
@@ -8717,6 +8722,22 @@ <h2 id="messagesFor">Messages</h2>\
87178722
data.chatmessage = data.reply;
87188723
}
87198724

8725+
// Block messages containing specific words/phrases
8726+
if (blockedWords.length) {
8727+
const messageText = (data.chatmessage || '').toLowerCase();
8728+
const nameText = (data.chatname || '').toLowerCase();
8729+
for (const word of blockedWords) {
8730+
if (messageText.includes(word) || nameText.includes(word)) {
8731+
if (blurred) {
8732+
makeBlurred = true;
8733+
} else {
8734+
return;
8735+
}
8736+
break;
8737+
}
8738+
}
8739+
}
8740+
87208741
if (data.chatmessage && data.chatmessage.startsWith("!")) {
87218742
if (filtercommands){
87228743
return;

popup.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,6 +1895,21 @@ <h2 class="streaming_chat_title">
18951895
</label>
18961896
<span data-translate="Exclude filtered from auto-show">🥅 Exclude filtered messages from auto-selecting </span>
18971897
</div>
1898+
<div data-keywords="filter block hide words phrases text content moderation blacklist blocklist censor mute suppress NOALBS">
1899+
<label class="switch">
1900+
<input type="checkbox" data-param1="blockedwords" />
1901+
<span class="slider round"></span>
1902+
</label>
1903+
<span>🚫 Block messages containing specific words/phrases</span>
1904+
<div class="isolate" style="margin-left: 50px; margin-top: 5px;">
1905+
<div class="blockedwords-list-container source-list-container" id="blockedwordsList"></div>
1906+
<div class="add-blockedword-container">
1907+
<input type="text" id="newBlockedWord" placeholder="Word or phrase to block" style="width: 180px;" />
1908+
<button id="addBlockedWord">Add</button>
1909+
</div>
1910+
<input type="text" id="blockedwordsInput" class="textInput hidden" data-textparam1="blockedwords" style="display: none;" />
1911+
</div>
1912+
</div>
18981913
<div data-keywords="donation scrub anonymize remove tip amount hide value private">
18991914
<label class="switch">
19001915
<input type="checkbox" data-param1="stripdonations" />

popup.js

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,99 @@ function updateSourceTypeList(type) {
10201020
`).join('');
10211021
}
10221022

1023+
// Blocked words tag system functions
1024+
function updateBlockedWordsList() {
1025+
const input = document.getElementById('blockedwordsInput');
1026+
const list = document.getElementById('blockedwordsList');
1027+
if (!input || !list) return;
1028+
1029+
const words = input.value.split(',')
1030+
.map(w => w.trim())
1031+
.filter(w => w);
1032+
1033+
list.innerHTML = words.map(word => `
1034+
<div class="username-tag">
1035+
<span>${escapeHtml(word)}</span>
1036+
<button class="remove-blockedword" data-word="${escapeHtml(word)}">×</button>
1037+
</div>
1038+
`).join('');
1039+
}
1040+
1041+
function escapeHtml(text) {
1042+
const div = document.createElement('div');
1043+
div.textContent = text;
1044+
return div.innerHTML;
1045+
}
1046+
1047+
function addBlockedWord(word) {
1048+
const input = document.getElementById('blockedwordsInput');
1049+
if (!input || !word) return;
1050+
1051+
const words = input.value.split(',').map(w => w.trim()).filter(w => w);
1052+
const trimmedWord = word.trim();
1053+
1054+
if (trimmedWord && !words.some(w => w.toLowerCase() === trimmedWord.toLowerCase())) {
1055+
words.push(trimmedWord);
1056+
input.value = words.join(', ');
1057+
updateBlockedWordsList();
1058+
updateSettings(input);
1059+
}
1060+
}
1061+
1062+
function removeBlockedWord(word) {
1063+
const input = document.getElementById('blockedwordsInput');
1064+
if (!input) return;
1065+
1066+
const words = input.value.split(',').map(w => w.trim()).filter(w => w);
1067+
const index = words.findIndex(w => w.toLowerCase() === word.toLowerCase());
1068+
1069+
if (index > -1) {
1070+
words.splice(index, 1);
1071+
input.value = words.join(', ');
1072+
updateBlockedWordsList();
1073+
updateSettings(input);
1074+
}
1075+
}
1076+
1077+
function setupBlockedWordsInput() {
1078+
const list = document.getElementById('blockedwordsList');
1079+
const addBtn = document.getElementById('addBlockedWord');
1080+
const newWordInput = document.getElementById('newBlockedWord');
1081+
1082+
if (!list || !addBtn || !newWordInput) return;
1083+
1084+
// Handle clicking remove button on tags
1085+
list.addEventListener('click', (e) => {
1086+
if (e.target.classList.contains('remove-blockedword')) {
1087+
removeBlockedWord(e.target.dataset.word);
1088+
}
1089+
});
1090+
1091+
// Handle add button click
1092+
addBtn.addEventListener('click', () => {
1093+
const word = newWordInput.value.trim();
1094+
if (word) {
1095+
addBlockedWord(word);
1096+
newWordInput.value = '';
1097+
}
1098+
});
1099+
1100+
// Handle Enter key in input
1101+
newWordInput.addEventListener('keypress', (e) => {
1102+
if (e.key === 'Enter') {
1103+
e.preventDefault();
1104+
const word = newWordInput.value.trim();
1105+
if (word) {
1106+
addBlockedWord(word);
1107+
newWordInput.value = '';
1108+
}
1109+
}
1110+
});
1111+
1112+
// Initialize the list from any existing value
1113+
updateBlockedWordsList();
1114+
}
1115+
10231116
// Function to setup source selection for a given input
10241117
function setupSourceSelection(inputId, isSettingBased = false) {
10251118
const input = isSettingBased ?
@@ -2114,6 +2207,11 @@ function processObjectSetting(key, settingObj, sync, paramNums, response) { // A
21142207
if (paramEle && paramEle.checked) {
21152208
updateSettings(paramEle, false, settingObj[textParamKey]);
21162209
}
2210+
2211+
// Refresh blocked words tag list if this is the blockedwords input
2212+
if (key === 'blockedwords') {
2213+
updateBlockedWordsList();
2214+
}
21172215
}
21182216
}
21192217

@@ -5469,7 +5567,10 @@ document.addEventListener("DOMContentLoaded", async function(event) {
54695567

54705568
// Initialize ProfileManager after DOM is ready
54715569
ProfileManager.init();
5472-
5570+
5571+
// Initialize blocked words tag input
5572+
setupBlockedWordsInput();
5573+
54735574
// Add event listener for save profile button
54745575
const saveProfileBtn = document.querySelector('button[data-action="saveProfile"]');
54755576
if (saveProfileBtn) {

0 commit comments

Comments
 (0)