// Translator credits and promo lines that sites paste into every chapter // ("Translator: …", "Discord: https://…", "Join the Discord…", ko-fi/Patreon). // A paragraph is dropped only when it is short and the whole thing is promo, so // story text that happens to mention a word like "discord" stays. // The illustrator's reader.js carries the same rule for in-app reading. const MAX_PROMO_LENGTH = 200; const URL = /https?:\/\/\S+|\b(dsc|discord)\.gg\/\S*/gi; const MAX_LINK_LABEL = 60; // "Discord: ", "Ko-Fi: ", but not prose with a link const PROMO = [ /^(discord|patreon|ko-?fi)\b\s*:?/i, /\b(join|support)\b.{0,40}\b(discord|patreon|ko-?fi)\b/i, /\bko-?fi\b|\bpatreon\b/i, /^(translators?|translated by|translation|editors?|edited by|editing|proofreaders?|proofread by|tl|tln|pr|ed)(\s*\/\s*\w+)*\s*[:-]/i, /\bread (the )?(latest|advanced?|more) chapters?\b/i, ]; export const isPromo = text => { const t = text.replace(/\s+/g, ' ').trim(); if (!t || t.length > MAX_PROMO_LENGTH) return false; const label = t.replace(URL, '').trim(); if (label.length < t.length && label.length <= MAX_LINK_LABEL) return true; return PROMO.some(r => r.test(t)); }; /** Remove promo paragraphs from a cheerio document; returns how many. */ export const stripPromo = $ => { let removed = 0; $('p, div, h3, h4, h5, h6, li').each((_, el) => { const node = $(el); // Innermost blocks only, so a wrapper holding the whole chapter is never dropped. if (node.find('p, div, li').length) return; if (isPromo(node.text())) { node.remove(); removed++; } }); return removed; };