image

Access unlimited bootcamps and 650+ courses

33
%OFF
Article image
Carlos CGS
Carlos CGS29/12/2025 07:49
Share

CodeVersePython2025: Missão Cumprida - 52/52

    🌌 Projeto CodeVerse Python – 52/2025

    📅 Encerramento Oficial do CodeVerse Python 2025

    👋 Fala, galera dev.

    Hoje é 29 de dezembro de 2025. Estamos naqueles últimos dias do final de 2025, e este não é apenas o último artigo do CodeVerse Python 2025. É o fechamento de um ciclo que durou 52 semanas, uma por semana, sem pular nenhuma, de muito aprendizado, descoberta e desafios, e eu não digo apenas da parte dos assinantes desta newsletter, mas muito da minha parte também, afinal foi um baita desafio que me ajudou muito em minha carreira, tanto na parte de resiliência quanto de ajustes de time, tempo e maturidade para me manter firme em meu propósito.

    image

    No artigo anterior (#51), falamos sobre publicação, presença digital e como mostrar nossos projetos ao mundo. Hoje, no Artigo 52, eu quis fechar da forma mais coerente possível com tudo que foi construído aqui: 👉 Código funcionando, integrado, corrigido e explicado.

    Para mostrar na prática o que ocorre no mundo dos devs, todo projeto que estamos finalizando damos mais um passada de olho, observamos alguma fix, algum reparo ou alguma melhoria que pode ser considerada, e não poderíamos ficar de fora disso. Antes de nos despedirmos oficialmente do CodeVerse Python 2025, vamos fazer duas correções importantes no Jarvis, algo extremamente realista no dia a dia de qualquer desenvolvedor:

    • 1️⃣ Ajustar a API de clima, fazendo o Jarvis falar a previsão em português corretamente
    • 2️⃣ Corrigir um bug de encerramento, garantindo que o botão de fechar realmente finalize a aplicação

    Isso não é detalhe. Isso é maturidade técnica.

    🛠️ Patch Final - Ajustes de Qualidade e Encerramento do J.A.R.V.I.S.

    Antes de encerrar oficialmente o CodeVerse Python 2025, fiz algo que todo desenvolvedor aprende com o tempo: olhar para o próprio código com calma, senso crítico e carinho. Nenhum projeto termina “perfeito”. Mas todo projeto pode terminar melhor do que começou.

    Por isso, apliquei dois ajustes finais no J.A.R.V.I.S., não para adicionar novas funcionalidades, mas para refinar a experiência e corrigir pequenos pontos que fazem toda a diferença no uso real da aplicação.

    🌦️ Ajuste 1 — Clima e previsão falando português de verdade

    Até então, a integração com a API do OpenWeather funcionava, mas retornava descrições em inglês e temperaturas em Kelvin e eu fazia uma conta para transforma-la em celcius. Tecnicamente correto, mas pouco elegante para um assistente que conversa em português.

    No patch final, ajustei a integração para:

    • Retornar descrições climáticas em português (pt-BR)
    • Trabalhar diretamente com graus Celsius, sem conversões manuais
    • Produzir respostas mais naturais e fluídas na fala do Jarvis

    Agora, quando o Jarvis informa o clima ou a previsão, ele realmente “fala a nossa língua”, entregando uma experiência muito mais próxima de um assistente real.

    Esse tipo de ajuste parece pequeno, mas mostra maturidade: pensar no usuário final, não apenas no funcionamento técnico.

    ❌ Ajuste 2 - Encerramento real da aplicação (adeus, bug chato)

    Durante os testes finais, identifiquei um comportamento clássico em aplicações com interface gráfica e threads: ao clicar para fechar o assistente, a janela sumia… mas o processo continuava rodando em segundo plano. Esse é aquele tipo de bug silencioso que todo dev já enfrentou. A correção envolveu três pontos importantes:

    • Garantir que o loop principal do assistente fosse interrompido corretamente
    • Encerrar a thread de escuta de forma segura
    • Amarrar o fechamento da aplicação tanto ao comando de voz (“desligar/encerrar”) quanto ao botão X da janela

    O resultado: agora, quando o Jarvis é encerrado, ele encerra de verdade. Sem processos presos, sem aplicação zumbi, sem gambiarra. Esse ajuste representa algo muito maior do que uma correção técnica: representa responsabilidade com o software que você entrega.

    🧠 Por que encerrar assim importa?

    Finalizar um projeto não é sair adicionando mais features até cansar. É saber parar, revisar, corrigir e entregar algo coeso. Esses dois ajustes finais simbolizam exatamente isso:

    • Atenção à experiência do usuário
    • Cuidado com detalhes
    • Consciência de arquitetura
    • Mentalidade de produto, não apenas de código

    Encerrar o CodeVerse Python 2025 dessa forma fecha o ciclo do jeito certo: com aprendizado, reflexão e evolução real. Porque no fim das contas, projetos não são sobre linhas de código. São sobre decisões. E essas decisões dizem muito sobre o dev que você está se tornando. Acredito ter muito mais bugs a serem corrigidos nesse nosso código, mas não conseguia fazer todos os testes, fiz apenas testes manuais em cada parte do código que escrevia, vendo se cada parte funciona e conversava uma com as outras.

    🧠 Código final do Jarvis — Versão consolidada (CodeVerse 2025)

    ⚠️ Observação importante Este código é propositalmente mantido em um único arquivo, como foi a proposta didática do projeto ao longo do ano. Em 2026, a ideia é modularizar e evoluir essa base.

    🐍 Jarvis.py — Versão Final (29/12/2025)

    import os
    import random
    import webbrowser
    import tkinter as tk
    from threading import Thread
    from datetime import datetime, timedelta
    from collections import deque
    import sys
    
    import requests
    import speech_recognition as sr
    import pyttsx3
    import google.generativeai as genai
    
    # ================= CONFIGURAÇÕES =================
    
    OPENWEATHER_KEY = "SUA_CHAVE_OPENWEATHER"
    GEMINI_KEY = "SUA_CHAVE_GEMINI"
    
    genai.configure(api_key=GEMINI_KEY)
    
    PLAYLISTS = {
      "foco": "https://www.youtube.com/playlist?list=SUA_PLAYLIST_FOCO",
      "estudo": "https://www.youtube.com/playlist?list=SUA_PLAYLIST_ESTUDO",
      "treino": "https://www.youtube.com/playlist?list=SUA_PLAYLIST_TREINO",
    }
    
    ARQUIVO_NOTAS = "notas_jarvis.txt"
    memoria = deque(maxlen=20)
    modo_privado = False
    
    
    # ==== FUNÇÕES DE MEMÓRIA E NOTAS ====
    
    
    def registrar_memoria(entrada, resposta):
      global modo_privado
      if modo_privado:
          return
      item = {
          "hora": datetime.now().strftime("%H:%M"),
          "entrada": entrada,
          "resposta": resposta,
      }
      memoria.append(item)
    
    
    def resumo_memoria():
      if not memoria:
          return "Ainda não tenho registros recentes, senhor."
      linhas = []
      for m in memoria:
          linhas.append(f"[{m['hora']}] Você: {m['entrada']} | Eu: {m['resposta']}")
      return "Aqui está um resumo das nossas últimas interações:\n" + "\n".join(linhas)
    
    
    def registrar_nota(texto):
      global modo_privado
      if modo_privado:
          return "Modo privado ativo, não vou registrar essa anotação, senhor."
      timestamp = datetime.now().strftime("%d/%m/%Y %H:%M")
      linha = f"[{timestamp}] {texto}\n"
      with open(ARQUIVO_NOTAS, "a", encoding="utf-8") as f:
          f.write(linha)
      return "Anotação registrada com sucesso, senhor."
    
    
    def ativar_modo_privado():
      global modo_privado
      modo_privado = True
      return "Modo privado ativado. Não irei registrar histórico nem anotações."
    
    
    def desativar_modo_privado():
      global modo_privado
      modo_privado = False
      return "Modo privado desativado. Voltarei a registrar suas interações."
    
    
    # ==== SERVIÇOS EXTERNOS ====
    # ✅ ALTERAÇÃO 1: CLIMA/PREVISÃO EM PT-BR + UNITS METRIC (°C)
    
    
    def obter_temperatura(cidade):
      # lang=pt_br -> descrição em português
      # units=metric -> temperatura em Celsius
      url = (
          "https://api.openweathermap.org/data/2.5/weather"
          f"?appid={OPENWEATHER_KEY}&q={cidade}&lang=pt_br&units=metric"
      )
      response = requests.get(url)
    
      if response.status_code == 200:
          data = response.json()
          temp = round(data["main"]["temp"], 1)
          desc = data["weather"][0]["description"]
          return f"A temperatura em {cidade} é de {temp} graus célcius com tempo {desc}."
      else:
          return "Não consegui encontrar essa cidade, senhor."
    
    
    def obter_previsao(cidade):
      url = (
          "https://api.openweathermap.org/data/2.5/forecast"
          f"?appid={OPENWEATHER_KEY}&q={cidade}&lang=pt_br&units=metric"
      )
      response = requests.get(url)
    
      if response.status_code == 200:
          dados = response.json()
          amanha = (datetime.now() + timedelta(days=1)).date()
    
          previsao = [
              item
              for item in dados["list"]
              if datetime.fromtimestamp(item["dt"]).date() == amanha
          ]
    
          if not previsao:
              return "Não encontrei dados de previsão para amanhã, senhor."
    
          temp = round(previsao[0]["main"]["temp"], 1)
          desc = previsao[0]["weather"][0]["description"]
    
          # Texto mais natural em PT-BR
          return f"A previsão para amanhã em {cidade} é de {desc}, com temperatura em torno de {temp} graus célcius."
      else:
          return "Não consegui obter a previsão, senhor."
    
    
    def pesquisar_na_internet(pergunta):
      regras = """
      Responda como J.A.R.V.I.S., assistente do Tony Stark:
      - Seja direto e educado.
      - Use no máximo 25 palavras.
      - Mantenha tom profissional e leve.
      """
      model = genai.GenerativeModel("gemini-2.5-flash")
      chat = model.start_chat(history=[])
      resposta = chat.send_message(regras + pergunta)
      return resposta.text
    
    
    def tocar_playlist(nome):
      nome = nome.lower().strip()
      if nome in PLAYLISTS:
          webbrowser.open(PLAYLISTS[nome])
          return f"Tocando a playlist de {nome}, senhor."
      return "Não encontrei essa playlist, senhor."
    
    
    # ==== APLICAÇÃO TKINTER + LOOP DE VOZ ====
    
    
    class JarvisApp:
      def __init__(self, root):
          self.root = root
          self.root.title("Jarvis - CodeVerse 2025")
          self.root.geometry("260x230")
    
          bg_color = "#0B3D2E"
          fg_color = "#E6F2EF"
          self.root.configure(bg=bg_color)
    
          self.label = tk.Label(
              root,
              text="Assistente de Voz - J.A.R.V.I.S.",
              bg=bg_color,
              fg=fg_color,
              font=("Helvetica", 9),
          )
          self.label.pack(pady=10)
    
          self.canvas = tk.Canvas(
              root, width=120, height=120, bg=bg_color, highlightthickness=0
          )
          self.circle = self.canvas.create_oval(
              35, 35, 85, 85, outline="#00FFFF", width=5
          )
          self.canvas.pack()
    
          self.btn_iniciar = tk.Button(
              root,
              text="Iniciar Assistente",
              command=self.iniciar_assistente,
              bg=fg_color,
              fg=bg_color,
              font=("Helvetica", 9),
          )
          self.btn_iniciar.pack(pady=5)
    
          self.running = False
    
          # ✅ ALTERAÇÃO 2: FECHAR NO X (WM_DELETE_WINDOW) ENCERRA DE VERDADE
          self.root.protocol("WM_DELETE_WINDOW", self.encerrar_app)
    
      def mudar_cor_circulo(self, cor):
          self.canvas.itemconfig(self.circle, outline=cor)
    
      def encerrar_app(self):
          """
          Encerra o loop, fecha a janela e finaliza a aplicação de verdade.
          """
          self.running = False
          try:
              self.root.quit()
          except:
              pass
          try:
              self.root.destroy()
          except:
              pass
    
      def iniciar_assistente(self):
          if not self.running:
              self.running = True
              # daemon=True evita que a thread segure o processo após fechar a janela
              thread = Thread(target=self.executar_assistente, daemon=True)
              thread.start()
              self.label.config(text="Assistente iniciado...")
    
      def executar_assistente(self):
          engine = pyttsx3.init()
          r = sr.Recognizer()
    
          def falar(texto):
              self.mudar_cor_circulo("#00FF00")
              engine.say(texto)
              engine.runAndWait()
              self.mudar_cor_circulo("#00FFFF")
    
          def ouvir():
              with sr.Microphone() as source:
                  r.adjust_for_ambient_noise(source)
                  audio = r.listen(source)
              try:
                  comando = r.recognize_google(audio, language="pt-BR").lower()
                  print("Você disse:", comando)
                  return comando
              except:
                  return ""
    
          # Saudação inicial (base #44 + clima do #46) — agora com descrição PT-BR
          saudacao = (
              "Bom dia"
              if datetime.now().hour < 12
              else "Boa tarde" if datetime.now().hour < 18 else "Boa noite"
          )
          clima_sp = obter_temperatura("São Paulo")
          falar(
              f"{saudacao}, senhor Carlos. Hoje é {datetime.now().strftime('%d/%m/%Y')}, "
              f"são {datetime.now().strftime('%H:%M')}. {clima_sp} Me chamo Jarvis."
          )
          falar("Se precisar de mim, diga meu nome, senhor.")
          self.label.config(text="Diga: 'Jarvis' para ativar.")
    
          while self.running:
              frase = ouvir()
              if not self.running:
                  break
              if not frase:
                  continue
    
              if "jarvis" in frase:
                  respostas_ativacao = [
                      "Sim, senhor?",
                      "Às ordens.",
                      "Estou aqui.",
                      "Pronto para ajudar.",
                  ]
                  resp_ativacao = random.choice(respostas_ativacao)
                  falar(resp_ativacao)
                  self.label.config(text="Aguardando comando...")
    
                  comando = ouvir()
                  if not self.running:
                      break
                  if not comando:
                      falar("Não consegui entender o comando, senhor.")
                      continue
    
                  resposta_texto = self.processar_comando(comando, falar, ouvir)
                  if resposta_texto:
                      registrar_memoria(comando, resposta_texto)
    
      def processar_comando(self, comando, falar, ouvir):
          comando = comando.lower()
          resposta = ""
    
          # COMANDOS LOCAIS (#45)
          if "abrir navegador" in comando:
              os.system("start chrome.exe")
              resposta = "Abrindo o navegador."
          elif "abrir calculadora" in comando:
              os.system("start calc.exe")
              resposta = "Abrindo a calculadora."
          elif "abrir word" in comando:
              os.system("start winword.exe")
              resposta = "Abrindo o Word."
          elif "abrir excel" in comando:
              os.system("start excel.exe")
              resposta = "Abrindo o Excel."
          elif "abrir vs code" in comando or "abrir vscode" in comando:
              os.system("start code")
              resposta = "Abrindo o Visual Studio Code."
    
          # HORA / DATA
          elif "que horas são" in comando:
              hora = datetime.now().strftime("%H:%M")
              resposta = f"Agora são {hora}."
          elif "que dia é hoje" in comando:
              data = datetime.now().strftime("%d/%m/%Y")
              resposta = f"Hoje é {data}."
    
          # CLIMA / PREVISÃO (#46) — agora em PT-BR
          elif "temperatura" in comando:
              falar("De qual cidade deseja saber, senhor?")
              cidade = ouvir()
              resposta = (
                  obter_temperatura(cidade)
                  if cidade
                  else "Não consegui ouvir o nome da cidade."
              )
          elif "previsão" in comando:
              falar("Para qual cidade devo consultar a previsão, senhor?")
              cidade = ouvir()
              resposta = (
                  obter_previsao(cidade)
                  if cidade
                  else "Não consegui ouvir o nome da cidade."
              )
    
          # PESQUISA COM IA (GEMINI) (#32, #46)
          elif "pesquisar" in comando or "perguntar" in comando:
              assunto = comando.replace("pesquisar", "").replace("perguntar", "").strip()
              if not assunto:
                  falar("Sobre o que deseja saber, senhor?")
                  assunto = ouvir()
              if assunto:
                  falar("Um momento, pesquisando.")
                  resposta = pesquisar_na_internet(assunto)
              else:
                  resposta = "Não consegui entender o assunto da pesquisa."
    
          # PLAYLISTS (#41, #42)
          elif "playlist" in comando or "tocar música" in comando:
              falar("Qual playlist o senhor deseja? Foco, estudo ou treino?")
              nome_playlist = ouvir()
              resposta = (
                  tocar_playlist(nome_playlist)
                  if nome_playlist
                  else "Não entendi o nome da playlist."
              )
    
          # NOTAS / TRANSCRIÇÃO (#40)
          elif "anotar" in comando or "registrar nota" in comando:
              falar("O que deseja anotar, senhor?")
              nota = ouvir()
              if nota:
                  resposta = registrar_nota(nota)
              else:
                  resposta = "Não consegui ouvir o conteúdo da anotação."
    
          # MEMÓRIA CONTEXTUAL (#39, #43)
          elif "o que falamos hoje" in comando or "histórico" in comando:
              resposta = resumo_memoria()
    
          # MODO PRIVADO
          elif "ativar modo privado" in comando:
              resposta = ativar_modo_privado()
          elif "desativar modo privado" in comando:
              resposta = desativar_modo_privado()
    
          # DESLIGAR (✅ FECHA DE VERDADE)
          elif "desligar" in comando or "encerrar" in comando:
              resposta = "Encerrando o sistema. Até mais, senhor."
              falar(resposta)
              self.encerrar_app()
              return resposta
    
          else:
              resposta = "Desculpe, não entendi o comando, senhor."
    
          if resposta:
              falar(resposta)
          return resposta
    
    
    if __name__ == "__main__":
      root = tk.Tk()
      app = JarvisApp(root)
      root.mainloop()
    

    🏆 Um reconhecimento que já vale a jornada inteira

    Antes de fechar esse ciclo, tem algo muito especial que eu preciso registrar aqui. O CodeVerse Python 2025 foi indicado ao DIO Awards 2025, na categoria Best Tech Project. E só de escrever isso, eu já sinto que tudo valeu a pena.

    image

    Esse projeto nunca foi pensado para competir ou ganhar prêmio. Ele nasceu como um desafio pessoal, uma tentativa de aprender de verdade e compartilhar o processo, sem filtros, sem personagem, sem fingir que eu sabia mais do que sabia. Estar entre os indicados da maior premiação tech da América Latina já é uma honra gigantesca. É um reconhecimento que valida não apenas o código, mas a consistência, a proposta e a forma como o conhecimento foi compartilhado ao longo de 52 semanas.

    Estar ali, ao lado de projetos e pessoas tão incríveis, já é vitória. Já é sinal de que aprender em público, errar em público e ensinar do jeito mais simples possível também tem valor. Esse reconhecimento não é sobre status. É sobre pertencimento. É sobre saber que essa jornada fez sentido para mais pessoas além de mim.

    👉 projetos feitos com verdade chegam mais longe do que a gente imagina.

    🚀 Agradecimentos finais sobre o projeto.

    Esse código não é perfeito. E, sinceramente? Ainda bem que não...

    Ele representa exatamente o que o CodeVerse Python 2025 sempre foi pra mim: aprendizado real, evolução aos poucos, código que melhora com o tempo, erro que aparece, erro que ensina, erro que vira ajuste. Nada de glamour. Só prática, constância e consciência técnica sendo construída na vida real.

    Esse projeto nasceu em dezembro de 2024, próximo ao ano novo quando estava refletido sobre minha vida e o que estava deixando de positivo ao mundo, foi ai que resolvi me desafiar a escrever um artigo por semana. No começo, parecia tranquilo. Uma vez por semana soava fácil, quase confortável demais. Eu realmente achei que daria tudo certo sem grandes dificuldades. Que iria me desafiar a aprender durante o processo para poder escrever cada um dos artigos.

    Mas o ano começou… e a vida veio junto.

    Promoção no trabalho, função nova, mais responsabilidade. Faculdade. Curso técnico. Casa. Família. Filhos. Aquela lista infinita que todo adulto conhece bem. O tempo que eu achava que teria simplesmente sumiu. E foi aí que veio a escolha: ou eu parava, ou eu dava um jeito.

    E eu dei um jeito. Escrevi no horário de almoço. Escrevi nos finais de semana. E, muitas vezes, escrevi de madrugada. Não foi bonito. Não foi confortável. Mas foi constante.

    Durante todo o ano de 2024 eu havia conseguido tirar do papel um sonho antigo: recriar o J.A.R.V.I.S.. Algo que, quando comecei a programar em 2016, parecia coisa de outro mundo. Naquela época, eu achava que só grandes empresas ou aqueles devs “fora da curva” conseguiriam fazer algo assim. Sabe aquele estereótipo do dev prodígio, que com 10 anos já hackeia a CIA? Pois é… eu me comparava com isso e pensava: “isso não é pra mim”.

    Só que a jornada mostrou outra coisa. Mostrou que muita coisa que parece distante, na verdade, só é mal explicada. Que estudar com calma, praticar, errar e insistir encurta caminhos. Em 2024 eu escrevi o código, testei, quebrei, arrumei, refatorei e vi funcionar. E em 2025 eu tomei uma decisão importante: compartilhar tudo isso com a comunidade Dev!

    Não como especialista. Não como dono da verdade. Mas como alguém que está aprendendo e quer ajudar quem está começando.

    Na faculdade, eu mesmo assistia aulas importantes sendo passadas de forma pesada, teórica demais, sem brilho. E isso quase fez a programação parecer chata, quando, na real, ela é absurda de incrível. Foi aí que eu decidi seguir outro caminho: explicar do jeito que eu gostaria de ter aprendido. Mais simples, mais visual, mais lúdico. Usando analogias, criatividade e o universo Marvel, porque fazia sentido pra mim. E, pelo visto, pra muita gente também.

    Hoje, fechando esse projeto, eu não sinto que estou encerrando algo. Sinto que estou construindo uma base, um início de algo muito maior. E se você chegou até aqui, guarda isso com você:

    • 👉 você é capaz, mesmo achando que não é
    • 👉 você não precisa se sentir pronto pra começar
    • 👉 disciplina faz mais milagre do que motivação

    O CodeVerse Python 2025 termina aqui com a minha imensa gratidão a todos que acompanharam essa trajetória em 2025. Mas o aprendizado não para. Nos vemos em 2026. Com mais bagagem, menos medo e novas ideias malucas pra tirar do papel.

    Segue link do meu LinkdIn e do repositório Oficial do Projeto no Github contendo todos os artigos de forma organizada, assim como o código do Jarvis: https://github.com/Carlos-CGS/CodeVerse-Python2025

    🚀🐍 CodeVerse Python 2025 - oficialmente encerrado.

    “Código nunca termina. Ele só evolui.”
    "Nunca estamos 100% prontos, é no caminho que evoluimos..."

    image

    Share
    Recommended for you
    Microsoft Certification Challenge #5 - AI 102
    Bradesco - GenAI & Dados
    GitHub Copilot - Código na Prática
    Comments (1)
    Fernando Araujo
    Fernando Araujo - 29/12/2025 09:39

    Perfeito, Carlos! Tarefa cumprida (e comprida também!)! Eu acompanhei todos os artigos desse projeto, lendo cada um deles. E como eu já disse num comentário em um artigo anterior: esse é um projeto que eu gostaria de ter feito! Mas foi você quem fez! Eu agradeço por você ter publicizado um projeto tão complexo e de forma muito didática! Vejo muita dedicação e capricho seus nele!!

    Tem muita coisa que eu posso aprender com ele. Sou fã de Python e também estou me aprofundando na linguagem, portanto, o projeto vai ser uma ótima fonte de aprendizagem para mim. De coisas reais, em um projeto real, não de códigos em exemplos de um livro.

    Entendo perfeitamente seu desejo de deixar alguma coisa, torná-la pública e dedicar seu tempo livre para fazer isso tudo. Isso é idealismo! Eu também sou bem assim! Quando eu decido fazer alguma coisa, vou até o fim, mesmo que ele não traga o reconhecimento merecido e passe por dificuldades para sua conclusão.

    Foi assim em 2024, quando decidi criar um grande projeto aqui, a série DIRETO AO PONTO, publicado artigos voltados para os iniciantes em programação, com conceitos básicos de programação e eletrônica básica do computador.

    Por ele, fui indicado ao DIO Awards de 2024 e consegui ganhar o prêmio. Mas, mesmo se o prêmio não tivesse vindo, a minha sensação era de vitória, por ter concluído o projeto planejado, e de satisfação, por muita gente ter reconhecido a validade, a qualidade e a relevância que eu coloquei nas publicações.

    Como você, eu também não faço projetos visando uma premiação, faço para deixar alguma coisa em retorno ao aprendizado. Se ela vier, será um bônus. Se não vier, o projeto vai ficar e pode servir para alguém! E eu o fiz! É como comercial antigo de tv: "Foi feito por mim!".

    Pois sinta-se assim: "Foi feito por você!". Você conseguiu!!!

    Concluindo meus comentários sobre seu projeto do Codeverse Pyhon 2025 (mais conhecido como Jarvis), parabéns pela conclusão de projeto tão relevante!!!!! E obrigado por tê-lo tornado público! Será uma ótima fonte de aprendizado para a comunidade!