Created
August 15, 2025 13:33
-
-
Save Jeiel0rbit/f099fca9dd2b1c2cd0231715d5269dd0 to your computer and use it in GitHub Desktop.
API TabNews + Gemini AI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| import requests | |
| import json | |
| from datetime import datetime, timedelta, timezone | |
| # --- CONFIGURAÇÕES --- | |
| # URL da API do TabNews através do proxy para evitar bloqueio do Cloudflare | |
| PROXY_URL = "https://corsproxy.io/?url=" | |
| # A estratégia 'relevant' é mais adequada para obter os posts com mais tabcoins | |
| TARGET_URL = "https://www.tabnews.com.br/api/v1/contents/NewsletterOficial?strategy=relevant" | |
| TABNEWS_API_URL = f"{PROXY_URL}{TARGET_URL}" | |
| # URL da API do Gemini com o modelo atualizado | |
| GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-lite:generateContent" | |
| # Chave da API do Gemini. | |
| GEMINI_API_KEY = "" | |
| # Cabeçalho personalizado para a requisição. | |
| HEADERS = { | |
| "User-Agent": "sou pessoa legítima apenas para meu projeto Square News" | |
| } | |
| # Instruções (prompt) para a formatação do texto pela API do Gemini | |
| GEMINI_PROMPT_TEMPLATE = """ | |
| Formate as manchetes abaixo para Discord no seguinte padrão: | |
| - Um cabeçalho no início com `:newspaper: **[hora] — [Square News](link)**`. Use um link genérico para o Square News. | |
| - Cada manchete no formato: | |
| **[TÍTULO EM MAIÚSCULO]** — [texto da notícia] | |
| - Palavras e valores importantes em **negrito**. | |
| - Termos conceituais ou estrangeiros em *itálico*. | |
| - Nenhuma numeração, apenas blocos de texto. | |
| - Links sempre embutidos no título geral, não nas manchetes individuais. | |
| - Não adicionar comentários, apenas formatar o texto. | |
| Exemplo de como deve ficar a saída: | |
| :newspaper: **11h — [Square News](https://is.gd/Square_News)** | |
| **CHIPS CEREBRAIS** — A Merge Labs, nova startup de implantes cerebrais apoiada por Sam Altman, entra no mercado avaliada em **US$ 850 milhões**, competindo com a Neuralink de Elon Musk. | |
| **MUNDO AFORA** — Reddit bloqueia bots do Internet Archive por raspagem de IA. Do Kwon, fundador da Luna e TerraUSD, se declara culpado de fraude. Bitcoin bate novo recorde. | |
| Agora, formate as seguintes manchetes: | |
| [cole as manchetes aqui] | |
| """ | |
| def fetch_tabnews_articles(): | |
| """ | |
| Busca os artigos da API do TabNews através de um proxy. | |
| Returns: | |
| list: Uma lista de dicionários de artigos, ou None em caso de erro. | |
| """ | |
| print("Buscando notícias no TabNews via proxy...") | |
| try: | |
| response = requests.get(TABNEWS_API_URL, headers=HEADERS) | |
| response.raise_for_status() | |
| return response.json() | |
| except requests.exceptions.RequestException as e: | |
| print(f"Erro ao buscar notícias do TabNews: {e}") | |
| if e.response is not None: | |
| print(f"Status Code: {e.response.status_code}") | |
| print(f"Resposta do servidor: {e.response.text}") | |
| return None | |
| def select_weekly_top_articles(articles, days=7, limit=7): | |
| """ | |
| Seleciona os 'limit' artigos mais relevantes dos últimos 'days' dias. | |
| A relevância é baseada no número de 'tabcoins'. | |
| Args: | |
| articles (list): A lista de artigos para filtrar. | |
| days (int): O número de dias no passado para considerar. | |
| limit (int): O número máximo de artigos a retornar. | |
| Returns: | |
| list: Uma lista com os artigos mais relevantes da semana. | |
| """ | |
| print(f"Selecionando os {limit} artigos mais relevantes dos últimos {days} dias...") | |
| weekly_articles = [] | |
| if not articles: | |
| return [] | |
| now = datetime.now(timezone.utc) | |
| time_threshold = now - timedelta(days=days) | |
| # 1. Filtra todos os artigos publicados na última semana | |
| for article in articles: | |
| published_at_str = article.get("published_at") | |
| if not published_at_str: | |
| continue | |
| try: | |
| published_at = datetime.fromisoformat(published_at_str.replace('Z', '+00:00')) | |
| if published_at >= time_threshold: | |
| weekly_articles.append(article) | |
| except ValueError: | |
| print(f"Aviso: Não foi possível analisar a data: {published_at_str}") | |
| # 2. Ordena os artigos da semana por relevância (mais tabcoins) | |
| weekly_articles.sort(key=lambda x: x.get('tabcoins', 0), reverse=True) | |
| # 3. Pega os 'limit' melhores artigos | |
| top_articles = weekly_articles[:limit] | |
| # 4. Formata a lista para o Gemini | |
| final_list = [] | |
| for article in top_articles: | |
| title = article.get("title") | |
| source_url = article.get("source_url") | |
| if title and source_url: | |
| final_list.append({ | |
| "title": title, | |
| "source_url": source_url | |
| }) | |
| print(f"Selecionados {len(final_list)} artigos mais relevantes da semana.") | |
| return final_list | |
| def format_with_gemini(articles): | |
| """ | |
| Envia os artigos para a API do Gemini para formatação. | |
| Args: | |
| articles (list): A lista de artigos a serem formatados. | |
| Returns: | |
| str: O texto formatado, ou None em caso de erro. | |
| """ | |
| if not articles: | |
| print("Nenhuma notícia para formatar.") | |
| return None | |
| print("Formatando notícias com a API do Gemini...") | |
| headlines_str = "" | |
| for article in articles: | |
| headlines_str += f"Título: {article['title']}\nFonte: {article['source_url']}\n\n" | |
| full_prompt = GEMINI_PROMPT_TEMPLATE.replace("[cole as manchetes aqui]", headlines_str.strip()) | |
| payload = { | |
| "contents": [{ | |
| "parts": [{"text": full_prompt}] | |
| }] | |
| } | |
| try: | |
| api_url_with_key = f"{GEMINI_API_URL}?key={GEMINI_API_KEY}" | |
| response = requests.post( | |
| api_url_with_key, | |
| headers={"Content-Type": "application/json"}, | |
| data=json.dumps(payload) | |
| ) | |
| response.raise_for_status() | |
| result = response.json() | |
| if (result.get("candidates") and | |
| result["candidates"][0].get("content") and | |
| result["candidates"][0]["content"].get("parts")): | |
| return result["candidates"][0]["content"]["parts"][0].get("text") | |
| else: | |
| print("Erro: Resposta da API do Gemini em formato inesperado.") | |
| print(result) | |
| return None | |
| except requests.exceptions.RequestException as e: | |
| print(f"Erro ao chamar a API do Gemini: {e}") | |
| if e.response is not None: | |
| print(f"Resposta do servidor: {e.response.text}") | |
| return None | |
| def main(): | |
| """ | |
| Função principal que orquestra a execução do script. | |
| """ | |
| all_articles = fetch_tabnews_articles() | |
| if all_articles: | |
| top_articles = select_weekly_top_articles(all_articles, days=7, limit=7) | |
| if top_articles: | |
| formatted_news = format_with_gemini(top_articles) | |
| if formatted_news: | |
| print("\n--- Notícias Formatadas para o Discord ---\n") | |
| print(formatted_news) | |
| else: | |
| print("\nFalha ao formatar as notícias.") | |
| else: | |
| print("\nNenhum artigo relevante encontrado na última semana.") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment