Fixed startup email

This commit is contained in:
Jonas Linter
2025-10-16 10:53:03 +02:00
parent 90e253b950
commit 6ad4df6990
4 changed files with 336 additions and 31 deletions

View File

@@ -14,8 +14,6 @@ server:
companyname: "99tales Gmbh" companyname: "99tales Gmbh"
res_id_source_context: "99tales" res_id_source_context: "99tales"
logger: logger:
level: "INFO" # Set to DEBUG for more verbose output level: "INFO" # Set to DEBUG for more verbose output
file: "alpinebits.log" # Log file path, or null for console only file: "alpinebits.log" # Log file path, or null for console only
@@ -40,6 +38,9 @@ alpine_bits_auth:
username: "erika" username: "erika"
password: !secret ERIKA_PASSWORD password: !secret ERIKA_PASSWORD
api_tokens:
- tLTI8wXF1OVEvUX7kdZRhSW3Qr5feBCz0mHo-kbnEp0
# Email configuration for monitoring and alerts # Email configuration for monitoring and alerts
email: email:
# SMTP server configuration # SMTP server configuration
@@ -55,9 +56,6 @@ email:
from_address: "info@99tales.net" # Sender address from_address: "info@99tales.net" # Sender address
from_name: "AlpineBits Monitor" # Sender display name from_name: "AlpineBits Monitor" # Sender display name
api_tokens:
- tLTI8wXF1OVEvUX7kdZRhSW3Qr5feBCz0mHo-kbnEp0
# Monitoring and alerting # Monitoring and alerting
monitoring: monitoring:
# Daily report configuration # Daily report configuration

View File

