Skip to content

Instantly share code, notes, and snippets.

@lucashmsilva
Last active August 30, 2024 15:22
Show Gist options
  • Save lucashmsilva/9f4596f2924ac5c1eb0721c2c41dc257 to your computer and use it in GitHub Desktop.
Save lucashmsilva/9f4596f2924ac5c1eb0721c2c41dc257 to your computer and use it in GitHub Desktop.
API para criar a função TESOURODIRETO Google Sheets
/*
* @return Retorna a cotação atual de um título específico do Tesouro Direto Junto com a taxa anual de retorno
* @customfunction
**/
function TESOURODIRETO(bondName) {
let srcURL = "https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json";
let jsonData = UrlFetchApp.fetch(srcURL);
let parsedData = JSON.parse(jsonData.getContentText()).response;
for(let bond of parsedData.TrsrBdTradgList) {
let currBondName = bond.TrsrBd.nm;
if (currBondName === bondName)
return [bond.TrsrBd.untrRedVal, bond.TrsrBd.anulInvstmtRate]; // créditos ao @figueiredods por ter encontrado o campo que retorna a taxa correta
}
throw new Error("Not Found");
}
/*
* @return Retorna a cotação atual de um título específico do Tesouro Direto
* @customfunction
**/
function TESOURODIRETO(bondName) {
let srcURL = "https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json";
let jsonData = UrlFetchApp.fetch(srcURL);
let parsedData = JSON.parse(jsonData.getContentText()).response;
for(let bond of parsedData.TrsrBdTradgList) {
let currBondName = bond.TrsrBd.nm;
if (currBondName === bondName)
return bond.TrsrBd.untrRedVal;
}
throw new Error("Not Found");
}
@CBOLIVEIRA1970
Copy link

OI Lucas, valeu obrigado pela ajuda!

@Tiagoalvesds
Copy link

Boa tarde, @lucashmsilva
Sou iniciante no google sheets, segui os passos no (https://www.reddit.com/r/investimentos/comments/hpl8j9/fun%C3%A7%C3%A3o_tesourodierto_no_google_sheets/), e após executar o script não estou conseguindo chamar a função na planilha.(Erro Função desconhecida).#NOME?
Pode me ajudar?

@lucashmsilva
Copy link
Author

Olá @Tiagoalvesds, pode acessar essa página [1] e me mostrar como está ai pra você?
1

Deve estar parecido com isso aqui:
2

@Tiagoalvesds
Copy link

Olá! aparece assim...

script

@lucashmsilva
Copy link
Author

@Tiagoalvesds apague toda a linha 1 e a linha 18. Daí salve em implantar e depois volte pra planilha e tente usar a função de novo.

@Tiagoalvesds
Copy link

implantei...mas ao pedir para rodar ele apareceu esse erro abaixo.. é normal?
image

@lucashmsilva
Copy link
Author

Certo, mas agora esse erro não é por um erro na função em si, mas sim porque você está usando no local errado.

Para usar a função você deve voltar à planilha e chamar a funciona como uma função padrão. Dessa forma:
image
Igual se faz no Excel. A diferença é que a função foi criada por você.

@Tiagoalvesds
Copy link

Opa! agora foi Lucas. obrigado! Só está aparecendo um número desconhecido de 6,24 e 6,34. vide exemplo abaixo.
image

@lucashmsilva
Copy link
Author

@Tiagoalvesds, da uma olhada nesse segundo script. Ele retorna somente o valor do título que é passado na função. Pode só copiar o código e substituir todo o código que você colocou lá. Vai ficar igual ao código do print que mandei na primeira resposta que mandei pra você.

Esse número que você está vendo ai é a taxa anual do título, além do valor.

@Tiagoalvesds
Copy link

@lucashmsilva , deu certo e funciona perfeitamente! Obrigado! Notei que a cotação puxada é a de resgate, teria como adicionar uma linha e/ou função que puxa a cotação de compra?

@lucashmsilva
Copy link
Author

lucashmsilva commented Feb 25, 2023

@Tiagoalvesds , cara teria que analisar o retorno desses dados no site no TD para descobrir qual campo é a cotação de compra para só então adaptar o código. Hoje em dia não me lembro muito bem, mas não deve ser difícil descobrir o campo. A fonte de dados é essa: json b3

@andretc-cit
Copy link

andretc-cit commented Aug 22, 2023

Fiz uma melhoria pra cachear a response do tesouro pq as vezes dá erro por chamar vezes seguidas :)

