>well y gotta admit TADC is still better than most real womanslop out there. gooseworx seems to be based, she doesnt shove her trans identity down everyones throats and just lives her quiet life
  • Welcome. Registering and having your account be approved may take a while since there's only one guy checking and approving accounts and guestposts
  • Boards are being moved around and added. Leave feedback in this thread and let jannies move your threads

Userscripts! Userscripts? Userscripts.

Fixed.
JavaScript:
// ==UserScript==
// @name         Soyjak.forum Live New Posts
// @namespace    https://greasyfork.org/
// @version      8.1.1
// @description  Polls XenForo for new thread posts every 5 seconds on the newest page only.
// @author       You
// @match        https://soyjak.forum/threads/*
// @match        https://*.soyjak.forum/threads/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function(){'use strict';

const INTERVAL=5000;
let busy=false;

const log=(...args)=>console.log('[Soyjak Live]',...args);
const getPosts=()=>[...document.querySelectorAll('.message.message--post')];

function getLastTimestamp(){
    const posts=getPosts();
    if(!posts.length)return null;
    const time=posts.at(-1).querySelector('time[data-timestamp]');
    if(!time){
        log('No timestamp found in last post:',posts.at(-1));
        return null;
    }
    const timestamp=Number(time.getAttribute('data-timestamp'));
    return Number.isFinite(timestamp)?timestamp:null;
}

function getCurrentPage(){
    const match=location.pathname.match(/\/page-(\d+)\/?$/);
    return match?Number(match[1]):1;
}

function getPageNumber(href){
    if(!href)return null;
    try{
        const url=new URL(href,location.href);
        const match=url.pathname.match(/\/page-(\d+)\/?$/);
        return match?Number(match[1]):1;
    }catch{
        return null;
    }
}

function getLastPage(){
    const current=getCurrentPage();
    const pages=[];

    document.querySelectorAll('.pageNav a[href]').forEach(link=>{
        const page=getPageNumber(link.getAttribute('href'));
        if(Number.isFinite(page))pages.push(page);
    });

    document.querySelectorAll('.pageNav-page,.pageNav-page--current').forEach(el=>{
        const text=el.textContent.trim();
        if(/^\d+$/.test(text))pages.push(Number(text));
    });

    return pages.length?Math.max(current,...pages):current;
}

function isLastPage(){
    const current=getCurrentPage();
    const last=getLastPage();
    log(`Pagination: current page ${current}, detected last page ${last}`);
    return current>=last;
}

function getNewPostsURL(after){
    const url=new URL(location.href);
    url.pathname=url.pathname.replace(/\/page-\d+\/?$/,'/');
    url.pathname=url.pathname.replace(/\/$/,'/new-posts');
    url.search='';
    url.searchParams.set('after',after);
    return url.href;
}

async function poll(){
    if(busy||!isLastPage())return;

    const after=getLastTimestamp();
    if(!after){
        log('Could not determine last post timestamp.');
        return;
    }

    busy=true;

    try{
        const url=getNewPostsURL(after);
        log('Checking:',url);

        const response=await fetch(url,{
            method:'GET',
            credentials:'same-origin',
            cache:'no-store',
            headers:{'X-Requested-With':'XMLHttpRequest'}
        });

        if(!response.ok)throw new Error(`HTTP ${response.status}`);

        const doc=new DOMParser().parseFromString(
            await response.text(),
            'text/html'
        );

        const newPosts=[
            ...doc.querySelectorAll('.message.message--post')
        ];

        if(!newPosts.length){
            log('No new posts.');
            return;
        }

        const container=document.querySelector('.block--messages');
        if(!container){
            log('Could not find message container.');
            return;
        }

        const existingIds=new Set(getPosts().map(post=>post.id));
        const freshPosts=newPosts.filter(post=>!existingIds.has(post.id));

        if(!freshPosts.length){
            log('Posts were already present.');
            return;
        }

        const atBottom=
            window.innerHeight+window.scrollY>=
            document.documentElement.scrollHeight-500;

        let insertAfter=getPosts().at(-1);

        for(const post of freshPosts){
            const imported=document.importNode(post,true);

            if(insertAfter)
                insertAfter.insertAdjacentElement('afterend',imported);
            else
                container.appendChild(imported);

            if(typeof XF!=='undefined'&&typeof XF.activate==='function')
                XF.activate(imported);

            insertAfter=imported;
        }

        log(`Inserted ${freshPosts.length} new post(s).`);

        if(atBottom)
            window.scrollTo({
                top:document.documentElement.scrollHeight,
                behavior:'smooth'
            });

    }catch(error){
        log('Error:',error);
    }finally{
        busy=false;
    }
}

if(!document.querySelector('.message.message--post'))return;

if(!isLastPage()){
    log('Not on the newest page. Live polling disabled.');
    return;
}

