// ==UserScript==
// @name Facebook Comment Sorter
// @namespace CustomScripts
// @description Forces Facebook comments to show "All Comments" or "Newest" instead of "Most Relevant"
// @author areen-c
// @match *://*.facebook.com/*
// @version 1.0
// @license MIT
// @icon https://www.google.com/s2/favicons?sz=64&domain=facebook.com
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
'use strict';
// CHANGE THIS VALUE TO "newest" IF YOU WANT NEWEST COMMENTS INSTEAD
const sortPreference = "all"; // Options: "newest", "all"
const processedUrls = new Set();
const processedButtons = new WeakSet();
const sortButtonTexts = {
newest: [
'newest', 'terbaru', 'most recent', 'recent',
'más recientes', 'reciente',
'plus récents', 'récent',
'neueste', 'aktuellste',
'mais recentes', 'recente',
'più recenti', 'recente',
'nieuwste', 'recent',
'новейшие', 'недавние',
'最新', '新的',
'最新', '新しい',
'الأحدث', 'حديث',
'नवीनतम', 'हाल का'
],
all: [
'all comments', 'semua komentar', 'all',
'todos los comentarios', 'todos',
'tous les commentaires', 'tous',
'alle kommentare', 'alle',
'todos os comentários', 'todos',
'tutti i commenti', 'tutti',
'alle reacties', 'alle',
'все комментарии', 'все',
'所有评论', '全部',
'すべてのコメント', 'すべて',
'كل التعليقات', 'الكل',
'सभी टिप्पणियां', 'सभी'
],
default: [
'most relevant', 'paling relevan', 'relevan', 'most popular', 'komentar teratas', 'oldest',
'más relevantes', 'relevante', 'más populares',
'plus pertinents', 'pertinent', 'plus populaires',
'relevanteste', 'beliebteste',
'mais relevantes', 'relevante', 'mais populares',
'più rilevanti', 'rilevante', 'più popolari',
'meest relevant', 'relevant', 'populairste',
'наиболее релевантные', 'популярные',
'最相关', '最热门',
'最も関連性の高い', '人気',
'الأكثر صلة', 'الأكثر شعبية',
'सबसे उपयुक्त', 'सबसे लोकप्रिय'
]
};
function findAndClickSortButtons() {
const potentialButtons = document.querySelectorAll('div[role="button"], span[role="button"]');
for (const button of potentialButtons) {
if (!button || !button.textContent || processedButtons.has(button)) continue;
const text = button.textContent.toLowerCase().trim();
if (sortButtonTexts.default.some(sortText => text.includes(sortText)) ||
text.includes('sort') || text.includes('urut') || text.includes('filter')) {
try {
processedButtons.add(button);
button.click();
setTimeout(() => {
const menuItems = document.querySelectorAll('[role="menuitem"], [role="menuitemradio"], [role="radio"]');
const targetTexts = sortPreference === "newest" ? sortButtonTexts.newest : sortButtonTexts.all;
if (menuItems.length === 0) {
processedButtons.delete(button);
return;
}
let found = false;
let targetItem = null;
for (const item of menuItems) {
if (!item.textContent) continue;
const itemText = item.textContent.toLowerCase().trim();
if (targetTexts.some(target => itemText === target)) {
targetItem = item;
found = true;
break;
}
}
if (!found) {
for (const item of menuItems) {
if (!item.textContent) continue;
const itemText = item.textContent.toLowerCase().trim();
if (targetTexts.some(target => itemText.includes(target))) {
targetItem = item;
found = true;
break;
}
}
}
if (!found) {
if (sortPreference === "newest" && menuItems.length >= 2) {
targetItem = menuItems[1];
found = true;
} else if (sortPreference === "all" && menuItems.length >= 3) {
targetItem = menuItems[2];
found = true;
} else if (menuItems.length >= 1) {
targetItem = menuItems[menuItems.length - 1];
found = true;
}
}
if (found && targetItem) {
targetItem.click();
} else {
processedButtons.delete(button);
}
}, 500);
} catch (error) {
processedButtons.delete(button);
}
}
}
}
function setupRequestIntercepts() {
const paramMappings = {
"newest": {
'feedback_filter': 'stream',
'order_by': 'time',
'comment_order': 'chronological',
'filter': 'stream',
'comment_filter': 'stream'
},
"all": {
'feedback_filter': 'all',
'order_by': 'ranked',
'comment_order': 'ranked_threaded',
'filter': 'all',
'comment_filter': 'all'
}
};
const params = paramMappings[sortPreference];
const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
if (typeof url === 'string' && !processedUrls.has(url)) {
if ((url.includes('/api/graphql/') || url.includes('feedback')) &&
(url.includes('comment') || url.includes('Comment'))) {
let modifiedUrl = url;
for (const [key, value] of Object.entries(params)) {
if (modifiedUrl.includes(`${key}=`)) {
modifiedUrl = modifiedUrl.replace(new RegExp(`${key}=([^&]*)`, 'g'), `${key}=${value}`);
} else {
modifiedUrl += (modifiedUrl.includes('?') ? '&' : '?') + `${key}=${value}`;
}
}
processedUrls.add(modifiedUrl);
return originalOpen.apply(this, [method, modifiedUrl]);
}
}
return originalOpen.apply(this, arguments);
};
if (window.fetch) {
const originalFetch = window.fetch;
window.fetch = function(resource, init) {
if (resource && typeof resource === 'string' && !processedUrls.has(resource)) {
if ((resource.includes('/api/graphql/') || resource.includes('feedback')) &&
(resource.includes('comment') || resource.includes('Comment'))) {
let modifiedUrl = resource;
for (const [key, value] of Object.entries(params)) {
if (modifiedUrl.includes(`${key}=`)) {
modifiedUrl = modifiedUrl.replace(new RegExp(`${key}=([^&]*)`, 'g'), `${key}=${value}`);
} else {
modifiedUrl += (modifiedUrl.includes('?') ? '&' : '?') + `${key}=${value}`;
}
}
processedUrls.add(modifiedUrl);
return originalFetch.call(this, modifiedUrl, init);
}
}
return originalFetch.apply(this, arguments);
};
}
}
function initialize() {
setupRequestIntercepts();
setTimeout(findAndClickSortButtons, 2000);
setInterval(findAndClickSortButtons, 5000);
let lastUrl = location.href;
new MutationObserver(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
setTimeout(findAndClickSortButtons, 2000);
processedUrls.clear();
}
}).observe(document, {subtree: true, childList: true});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
})();