O que é isso?
É um extrator de dados automático em formato de bookmarklet para as reuniões semanais das Testemunhas de Jeová. Ele transforma a página de texto do site WOL (Watchtower Online Library) em um arquivo de dados organizado (JSON).
O que é isso?
É um extrator de dados automático em formato de bookmarklet para as reuniões semanais das Testemunhas de Jeová. Ele transforma a página de texto do site WOL (Watchtower Online Library) em um arquivo de dados organizado (JSON).
Extrai e estrutura em JSON os dados da reunião semanal da congregação diretamente da página do Watchtower Online Library (WOL).
O bookmarklet é um trecho de JavaScript executado diretamente no navegador, sem depender de nenhuma extensão, servidor ou instalação. Quando executado na página de uma reunião semanal do WOL (wol.jw.org), ele:
h2 e h3 dentro do #article para identificar seções e atividades da reunião.1., 2....) de elementos de transição (cânticos, orações, comentários finais).(10 min)) e converte para segundos.<textarea> estilizado, pronto para copiar.Você precisa estar em uma página de reunião semanal do WOL, por exemplo:
https://wol.jw.org/pt/wol/d/r5/lp-t/202026087
Para usar como bookmarklet
Método alternativo
Passo 1 — Abra o Console do Desenvolvedor do navegador:
| Navegador | Atalho Windows/Linux | Atalho macOS |
|---|---|---|
| Chrome / Edge | F12 ou Ctrl+Shift+J |
Cmd+Option+J |
| Firefox | F12 ou Ctrl+Shift+K |
Cmd+Option+K |
| Safari | Habilite o menu Desenvolvedor em Preferências → Avançado, depois Cmd+Option+C |
Passo 2 — Clique na aba Console.
Passo 3 — Cole todo o código JavaScript do bookmarklet no campo de entrada do console.
Passo 4 — Pressione Enter.
Passo 5 — Uma nova aba será aberta automaticamente com o JSON completo da reunião exibido em um textarea de fundo escuro. Selecione tudo (Ctrl+A / Cmd+A) e copie.
⚠️ Atenção com bloqueadores de popup: Se a nova aba não abrir, verifique se o navegador bloqueou o popup e autorize-o clicando no ícone que aparece na barra de endereços.
O JSON possui dois blocos principais: metadata e atividades.
"metadata": {
"uid_semana": "2026-04-13",
"semana_label": "13-19 DE ABRIL",
"total_segundos": 6300,
"logs": [
"ℹ️ Saldo distribuído: 351s por oração (Total: 701.54s)."
]
}| Campo | Tipo | Descrição |
|---|---|---|
uid_semana |
string |
Data da segunda-feira da semana no formato YYYY-MM-DD. Serve como chave única para identificar a semana. |
semana_label |
string |
Título da semana conforme exibido no WOL, ex.: "13-19 DE ABRIL". |
total_segundos |
number |
Duração total da reunião em segundos (105 min = 6300 s). Constante. |
logs |
array |
Mensagens geradas durante o processamento, como avisos sobre cânticos não encontrados e o cálculo do saldo de orações. |
Cada elemento do array representa uma parte da reunião.
{
"id": "2026-04-13_03",
"is_apostila": true,
"secao": "TESOUROS DA PALAVRA DE DEUS",
"numero": 1,
"titulo_limpo": "Jesus mostrou um grande amor!",
"recursos": [ ... ],
"conteudo_texto": [ ... ],
"ponto_a_considerar": null,
"referencias": [ ... ]
}| Campo | Tipo | Descrição |
|---|---|---|
id |
string |
Identificador único no formato YYYY-MM-DD_NN, onde NN é o índice do header na página. |
is_apostila |
boolean |
true se o item é uma parte numerada da apostila; false para cânticos, orações e transições. |
secao |
string |
Seção da reunião à qual o item pertence (ex.: "TESOUROS DA PALAVRA DE DEUS"). |
numero |
number |
Número do item na apostila (1, 2, 3...) ou -1 para itens não numerados. |
titulo_limpo |
string |
Título da atividade sem o número, sem a indicação de tempo e sem metadados extras. |
recursos |
array |
Lista de recursos de tempo associados à atividade. Ver tabela abaixo. |
conteudo_texto |
array<string> |
Linhas de texto do corpo da atividade (pontos a desenvolver, instruções, perguntas). |
ponto_a_considerar |
number|null |
Número da lição do livro Pregue e Ensine (th) referenciada, quando aplicável. |
referencias |
array |
Links extraídos do conteúdo: versículos, publicações, vídeos. Ver tabela abaixo. |
Cada atividade pode ter um ou mais recursos, representando diferentes "fatias" de tempo.
{
"tipo": "AUDIO",
"label": "Cântico 18",
"segundos": 157.32,
"origem_tempo": "MP3"
}| Campo | Tipo | Descrição |
|---|---|---|
tipo |
string |
Categoria do recurso. Valores possíveis: AUDIO, ORACAO, PARTE. |
label |
string |
Descrição legível do recurso. |
segundos |
number |
Duração em segundos (pode ter casas decimais para áudios). |
origem_tempo |
string |
Origem do valor de duração. Ver tabela de origens abaixo. |
Origens de tempo possíveis:
| Valor | Significado |
|---|---|
MP3 |
Duração obtida do banco de dados interno de cânticos (medição do arquivo MP3). |
AVG |
Cântico não encontrado no banco; aplicada a média dos 163 cânticos. |
WOL |
Duração extraída diretamente do texto da página do WOL (ex.: (10 min)). |
CAL |
Duração calculada pelo algoritmo de saldo (usado para orações/transições). |
{
"texto": "Isa. 53:3;",
"url": "https://wol.jw.org/pt/wol/bc/r5/lp-t/202026087/1/0"
}| Campo | Tipo | Descrição |
|---|---|---|
texto |
string |
Texto âncora do link na página. |
url |
string |
URL completa do link. |
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://gist.github.com/shuantsu/wol-reuniao-schema.json",
"title": "WOL Reunião Meio de Semana",
"description": "Schema para o JSON gerado pelo bookmarklet de extração de reuniões do Watchtower Online Library (wol.jw.org).",
"type": "object",
"required": ["metadata", "atividades"],
"additionalProperties": false,
"properties": {
"metadata": {
"type": "object",
"title": "Metadados da Reunião",
"description": "Informações gerais sobre a semana e o processamento realizado pelo bookmarklet.",
"required": ["uid_semana", "semana_label", "total_segundos", "logs"],
"additionalProperties": false,
"properties": {
"uid_semana": {
"type": "string",
"title": "UID da Semana",
"description": "Data da segunda-feira da semana no formato ISO 8601 (YYYY-MM-DD). Serve como chave única para identificar a semana.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"examples": ["2026-04-13"]
},
"semana_label": {
"type": "string",
"title": "Rótulo da Semana",
"description": "Título da semana conforme exibido no WOL, extraído do h1 da página.",
"minLength": 1,
"examples": ["13-19 DE ABRIL"]
},
"total_segundos": {
"type": "integer",
"title": "Total de Segundos da Reunião",
"description": "Duração total da reunião em segundos. Valor fixo de 6300 (105 minutos).",
"const": 6300,
"examples": [6300]
},
"logs": {
"type": "array",
"title": "Logs de Processamento",
"description": "Mensagens geradas durante a extração. Podem indicar avisos (cântico não encontrado no banco) ou informações sobre o cálculo de saldo das orações.",
"items": {
"type": "string",
"minLength": 1
},
"examples": [
["ℹ️ Saldo distribuído: 351s por oração (Total: 701.54s)."],
["⚠️ Cântico 999 não encontrado. Média de 158.42s aplicada."]
]
}
}
},
"atividades": {
"type": "array",
"title": "Atividades da Reunião",
"description": "Lista ordenada de todas as partes da reunião identificadas na página do WOL, incluindo cânticos, orações, partes da apostila e transições.",
"minItems": 1,
"items": {
"$ref": "#/definitions/Atividade"
}
}
},
"definitions": {
"Atividade": {
"type": "object",
"title": "Atividade",
"description": "Representa uma parte individual da reunião: pode ser uma parte numerada da apostila, um cântico, uma oração ou comentários finais.",
"required": [
"id",
"is_apostila",
"secao",
"numero",
"titulo_limpo",
"recursos",
"conteudo_texto",
"ponto_a_considerar",
"referencias"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"title": "ID da Atividade",
"description": "Identificador único da atividade no formato YYYY-MM-DD_NN, onde NN é o índice com dois dígitos do header correspondente na página.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}_\\d{2}$",
"examples": ["2026-04-13_01", "2026-04-13_03"]
},
"is_apostila": {
"type": "boolean",
"title": "É Item da Apostila",
"description": "true quando o item é uma parte numerada da apostila semanal (ex.: '1. Título'). false para cânticos, orações e transições sem número.",
"examples": [true, false]
},
"secao": {
"type": "string",
"title": "Seção da Reunião",
"description": "Nome da seção da reunião à qual a atividade pertence, extraído dos elementos h2 da página.",
"minLength": 1,
"examples": [
"ABERTURA",
"TESOUROS DA PALAVRA DE DEUS",
"FAÇA SEU MELHOR NO MINISTÉRIO",
"NOSSA VIDA CRISTÃ"
]
},
"numero": {
"type": "integer",
"title": "Número do Item",
"description": "Número sequencial do item na apostila (1, 2, 3...). Vale -1 para itens não numerados como cânticos, orações e comentários finais.",
"minimum": -1,
"examples": [1, 2, 3, -1]
},
"titulo_limpo": {
"type": "string",
"title": "Título Limpo",
"description": "Título da atividade com o número de item, a indicação de tempo e metadados extras removidos.",
"minLength": 1,
"examples": [
"Jesus mostrou um grande amor!",
"Cântico 18 e oração",
"Comentários finais"
]
},
"recursos": {
"type": "array",
"title": "Recursos de Tempo",
"description": "Lista de fatias de tempo associadas à atividade. Uma atividade pode ter múltiplos recursos (ex.: cântico + oração + parte WOL).",
"minItems": 0,
"items": {
"$ref": "#/definitions/Recurso"
}
},
"conteudo_texto": {
"type": "array",
"title": "Conteúdo Textual",
"description": "Linhas de texto extraídas do corpo da atividade (parágrafos e itens de lista). Pode incluir pontos a desenvolver, instruções e perguntas.",
"items": {
"type": "string",
"minLength": 1
},
"examples": [
[
"Jesus seria desprezado. (Isa. 53:3; Mat. 26:67, 68)",
"Por amor, Jesus estava disposto a sofrer."
],
[]
]
},
"ponto_a_considerar": {
"title": "Ponto a Considerar (th)",
"description": "Número da lição do livro 'Pregue e Ensine' (th) referenciada na atividade, quando aplicável. null se não houver referência a uma lição th.",
"oneOf": [
{
"type": "integer",
"minimum": 1,
"examples": [10, 4, 7]
},
{
"type": "null"
}
]
},
"referencias": {
"type": "array",
"title": "Referências e Links",
"description": "Links extraídos do conteúdo da atividade: versículos bíblicos, publicações da organização e vídeos.",
"items": {
"$ref": "#/definitions/Referencia"
}
}
}
},
"Recurso": {
"type": "object",
"title": "Recurso de Tempo",
"description": "Representa uma fatia de tempo dentro de uma atividade, com sua categoria, duração e origem do valor.",
"required": ["tipo", "label", "segundos", "origem_tempo"],
"additionalProperties": false,
"properties": {
"tipo": {
"type": "string",
"title": "Tipo do Recurso",
"description": "Categoria do recurso de tempo.",
"enum": ["AUDIO", "ORACAO", "PARTE"],
"x-enumDescriptions": {
"AUDIO": "Cântico do cancioneiro. Duração medida a partir dos arquivos MP3 oficiais.",
"ORACAO": "Oração de abertura ou encerramento. Duração calculada por saldo de tempo.",
"PARTE": "Parte regular da apostila ou transição com tempo definido pelo WOL."
}
},
"label": {
"type": "string",
"title": "Rótulo do Recurso",
"description": "Descrição legível do recurso, usada para identificação humana.",
"minLength": 1,
"examples": ["Cântico 18", "Oração", "Jesus mostrou um grande amor!", "Comentários finais"]
},
"segundos": {
"type": "number",
"title": "Duração em Segundos",
"description": "Duração do recurso em segundos. Pode conter casas decimais para recursos do tipo AUDIO (medições de MP3).",
"minimum": 0,
"examples": [157.32, 351, 600, 1800]
},
"origem_tempo": {
"type": "string",
"title": "Origem do Valor de Tempo",
"description": "Indica como o valor de 'segundos' foi obtido.",
"enum": ["MP3", "AVG", "WOL", "CAL"],
"x-enumDescriptions": {
"MP3": "Duração obtida do banco de dados interno, baseada na medição do arquivo MP3 oficial do cântico.",
"AVG": "Cântico não encontrado no banco de dados; aplicada a média aritmética dos 163 cânticos cadastrados.",
"WOL": "Duração extraída diretamente do texto da página do WOL (ex.: indicação '(10 min)' convertida para 600 segundos).",
"CAL": "Duração calculada pelo algoritmo de saldo: tempo restante após descontar WOL e MP3, distribuído igualmente entre as orações."
}
}
}
},
"Referencia": {
"type": "object",
"title": "Referência / Link",
"description": "Um hyperlink extraído do conteúdo da atividade, com o texto âncora e a URL de destino.",
"required": ["texto", "url"],
"additionalProperties": false,
"properties": {
"texto": {
"type": "string",
"title": "Texto Âncora",
"description": "Texto visível do link na página do WOL.",
"minLength": 1,
"examples": ["Isa. 53:3;", "th lição 10", "lmd lição 4 ponto 4", "Mostre o VÍDEO"]
},
"url": {
"type": "string",
"title": "URL do Link",
"description": "URL completa de destino do link. Pode apontar para wol.jw.org (versículos e publicações) ou jw.org (vídeos e outros recursos).",
"format": "uri",
"examples": [
"https://wol.jw.org/pt/wol/bc/r5/lp-t/202026087/1/0",
"https://wol.jw.org/pt/wol/pc/r5/lp-t/202026087/4/0",
"https://www.jw.org/finder?wtlocale=T&lank=pub-pk_55_VIDEO"
]
}
}
}
}
}Segunda-feira = data atual - (dia_da_semana - 1), exceto domingo → volta 6 dias
O resultado vira o uid_semana e prefixo de todos os ids, garantindo unicidade por semana.
O objeto dbCanticos mapeia os números de 1 a 163 para suas durações em segundos (ex.: "18": 157.32). Esses valores foram medidos a partir dos arquivos MP3 oficiais do cancioneiro. Quando um cântico não está no banco, aplica-se a média aritmética de todos os 163 valores:
O algoritmo segue:
Onde:
Esse cálculo distribui o tempo "sobrante" igualmente entre as orações de abertura e encerramento.
O JSON gerado é limpo, legível e previsível, permitindo:
conteudo_texto e referencias.segundos de cada recurso, identificando partes que possam estourar o tempo.uid_semana, cria-se um arquivo completo de todas as reuniões ao longo do ano.O formato estruturado é ideal para ser passado como contexto para modelos de linguagem. Exemplos de prompts que se tornam possíveis:
| Objetivo | Como o JSON ajuda |
|---|---|
| Resumo da reunião | Passe o JSON completo e peça um resumo em linguagem natural por seção. |
| Geração de perguntas de estudo | Use conteudo_texto de cada atividade para gerar perguntas de revisão. |
| Explicação de referências | Use referencias[].texto para pedir ao modelo que explique cada versículo citado. |
| Plano de preparação | Peça ao modelo para criar um plano de estudo diário baseado nas atividades da semana. |
| Comparação entre semanas | Passe JSONs de semanas diferentes e peça análise de tendências de temas. |
| Tradução e adaptação | Adapte o conteúdo para diferentes níveis de linguagem (crianças, novos estudantes). |
O JSON é especialmente valioso para LLMs porque:
secao, numero e id permitem filtrar e cruzar dados facilmente.referencias permitem ao modelo (ou ao código) buscar conteúdo adicional.Todos os exemplos abaixo assumem que o JSON foi salvo em um arquivo chamado reuniao.json.
import json
with open("reuniao.json", "r", encoding="utf-8") as f:
data = json.load(f)for ativ in data["atividades"]:
total_seg = sum(r["segundos"] for r in ativ["recursos"])
minutos = total_seg / 60
print(f"[{ativ['secao']}] {ativ['titulo_limpo']} — {minutos:.1f} min ({total_seg:.0f}s)")Saída esperada:
[ISAÍAS 52-53] Cântico 18 e oração — 9.5 min (568s)
[TESOUROS DA PALAVRA DE DEUS] Jesus mostrou um grande amor! — 10.0 min (600s)
[TESOUROS DA PALAVRA DE DEUS] Joias espirituais — 10.0 min (600s)
...
apostila = [a for a in data["atividades"] if a["is_apostila"]]
for item in apostila:
print(f" {item['numero']}. {item['titulo_limpo']} [{item['secao']}]")for ativ in data["atividades"]:
for recurso in ativ["recursos"]:
if recurso["tipo"] == "AUDIO":
print(f"{recurso['label']} — {recurso['segundos']}s (origem: {recurso['origem_tempo']})")todas_refs = []
for ativ in data["atividades"]:
for ref in ativ["referencias"]:
todas_refs.append({
"atividade": ativ["titulo_limpo"],
"texto": ref["texto"],
"url": ref["url"]
})
# Remover duplicatas pelo texto
vistos = set()
unicas = []
for r in todas_refs:
if r["texto"] not in vistos:
vistos.add(r["texto"])
unicas.append(r)
for r in unicas:
print(f" {r['texto']:30s} → {r['url']}")from collections import defaultdict
tempos_por_secao = defaultdict(float)
for ativ in data["atividades"]:
for recurso in ativ["recursos"]:
tempos_por_secao[ativ["secao"]] += recurso["segundos"]
for secao, total in tempos_por_secao.items():
print(f"{secao}: {total/60:.1f} min")Saída esperada:
ISAÍAS 52-53: 9.5 min
TESOUROS DA PALAVRA DE DEUS: 24.0 min
FAÇA SEU MELHOR NO MINISTÉRIO: 12.0 min
NOSSA VIDA CRISTÃ: 59.5 min
total_calculado = sum(
r["segundos"]
for ativ in data["atividades"]
for r in ativ["recursos"]
)
limite = data["metadata"]["total_segundos"]
diferenca = total_calculado - limite
if diferenca > 0:
print(f"⚠️ Reunião EXCEDE o tempo em {diferenca:.0f}s ({diferenca/60:.1f} min)")
elif diferenca < 0:
print(f"✅ Reunião termina {abs(diferenca):.0f}s ({abs(diferenca)/60:.1f} min) antes do limite")
else:
print("✅ Reunião está exatamente no tempo!")livro_alvo = "th"
for ativ in data["atividades"]:
refs_do_livro = [r for r in ativ["referencias"] if livro_alvo in r["texto"]]
if refs_do_livro:
print(f"\n📌 {ativ['titulo_limpo']}")
for r in refs_do_livro:
print(f" → {r['texto']}: {r['url']}")
if ativ["ponto_a_considerar"] is not None:
print(f" ⭐ Ponto th a considerar: lição {ativ['ponto_a_considerar']}")import csv
with open("resumo_reuniao.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["semana", "secao", "numero", "titulo", "tempo_minutos", "is_apostila"])
semana = data["metadata"]["uid_semana"]
for ativ in data["atividades"]:
total_seg = sum(r["segundos"] for r in ativ["recursos"])
writer.writerow([
semana,
ativ["secao"],
ativ["numero"] if ativ["numero"] != -1 else "",
ativ["titulo_limpo"],
round(total_seg / 60, 2),
ativ["is_apostila"]
])
print("✅ CSV gerado: resumo_reuniao.csv")def montar_prompt_llm(data: dict) -> str:
semana = data["metadata"]["semana_label"]
linhas = [f"# Reunião da semana de {semana}\n"]
for ativ in data["atividades"]:
if not ativ["is_apostila"]:
continue
total_seg = sum(r["segundos"] for r in ativ["recursos"])
linhas.append(f"## {ativ['numero']}. {ativ['titulo_limpo']} ({total_seg/60:.0f} min)")
linhas.append(f"**Seção:** {ativ['secao']}")
if ativ["conteudo_texto"]:
linhas.append("**Conteúdo:**")
for linha in ativ["conteudo_texto"]:
linhas.append(f"- {linha}")
if ativ["referencias"]:
refs_texto = ", ".join(r["texto"] for r in ativ["referencias"])
linhas.append(f"**Referências:** {refs_texto}")
linhas.append("")
return "\n".join(linhas)
prompt = montar_prompt_llm(data)
print(prompt)
# Cole o resultado diretamente no chat de qualquer LLMimport os
import glob
semanas = []
for arquivo in glob.glob("reunioes/*.json"):
with open(arquivo, "r", encoding="utf-8") as f:
semanas.append(json.load(f))
# Ordenar cronologicamente
semanas.sort(key=lambda x: x["metadata"]["uid_semana"])
# Exemplo: listar todas as semanas processadas
for s in semanas:
total_ativ = len([a for a in s["atividades"] if a["is_apostila"]])
print(f"{s['metadata']['uid_semana']} — {s['metadata']['semana_label']} ({total_ativ} partes da apostila)")| Valor | Descrição |
|---|---|
AUDIO |
Cântico |
ORACAO |
Oração (tempo calculado por saldo) |
PARTE |
Parte regular com tempo definido no WOL |
| Valor | Descrição |
|---|---|
MP3 |
Medição real do arquivo de áudio do cântico |
AVG |
Média dos 163 cânticos (fallback) |
WOL |
Extraído do texto da página ((X min)) |
CAL |
Calculado por saldo (orações e transições) |
| Seção | Descrição |
|---|---|
ABERTURA |
Cântico e oração de abertura |
TESOUROS DA PALAVRA DE DEUS |
Partes de estudo bíblico (itens 1–3) |
FAÇA SEU MELHOR NO MINISTÉRIO |
Partes de treinamento ministerial (4–6) |
NOSSA VIDA CRISTÃ |
Partes de aplicação cristã (7–8) + encerramento |
| (function() { | |
| const dbCanticos = {"1":137.88,"2":176.208,"3":197.136,"4":225.528,"5":152.712,"6":139.176,"7":200.352,"8":143.544,"9":133.992,"10":109.248,"11":160.92,"12":130.776,"13":106.992,"14":120.24,"15":141.96,"16":138.696,"17":135.0,"18":157.32,"19":200.544,"20":176.928,"21":134.616,"22":159.96,"23":211.68,"24":164.712,"25":159.768,"26":260.112,"27":138.384,"28":140.784,"29":150.816,"30":158.328,"31":165.864,"32":214.488,"33":179.472,"34":176.664,"35":159.792,"36":138.696,"37":125.352,"38":169.584,"39":130.68,"40":134.736,"41":117.768,"42":164.928,"43":140.448,"44":152.976,"45":133.272,"46":166.656,"47":123.096,"48":157.128,"49":110.328,"50":118.992,"51":144.576,"52":157.224,"53":159.36,"54":154.2,"55":180.816,"56":185.472,"57":164.208,"58":178.944,"59":119.736,"60":151.512,"61":139.656,"62":160.128,"63":182.712,"64":99.168,"65":127.92,"66":159.888,"67":125.88,"68":114.12,"69":126.72,"70":120.624,"71":178.896,"72":118.272,"73":139.824,"74":149.328,"75":238.272,"76":182.448,"77":159.768,"78":161.352,"79":176.328,"80":115.56,"81":165.816,"82":126.408,"83":139.752,"84":173.592,"85":130.368,"86":120.624,"87":123.816,"88":133.296,"89":157.656,"90":175.776,"91":173.568,"92":161.568,"93":95.664,"94":128.76,"95":134.472,"96":170.76,"97":188.88,"98":114.432,"99":135.192,"100":129.936,"101":116.64,"102":158.4,"103":169.776,"104":124.008,"105":174.864,"106":124.752,"107":176.76,"108":216.504,"109":110.016,"110":133.128,"111":141.048,"112":137.136,"113":151.464,"114":137.208,"115":136.8,"116":104.208,"117":130.176,"118":135.48,"119":134.88,"120":126.144,"121":136.488,"122":181.608,"123":130.44,"124":139.488,"125":167.928,"126":128.208,"127":125.976,"128":133.8,"129":212.088,"130":156.192,"131":103.92,"132":127.8,"133":92.352,"134":150.744,"135":143.952,"136":170.592,"137":162.576,"138":171.984,"139":204.096,"140":157.2,"141":142.752,"142":107.448,"143":147.84,"144":129.432,"145":185.208,"146":222.672,"147":139.704,"148":232.176,"149":141.504,"150":140.88,"151":230.592,"152":191.016,"153":257.976,"154":235.392,"155":254.52,"156":265.992,"157":214.704,"158":249.744,"159":301.44,"160":275.76,"161":262.464,"162":251.496,"163":232.2}; | |
| const getMonday = () => { | |
| const d = new Date(); | |
| const day = d.getDay(), diff = d.getDate() - day + (day == 0 ? -6 : 1); | |
| return new Date(d.setDate(diff)).toISOString().split('T')[0]; | |
| }; | |
| const article = document.querySelector('#article'); | |
| const mondayId = getMonday(); | |
| const result = { | |
| metadata: { | |
| uid_semana: mondayId, | |
| semana_label: article.querySelector('h1')?.innerText.trim(), | |
| total_segundos: 6300, | |
| logs: [] | |
| }, | |
| atividades: [] | |
| }; | |
| const headers = Array.from(article.querySelectorAll('h2, h3')).filter(h => { | |
| const t = h.innerText.trim(); | |
| return h.tagName === 'H2' || (h.tagName === 'H3' && (/^\d+\./.test(t) || /Cântico/i.test(t) || /Comentários/i.test(t))); | |
| }); | |
| let currentSecao = "ABERTURA", tempoWOL = 0, tempoMP3 = 0, oracoes = []; | |
| headers.forEach((header, index) => { | |
| const text = header.innerText.trim(); | |
| if (header.tagName === 'H2') { currentSecao = text; return; } | |
| const numMatch = text.match(/^(\d+)\./); | |
| const atv = { | |
| id: `${mondayId}_${index.toString().padStart(2, '0')}`, | |
| is_apostila: numMatch !== null, | |
| secao: currentSecao, | |
| numero: numMatch ? parseInt(numMatch[1]) : -1, | |
| titulo_limpo: text.replace(/^\d+\.\s*/, '').replace(/\(\d+\s*min\)/, '').split('|')[0].trim(), | |
| recursos: [], | |
| conteudo_texto: [], | |
| referencias: [] | |
| }; | |
| const range = document.createRange(); | |
| range.setStartAfter(header); | |
| const next = headers[index + 1]; | |
| if (next) range.setEndBefore(next); | |
| else range.setEndBefore(article.querySelector('.pswp, #questionContainer') || article.lastElementChild); | |
| const tempDiv = document.createElement('div'); | |
| tempDiv.appendChild(range.cloneContents()); | |
| const textSet = new Set(); | |
| tempDiv.querySelectorAll('p, li').forEach(node => { | |
| let txt = node.innerText.replace(/Sua resposta/g, "").trim(); | |
| if (txt && txt !== atv.titulo_limpo && !/^\(\d+\s*min\)$/.test(txt)) { | |
| textSet.add(txt); | |
| } | |
| }); | |
| atv.conteudo_texto = Array.from(textSet); | |
| // LÓGICA DO PONTO A CONSIDERAR (Seção específica ou Leitura da Bíblia) | |
| if (currentSecao.includes("MINISTÉRIO") || atv.titulo_limpo.includes("Leitura da Bíblia")) { | |
| atv.conteudo_texto.some(txt => { | |
| const match = txt.match(/\(([^)]+)\)$/); | |
| if (match) { | |
| atv.ponto_a_considerar = match[1]; | |
| return true; | |
| } | |
| return false; | |
| }); | |
| } | |
| const timeMatch = text.match(/\((\d+)\s*min\)/) || tempDiv.textContent.match(/(\d+)\s*min/); | |
| const segWOL = timeMatch ? parseInt(timeMatch[1]) * 60 : 0; | |
| const cMatch = text.match(/Cântico\s+(\d+)/i); | |
| if (cMatch) { | |
| const cid = cMatch[1]; | |
| const dur = dbCanticos[cid] || 161; | |
| tempoMP3 += dur; | |
| atv.recursos.push({ tipo: "AUDIO", label: `Cântico ${cid}`, segundos: parseFloat(dur.toFixed(2)), origem_tempo: dbCanticos[cid] ? "MP3" : "AVG" }); | |
| } | |
| if (/oração/i.test(text)) { | |
| const resO = { tipo: "ORACAO", label: "Oração", segundos: 0, origem_tempo: "CAL" }; | |
| atv.recursos.push(resO); | |
| oracoes.push(resO); | |
| } | |
| if (segWOL > 0) { | |
| tempoWOL += segWOL; | |
| atv.recursos.push({ tipo: "PARTE", label: atv.titulo_limpo, segundos: segWOL, origem_tempo: "WOL" }); | |
| } | |
| tempDiv.querySelectorAll('a').forEach(a => { | |
| const tA = a.textContent.trim(); | |
| if (tA && !a.href.includes('#page')) { | |
| atv.referencias.push({ texto: tA, url: a.href }); | |
| } | |
| }); | |
| result.atividades.push(atv); | |
| }); | |
| const saldo = 6300 - tempoWOL - tempoMP3; | |
| if (oracoes.length > 0) { | |
| const cada = Math.round(saldo / oracoes.length); | |
| result.metadata.logs.push(`ℹ️ Saldo distribuído: ${cada}s por oração.`); | |
| oracoes.forEach(r => r.segundos = cada); | |
| } | |
| const win = window.open("", "_blank"); | |
| win.document.write(`<html><body style="margin:0;background:#1e1e1e;padding:20px;"><textarea style="width:100%;height:95vh;background:#1a1b26;color:#9ece6a;border:1px solid #414868;padding:15px;font-family:monospace;font-size:13px;outline:none;border-radius:4px;line-height:1.5;">${JSON.stringify(result, null, 4)}</textarea></body></html>`); | |
| win.document.close(); | |
| })(); |
| { | |
| "metadata": { | |
| "uid_semana": "2026-04-13", | |
| "semana_label": "13-19 DE ABRIL", | |
| "total_segundos": 6300, | |
| "logs": [ | |
| "ℹ️ Saldo distribuído: 351s por oração." | |
| ] | |
| }, | |
| "atividades": [ | |
| { | |
| "id": "2026-04-13_01", | |
| "is_apostila": false, | |
| "secao": "ISAÍAS 52-53", | |
| "numero": -1, | |
| "titulo_limpo": "Cântico 18 e oração", | |
| "recursos": [ | |
| { | |
| "tipo": "AUDIO", | |
| "label": "Cântico 18", | |
| "segundos": 157.32, | |
| "origem_tempo": "MP3" | |
| }, | |
| { | |
| "tipo": "ORACAO", | |
| "label": "Oração", | |
| "segundos": 351, | |
| "origem_tempo": "CAL" | |
| }, | |
| { | |
| "tipo": "PARTE", | |
| "label": "Cântico 18 e oração", | |
| "segundos": 60, | |
| "origem_tempo": "WOL" | |
| } | |
| ], | |
| "conteudo_texto": [], | |
| "referencias": [] | |
| }, | |
| { | |
| "id": "2026-04-13_03", | |
| "is_apostila": true, | |
| "secao": "TESOUROS DA PALAVRA DE DEUS", | |
| "numero": 1, | |
| "titulo_limpo": "Jesus mostrou um grande amor!", | |
| "recursos": [ | |
| { | |
| "tipo": "PARTE", | |
| "label": "Jesus mostrou um grande amor!", | |
| "segundos": 600, | |
| "origem_tempo": "WOL" | |
| } | |
| ], | |
| "conteudo_texto": [ | |
| "Jesus seria desprezado. (Isa. 53:3; Mat. 26:67, 68; w10 15/11 7 § 2)", | |
| "Jesus aceitaria passar por sofrimento. (Isa. 53:7; ip-2 205 § 25)", | |
| "Por amor, Jesus estava disposto a sofrer para fazer a vontade de Jeová e levar embora os nossos pecados. (Isa. 53:10-12; João 14:31; 15:13)" | |
| ], | |
| "referencias": [ | |
| { | |
| "texto": "Isa. 53:3;", | |
| "url": "https://wol.jw.org/pt/wol/bc/r5/lp-t/202026087/1/0" | |
| }, | |
| { | |
| "texto": "Mat. 26:67, 68", | |
| "url": "https://wol.jw.org/pt/wol/bc/r5/lp-t/202026087/1/1" | |
| }, | |
| { | |
| "texto": "w10 15/11 7 § 2", | |
| "url": "https://wol.jw.org/pt/wol/pc/r5/lp-t/202026087/1/0" | |
| }, | |
| { | |
| "texto": "Isa. 53:7", | |
| "url": "https://wol.jw.org/pt/wol/bc/r5/lp-t/202026087/2/0" | |
| }, | |
| { | |
| "texto": "ip-2 205 § 25", | |
| "url": "https://wol.jw.org/pt/wol/pc/r5/lp-t/202026087/2/0" | |
| }, | |
| { | |
| "texto": "Isa. 53:10-12;", | |
| "url": "https://wol.jw.org/pt/wol/bc/r5/lp-t/202026087/3/0" | |
| }, | |
| { | |
| "texto": "João 14:31;", | |
| "url": "https://wol.jw.org/pt/wol/bc/r5/lp-t/202026087/3/1" | |
| }, | |
| { | |
| "texto": "15:13", | |
| "url": "https://wol.jw.org/pt/wol/bc/r5/lp-t/202026087/3/2" | |
| } | |
| ] | |
| }, | |
| { | |
| "id": "2026-04-13_04", | |
| "is_apostila": true, | |
| "secao": "TESOUROS DA PALAVRA DE DEUS", | |
| "numero": 2, | |
| "titulo_limpo": "Joias espirituais", | |
| "recursos": [ | |
| { | |
| "tipo": "PARTE", | |
| "label": "Joias espirituais", | |
| "segundos": 600, | |
| "origem_tempo": "WOL" | |
| } | |
| ], | |
| "conteudo_texto": [ | |
| "Isa. 52:11 — O que podemos aprender com essa ordem profética? (it “Utensílios” § 2)", | |
| "Que joias espirituais você encontrou na leitura da Bíblia desta semana?" | |
| ], | |
| "referencias": [ | |
| { | |
| "texto": "Isa. 52:11", | |
| "url": "https://wol.jw.org/pt/wol/bc/r5/lp-t/202026087/4/0" | |
| }, | |
| { | |
| "texto": "it “Utensílios” § 2", | |
| "url": "https://wol.jw.org/pt/wol/pc/r5/lp-t/202026087/3/0" | |
| } | |
| ] | |
| }, | |
| { | |
| "id": "2026-04-13_05", | |
| "is_apostila": true, | |
| "secao": "TESOUROS DA PALAVRA DE DEUS", | |
| "numero": 3, | |
| "titulo_limpo": "Leitura da Bíblia", | |
| "recursos": [ | |
| { | |
| "tipo": "PARTE", | |
| "label": "Leitura da Bíblia", | |
| "segundos": 240, | |
| "origem_tempo": "WOL" | |
| } | |
| ], | |
| "conteudo_texto": [ | |
| "(4 min) Isa. 53:3-12 (th lição 10)" | |
| ], | |
| "referencias": [ | |
| { | |
| "texto": "Isa. 53:3-12", | |
| "url": "https://wol.jw.org/pt/wol/bc/r5/lp-t/202026087/5/0" | |
| }, | |
| { | |
| "texto": "th lição 10", | |
| "url": "https://wol.jw.org/pt/wol/pc/r5/lp-t/202026087/4/0" | |
| } | |
| ], | |
| "ponto_a_considerar": "th lição 10" | |
| }, | |
| { | |
| "id": "2026-04-13_07", | |
| "is_apostila": true, | |
| "secao": "FAÇA SEU MELHOR NO MINISTÉRIO", | |
| "numero": 4, | |
| "titulo_limpo": "Iniciando conversas", | |
| "recursos": [ | |
| { | |
| "tipo": "PARTE", | |
| "label": "Iniciando conversas", | |
| "segundos": 240, | |
| "origem_tempo": "WOL" | |
| } | |
| ], | |
| "conteudo_texto": [ | |
| "(4 min) DE CASA EM CASA. Ofereça um estudo bíblico. (lmd lição 4 ponto 4)" | |
| ], | |
| "referencias": [ | |
| { | |
| "texto": "lmd lição 4 ponto 4", | |
| "url": "https://wol.jw.org/pt/wol/pc/r5/lp-t/202026087/5/0" | |
| } | |
| ], | |
| "ponto_a_considerar": "lmd lição 4 ponto 4" | |
| }, | |
| { | |
| "id": "2026-04-13_08", | |
| "is_apostila": true, | |
| "secao": "FAÇA SEU MELHOR NO MINISTÉRIO", | |
| "numero": 5, | |
| "titulo_limpo": "Iniciando conversas", | |
| "recursos": [ | |
| { | |
| "tipo": "PARTE", | |
| "label": "Iniciando conversas", | |
| "segundos": 180, | |
| "origem_tempo": "WOL" | |
| } | |
| ], | |
| "conteudo_texto": [ | |
| "(3 min) DE CASA EM CASA. Fale sobre uma das verdades do apêndice A da brochura Ame as Pessoas. (lmd lição 3 ponto 3)" | |
| ], | |
| "referencias": [ | |
| { | |
| "texto": "apêndice A", | |
| "url": "https://wol.jw.org/pt/wol/pc/r5/lp-t/202026087/6/0" | |
| }, | |
| { | |
| "texto": "lmd lição 3 ponto 3", | |
| "url": "https://wol.jw.org/pt/wol/pc/r5/lp-t/202026087/7/0" | |
| } | |
| ], | |
| "ponto_a_considerar": "lmd lição 3 ponto 3" | |
| }, | |
| { | |
| "id": "2026-04-13_09", | |
| "is_apostila": true, | |
| "secao": "FAÇA SEU MELHOR NO MINISTÉRIO", | |
| "numero": 6, | |
| "titulo_limpo": "Cultivando o interesse", | |
| "recursos": [ | |
| { | |
| "tipo": "PARTE", | |
| "label": "Cultivando o interesse", | |
| "segundos": 300, | |
| "origem_tempo": "WOL" | |
| } | |
| ], | |
| "conteudo_texto": [ | |
| "(5 min) DE CASA EM CASA. Ofereça um estudo bíblico para uma pessoa que assistiu à Celebração. (lmd lição 8 ponto 3)" | |
| ], | |
| "referencias": [ | |
| { | |
| "texto": "lmd lição 8 ponto 3", | |
| "url": "https://wol.jw.org/pt/wol/pc/r5/lp-t/202026087/8/0" | |
| } | |
| ], | |
| "ponto_a_considerar": "lmd lição 8 ponto 3" | |
| }, | |
| { | |
| "id": "2026-04-13_11", | |
| "is_apostila": false, | |
| "secao": "NOSSA VIDA CRISTÃ", | |
| "numero": -1, | |
| "titulo_limpo": "Cântico 20", | |
| "recursos": [ | |
| { | |
| "tipo": "AUDIO", | |
| "label": "Cântico 20", | |
| "segundos": 176.93, | |
| "origem_tempo": "MP3" | |
| } | |
| ], | |
| "conteudo_texto": [], | |
| "referencias": [] | |
| }, | |
| { | |
| "id": "2026-04-13_12", | |
| "is_apostila": true, | |
| "secao": "NOSSA VIDA CRISTÃ", | |
| "numero": 7, | |
| "titulo_limpo": "Torne-se Amigo de Jeová — O Melhor Presente de Todos", | |
| "recursos": [ | |
| { | |
| "tipo": "PARTE", | |
| "label": "Torne-se Amigo de Jeová — O Melhor Presente de Todos", | |
| "segundos": 900, | |
| "origem_tempo": "WOL" | |
| } | |
| ], | |
| "conteudo_texto": [ | |
| "(15 min) Consideração.", | |
| "Mostre o VÍDEO. Depois, pergunte:", | |
| "Por que a morte de Jesus é um presente que Jeová nos deu?", | |
| "Como podemos mostrar gratidão pelo que Jesus fez por nós?" | |
| ], | |
| "referencias": [ | |
| { | |
| "texto": "Mostre o VÍDEO", | |
| "url": "https://www.jw.org/finder?wtlocale=T&lank=pub-pk_55_VIDEO" | |
| } | |
| ] | |
| }, | |
| { | |
| "id": "2026-04-13_13", | |
| "is_apostila": true, | |
| "secao": "NOSSA VIDA CRISTÃ", | |
| "numero": 8, | |
| "titulo_limpo": "Estudo bíblico de congregação", | |
| "recursos": [ | |
| { | |
| "tipo": "PARTE", | |
| "label": "Estudo bíblico de congregação", | |
| "segundos": 1800, | |
| "origem_tempo": "WOL" | |
| } | |
| ], | |
| "conteudo_texto": [ | |
| "(30 min) lfb histórias 76-77" | |
| ], | |
| "referencias": [ | |
| { | |
| "texto": "lfb histórias 76-77", | |
| "url": "https://wol.jw.org/pt/wol/pc/r5/lp-t/202026087/10/0" | |
| } | |
| ] | |
| }, | |
| { | |
| "id": "2026-04-13_14", | |
| "is_apostila": false, | |
| "secao": "NOSSA VIDA CRISTÃ", | |
| "numero": -1, | |
| "titulo_limpo": "Comentários finais", | |
| "recursos": [ | |
| { | |
| "tipo": "AUDIO", | |
| "label": "Cântico 57", | |
| "segundos": 164.21, | |
| "origem_tempo": "MP3" | |
| }, | |
| { | |
| "tipo": "ORACAO", | |
| "label": "Oração", | |
| "segundos": 351, | |
| "origem_tempo": "CAL" | |
| }, | |
| { | |
| "tipo": "PARTE", | |
| "label": "Comentários finais", | |
| "segundos": 180, | |
| "origem_tempo": "WOL" | |
| } | |
| ], | |
| "conteudo_texto": [], | |
| "referencias": [] | |
| } | |
| ] | |
| } |