var cache = CacheService.getScriptCache();

function TESOURODIRETO(bondName, argumento = "r") {
  var tesouro = getCachedTesouro();

  if (bondName == "tesouro-selic-2027")
      bondName = "Tesouro Selic 2027";

  if (bondName == "tesouro-selic-2029")
    bondName = "Tesouro Selic 2029";

  if (bondName == "tesouro-prefixado-2025")
    bondName = "Tesouro Prefixado 2025";

  if (!tesouro) {
    Logger.log("No cached tesouro info found");
    updateTesouroCache();
  }

  tesouro = getCachedTesouro();

  for (let bond of tesouro.TrsrBdTradgList) {
    console.log(bond);
    let currBondName = bond.TrsrBd.nm;
    if (currBondName.toLowerCase() === bondName.toLowerCase())
      if (argumento == "r")
        return bond.TrsrBd.untrRedVal;
      else
        return bond.TrsrBd.untrInvstmtVal;
  }

  return 0;
}

function getCachedTesouro() {
  var cached = cache.get("tesouro");
  if (cached) {
    try {
      var tesouro = JSON.parse(cached).response;
      return tesouro;
    } catch (e) {
      Logger.log("Error while parsing tesouro from cache");
    }
  }
}

function updateTesouroCache() {
  Logger.log("Updating tesouro cached information");

  var response = UrlFetchApp.fetch("https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json");
  var content = response.getContentText();
  
  try {
    var data = JSON.parse(content);
    
    var cachedTesouro = {};
    cachedTesouro["tesouro"] = JSON.stringify(data)
    cache.putAll(cachedTesouro);

  } catch (e) {
    Logger.log("Error while parsing response from tesouro: " + content);
  }
}

@tuliopascoal
Copy link

Alguém com erro desde o final dessa semana (quinta-feira ou sexta-feira 15/16 Agosto 2024)?
Nos logs, recebo um erro 403. Checar o erro abaixo:

Exception: Request failed for https://www.tesourodireto.com.br returned code 403. Truncated server response: <title>Just a moment...</title><meta htt... (use muteHttpExceptions option to examine full response)

Acessando a URL (https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json) via browser, o acesso acontece normalmente.
Será que seja algum bloqueio do site Tesouro Direto devido a grande quantidade de requisições automatizadas com o Google Script?

Grato.

@JpOnline
Copy link

Alguém com erro desde o final dessa semana (quinta-feira ou sexta-feira 15/16 Agosto 2024)? Nos logs, recebo um erro 403. Checar o erro abaixo:

Exception: Request failed for https://www.tesourodireto.com.br returned code 403. Truncated server response: <title>Just a moment...</title><meta htt... (use muteHttpExceptions option to examine full response)

Acessando a URL (https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json) via browser, o acesso acontece normalmente. Será que seja algum bloqueio do site Tesouro Direto devido a grande quantidade de requisições automatizadas com o Google Script?

Grato.

Também estou recebendo esse erro. Ainda estou investigando tbm, sem muito insight além do que vc já falou.

@tuliopascoal
Copy link

Já tentei investigar mais a fundo e sem novidades.
Creio que seja bloqueio por parte do Tesouro Direto mesmo.

Alguém mais focado em dev no Google Script saberia se existe outro modo de dar fetch em páginas html/json sem ser por "UrlFetchApp"? Talvez isso possa gerar uma requição diferente não identificada/bloqueada pelo TesouroDireto.

