diff --git a/.BD_manager_Mysql.py.swn b/.BD_manager_Mysql.py.swn
new file mode 100644
index 0000000..552afc8
Binary files /dev/null and b/.BD_manager_Mysql.py.swn differ
diff --git a/.BD_manager_Mysql.py.swo b/.BD_manager_Mysql.py.swo
new file mode 100644
index 0000000..7564765
Binary files /dev/null and b/.BD_manager_Mysql.py.swo differ
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b7faf40
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,207 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[codz]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py.cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# UV
+# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+#uv.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+#poetry.toml
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
+# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
+#pdm.lock
+#pdm.toml
+.pdm-python
+.pdm-build/
+
+# pixi
+# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
+#pixi.lock
+# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
+# in the .venv directory. It is recommended not to include this directory in version control.
+.pixi
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.envrc
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+# Abstra
+# Abstra is an AI-powered process automation framework.
+# Ignore directories containing user credentials, local state, and settings.
+# Learn more at https://abstra.io/docs
+.abstra/
+
+# Visual Studio Code
+# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
+# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
+# and can be added to the global gitignore or merged into this file. However, if you prefer,
+# you could uncomment the following to ignore the entire vscode folder
+# .vscode/
+
+# Ruff stuff:
+.ruff_cache/
+
+# PyPI configuration file
+.pypirc
+
+# Cursor
+# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
+# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
+# refer to https://docs.cursor.com/context/ignore-files
+.cursorignore
+.cursorindexingignore
+
+# Marimo
+marimo/_static/
+marimo/_lsp/
+__marimo__/
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/V1.iml b/.idea/V1.iml
new file mode 100644
index 0000000..f936df0
--- /dev/null
+++ b/.idea/V1.iml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml
new file mode 100644
index 0000000..d7a6c2c
--- /dev/null
+++ b/.idea/checkstyle-idea.xml
@@ -0,0 +1,16 @@
+
+
+
+ 10.24.0
+ JavaOnly
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..9c69411
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..c43501b
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BD_manager.py b/BD_manager.py
new file mode 100644
index 0000000..03e9cc8
--- /dev/null
+++ b/BD_manager.py
@@ -0,0 +1,175 @@
+import sqlite3
+from typing import Optional, Dict, List, Any
+
+db_path = "agenda.db"
+
+def inserir_acompanhamento(chat_id: int, status: str, nome: str) -> None:
+ """
+ Insere um registro na tabela usuarios.
+
+ :param db_path: Caminho do banco de dados SQLite
+ :param chat_id: ID do chat (ex: Telegram)
+ :param status: Status do usuário
+ """
+ conn = sqlite3.connect(db_path)
+ cursor = conn.cursor()
+
+ query = """
+ INSERT INTO acompanhamento (chat_id, status, nome)
+ VALUES (?, ?, ?)
+ """
+
+ cursor.execute(query, (chat_id, status, nome))
+ conn.commit()
+ conn.close()
+
+
+
+
+def atualizar_acompanhamento(
+ chat_id: int,
+ campo: str,
+ information: str
+) -> bool:
+ """
+ Atualiza o status do último registro (maior id) de um chat_id.
+
+ :param db_path: Caminho do banco SQLite
+ :param chat_id: Chat ID a ser atualizado
+ :param novo_status: Novo status
+ :return: True se atualizou, False se não encontrou registro
+ """
+ conn = sqlite3.connect(db_path)
+ cursor = conn.cursor()
+
+ # Localiza o último registro do chat_id
+ cursor.execute(
+ """
+ SELECT id
+ FROM acompanhamento
+ WHERE chat_id = ?
+ ORDER BY id DESC
+ LIMIT 1
+ """,
+ (chat_id,)
+ )
+
+ row = cursor.fetchone()
+
+ if row is None:
+ conn.close()
+ return False
+
+ ultimo_id = row[0]
+
+ # Atualiza apenas o registro mais recente
+ cursor.execute(
+ f"UPDATE acompanhamento SET {campo} = ? WHERE id = ?"
+,
+ (information, ultimo_id)
+ )
+
+ conn.commit()
+ conn.close()
+ return True
+
+#atualizar_acompanhamento(614413127, "nome","zézé")
+
+
+def buscar_ultimo_chat(
+ chat_id: int
+) -> Optional[Dict[str, object]]:
+ """
+ Retorna o último registro (maior id) de um chat_id.
+
+ :param db_path: Caminho do banco SQLite
+ :param chat_id: Chat ID a ser consultado
+ :return: Dicionário com os dados ou None se não encontrar
+ """
+ conn = sqlite3.connect(db_path)
+ conn.row_factory = sqlite3.Row
+ cursor = conn.cursor()
+
+ cursor.execute(
+ """
+ SELECT id, chat_id, status, nome, data_event, time_event, horarios_disponiveis
+ FROM acompanhamento
+ WHERE chat_id = ?
+ ORDER BY id DESC
+ LIMIT 1
+ """,
+ (chat_id,)
+ )
+
+ row = cursor.fetchone()
+ conn.close()
+
+ if row is None:
+ return None
+
+ return {
+ "id": row["id"],
+ "chat_id": row["chat_id"],
+ "status": row["status"],
+ "nome": row["nome"],
+ "data_event": row["data_event"],
+ "time_event": row["time_event"],
+ "horarios_disponiveis": row["horarios_disponiveis"],
+
+ }
+
+#print(buscar_ultimo_chat(614413127)['horarios_disponiveis'])
+
+def inserir_evento(event_date, start_time, end_time, title, description, chat_id, name, created_by) -> None:
+ """
+ Insere um registro na tabela usuarios.
+
+ :param db_path: Caminho do banco de dados SQLite
+ :param chat_id: ID do chat (ex: Telegram)
+ :param status: Status do usuário
+ """
+ conn = sqlite3.connect(db_path)
+ cursor = conn.cursor()
+
+ query = """
+ INSERT INTO events (event_date, start_time, end_time, title, description, chat_id, name, created_by)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
+ """
+
+ cursor.execute(query, (event_date, start_time, end_time, title, description, chat_id, name, created_by))
+ conn.commit()
+ conn.close()
+
+#--- Query agenda
+def listar_agenda(campo: str, parametro: Any) -> List[Dict[str, Any]]:
+ colunas_permitidas = {"id","periodo", "data", "nome", "status"} # ajuste conforme sua tabela
+
+ if campo not in colunas_permitidas:
+ raise ValueError("Campo inválido para consulta.")
+
+ try:
+ conn = sqlite3.connect(db_path)
+ conn.row_factory = sqlite3.Row
+ cursor = conn.cursor()
+
+ query = f"SELECT * FROM agenda WHERE {campo} = ?;"
+ cursor.execute(query, (parametro,))
+
+ rows = cursor.fetchall()
+ return [dict(row) for row in rows]
+
+ except sqlite3.Error as e:
+ print(f"Erro na consulta: {e}")
+ return []
+
+ finally:
+ if conn:
+ conn.close()
+
+
+'''a = listar_agenda("periodo","manhã")
+print(a)
+print(a[1]['horario'])
+
+for z in a:
+ print(z['horario'])'''
\ No newline at end of file
diff --git a/BD_manager_Mysql.py b/BD_manager_Mysql.py
new file mode 100644
index 0000000..09ca10e
--- /dev/null
+++ b/BD_manager_Mysql.py
@@ -0,0 +1,229 @@
+import mysql.connector
+from mysql.connector import Error
+from typing import Optional, Dict, List, Dict, Any
+from datetime import datetime
+
+
+
+
+DB_CONFIG = {
+ "host": "mysql-1ee2345c-glauberroberto-0e4e.h.aivencloud.com",
+ "user": "avnadmin",
+ "password": "AVNS_rmoyFuLW827cdgCMPwh",
+ "database": "agenda",
+ "port": 27341
+}
+
+def get_connection():
+ return mysql.connector.connect(**DB_CONFIG)
+
+def inserir_acompanhamento(chat_id: int, status: str, nome: str) -> None:
+ conn = get_connection()
+ cursor = conn.cursor()
+
+ query = """
+ INSERT INTO acompanhamento (chat_id, status, nome)
+ VALUES (%s, %s, %s)
+ """
+
+ cursor.execute(query, (chat_id, status, nome))
+ conn.commit()
+
+ cursor.close()
+ conn.close()
+
+def atualizar_acompanhamento(chat_id: int, campo: str, information: str) -> bool:
+ campos_permitidos = {"status", "nome", "data_event", "time_event"}
+
+ if campo not in campos_permitidos:
+ raise ValueError("Campo não permitido")
+
+ conn = get_connection()
+ cursor = conn.cursor()
+
+ cursor.execute(
+ """
+ SELECT id
+ FROM acompanhamento
+ WHERE chat_id = %s
+ ORDER BY id DESC
+ LIMIT 1
+ """,
+ (chat_id,)
+ )
+
+ row = cursor.fetchone()
+
+ if row is None:
+ cursor.close()
+ conn.close()
+ return False
+
+ ultimo_id = row[0]
+
+ query = f"UPDATE acompanhamento SET {campo} = %s WHERE id = %s"
+ cursor.execute(query, (information, ultimo_id))
+
+ conn.commit()
+ cursor.close()
+ conn.close()
+ return True
+
+def buscar_ultimo_chat(chat_id: int) -> Optional[Dict[str, object]]:
+ conn = get_connection()
+ cursor = conn.cursor(dictionary=True)
+
+ cursor.execute(
+ """
+ SELECT id, chat_id, status, nome, data_event, time_event
+ FROM acompanhamento
+ WHERE chat_id = %s
+ ORDER BY id DESC
+ LIMIT 1
+ """,
+ (chat_id,)
+ )
+
+ row = cursor.fetchone()
+
+ cursor.close()
+ conn.close()
+
+ return row
+
+
+def get_events():
+ conn = get_connection()
+ cur = conn.cursor(dictionary=True)
+ cur.execute("""
+ SELECT * FROM events
+ """)
+ events = cur.fetchall()
+ cur.close()
+ conn.close()
+ return events
+
+def update_events(id, campo, informacao):
+ try:
+ # Conexão com o banco de dados
+ conn = get_connection()
+ cursor = conn.cursor()
+
+ cursor = conn.cursor()
+
+ # Query de atualização
+ sql = f"""
+ UPDATE events
+ SET {campo} = {informacao}
+ WHERE id = {id};
+ """
+
+ cursor.execute(sql)
+ conn.commit() # Confirma a transação
+
+ print(f"{cursor.rowcount} registros atualizados.")
+
+ except mysql.connector.Error as erro:
+ print(f"Erro ao atualizar: {erro}")
+ finally:
+ if conn.is_connected():
+ cursor.close()
+ conn.close()
+ print("Conexão encerrada.")
+#update_events(18, "avisos","1")
+
+
+
+
+
+def inserir_evento(
+ event_date,
+ start_time,
+ end_time,
+ title,
+ description,
+ chat_id,
+ name,
+ created_by
+) -> None:
+ conn = get_connection()
+ cursor = conn.cursor()
+
+ query = """
+ INSERT INTO events
+ (event_date, start_time, end_time, title, description, chat_id, name, created_by)
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
+ """
+
+ cursor.execute(
+ query,
+ (event_date, start_time, end_time, title, description, chat_id, name, created_by)
+ )
+
+ conn.commit()
+ cursor.close()
+ conn.close()
+
+
+
+#--- Query agenda
+def listar_agenda(campo: str, parametro: Any) -> List[Dict[str, Any]]:
+ colunas_permitidas = {"id", "periodo", "data", "nome", "status"} # ajuste conforme sua tabela
+
+ if campo not in colunas_permitidas:
+ raise ValueError("Campo inválido para consulta.")
+
+ try:
+ conn = get_connection()
+
+ cursor = conn.cursor(dictionary=True)
+
+ query = f"SELECT * FROM agenda WHERE {campo} = %s AND disponibilidade = 'sim';"
+ cursor.execute(query, (parametro,))
+
+ rows = cursor.fetchall()
+ return rows
+
+ except mysql.connector.Error as e:
+ print(f"Erro na consulta: {e}")
+ return []
+
+ finally:
+ if conn:
+ conn.close()
+
+
+
+
+
+
+def atualizar_agenda(id, campo, informacao):
+ try:
+ # Conexão com o banco de dados
+ conn = get_connection()
+ cursor = conn.cursor()
+
+ cursor = conn.cursor()
+
+ # Query de atualização
+ sql = f"""
+ UPDATE agenda
+ SET {campo} = {informacao}
+ WHERE id = {id};
+ """
+
+ cursor.execute(sql)
+ conn.commit() # Confirma a transação
+
+ print(f"{cursor.rowcount} registros atualizados.")
+
+ except mysql.connector.Error as erro:
+ print(f"Erro ao atualizar: {erro}")
+ finally:
+ if conn.is_connected():
+ cursor.close()
+ conn.close()
+ print("Conexão encerrada.")
+
+
+#atualizar_agenda(6, "disponibilidade", 7)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2f807a6
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Glauberrf
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Lembrator.py b/Lembrator.py
new file mode 100644
index 0000000..24e5568
--- /dev/null
+++ b/Lembrator.py
@@ -0,0 +1,117 @@
+from BD_manager_Mysql import get_events
+from datetime import datetime
+from time import sleep
+import asyncio
+from telegram import Bot
+import os
+from twilio.rest import Client
+from dotenv import load_dotenv
+from BD_manager_Mysql import update_events
+
+
+
+# ==========================
+# Enviar mensagem WhatsApp
+# ==========================
+def enviar_whatsapp(numero_destino, mensagem):
+
+ # Carrega variáveis do arquivo .env
+ load_dotenv()
+
+ # Credenciais Twilio
+ ACCOUNT_SID = os.getenv("TWILIO_ACCOUNT_SID")
+ AUTH_TOKEN = os.getenv("TWILIO_AUTH_TOKEN")
+ TWILIO_WHATSAPP_NUMBER = os.getenv("TWILIO_WHATSAPP_NUMBER")
+
+
+
+
+ # Cria cliente Twilio
+ client = Client(ACCOUNT_SID, AUTH_TOKEN)
+
+ numero_destino = numero_destino.replace("whatsapp:", "")
+ message = client.messages.create(
+ from_=TWILIO_WHATSAPP_NUMBER,
+ body=mensagem,
+ to=f"whatsapp:{numero_destino}"
+ )
+
+ return message.sid
+
+
+TOKEN = "8402367863:AAGoEBHvoK7YRdTLXCBqaZ-PVQlFp_1V3zI"
+
+def enviar_mensagem_telegram(chat_id, mensagem):
+ async def enviar_mensagem(chat_id, mensagem):
+ bot = Bot(token=TOKEN)
+ await bot.send_message(chat_id=chat_id, text=mensagem)
+
+ # chamada da função
+ asyncio.run(enviar_mensagem(chat_id, mensagem))
+
+
+while True:
+ sleep(3600) # Espera 10 segundos antes de verificar novamente
+ events = get_events()
+
+ for event in events:
+ #print(f"ID: {event['id']}, Date: {event['event_date']}, Start: {event['start_time']}, End: {event['end_time']}, Title: {event['title']}, Description: {event['description']}, Chat ID: {event['chat_id']}, Name: {event['name']}, Created By: {event['created_by']}")
+ #print(event['event_date'],event['start_time'])
+ id_envent = event['id']
+ event_date = event['event_date']
+ start_time = event['start_time']
+ name = event['name']
+ chat_id = event['chat_id']
+ avisos = event['avisos']
+ created_by = event['created_by']
+
+ # Juntar data + hora em um único datetime
+ evento_datetime = datetime.strptime(
+ f"{event_date} {start_time}",
+ "%Y-%m-%d %H:%M:%S"
+ )
+
+ agora = datetime.now() # horário atual
+
+ diferenca = evento_datetime - agora
+ horas_faltando = diferenca.total_seconds() / 3600
+
+ #quando faltar 24 horas um aviso será enviado
+ if(horas_faltando <= 24 and horas_faltando > 0 and avisos == "0"):
+
+ print(f"Evento em {evento_datetime} - faltam {horas_faltando:.2f} horas")
+ if(created_by == "Telegram"):
+ mensagem = f"Olá {name}, voce tem um horario agendado dia {event_date} as {start_time}"
+ update_events(id_envent, "avisos", "1")
+ enviar_mensagem_telegram(str(chat_id), mensagem)
+ if(created_by == "Whatsapp"):
+ mensagem = f"Olá {name}, voce tem um horario agendado dia {event_date} as {start_time}"
+ update_events(id_envent, "avisos", "1")
+ enviar_whatsapp(chat_id, mensagem)
+
+ #quando faltar 12 horas um aviso será enviado
+ if(horas_faltando <= 12 and horas_faltando > 0 and avisos == "1"):
+
+ print(f"Evento em {evento_datetime} - faltam {horas_faltando:.2f} horas")
+ if(created_by == "Telegram"):
+ mensagem = f"Olá {name}, voce tem um horario agendado dia {event_date} as {start_time}"
+ update_events(id_envent, "avisos", "2")
+ enviar_mensagem_telegram(str(chat_id), mensagem)
+ if(created_by == "Whatsapp"):
+ mensagem = f"Olá {name}, voce tem um horario agendado dia {event_date} as {start_time}"
+ update_events(id_envent, "avisos", "2")
+ enviar_whatsapp(chat_id, mensagem)
+
+
+ #quando faltar 3 horas um aviso será enviado
+ if(horas_faltando <= 3 and horas_faltando > 0 and avisos == "2"):
+
+ print(f"Evento em {evento_datetime} - faltam {horas_faltando:.2f} horas")
+ if(created_by == "Telegram"):
+ mensagem = f"Olá {name}, voce tem um horario agendado dia {event_date} as {start_time}"
+ update_events(id_envent, "avisos", "3")
+ enviar_mensagem_telegram(str(chat_id), mensagem)
+ if(created_by == "Whatsapp"):
+ mensagem = f"Olá {name}, voce tem um horario agendado dia {event_date} as {start_time}"
+ update_events(id_envent, "avisos", "3")
+ enviar_whatsapp(chat_id, mensagem)
diff --git a/Limpar_Sessoes.py b/Limpar_Sessoes.py
new file mode 100644
index 0000000..6feb07e
--- /dev/null
+++ b/Limpar_Sessoes.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+import os
+from datetime import datetime, timedelta
+from BD_manager_Mysql import limpar_sessoes_expiradas
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+LOG_FILE = os.path.join(SCRIPT_DIR, "log_limpeza.txt")
+
+def log_msg(msg):
+ """Escreve a mensagem no arquivo de log e imprime no console."""
+ try:
+ with open(LOG_FILE, "a") as f:
+ f.write(f"{msg}\n")
+ print(msg)
+ except Exception as e:
+ print(f"Erro ao escrever no log: {e}")
+
+def limpar_registros_expirados():
+ agora = datetime.now()
+ limite = agora - timedelta(minutes=2)
+
+ try:
+ ids_removidos = limpar_sessoes_expiradas(limite)
+
+ if ids_removidos:
+ ids_str = ",".join(map(str, ids_removidos))
+ log_msg(f"[{agora.strftime('%Y-%m-%d %H:%M:%S')}] 🗑️ Removidos IDs: {ids_str}")
+ else:
+ pass
+
+ except Exception as e:
+ log_msg(f"[{agora.strftime('%Y-%m-%d %H:%M:%S')}] ❌ Erro: {e}")
+
+if __name__ == "__main__":
+ limpar_registros_expirados()
\ No newline at end of file
diff --git a/Main_Receiver-Twilio01.py b/Main_Receiver-Twilio01.py
new file mode 100644
index 0000000..728442b
--- /dev/null
+++ b/Main_Receiver-Twilio01.py
@@ -0,0 +1,260 @@
+import os
+from flask import Flask, request, jsonify
+from twilio.rest import Client
+from twilio.twiml.messaging_response import MessagingResponse
+from dotenv import load_dotenv
+
+from datetime import datetime
+
+###
+from BD_manager_Mysql import inserir_acompanhamento, buscar_ultimo_chat, atualizar_acompanhamento, inserir_evento, listar_agenda, atualizar_agenda
+
+###
+
+load_dotenv()
+
+app = Flask(__name__)
+
+# Credenciais
+ACCOUNT_SID = os.getenv("TWILIO_ACCOUNT_SID")
+AUTH_TOKEN = os.getenv("TWILIO_AUTH_TOKEN")
+TWILIO_WHATSAPP_NUMBER = os.getenv("TWILIO_WHATSAPP_NUMBER")
+
+client = Client(ACCOUNT_SID, AUTH_TOKEN)
+
+
+#Funções de fluxo
+def MostrarPeriodos(chat_id, name, status):
+ #
+
+ if(status == "10" or status == "null"):
+ inserir_acompanhamento(chat_id, "2", name)
+
+ else:
+ atualizar_acompanhamento(chat_id, "status", "2")
+
+
+ resposta = f"Qual periodo você gostaria de agendar ?\n1-Manhã\n2-Tarde\n3-Noite"
+ return resposta
+
+def MostrarHorarios(texto_recebido, chat_id):
+ print("Entrou na função de consultar os horarios")
+ z = ""
+ try:
+ if(texto_recebido == "1"):
+ horarios = listar_agenda("periodo","manhã")
+ resposta = horarios
+ for horario in horarios:
+ z = z + str(horario['id'])+" - "+horario['data'].strftime("%d/%m/%Y")+" as "+str(horario['horario'])+"\n"
+ resposta = f"Qual horario você gostaria de agendar ?\n"+z+"\n0 - Voltar"
+ atualizar_acompanhamento(chat_id, "status", "3")
+ return resposta
+
+ elif(texto_recebido == "2"):
+ horarios = listar_agenda("periodo","tarde")
+ resposta = horarios
+ for horario in horarios:
+ z = z + str(horario['id'])+" - "+horario['data'].strftime("%d/%m/%Y")+" as "+str(horario['horario'])+"\n"
+ resposta = f"Qual horario você gostaria de agendar ?\n"+z+"\n0 - Voltar"
+ atualizar_acompanhamento(chat_id, "status", "3")
+ return resposta
+
+ elif(texto_recebido == "3"):
+ horarios = listar_agenda("periodo","noite")
+ resposta = horarios
+ for horario in horarios:
+ z = z + str(horario['id'])+" - "+horario['data'].strftime("%d/%m/%Y")+" as "+str(horario['horario'])+"\n"
+ resposta = f"Qual horario você gostaria de agendar ?\n"+z+"\n0 - Voltar"
+ atualizar_acompanhamento(chat_id, "status", "3")
+ return resposta
+
+
+
+
+ else:
+ resposta = "Opção inválida, por favor digite um periodo válido\n1 - manhã\n2 - tarde\n3 - noite"
+ return resposta
+ except ValueError:
+ resposta = f"Você não digitou uma opção válida"
+ return resposta
+
+###############
+
+
+# ==========================
+# WEBHOOK (receber mensagem)
+# ==========================
+@app.route("/webhook", methods=["POST"])
+def webhook():
+ texto_recebido = request.form.get("Body", "").strip().lower()
+ name = request.form.get("ProfileName")
+ sender = request.form.get("From")
+
+ print(f"Mensagem recebida de {name}: {sender}: {texto_recebido}")
+
+ chat_id = sender
+
+ response = MessagingResponse()
+ resposta = response.message()
+
+
+
+ print(f"Mensagem recebida de {chat_id}: {texto_recebido}")
+
+ # Remove sufixo "@c.us" do número
+ #numero_remetente = numero_remetente.replace("@c.us", "")
+ chat_id = chat_id.replace("@c.us", "")
+
+ try:
+ status = buscar_ultimo_chat(chat_id)['status']
+ except:
+ status = "null"
+
+ print("Status: ", status)
+
+ #Começo do atendimento (Fluxo iniciado)
+ '''if(texto_recebido.lower() == "oi" or texto_recebido.lower() == "sim"):
+ resposta = f"Olá! você gostaria de agendar um horário?\n Digite\n1-SIM\n2-NÃO"
+ inserir_acompanhamento(chat_id, "1", name)'''
+ if(texto_recebido == "2" and status == "10") :
+ resposta.body("Sem problemas, qualquer coisa estou aqui")
+ print("ChatID: ", chat_id)
+ print("Status: ", status)
+ print("Status type: ", type(status))
+
+
+
+
+
+ #Mostrar periodos Manhã, tarde e Noite
+ elif(texto_recebido == "1" and status == "10" or texto_recebido == "1" and status == "null") :
+ print("Mostrar os periodos manhã, tarde e noite")
+ resposta.body(MostrarPeriodos(chat_id, name, status))
+
+ '''atualizar_acompanhamento(chat_id, "status", "2")
+ resposta = f"Qual periodo você gostaria de agendar ?\n1-Manhã\n2-Tarde\n3-Noite"'''
+
+
+ ##mostrar horarios
+ elif(status == "2") :
+ print("mostrar os horarios disponiveis")
+ resposta.body(MostrarHorarios(texto_recebido, chat_id))
+
+ #Seleção dos horarios disponiveis
+ elif(texto_recebido != "0" and status == "3") :
+
+ try:
+ #converter data
+ id = texto_recebido
+ data_horario_agenda = listar_agenda("id",id)
+
+ print("Data Agenda: ",data_horario_agenda)
+ print("Data agenda data: ",data_horario_agenda[0]['data'])
+
+ #sqlite
+ #data_convertida = datetime.strptime(data_horario_agenda[0]['data'], "%d/%m/%Y").strftime("%Y-%m-%d")
+ #mysql
+ data_convertida = data_horario_agenda[0]['data'].strftime("%Y-%m-%d")
+ datetime.strptime(data_convertida, "%Y-%m-%d")
+
+ atualizar_acompanhamento(chat_id, "data_event", data_convertida)
+
+ atualizar_acompanhamento(chat_id, "time_event", data_horario_agenda[0]['horario'])
+
+ atualizar_acompanhamento(chat_id, "status", "4")
+
+ #atualizar a disponibilidade da agenda para não aparecer depois da data ser agendada
+ atualizar_agenda(int(texto_recebido), "disponibilidade", "'nao'")
+
+ resposta.body(f"Você gostaria de adicionar algum comentários ?\n1 - Sim\n2 -Não")
+
+
+ except ValueError:
+ resposta.body(f"A data que você digitou não está no formato correto.\nDigite a data no seguinte formato dd/mm/yyyy")
+ except IndexError:
+ resposta.body("Nenhum evento encontrado para esse ID, por favor, selecione uma das datas que lhe enviei.")
+
+ #Adicionar comentário caso a resposta seja sim para adicionar
+ elif(texto_recebido == "1" and status == "4"):
+ atualizar_acompanhamento(chat_id, "status", "5")
+ resposta.body("Por favor, escreva o seu comentário")
+
+ #Capturando a mensagem para ser inserida no banco
+ elif(status == "5"):
+ atualizar_acompanhamento(chat_id, "status", "10")
+ data_agendada = buscar_ultimo_chat(chat_id)["data_event"]
+ #data_agendada = datetime.strptime(data_agendada, "%Y-%m-%d")
+ data_agendada_formatada = data_agendada.strftime("%d/%m/%Y")
+
+ horario_agendado = buscar_ultimo_chat(chat_id)["time_event"]
+
+ inserir_evento(buscar_ultimo_chat(chat_id)["data_event"],buscar_ultimo_chat(chat_id)["time_event"],"00:30:00","Padão Titulo",texto_recebido,chat_id,name,"Whatsapp")
+ resposta.body(f"Então agendamos para {data_agendada_formatada} as {horario_agendado} !\nObrigado !")
+
+ #Caso a resposta de inserir uma mensagem seja "Não"
+ elif(texto_recebido == "2" and status == "4"):
+ atualizar_acompanhamento(chat_id, "status", "10")
+ data_agendada = buscar_ultimo_chat(chat_id)["data_event"]
+ #data_agendada = datetime.strptime(data_agendada, "%Y-%m-%d")
+ data_agendada_formatada = data_agendada.strftime("%d/%m/%Y")
+
+ horario_agendado = buscar_ultimo_chat(chat_id)["time_event"]
+
+ inserir_evento(buscar_ultimo_chat(chat_id)["data_event"],buscar_ultimo_chat(chat_id)["time_event"],"00:30:00","Padão Titulo",texto_recebido,chat_id,name,"Whatsapp")
+ resposta.body(f"Então agendamos para {data_agendada_formatada} as {horario_agendado} !\nObrigado !")
+
+
+ #voltar para priodo
+ elif(texto_recebido == "0" and status == "3"):
+
+ atualizar_acompanhamento(chat_id, "status", "2")
+ resposta.body(MostrarPeriodos(chat_id, name, status))
+
+
+
+
+ #Tratamento de mensagem inválida ao bot
+ elif(texto_recebido.lower() == "quem é você?" or texto_recebido.lower() == "quem e você?"):
+ resposta.body(f"Eu sou um Bot de agendamento!")
+ #await update.message.reply_text(resposta)
+ #resposta = f"Você escreveu: {texto_recebido}"
+
+ elif(texto_recebido.lower() == "2" and status == "null" or texto_recebido.lower() == "2" and status == "10"):
+ resposta.body(f"Então tudo bem !\nSe precisar é só me chamar.")
+
+ else:
+ resposta.body(f"Olá, você gostaria de agendar um horario ?\n1 - Sim\n2 - Não")
+
+ return str(response)
+
+
+# ==========================
+# ENVIO ATIVO (via API REST)
+# ==========================
+@app.route("/send", methods=["POST"])
+def send_message():
+ data = request.json
+ to_number = data.get("to")
+ message_text = data.get("message")
+
+ try:
+ message = client.messages.create(
+ from_=TWILIO_WHATSAPP_NUMBER,
+ body=message_text,
+ to=f"whatsapp:{to_number}"
+ )
+
+ return jsonify({
+ "status": "success",
+ "sid": message.sid
+ }), 200
+
+ except Exception as e:
+ return jsonify({
+ "status": "error",
+ "message": str(e)
+ }), 500
+
+
+if __name__ == "__main__":
+ app.run(port=5000, debug=True)
diff --git a/Main_Receiver-Whatsapp.py b/Main_Receiver-Whatsapp.py
new file mode 100644
index 0000000..bb61ee8
--- /dev/null
+++ b/Main_Receiver-Whatsapp.py
@@ -0,0 +1,241 @@
+
+from flask import Flask, request, jsonify
+import requests
+from datetime import datetime
+
+###
+from BD_manager_Mysql import inserir_acompanhamento, buscar_ultimo_chat, atualizar_acompanhamento, inserir_evento, listar_agenda, atualizar_agenda
+
+###
+
+
+#Funções de fluxo
+def MostrarPeriodos(chat_id, name, status):
+ #
+
+ if(status == "10" or status == "null"):
+ inserir_acompanhamento(chat_id, "2", name)
+
+ else:
+ atualizar_acompanhamento(chat_id, "status", "2")
+
+
+ resposta = f"Qual periodo você gostaria de agendar ?\n1-Manhã\n2-Tarde\n3-Noite"
+ return resposta
+
+def MostrarHorarios(texto_recebido, chat_id):
+ print("Entrou na função de consultar os horarios")
+ z = ""
+ try:
+ if(texto_recebido == "1"):
+ horarios = listar_agenda("periodo","manhã")
+ resposta = horarios
+ for horario in horarios:
+ z = z + str(horario['id'])+" - "+horario['data'].strftime("%d/%m/%Y")+" as "+str(horario['horario'])+"\n"
+ resposta = f"Qual horario você gostaria de agendar ?\n"+z+"\n0 - Voltar"
+ atualizar_acompanhamento(chat_id, "status", "3")
+ return resposta
+
+ elif(texto_recebido == "2"):
+ horarios = listar_agenda("periodo","tarde")
+ resposta = horarios
+ for horario in horarios:
+ z = z + str(horario['id'])+" - "+horario['data'].strftime("%d/%m/%Y")+" as "+str(horario['horario'])+"\n"
+ resposta = f"Qual horario você gostaria de agendar ?\n"+z+"\n0 - Voltar"
+ atualizar_acompanhamento(chat_id, "status", "3")
+ return resposta
+
+ elif(texto_recebido == "3"):
+ horarios = listar_agenda("periodo","noite")
+ resposta = horarios
+ for horario in horarios:
+ z = z + str(horario['id'])+" - "+horario['data'].strftime("%d/%m/%Y")+" as "+str(horario['horario'])+"\n"
+ resposta = f"Qual horario você gostaria de agendar ?\n"+z+"\n0 - Voltar"
+ atualizar_acompanhamento(chat_id, "status", "3")
+ return resposta
+
+
+
+
+ else:
+ resposta = "Opção inválida, por favor digite um periodo válido\n1 - manhã\n2 - tarde\n3 - noite"
+ return resposta
+ except ValueError:
+ resposta = f"Você não digitou uma opção válida"
+ return resposta
+
+###############
+
+
+
+app = Flask(__name__)
+
+# 🔧 Configurações da sua instância UltraMsg
+INSTANCE_ID = ""
+TOKEN = ""
+
+# URL da API para enviar mensagens
+API_URL = f"https://api.ultramsg.com/{INSTANCE_ID}/messages/chat"
+
+@app.route('/webhook', methods=['POST'])
+def webhook():
+ payload = request.json
+ print("🔵 DADOS RECEBIDOS:")
+ print(payload)
+
+ # Extrai a mensagem de dentro de 'data'
+ data = payload.get("data", {})
+ texto_recebido = data.get("body", "")
+ #numero_remetente = data.get("from", "")
+ chat_id = data.get("from", "")
+ name = data.get("pushname", "")
+
+ resposta = ""
+
+ print(f"Mensagem recebida de {chat_id}: {texto_recebido}")
+
+ # Remove sufixo "@c.us" do número
+ #numero_remetente = numero_remetente.replace("@c.us", "")
+ chat_id = chat_id.replace("@c.us", "")
+
+ try:
+ status = buscar_ultimo_chat(chat_id)['status']
+ except:
+ status = "null"
+
+ print("Status: ", status)
+
+ #Começo do atendimento (Fluxo iniciado)
+ '''if(texto_recebido.lower() == "oi" or texto_recebido.lower() == "sim"):
+ resposta = f"Olá! você gostaria de agendar um horário?\n Digite\n1-SIM\n2-NÃO"
+ inserir_acompanhamento(chat_id, "1", name)'''
+ if(texto_recebido == "2" and status == "10") :
+ resposta = "Sem problemas, qualquer coisa estou aqui"
+ print("ChatID: ", chat_id)
+ print("Status: ", status)
+ print("Status type: ", type(status))
+
+
+
+
+
+ #Mostrar periodos Manhã, tarde e Noite
+ elif(texto_recebido == "1" and status == "10" or texto_recebido == "1" and status == "null") :
+ print("Mostrar os periodos manhã, tarde e noite")
+ resposta = MostrarPeriodos(chat_id, name, status)
+
+ '''atualizar_acompanhamento(chat_id, "status", "2")
+ resposta = f"Qual periodo você gostaria de agendar ?\n1-Manhã\n2-Tarde\n3-Noite"'''
+
+
+ ##mostrar horarios
+ elif(status == "2") :
+ print("mostrar os horarios disponiveis")
+ resposta = MostrarHorarios(texto_recebido, chat_id)
+
+ #Seleção dos horarios disponiveis
+ elif(texto_recebido != "0" and status == "3") :
+
+ try:
+ #converter data
+ id = texto_recebido
+ data_horario_agenda = listar_agenda("id",id)
+
+ print("Data Agenda: ",data_horario_agenda)
+ print("Data agenda data: ",data_horario_agenda[0]['data'])
+
+ #sqlite
+ #data_convertida = datetime.strptime(data_horario_agenda[0]['data'], "%d/%m/%Y").strftime("%Y-%m-%d")
+ #mysql
+ data_convertida = data_horario_agenda[0]['data'].strftime("%Y-%m-%d")
+ datetime.strptime(data_convertida, "%Y-%m-%d")
+
+ atualizar_acompanhamento(chat_id, "data_event", data_convertida)
+
+ atualizar_acompanhamento(chat_id, "time_event", data_horario_agenda[0]['horario'])
+
+ atualizar_acompanhamento(chat_id, "status", "4")
+
+ #atualizar a disponibilidade da agenda para não aparecer depois da data ser agendada
+ atualizar_agenda(int(texto_recebido), "disponibilidade", "1")
+
+ resposta = f"Você gostaria de adicionar algum comentários ?\n1 - Sim\n2 -Não"
+
+
+ except ValueError:
+ resposta = f"A data que você digitou não está no formato correto.\nDigite a data no seguinte formato dd/mm/yyyy"
+ except IndexError:
+ resposta = "Nenhum evento encontrado para esse ID, por favor, selecione uma das datas que lhe enviei."
+
+ #Adicionar comentário caso a resposta seja sim para adicionar
+ elif(texto_recebido == "1" and status == "4"):
+ atualizar_acompanhamento(chat_id, "status", "5")
+ resposta = "Por favor, escreva o seu comentário"
+
+ #Capturando a mensagem para ser inserida no banco
+ elif(status == "5"):
+ atualizar_acompanhamento(chat_id, "status", "10")
+ data_agendada = buscar_ultimo_chat(chat_id)["data_event"]
+ #data_agendada = datetime.strptime(data_agendada, "%Y-%m-%d")
+ data_agendada_formatada = data_agendada.strftime("%d/%m/%Y")
+
+ horario_agendado = buscar_ultimo_chat(chat_id)["time_event"]
+
+ inserir_evento(buscar_ultimo_chat(chat_id)["data_event"],buscar_ultimo_chat(chat_id)["time_event"],"00:30:00","Padão Titulo",texto_recebido,chat_id,name,"Telegram")
+ resposta = f"Então agendamos para {data_agendada_formatada} as {horario_agendado} !\nObrigado !"
+
+ #Caso a resposta de inserir uma mensagem seja "Não"
+ elif(texto_recebido == "2" and status == "4"):
+ atualizar_acompanhamento(chat_id, "status", "10")
+ data_agendada = buscar_ultimo_chat(chat_id)["data_event"]
+ #data_agendada = datetime.strptime(data_agendada, "%Y-%m-%d")
+ data_agendada_formatada = data_agendada.strftime("%d/%m/%Y")
+
+ horario_agendado = buscar_ultimo_chat(chat_id)["time_event"]
+
+ inserir_evento(buscar_ultimo_chat(chat_id)["data_event"],buscar_ultimo_chat(chat_id)["time_event"],"00:30:00","Padão Titulo",texto_recebido,chat_id,name,"Telegram")
+ resposta = f"Então agendamos para {data_agendada_formatada} as {horario_agendado} !\nObrigado !"
+
+
+ #voltar para priodo
+ elif(texto_recebido == "0" and status == "3"):
+
+ atualizar_acompanhamento(chat_id, "status", "2")
+ resposta = MostrarPeriodos(chat_id, name, status)
+
+
+
+
+ #Tratamento de mensagem inválida ao bot
+ elif(texto_recebido.lower() == "quem é você?" or texto_recebido.lower() == "quem e você?"):
+ resposta = f"Eu sou um Bot de agendamento!"
+ #await update.message.reply_text(resposta)
+ #resposta = f"Você escreveu: {texto_recebido}"
+
+ elif(texto_recebido.lower() == "2" and status == "null" or texto_recebido.lower() == "2" and status == "10"):
+ resposta = f"Então tudo bem !\nSe precisar é só me chamar."
+
+ else:
+ resposta = f"Olá, você gostaria de agendar um horario ?\n1 - Sim\n2 - Não"
+
+
+
+ # Envia a resposta
+ API_URL = f"https://api.ultramsg.com/{INSTANCE_ID}/messages/chat"
+ payload_resposta = {
+ "token": TOKEN,
+ #"to": numero_remetente,
+ "to": chat_id,
+ "body": resposta
+ }
+
+ r = requests.post(API_URL, data=payload_resposta)
+ print("🟢 Enviando resposta:", resposta)
+ print("🟡 Status da API:", r.status_code)
+ print("🔴 Resposta da API:", r.text)
+
+ return jsonify({"status": "mensagem processada"}), 200
+
+
+if __name__ == '__main__':
+ app.run(port=5000)
diff --git a/Main_Receiver_Telegram.py b/Main_Receiver_Telegram.py
new file mode 100644
index 0000000..a2c9e8b
--- /dev/null
+++ b/Main_Receiver_Telegram.py
@@ -0,0 +1,232 @@
+from telegram import Update
+from telegram.ext import (
+ ApplicationBuilder,
+ ContextTypes,
+ MessageHandler,
+ CommandHandler,
+ filters
+)
+from datetime import datetime
+
+import json
+
+
+#from BD_manager import inserir_acompanhamento, buscar_ultimo_chat, atualizar_acompanhamento, inserir_evento, listar_agenda
+
+from BD_manager_Mysql import inserir_acompanhamento, buscar_ultimo_chat, atualizar_acompanhamento, inserir_evento, listar_agenda, atualizar_agenda
+
+TOKEN = "8402367863:AAGoEBHvoK7YRdTLXCBqaZ-PVQlFp_1V3zI"
+#TOKEN = st.secrets["api"]["token"]
+
+
+#Funções de fluxo
+def MostrarPeriodos(chat_id, name, status):
+ #
+
+ if(status == "10" or status == "null"):
+ inserir_acompanhamento(chat_id, "2", name)
+
+ else:
+ atualizar_acompanhamento(chat_id, "status", "2")
+
+
+ resposta = f"Qual periodo você gostaria de agendar ?\n1-Manhã\n2-Tarde\n3-Noite"
+ return resposta
+
+def MostrarHorarios(texto_recebido, chat_id):
+ print("Entrou na função de consultar os horarios")
+ z = ""
+ try:
+ if(texto_recebido == "1"):
+ horarios = listar_agenda("periodo","manhã")
+ resposta = horarios
+ for horario in horarios:
+ z = z + str(horario['id'])+" - "+horario['data'].strftime("%d/%m/%Y")+" as "+str(horario['horario'])+"\n"
+ resposta = f"Qual horario você gostaria de agendar ?\n"+z+"\n0 - Voltar"
+ atualizar_acompanhamento(chat_id, "status", "3")
+ return resposta
+
+ elif(texto_recebido == "2"):
+ horarios = listar_agenda("periodo","tarde")
+ resposta = horarios
+ for horario in horarios:
+ z = z + str(horario['id'])+" - "+horario['data'].strftime("%d/%m/%Y")+" as "+str(horario['horario'])+"\n"
+ resposta = f"Qual horario você gostaria de agendar ?\n"+z+"\n0 - Voltar"
+ atualizar_acompanhamento(chat_id, "status", "3")
+ return resposta
+
+ elif(texto_recebido == "3"):
+ horarios = listar_agenda("periodo","noite")
+ resposta = horarios
+ for horario in horarios:
+ z = z + str(horario['id'])+" - "+horario['data'].strftime("%d/%m/%Y")+" as "+str(horario['horario'])+"\n"
+ resposta = f"Qual horario você gostaria de agendar ?\n"+z+"\n0 - Voltar"
+ atualizar_acompanhamento(chat_id, "status", "3")
+ return resposta
+
+
+
+
+ else:
+ resposta = "Opção inválida, por favor digite um periodo válido\n1 - manhã\n2 - tarde\n3 - noite"
+ return resposta
+ except ValueError:
+ resposta = f"Você não digitou uma opção válida"
+ return resposta
+
+###############
+
+# Comando /start
+async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
+ await update.message.reply_text(
+ "Olá! Eu sou um bot de agendamento da Malu.\nComo vai ?"
+
+
+ )
+
+# Recebe mensagens de texto
+async def receber_mensagem(update: Update, context: ContextTypes.DEFAULT_TYPE):
+ texto_recebido = update.message.text
+ chat_id = update.effective_chat.id
+ name = update.effective_chat.full_name
+ print(f"{chat_id=}, {name=}")
+
+ print(f"{texto_recebido=}")
+
+ try:
+ status = buscar_ultimo_chat(chat_id)['status']
+ except:
+ status = "null"
+
+ print("Status: ", status)
+
+ #Começo do atendimento (Fluxo iniciado)
+ '''if(texto_recebido.lower() == "oi" or texto_recebido.lower() == "sim"):
+ resposta = f"Olá! você gostaria de agendar um horário?\n Digite\n1-SIM\n2-NÃO"
+ inserir_acompanhamento(chat_id, "1", name)'''
+ if(texto_recebido == "2" and status == "10") :
+ resposta = "Sem problemas, qualquer coisa estou aqui"
+ print("ChatID: ", chat_id)
+ print("Status: ", status)
+ print("Status type: ", type(status))
+
+
+
+
+
+ #Mostrar periodos Manhã, tarde e Noite
+ elif(texto_recebido == "1" and status == "10" or texto_recebido == "1" and status == "null") :
+ print("Mostrar os periodos manhã, tarde e noite")
+ resposta = MostrarPeriodos(chat_id, name, status)
+
+ '''atualizar_acompanhamento(chat_id, "status", "2")
+ resposta = f"Qual periodo você gostaria de agendar ?\n1-Manhã\n2-Tarde\n3-Noite"'''
+
+
+ ##mostrar horarios
+ elif(status == "2") :
+ print("mostrar os horarios disponiveis")
+ resposta = MostrarHorarios(texto_recebido, chat_id)
+
+ #Seleção dos horarios disponiveis
+ elif(texto_recebido != "0" and status == "3") :
+
+ try:
+ #converter data
+ id = texto_recebido
+ data_horario_agenda = listar_agenda("id",id)
+
+ print("Data Agenda: ",data_horario_agenda)
+ print("Data agenda data: ",data_horario_agenda[0]['data'])
+
+ #sqlite
+ #data_convertida = datetime.strptime(data_horario_agenda[0]['data'], "%d/%m/%Y").strftime("%Y-%m-%d")
+ #mysql
+ data_convertida = data_horario_agenda[0]['data'].strftime("%Y-%m-%d")
+ datetime.strptime(data_convertida, "%Y-%m-%d")
+
+ atualizar_acompanhamento(chat_id, "data_event", data_convertida)
+
+ atualizar_acompanhamento(chat_id, "time_event", data_horario_agenda[0]['horario'])
+
+ atualizar_acompanhamento(chat_id, "status", "4")
+
+ #atualizar a disponibilidade da agenda para não aparecer depois da data ser agendada
+ atualizar_agenda(int(texto_recebido), "disponibilidade", "'nao'")
+
+ resposta = f"Você gostaria de adicionar algum comentários ?\n1 - Sim\n2 -Não"
+
+
+ except ValueError:
+ resposta = f"A data que você digitou não está no formato correto.\nDigite a data no seguinte formato dd/mm/yyyy"
+ except IndexError:
+ resposta = "Nenhum evento encontrado para esse ID, por favor, selecione uma das datas que lhe enviei."
+
+ #Adicionar comentário caso a resposta seja sim para adicionar
+ elif(texto_recebido == "1" and status == "4"):
+ atualizar_acompanhamento(chat_id, "status", "5")
+ resposta = "Por favor, escreva o seu comentário"
+
+ #Capturando a mensagem para ser inserida no banco
+ elif(status == "5"):
+ atualizar_acompanhamento(chat_id, "status", "10")
+ data_agendada = buscar_ultimo_chat(chat_id)["data_event"]
+ #data_agendada = datetime.strptime(data_agendada, "%Y-%m-%d")
+ data_agendada_formatada = data_agendada.strftime("%d/%m/%Y")
+
+ horario_agendado = buscar_ultimo_chat(chat_id)["time_event"]
+
+ inserir_evento(buscar_ultimo_chat(chat_id)["data_event"],buscar_ultimo_chat(chat_id)["time_event"],"00:30:00","Padão Titulo",texto_recebido,chat_id,name,"Telegram")
+ resposta = f"Então agendamos para {data_agendada_formatada} as {horario_agendado} !\nObrigado !"
+
+ #Caso a resposta de inserir uma mensagem seja "Não"
+ elif(texto_recebido == "2" and status == "4"):
+ atualizar_acompanhamento(chat_id, "status", "10")
+ data_agendada = buscar_ultimo_chat(chat_id)["data_event"]
+ #data_agendada = datetime.strptime(data_agendada, "%Y-%m-%d")
+ data_agendada_formatada = data_agendada.strftime("%d/%m/%Y")
+
+ horario_agendado = buscar_ultimo_chat(chat_id)["time_event"]
+
+ inserir_evento(buscar_ultimo_chat(chat_id)["data_event"],buscar_ultimo_chat(chat_id)["time_event"],"00:30:00","Padão Titulo",texto_recebido,chat_id,name,"Telegram")
+ resposta = f"Então agendamos para {data_agendada_formatada} as {horario_agendado} !\nObrigado !"
+
+
+ #voltar para priodo
+ elif(texto_recebido == "0" and status == "3"):
+
+ atualizar_acompanhamento(chat_id, "status", "2")
+ resposta = MostrarPeriodos(chat_id, name, status)
+
+
+
+
+ #Tratamento de mensagem inválida ao bot
+ elif(texto_recebido.lower() == "quem é você?" or texto_recebido.lower() == "quem e você?"):
+ resposta = f"Eu sou um Bot de agendamento!"
+ #await update.message.reply_text(resposta)
+ #resposta = f"Você escreveu: {texto_recebido}"
+ elif(texto_recebido.lower() == "2" and status == "null" or texto_recebido.lower() == "2" and status == "10"):
+ #elif(texto_recebido.lower() == "2" and buscar_ultimo_chat(chat_id)['status'] == "1"):
+ resposta = f"Então tudo bem !\nSe precisar é só me chamar."
+
+ else:
+ resposta = f"Olá, você gostaria de agendar um horario ?\n1 - Sim\n2 - Não"
+ await update.message.reply_text(resposta)
+
+def main():
+ app = ApplicationBuilder().token(TOKEN).build()
+
+ # Comandos
+ app.add_handler(CommandHandler("start", start))
+
+ # Mensagens de texto
+ app.add_handler(
+ MessageHandler(filters.TEXT & ~filters.COMMAND, receber_mensagem)
+ )
+
+ print("Bot em execução...")
+ app.run_polling()
+
+if __name__ == "__main__":
+ main()
diff --git a/Painel_Prestador_Mysql.py b/Painel_Prestador_Mysql.py
new file mode 100644
index 0000000..dc9656d
--- /dev/null
+++ b/Painel_Prestador_Mysql.py
@@ -0,0 +1,240 @@
+import streamlit as st
+import mysql.connector
+import hashlib
+from datetime import date, time, datetime, timedelta
+from streamlit_calendar import calendar
+
+# =========================
+# CONFIG
+# =========================
+st.set_page_config(page_title="Agenda Compartilhada", layout="wide")
+
+DB_CONFIG = {
+ "host": st.secrets["db"]["host"],
+ "user": st.secrets["db"]["user"],
+ "password": st.secrets["db"]["password"],
+ "database": st.secrets["db"]["database"],
+ "port": st.secrets["db"]["port"]
+}
+
+# =========================
+# UTILS
+# =========================
+def timedelta_to_time(td):
+ seconds = int(td.total_seconds())
+ return time(seconds // 3600, (seconds % 3600) // 60)
+
+# =========================
+# DATABASE
+# =========================
+def get_connection():
+ return mysql.connector.connect(**DB_CONFIG)
+
+# =========================
+# AUTH
+# =========================
+def hash_password(password):
+ return hashlib.sha256(password.encode()).hexdigest()
+
+def authenticate(username, password):
+ conn = get_connection()
+ cur = conn.cursor(dictionary=True)
+ cur.execute(
+ "SELECT id FROM users WHERE username=%s AND password_hash=%s",
+ (username, hash_password(password))
+ )
+ user = cur.fetchone()
+ cur.close()
+ conn.close()
+ return user is not None
+
+# =========================
+# EVENTS
+# =========================
+def get_events():
+ conn = get_connection()
+ cur = conn.cursor(dictionary=True)
+ cur.execute("SELECT * FROM events")
+ rows = cur.fetchall()
+ cur.close()
+ conn.close()
+ return rows
+
+def add_event(data):
+ conn = get_connection()
+ cur = conn.cursor()
+ cur.execute("""
+ INSERT INTO events
+ (event_date,start_time,end_time,title,description,chat_id,name,created_by)
+ VALUES (%s,%s,%s,%s,%s,%s,%s,%s)
+ """, tuple(data.values()))
+ conn.commit()
+ cur.close()
+ conn.close()
+
+def update_event(event_id, data):
+ conn = get_connection()
+ cur = conn.cursor()
+ cur.execute("""
+ UPDATE events SET
+ event_date=%s,start_time=%s,end_time=%s,
+ title=%s,description=%s,chat_id=%s,name=%s
+ WHERE id=%s
+ """, (*data.values(), event_id))
+ conn.commit()
+ cur.close()
+ conn.close()
+
+def delete_event(event_id):
+ conn = get_connection()
+ cur = conn.cursor()
+ cur.execute("DELETE FROM events WHERE id=%s", (event_id,))
+ conn.commit()
+ cur.close()
+ conn.close()
+
+# =========================
+# SESSION
+# =========================
+st.session_state.setdefault("logged_in", False)
+st.session_state.setdefault("mode", "idle")
+st.session_state.setdefault("selected_event", None)
+st.session_state.setdefault("selected_date", None)
+
+# =========================
+# LOGIN
+# =========================
+if not st.session_state.logged_in:
+ st.title("🔐 Login")
+ u = st.text_input("Usuário")
+ p = st.text_input("Senha", type="password")
+ if st.button("Entrar"):
+ if authenticate(u, p):
+ st.session_state.logged_in = True
+ st.session_state.username = u
+ st.rerun()
+ else:
+ st.error("Credenciais inválidas")
+ st.stop()
+
+# =========================
+# APP
+# =========================
+st.title("📅 Agenda Compartilhada")
+
+col_cal, col_form = st.columns([2, 1])
+
+events_db = get_events()
+
+calendar_events = []
+for ev in events_db:
+ calendar_events.append({
+ "id": ev["id"],
+ "title": ev["title"],
+ "start": datetime.combine(
+ ev["event_date"],
+ timedelta_to_time(ev["start_time"])
+ ).isoformat(),
+ "end": datetime.combine(
+ ev["event_date"],
+ timedelta_to_time(ev["end_time"])
+ ).isoformat(),
+ })
+
+# =========================
+# CALENDAR (LEFT)
+# =========================
+with col_cal:
+ cal = calendar(
+ events=calendar_events,
+ options={"initialView": "dayGridMonth", "selectable": True},
+ custom_css=".fc { font-size: 0.85rem; }"
+ )
+
+# =========================
+# CALENDAR INTERACTIONS
+# =========================
+if cal.get("dateClick"):
+ st.session_state.mode = "new"
+ st.session_state.selected_date = datetime.fromisoformat(
+ cal["dateClick"]["date"]
+ ).date()
+
+if cal.get("eventClick"):
+ st.session_state.mode = "edit"
+ st.session_state.selected_event = next(
+ e for e in events_db if e["id"] == int(cal["eventClick"]["event"]["id"])
+ )
+
+# =========================
+# SIDE PANEL (RIGHT)
+# =========================
+with col_form:
+ st.subheader("🛠 Ações")
+
+ # -------------------------
+ # NEW EVENT
+ # -------------------------
+ if st.session_state.mode == "new":
+ st.info(f"Novo evento em {st.session_state.selected_date}")
+
+ with st.form("new_event"):
+ d = st.date_input("Data", st.session_state.selected_date)
+ s = st.time_input("Início", time(9, 0))
+ e = st.time_input("Fim", time(10, 0))
+ t = st.text_input("Título")
+ desc = st.text_area("Descrição")
+ chat = st.text_input("Chat ID")
+ name = st.text_input("Nome")
+
+ if st.form_submit_button("Salvar"):
+ add_event({
+ "event_date": d,
+ "start_time": s,
+ "end_time": e,
+ "title": t,
+ "description": desc,
+ "chat_id": chat,
+ "name": name,
+ "created_by": st.session_state.username
+ })
+ st.session_state.mode = "idle"
+ st.rerun()
+
+ # -------------------------
+ # EDIT EVENT
+ # -------------------------
+ elif st.session_state.mode == "edit":
+ ev = st.session_state.selected_event
+ st.info(f"Editando: {ev['title']}")
+
+ with st.form("edit_event"):
+ d = st.date_input("Data", ev["event_date"])
+ s = st.time_input("Início", timedelta_to_time(ev["start_time"]))
+ e = st.time_input("Fim", timedelta_to_time(ev["end_time"]))
+ t = st.text_input("Título", ev["title"])
+ desc = st.text_area("Descrição", ev["description"])
+ chat = st.text_input("Chat ID", ev["chat_id"])
+ name = st.text_input("Nome", ev["name"])
+
+ if st.form_submit_button("Atualizar"):
+ update_event(ev["id"], {
+ "event_date": d,
+ "start_time": s,
+ "end_time": e,
+ "title": t,
+ "description": desc,
+ "chat_id": chat,
+ "name": name
+ })
+ st.session_state.mode = "idle"
+ st.rerun()
+
+ if st.form_submit_button("Excluir"):
+ delete_event(ev["id"])
+ st.session_state.mode = "idle"
+ st.rerun()
+
+ else:
+ st.write("⬅️ Clique em um dia ou evento no calendário")
+
diff --git a/README.md b/README.md
index e69de29..15a9f5a 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,88 @@
+# Sistema de Agendamento Multi-Canal
+
+Este projeto é um sistema completo de agendamento que permite aos clientes marcarem horários de forma automatizada via WhatsApp (integrações Twilio e UltraMsg) e Telegram. O sistema inclui também um painel administrativo web para gestão da agenda e um serviço de lembretes automáticos.
+
+## 🚀 Funcionalidades
+
+* **Agendamento via Chatbot:** Fluxo automatizado onde o cliente escolhe o período (Manhã/Tarde/Noite) e o horário disponível.
+* **Múltiplos Canais de Atendimento:**
+ * **WhatsApp:** Suporte via API oficial (Twilio) e não-oficial (UltraMsg).
+ * **Telegram:** Bot nativo para agendamentos.
+* **Painel Administrativo (Dashboard):** Interface visual desenvolvida em Streamlit para que o prestador de serviço possa:
+ * Visualizar agendamentos em formato de calendário.
+ * Criar, editar e excluir eventos manualmente.
+* **Lembretes Automáticos (`Lembrator`):** Serviço em segundo plano que notifica os clientes sobre seus horários com antecedência de 24h, 12h e 3h.
+* **Banco de Dados:** Persistência de dados utilizando MySQL (com suporte legado/local para SQLite).
+
+## 📂 Estrutura de Arquivos
+
+### Principais
+* **`Main_Receiver-Twilio01.py`**: Servidor Flask que atua como Webhook para o Twilio (WhatsApp).
+* **`Main_Receiver-Whatsapp.py`**: Servidor Flask para integração com a API UltraMsg (WhatsApp).
+* **`Main_Receiver_Telegram.py`**: Bot do Telegram que utiliza a biblioteca `python-telegram-bot`.
+* **`Painel_Prestador_Mysql.py`**: Aplicação Streamlit para gestão da agenda pelo prestador.
+* **`Lembrator.py`**: Script de automação para envio de lembretes.
+
+### Banco de Dados
+* **`BD_manager_Mysql.py`**: Gerenciador de conexão e queries para MySQL (usado em produção).
+* **`BD_manager.py`**: Versão SQLite (uso local/teste).
+
+## 🛠️ Instalação e Configuração
+
+1. **Instale as dependências:**
+ ```bash
+ pip install -r requirements.txt
+ ```
+
+2. **Configuração de Ambiente:**
+ Crie um arquivo `.env` na raiz do projeto para armazenar suas credenciais sensíveis:
+ ```env
+ # Twilio
+ TWILIO_ACCOUNT_SID=seu_sid
+ TWILIO_AUTH_TOKEN=seu_token
+ TWILIO_WHATSAPP_NUMBER=whatsapp:+14155238886
+
+ # Banco de Dados (se não usar st.secrets)
+ DB_HOST=seu_host
+ DB_USER=seu_user
+ DB_PASSWORD=sua_senha
+ DB_NAME=agenda
+ DB_PORT=porta
+ ```
+ *Nota: O painel Streamlit utiliza `st.secrets` para configuração do banco.*
+
+3. **Banco de Dados:**
+ Certifique-se de que as tabelas `events`, `acompanhamento` e `agenda` estejam criadas no seu banco de dados MySQL conforme esperado pelos scripts `BD_manager_Mysql.py`.
+
+## ▶️ Como Executar
+
+### 1. Iniciar os Chatbots
+Para ativar o atendimento automático, execute o script correspondente à plataforma desejada:
+
+* **WhatsApp (Twilio):**
+ ```bash
+ python Main_Receiver-Twilio01.py
+ ```
+* **Telegram:**
+ ```bash
+ python Main_Receiver_Telegram.py
+ ```
+
+### 2. Painel Administrativo
+Para abrir a interface de gestão:
+```bash
+streamlit run Painel_Prestador_Mysql.py
+```
+
+### 3. Serviço de Lembretes
+Para iniciar o monitoramento e envio de avisos:
+```bash
+python Lembrator.py
+```
+
+## 📦 Dependências Principais
+* `Flask`: Servidor web para os webhooks.
+* `mysql-connector-python`: Driver de conexão com o MySQL.
+* `streamlit` & `streamlit-calendar`: Interface do painel administrativo.
+* `twilio`: SDK para envio de mensagens via Twilio.
+* `python-telegram-bot`: SDK para o bot do Telegram.
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..2e72961
Binary files /dev/null and b/requirements.txt differ