@@ -299,10 +299,11 @@ async def lifespan(app: FastAPI):
report_scheduler.set_stats_collector(stats_collector.collect_stats) report_scheduler.set_stats_collector(stats_collector.collect_stats)
_LOGGER.info("Stats collector initialized and hooked up to report scheduler") _LOGGER.info("Stats collector initialized and hooked up to report scheduler")
# Send a test daily report on startup for testing # Send a test daily report on startup for testing (with 24-hour lookback)
_LOGGER.info("Sending test daily report on startup") _LOGGER.info("Sending test daily report on startup (last 24 hours)")
try: try:
stats = await stats_collector.collect_stats() # Use lookback_hours=24 to get stats from last 24 hours
stats = await stats_collector.collect_stats(lookback_hours=24)
success = await email_service.send_daily_report( success = await email_service.send_daily_report(
recipients=report_scheduler.recipients, recipients=report_scheduler.recipients,
stats=stats, stats=stats,

View File

@@ -492,14 +492,25 @@ class ReservationStatsCollector:
len(self._hotel_map), len(self._hotel_map),
) )
async def collect_stats(self) -> dict[str, Any]: async def collect_stats(self, lookback_hours: int | None = None) -> dict[str, Any]:
"""Collect reservation statistics for the reporting period. """Collect reservation statistics for the reporting period.
Args:
lookback_hours: Optional override to look back N hours from now.
If None, uses time since last report.
Returns: Returns:
Dictionary with statistics including reservations per hotel Dictionary with statistics including reservations per hotel
""" """
now = datetime.now() now = datetime.now()
if lookback_hours is not None:
# Override mode: look back N hours from now
period_start = now - timedelta(hours=lookback_hours)
period_end = now
else:
# Normal mode: since last report
period_start = self._last_report_time period_start = self._last_report_time
period_end = now period_end = now
@@ -538,7 +549,8 @@ class ReservationStatsCollector:
# Sort by reservation count descending # Sort by reservation count descending
hotels_stats.sort(key=lambda x: x["reservations"], reverse=True) hotels_stats.sort(key=lambda x: x["reservations"], reverse=True)
# Update last report time # Update last report time only in normal mode (not lookback mode)
if lookback_hours is None:
self._last_report_time = now self._last_report_time = now
stats = { stats = {

294
test_smtp.py Normal file
View File

@@ -0,0 +1,294 @@
#!/usr/bin/env python3
"""Test script to diagnose SMTP connection issues.
This script tests SMTP connectivity with different configurations to help
identify whether the issue is with credentials, network, ports, or TLS settings.
"""
import smtplib
import ssl
import sys
from datetime import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# Load configuration from config.yaml
try:
from alpine_bits_python.config_loader import load_config
print("Loading configuration from config.yaml...")
config = load_config()
email_config = config.get("email", {})
smtp_config = email_config.get("smtp", {})
SMTP_HOST = smtp_config.get("host", "smtp.titan.email")
SMTP_PORT = smtp_config.get("port", 465)
SMTP_USERNAME = smtp_config.get("username", "")
SMTP_PASSWORD = smtp_config.get("password", "")
USE_TLS = smtp_config.get("use_tls", False)
USE_SSL = smtp_config.get("use_ssl", True)
FROM_ADDRESS = email_config.get("from_address", "info@99tales.net")
FROM_NAME = email_config.get("from_name", "AlpineBits Monitor")
# Get test recipient
monitoring_config = email_config.get("monitoring", {})
daily_report = monitoring_config.get("daily_report", {})
recipients = daily_report.get("recipients", [])
TEST_RECIPIENT = recipients[0] if recipients else "jonas@vaius.ai"
print(f"✓ Configuration loaded successfully")
print(f" SMTP Host: {SMTP_HOST}")
print(f" SMTP Port: {SMTP_PORT}")
print(f" Username: {SMTP_USERNAME}")
print(f" Password: {'***' if SMTP_PASSWORD else '(not set)'}")
print(f" Use SSL: {USE_SSL}")
print(f" Use TLS: {USE_TLS}")
print(f" From: {FROM_ADDRESS}")
print(f" Test Recipient: {TEST_RECIPIENT}")
print()
except Exception as e:
print(f"✗ Failed to load configuration: {e}")
print("Using default values for testing...")
SMTP_HOST = "smtp.titan.email"
SMTP_PORT = 465
SMTP_USERNAME = input("Enter SMTP username: ")
SMTP_PASSWORD = input("Enter SMTP password: ")
USE_TLS = False
USE_SSL = True
FROM_ADDRESS = "info@99tales.net"
FROM_NAME = "AlpineBits Monitor"
TEST_RECIPIENT = input("Enter test recipient email: ")
print()
def create_test_message(subject: str) -> MIMEMultipart:
"""Create a test email message."""
msg = MIMEMultipart("alternative")
msg["Subject"] = subject
msg["From"] = f"{FROM_NAME} <{FROM_ADDRESS}>"
msg["To"] = TEST_RECIPIENT
msg["Date"] = datetime.now().strftime("%a, %d %b %Y %H:%M:%S %z")
body = f"""SMTP Connection Test - {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
This is a test email to verify SMTP connectivity.
Configuration:
- SMTP Host: {SMTP_HOST}
- SMTP Port: {SMTP_PORT}
- Use SSL: {USE_SSL}
- Use TLS: {USE_TLS}
If you received this email, the SMTP configuration is working correctly!
"""
msg.attach(MIMEText(body, "plain"))
return msg
def test_smtp_connection(host: str, port: int, timeout: int = 10) -> bool:
"""Test basic TCP connection to SMTP server."""
import socket
print(f"Test 1: Testing TCP connection to {host}:{port}...")
try:
sock = socket.create_connection((host, port), timeout=timeout)
sock.close()
print(f"✓ TCP connection successful to {host}:{port}")
return True
except socket.timeout:
print(f"✗ Connection timed out after {timeout} seconds")
print(f" This suggests a network/firewall issue blocking access to {host}:{port}")
return False
except socket.error as e:
print(f"✗ Connection failed: {e}")
return False
def test_smtp_ssl(host: str, port: int, username: str, password: str, timeout: int = 30) -> bool:
"""Test SMTP connection with SSL."""
print(f"\nTest 2: Testing SMTP with SSL (port {port})...")
try:
context = ssl.create_default_context()
with smtplib.SMTP_SSL(host, port, timeout=timeout, context=context) as server:
print(f"✓ Connected to SMTP server with SSL")
# Try to get server info
server.ehlo()
print(f"✓ EHLO successful")
# Try authentication if credentials provided
if username and password:
print(f" Attempting authentication as: {username}")
server.login(username, password)
print(f"✓ Authentication successful")
else:
print(f"⚠ No credentials provided, skipping authentication")
return True
except smtplib.SMTPAuthenticationError as e:
print(f"✗ Authentication failed: {e}")
print(f" Check your username and password")
return False
except socket.timeout:
print(f"✗ Connection timed out after {timeout} seconds")
print(f" Try increasing timeout or check network/firewall")
return False
except Exception as e:
print(f"✗ SMTP SSL failed: {e}")
return False
def test_smtp_tls(host: str, port: int, username: str, password: str, timeout: int = 30) -> bool:
"""Test SMTP connection with STARTTLS."""
print(f"\nTest 3: Testing SMTP with STARTTLS (port {port})...")
try:
with smtplib.SMTP(host, port, timeout=timeout) as server:
print(f"✓ Connected to SMTP server")
# Try STARTTLS
context = ssl.create_default_context()
server.starttls(context=context)
print(f"✓ STARTTLS successful")
# Try authentication if credentials provided
if username and password:
print(f" Attempting authentication as: {username}")
server.login(username, password)
print(f"✓ Authentication successful")
else:
print(f"⚠ No credentials provided, skipping authentication")
return True
except smtplib.SMTPAuthenticationError as e:
print(f"✗ Authentication failed: {e}")
return False
except socket.timeout:
print(f"✗ Connection timed out after {timeout} seconds")
return False
except Exception as e:
print(f"✗ SMTP TLS failed: {e}")
return False
def send_test_email(host: str, port: int, username: str, password: str,
use_ssl: bool, use_tls: bool, timeout: int = 30) -> bool:
"""Send an actual test email."""
print(f"\nTest 4: Sending test email...")
try:
msg = create_test_message("SMTP Test Email - AlpineBits")
if use_ssl:
context = ssl.create_default_context()
with smtplib.SMTP_SSL(host, port, timeout=timeout, context=context) as server:
if username and password:
server.login(username, password)
server.send_message(msg, FROM_ADDRESS, [TEST_RECIPIENT])
else:
with smtplib.SMTP(host, port, timeout=timeout) as server:
if use_tls:
context = ssl.create_default_context()
server.starttls(context=context)
if username and password:
server.login(username, password)
server.send_message(msg, FROM_ADDRESS, [TEST_RECIPIENT])
print(f"✓ Test email sent successfully to {TEST_RECIPIENT}")
print(f" Check your inbox!")
return True
except Exception as e:
print(f"✗ Failed to send email: {e}")
return False
def main():
"""Run all SMTP tests."""
print("=" * 70)
print("SMTP Connection Test Script")
print("=" * 70)
print()
# Test 1: Basic TCP connection
tcp_ok = test_smtp_connection(SMTP_HOST, SMTP_PORT, timeout=10)
if not tcp_ok:
print("\n" + "=" * 70)
print("DIAGNOSIS: Cannot establish TCP connection to SMTP server")
print("=" * 70)
print("\nPossible causes:")
print("1. The SMTP server is down or unreachable")
print("2. A firewall is blocking the connection")
print("3. The host or port is incorrect")
print("4. Network connectivity issues from your container/server")
print("\nTroubleshooting:")
print(f"- Verify the server is correct: {SMTP_HOST}")
print(f"- Verify the port is correct: {SMTP_PORT}")
print("- Check if your container/server has outbound internet access")
print("- Try from a different network or machine")
print(f"- Use telnet/nc to test: telnet {SMTP_HOST} {SMTP_PORT}")
return 1
# Test 2 & 3: Try both SSL and TLS
ssl_ok = False
tls_ok = False
if USE_SSL:
ssl_ok = test_smtp_ssl(SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, timeout=30)
# Also try common alternative ports
if not ssl_ok and SMTP_PORT == 465:
print("\n⚠ Port 465 failed, trying port 587 with STARTTLS...")
tls_ok = test_smtp_tls(SMTP_HOST, 587, SMTP_USERNAME, SMTP_PASSWORD, timeout=30)
if USE_TLS:
tls_ok = test_smtp_tls(SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, timeout=30)
if not ssl_ok and not tls_ok:
print("\n" + "=" * 70)
print("DIAGNOSIS: Cannot authenticate or establish secure connection")
print("=" * 70)
print("\nPossible causes:")
print("1. Wrong username or password")
print("2. Wrong port for the encryption method")
print("3. SSL/TLS version mismatch")
print("\nTroubleshooting:")
print("- Verify your credentials are correct")
print("- Port 465 typically uses SSL")
print("- Port 587 typically uses STARTTLS")
print("- Port 25 is usually unencrypted (not recommended)")
return 1
# Test 4: Send actual email
send_ok = send_test_email(
SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD,
USE_SSL, USE_TLS, timeout=30
)
print("\n" + "=" * 70)
if send_ok:
print("✓ ALL TESTS PASSED!")
print("=" * 70)
print("\nYour SMTP configuration is working correctly.")
print(f"Check {TEST_RECIPIENT} for the test email.")
else:
print("⚠ PARTIAL SUCCESS")
print("=" * 70)
print("\nConnection and authentication work, but email sending failed.")
print("This might be a temporary issue. Try again.")
return 0
if __name__ == "__main__":
try:
sys.exit(main())
except KeyboardInterrupt:
print("\n\nTest cancelled by user")
sys.exit(1)