@rafaelgp23
Copy link

om dia, parece que deu ruim nessa API:

function TESOURODIRETO(bondName, argumento="r") {
  let srcURL = "https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json";
  let jsondata = UrlFetchApp.fetch(srcURL);
}

Parece que o dominio dessa API tá bloqueando chamadas pelo servidor do Google Script:

Exception: Request failed for https://www.tesourodireto.com.br returned code 403. Truncated server response: <!DOCTYPE html><html lang="en-US"><head><title>Just a moment...</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta htt... (use muteHttpExceptions option to examine full response)

@rafaelgp23
Copy link

rafaelgp23 commented Aug 21, 2024

Já tentei investigar mais a fundo e sem novidades. Creio que seja bloqueio por parte do Tesouro Direto mesmo.

Alguém mais focado em dev no Google Script saberia se existe outro modo de dar fetch em páginas html/json sem ser por "UrlFetchApp"? Talvez isso possa gerar uma requição diferente não identificada/bloqueada pelo TesouroDireto.

ou talvez um bypass/workaround, um dominio intermediario processar a chamada da API e no Google script fazer a chamada desse intermediário

Abri uma pergunta no stackoverflow pra ver se consigo ajuda nisso: https://stackoverflow.com/questions/78897095/bypass-workaround-for-urlfetchapp-request-failed-returned-code-403

@lucashmsilva
Copy link
Author

O que está rolando é que agora o Site do Tesouro Direto está usando a Cloudflare. Com isso, estão barrando a request do Google Script, pois estende que ela é um bot. Tentei implementar um proxy, na mesma linha do que o @rafaelgp23 propôs, mas não tive sucesso, nem rodando no meu PC local, antes de subir o proxy para um server.

Assim que tiver um pouco mais de tempo, vou voltar nessa ideia do proxy. Mas até lá, estamos sendo barrados 🙁

@rafaelgp23
Copy link

@lucashmsilva vc tem o contato do dev/owner dessa API? Talvez reportar o problema direto para ele(s)? Não faz sentido um domínio de uma API pública bloquear chamada de função do Google Script.

@lucashmsilva
Copy link
Author

lucashmsilva commented Aug 21, 2024 via email

@tuliopascoal
Copy link

Coloquei algumas informações nessa thread em relação ao que já tentei para entrar em contato com alguém do Tesouro Direto.

Link:
https://gist.github.com/danperrout/b27197056fa38d0d669332647ab89d7a?permalink_comment_id=5159964#gistcomment-5159964

@rafaelgp23
Copy link

rafaelgp23 commented Aug 22, 2024

Realmente, a API tá tentando validar se é um humano fazendo uma chama, mas isso não faz sentido algum, vai totalmente contra o conceito de API:

Isso fica bem claro no "Run code snippet" da descrição do post que fiz no stackoverflow:

https://stackoverflow.com/questions/78897095/bypass-workaround-for-urlfetchapp-request-failed-returned-code-403

E realmente a solução lógica para isso seria reportar o problema para o admin da API mudar o settings no cloudflare para corrigir isso, mas voltamos ao problema de conseguir entrar em contato.

Sem querer polemizar, mas se tratando de serviço público/federal, já sabem que contato e conseguir essa solução ideal é bem difícil.

@EduardoJM
Copy link

EduardoJM commented Aug 22, 2024

Enviei mensagem pela central de atendimentos do Tesouro Direto mas não sei se terei alguma resposta.

Sobre os proxys, pelo nível de bloqueio com CloudFlare, o que dá pra fazer, por enquanto, é implementar algo usando Playwright ou Selenium pra fazer essa req simulando um usuário real de X em X tempos e baixar o JSON e prover um endpoint que devolva ele com Nginx...

@rafaelgp23
Copy link

@brvfr1
Copy link

brvfr1 commented Aug 30, 2024

Alguém já conseguiu solucionar o problema?

@rafaelgp23
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment