Adiciona botões flutuantes para acessar rapidamente artigos bloqueados por paywall em dezenas de sites de notícias e mídia brasileiros e alguns internacionais, utilizando 12ft.io e Archive.is. Desenvolvido para funcionar de forma robusta em Single Page Applications (SPAs) e com carregamento dinâmico de conteúdo.
当前为
// ==UserScript==
// @name Quebra-Paywall BR (12ft.io & Archive.is)
// @namespace http://tampermonkey.net/
// @version 1.2
// @description Adiciona botões flutuantes para acessar rapidamente artigos bloqueados por paywall em dezenas de sites de notícias e mídia brasileiros e alguns internacionais, utilizando 12ft.io e Archive.is. Desenvolvido para funcionar de forma robusta em Single Page Applications (SPAs) e com carregamento dinâmico de conteúdo.
// @author Bruno Fortunato & Colaboradores (Comunidade Gemini/IA)
// @homepage https://github.com/BrunoFortunato/Quebra-Paywall-BR
// @license MIT
// @match *://*.folha.uol.com.br/*
// @match *://*.estadao.com.br/*
// @match *://*.oglobo.globo.com/*
// @match *://*.valor.globo.com.br/*
// @match *://*.gazetadopovo.com.br/*
// @match *://*.correiobraziliense.com.br/*
// @match *://*.uol.com.br/*
// @match *://*.gauchazh.clicrbs.com.br/*
// @match *://*.nsctotal.com.br/*
// @match *://*.diariocatarinense.clicrbs.com.br/*
// @match *://*.jornaldocomercio.com/*
// @match *://*.jc.ne10.uol.com.br/*
// @match *://*.em.com.br/*
// @match *://*.otempo.com.br/*
// @match *://*.jota.info/*
// @match *://*.poder360.com.br/*
// @match *://*.diariodonordeste.verdesmares.com.br/*
// @match *://*.opovo.com.br/*
// @match *://*.correio24horas.com.br/*
// @match *://*.atarde.uol.com.br/*
// @match *://*.gazetaonline.com.br/*
// @match *://*.abril.com.br/*
// @match *://*.veja.abril.com.br/*
// @match *://*.exame.com/*
// @match *://*.istoedinheiro.com.br/*
// @match *://*.cartacapital.com.br/*
// @match *://*.diariosp.com.br/*
// @match *://*.jornaldebrasilia.com.br/*
// @match *://*.folhadelondrina.com.br/*
// @match *://*.odiario.com/*
// @match *://*.nexojornal.com.br/*
// @match *://*.jornalggn.com.br/*
// @match *://*.observatoriodaimprensa.com.br/*
// @match *://*.terra.com.br/*
// @match *://*.infomoney.com.br/*
// @match *://*.sunoresearch.com.br/*
// @match *://*.moneytimes.com.br/*
// @match *://*.seudinheiro.com/*
// @match *://*.cnnbrasil.com.br/*
// @match *://*.band.uol.com.br/*
// @match *://*.r7.com/*
// @match *://*.metropoles.com/*
// @match *://*.gazetabrasil.com.br/*
// @match *://*.brasil247.com/*
// @match *://*.theintercept.com.br/*
// @match *://*.esmaelmorais.com.br/*
// @match *://*.blogdopim.com.br/*
// @match *://*.blogdosakamoto.blogosfera.uol.com.br/*
// @match *://*.revistaforum.com.br/*
// @match *://*.redebrasilatual.com.br/*
// @match *://*.conversaafiada.com.br/*
// @match *://*.operamundi.uol.com.br/*
// @match *://*.brasildefato.com.br/*
// @match *://*.ihu.unisinos.br/*
// @match *://*.catracalivre.com.br/*
// @match *://*.nsja.com.br/*
// @match *://*.parana-online.com.br/*
// @match *://*.paranaportal.uol.com.br/*
// @match *://*.tribunapr.com.br/*
// @match *://*.correiodopovo.com.br/*
// @match *://*.jornalnh.com.br/*
// @match *://*.diariopopular.com.br/*
// @match *://*.agora.uol.com.br/*
// @match *://*.sbtnews.com.br/*
// @match *://*.jovempan.com.br/*
// @match *://*.bandnewsfm.com.br/*
// @match *://*.cbn.globoradio.globo.com/*
// @match *://*.brpolitico.com.br/*
// @match *://*.correiopopular.com.br/*
// @match *://*.crusoe.com.br/*
// @match *://*.diariodaregiao.com.br/*
// @match *://*.dgabc.com.br/* // Diário do Grande ABC
// @match *://*.diarinho.com.br/*
// @match *://*.diariodecanoas.com.br/*
// @match *://*.epoca.globo.com/* // Revista Época
// @match *://*.jornalpioneiro.com.br/*
// @match *://*.jornalvs.com.br/*
// @match *://*.revistagalileu.globo.com/* // Revista Galileu
// @match *://*.epocanegocios.globo.com/* // Adicionado: Revista Época Negócios
// @match *://*.marieclaire.globo.com/* // Adicionado: Revista Marie Claire
// @match *://*.globorural.globo.com/* // Adicionado: Revista Globo Rural
// @match *://*.revistapegn.globo.com/* // Adicionado: Pequenas Empresas Grandes Negócios
// @match *://*.nytimes.com/* // Adicionado: New York Times (Internacional)
// @match *://*.elpais.com/* // Adicionado: El País (Internacional)
// @match *://*.economist.com/* // Adicionado: The Economist (Internacional)
// @match *://*.opopular.com.br/* // Adicionado: O Popular
// @match *://*.diariodesantamaria.com.br/* // Adicionado: Diário de Santa Maria
// @match *://*.glamour.globo.com/* // Adicionado: Revista Glamour
// @match *://*.atribuna.com.br/* // Adicionado: Jornal A Tribuna (Santos)
// @match *://*.umdoisesportes.com.br/* // Adicionado: Um Dois Esportes
// @match *://*.gaz.com.br/* // Adicionado: GAZ
// @match *://*.semprefamilia.com.br/* // Adicionado: Sempre Família
// @match *://*.jornaldacidadeonline.com.br/* // Sugestão adicional
// @match *://*.revistacrescer.globo.com/* // Sugestão adicional
// @match *://*.revistamonet.globo.com/* // Sugestão adicional
// @match *://*.casavogue.globo.com/* // Sugestão adicional
// @match *://*.gq.globo.com/* // Sugestão adicional
// @match *://*.casaclaudia.abril.com.br/* // Sugestão adicional
// @match *://*.claudia.abril.com.br/* // Sugestão adicional
// @match *://*.mdemulher.abril.com.br/* // Sugestão adicional
// @match *://*.viagemeturismo.abril.com.br/* // Sugestão adicional
// @match *://*.exame.com/* // Para garantir abrangência de subdomínios Exame
// @grant GM_addStyle
// @grant window.location
// ==/UserScript==
(function() {
'use strict';
let lastUrl = window.location.href; // Variável para armazenar a última URL conhecida
let checkInterval = null; // Para controlar o setInterval
// Estilos comuns para os botões flutuantes
GM_addStyle(`
.open-paywall-button {
position: fixed;
bottom: 20px;
color: white;
padding: 10px 15px;
border: none;
border-radius: 5px;
font-size: 14px;
font-family: Arial, sans-serif;
cursor: pointer;
z-index: 2147483647; /* Tenta garantir que fique no topo */
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
opacity: 0.85;
transition: opacity 0.3s ease, background-color 0.3s ease;
}
.open-paywall-button:hover {
opacity: 1;
}
#open-in-12ft-button {
left: 20px; /* Movido para a esquerda */
background-color: #007bff; /* Azul */
}
#open-in-12ft-button:hover {
background-color: #0056b3; /* Azul mais escuro */
}
#open-in-archive-button {
left: 140px; /* Posição à direita do 12ft.io */
background-color: #6c757d; /* Cinza para Archive.is */
}
#open-in-archive-button:hover {
background-color: #5a6268; /* Cinza mais escuro */
}
`);
// Função para criar um elemento de botão
function createButtonElement(id, text, urlPrefix, alertMessage) {
const button = document.createElement('button');
button.id = id;
button.className = 'open-paywall-button';
button.innerHTML = text;
button.addEventListener('click', function(event) {
event.stopPropagation();
const currentUrl = window.location.href;
if (!currentUrl) {
alert('Não foi possível obter a URL atual.');
return;
}
// Verifica se a URL já é do serviço alvo para evitar loops
if (currentUrl.startsWith(urlPrefix)) {
alert(alertMessage);
return;
}
const newUrl = urlPrefix + currentUrl;
console.log('Redirecionando para: ' + newUrl);
window.location.href = newUrl;
});
return button;
}
// Função para remover os botões existentes
function removeExistingButtons() {
const button12ft = document.getElementById('open-in-12ft-button');
const buttonArchive = document.getElementById('open-in-archive-button');
if (button12ft) {
button12ft.remove();
}
if (buttonArchive) {
buttonArchive.remove();
}
}
// Função principal para adicionar todos os botões
function addAllButtons() {
// Se os botões já estão presentes e visíveis, não faz nada
if (document.getElementById('open-in-12ft-button') && document.getElementById('open-in-archive-button')) {
// Verifica se eles estão no body (pode ser que o DOM tenha sido manipulado e eles fiquem "soltos")
if (document.body.contains(document.getElementById('open-in-12ft-button')) &&
document.body.contains(document.getElementById('open-in-archive-button'))) {
return;
}
}
// Garante que o body esteja pronto
if (!document.body) {
console.log('Document body not ready yet, deferring button addition.');
return;
}
console.log('Attempting to add/re-add buttons...');
removeExistingButtons(); // Remove quaisquer botões antigos para garantir um estado limpo
// Adiciona o botão para 12ft.io
const button12ft = createButtonElement(
'open-in-12ft-button',
'🔓 12ft.io',
'https://12ft.io/',
'Esta página já está aberta com o 12ft.io.'
);
document.body.appendChild(button12ft);
// Adiciona o botão para Archive.is
const buttonArchive = createButtonElement(
'open-in-archive-button',
'🏛️ Archive.is',
'https://archive.is/',
'Esta página já está aberta com o Archive.is.'
);
document.body.appendChild(buttonArchive);
console.log('Buttons successfully processed.');
}
// --- Estratégia de Injeção e Monitoramento ---
// Função de verificação periódica que tenta adicionar os botões
function periodicCheckAndAdd() {
const currentUrl = window.location.href;
// Se a URL mudou, consideramos uma "nova" página e forçamos a recriação
if (currentUrl !== lastUrl) {
console.log('URL changed. Forcing re-addition of buttons.');
lastUrl = currentUrl;
addAllButtons();
} else {
// Se a URL não mudou, mas os botões não estão visíveis no DOM, tenta adicioná-los
if (!document.getElementById('open-in-12ft-button') || !document.getElementById('open-in-archive-button')) {
console.log('Buttons missing on same URL. Attempting to add.');
addAllButtons();
}
}
}
// Iniciar a verificação periódica um pouco depois do carregamento
// e limpá-la se o body não existir ou se a página for embora
function initializePeriodicCheck() {
if (checkInterval) {
clearInterval(checkInterval); // Limpa qualquer intervalo anterior
}
checkInterval = setInterval(periodicCheckAndAdd, 500); // Tenta a cada 500ms
}
// 1. No carregamento inicial da página (DOMContentLoaded)
// Isso é a primeira tentativa para garantir que os botões sejam adicionados rapidamente.
document.addEventListener('DOMContentLoaded', function() {
addAllButtons();
initializePeriodicCheck(); // Inicia a checagem periódica após o DOM estar pronto
});
// 2. Para lidar com o botão de voltar/avançar do navegador (popstate)
// Este evento pode indicar uma "nova" página no histórico, então tentamos adicionar.
window.addEventListener('popstate', function() {
console.log('Popstate event. Checking for buttons.');
periodicCheckAndAdd(); // Usa a função de verificação para revalidar
});
// 3. Monitoramento de URL e reinício do setInterval se necessário
// Esta é uma "rede de segurança" para garantir que o setInterval esteja sempre ativo
// e que a lastUrl esteja correta em caso de navegações complexas.
let initialUrl = window.location.href;
new MutationObserver(function(mutations) {
if (window.location.href !== initialUrl) {
initialUrl = window.location.href;
console.log('URL changed via MutationObserver, re-initializing periodic check.');
initializePeriodicCheck(); // Reinicia o intervalo para garantir consistência
addAllButtons(); // Força a adição imediata também
}
}).observe(document, { childList: true, subtree: true, attributes: true });
// Caso o script seja executado antes do DOMContentLoaded (modo "document-start" do Tampermonkey)
// E o body já esteja presente, tenta adicionar os botões imediatamente.
if (document.body) {
addAllButtons();
initializePeriodicCheck();
}
})();