log('Started. Polling every 5 seconds.');
setTimeout(poll,1000);
setInterval(poll,INTERVAL);

})();
 
Fixed.
JavaScript:
// ==UserScript==
// @name         Soyjak.forum Live New Posts
// @namespace    https://greasyfork.org/
// @version      8.1.1
// @description  Polls XenForo for new thread posts every 5 seconds on the newest page only.
// @author       You
// @match        https://soyjak.forum/threads/*
// @match        https://*.soyjak.forum/threads/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function(){'use strict';

const INTERVAL=5000;
let busy=false;

const log=(...args)=>console.log('[Soyjak Live]',...args);
const getPosts=()=>[...document.querySelectorAll('.message.message--post')];

function getLastTimestamp(){
    const posts=getPosts();
    if(!posts.length)return null;
    const time=posts.at(-1).querySelector('time[data-timestamp]');
    if(!time){
        log('No timestamp found in last post:',posts.at(-1));
        return null;
    }
    const timestamp=Number(time.getAttribute('data-timestamp'));
    return Number.isFinite(timestamp)?timestamp:null;
}

function getCurrentPage(){
    const match=location.pathname.match(/\/page-(\d+)\/?$/);
    return match?Number(match[1]):1;
}

function getPageNumber(href){
    if(!href)return null;
    try{
        const url=new URL(href,location.href);
        const match=url.pathname.match(/\/page-(\d+)\/?$/);
        return match?Number(match[1]):1;
    }catch{
        return null;
    }
}

function getLastPage(){
    const current=getCurrentPage();
    const pages=[];

    document.querySelectorAll('.pageNav a[href]').forEach(link=>{
        const page=getPageNumber(link.getAttribute('href'));
        if(Number.isFinite(page))pages.push(page);
    });

    document.querySelectorAll('.pageNav-page,.pageNav-page--current').forEach(el=>{
        const text=el.textContent.trim();
        if(/^\d+$/.test(text))pages.push(Number(text));
    });

    return pages.length?Math.max(current,...pages):current;
}

function isLastPage(){
    const current=getCurrentPage();
    const last=getLastPage();
    log(`Pagination: current page ${current}, detected last page ${last}`);
    return current>=last;
}

function getNewPostsURL(after){
    const url=new URL(location.href);
    url.pathname=url.pathname.replace(/\/page-\d+\/?$/,'/');
    url.pathname=url.pathname.replace(/\/$/,'/new-posts');
    url.search='';
    url.searchParams.set('after',after);
    return url.href;
}

async function poll(){
    if(busy||!isLastPage())return;

    const after=getLastTimestamp();
    if(!after){
        log('Could not determine last post timestamp.');
        return;
    }

    busy=true;

    try{
        const url=getNewPostsURL(after);
        log('Checking:',url);

        const response=await fetch(url,{
            method:'GET',
            credentials:'same-origin',
            cache:'no-store',
            headers:{'X-Requested-With':'XMLHttpRequest'}
        });

        if(!response.ok)throw new Error(`HTTP ${response.status}`);

        const doc=new DOMParser().parseFromString(
            await response.text(),
            'text/html'
        );

        const newPosts=[
            ...doc.querySelectorAll('.message.message--post')
        ];

        if(!newPosts.length){
            log('No new posts.');
            return;
        }

        const container=document.querySelector('.block--messages');
        if(!container){
            log('Could not find message container.');
            return;
        }

        const existingIds=new Set(getPosts().map(post=>post.id));
        const freshPosts=newPosts.filter(post=>!existingIds.has(post.id));

        if(!freshPosts.length){
            log('Posts were already present.');
            return;
        }

        const atBottom=
            window.innerHeight+window.scrollY>=
            document.documentElement.scrollHeight-500;

        let insertAfter=getPosts().at(-1);

        for(const post of freshPosts){
            const imported=document.importNode(post,true);

            if(insertAfter)
                insertAfter.insertAdjacentElement('afterend',imported);
            else
                container.appendChild(imported);

            if(typeof XF!=='undefined'&&typeof XF.activate==='function')
                XF.activate(imported);

            insertAfter=imported;
        }

        log(`Inserted ${freshPosts.length} new post(s).`);

        if(atBottom)
            window.scrollTo({
                top:document.documentElement.scrollHeight,
                behavior:'smooth'
            });

    }catch(error){
        log('Error:',error);
    }finally{
        busy=false;
    }
}

if(!document.querySelector('.message.message--post'))return;

if(!isLastPage()){
    log('Not on the newest page. Live polling disabled.');
    return;
}

log('Started. Polling every 5 seconds.');
setTimeout(poll,1000);
setInterval(poll,INTERVAL);

})();
[addscript=now]
 
Back
Top Bottom