Initial MCM project: FastAPI + Textual TUI unified messenger

MultiCustomerMessenger supporting Telegram (python-telegram-bot),
WhatsApp (Green API) and SMS (python-gsmmodem-new). REST API with
Bearer-token auth, SQLAlchemy models for MariaDB, APScheduler for
background polling, and Textual TUI running in same asyncio event-loop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 14:43:19 +01:00
commit 7a8c8149c9
38 changed files with 2072 additions and 0 deletions

103
main.py Normal file
View File

@@ -0,0 +1,103 @@
"""MCM MultiCustomerMessenger
Entry Point: Startet FastAPI (Uvicorn) und Textual TUI im selben asyncio-Event-Loop.
Für den systemd-Dienst (ohne TUI) siehe main_api_only.py.
"""
from __future__ import annotations
import asyncio
import logging
import sys
import uvicorn
from api.app import app as fastapi_app
from api.routes import channels as channels_router
from channels.sms_channel import SmsChannel
from channels.telegram_channel import TelegramChannel
from channels.whatsapp_channel import WhatsAppChannel
from config import settings
from db.database import init_db
from services import message_service
from tasks.receiver import build_scheduler
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
logger = logging.getLogger("mcm")
async def _run_api() -> None:
"""Uvicorn als Task in der bestehenden Event-Loop."""
config = uvicorn.Config(
fastapi_app,
host=settings.host,
port=settings.port,
log_level="warning",
access_log=False,
)
server = uvicorn.Server(config)
await server.serve()
async def main(with_tui: bool = True) -> None:
logger.info("MCM starting…")
# 1. Datenbank initialisieren
init_db()
# 2. Kanäle instanziieren
telegram = TelegramChannel()
whatsapp = WhatsAppChannel()
sms = SmsChannel()
# 3. Inbound-Callback setzen
telegram.set_inbound_callback(message_service.handle_inbound)
whatsapp.set_inbound_callback(message_service.handle_inbound)
sms.set_inbound_callback(message_service.handle_inbound)
# 4. Channel-Referenzen in Services/Routes registrieren
message_service.register_channels(telegram, whatsapp, sms)
channels_router.register(telegram, whatsapp, sms)
# 5. Kanäle starten
await telegram.start()
await whatsapp.start()
await sms.start()
# 6. Hintergrund-Tasks starten (WhatsApp-Polling etc.)
scheduler = build_scheduler(whatsapp)
scheduler.start()
# 7. Uvicorn als Hintergrund-Task starten
api_task = asyncio.create_task(_run_api(), name="mcm-api")
logger.info("API running on http://%s:%d", settings.host, settings.port)
try:
if with_tui:
# 8a. TUI starten (blockiert bis der Nutzer beendet)
from tui.app import MCMApp
tui = MCMApp()
await tui.run_async()
else:
# 8b. Nur API wartet auf Ctrl-C / SIGTERM
logger.info("Running in API-only mode (no TUI)")
await api_task
finally:
logger.info("MCM shutting down…")
scheduler.shutdown(wait=False)
await telegram.stop()
await whatsapp.stop()
await sms.stop()
api_task.cancel()
if __name__ == "__main__":
no_tui = "--no-tui" in sys.argv
try:
asyncio.run(main(with_tui=not no_tui))
except KeyboardInterrupt:
pass