Fixed startup email
This commit is contained in:
@@ -14,11 +14,9 @@ server:
|
||||
companyname: "99tales Gmbh"
|
||||
res_id_source_context: "99tales"
|
||||
|
||||
|
||||
|
||||
logger:
|
||||
level: "INFO" # Set to DEBUG for more verbose output
|
||||
file: "alpinebits.log" # Log file path, or null for console only
|
||||
level: "INFO" # Set to DEBUG for more verbose output
|
||||
file: "alpinebits.log" # Log file path, or null for console only
|
||||
|
||||
alpine_bits_auth:
|
||||
- hotel_id: "39054_001"
|
||||
@@ -40,48 +38,48 @@ alpine_bits_auth:
|
||||
username: "erika"
|
||||
password: !secret ERIKA_PASSWORD
|
||||
|
||||
api_tokens:
|
||||
- tLTI8wXF1OVEvUX7kdZRhSW3Qr5feBCz0mHo-kbnEp0
|
||||
|
||||
# Email configuration for monitoring and alerts
|
||||
email:
|
||||
# SMTP server configuration
|
||||
smtp:
|
||||
host: "smtp.titan.email" # Your SMTP server
|
||||
port: 465 # Usually 587 for TLS, 465 for SSL
|
||||
username: info@99tales.net # SMTP username
|
||||
password: !secret EMAIL_PASSWORD # SMTP password
|
||||
use_tls: false # Use STARTTLS
|
||||
use_ssl: true # Use SSL/TLS from start
|
||||
host: "smtp.titan.email" # Your SMTP server
|
||||
port: 465 # Usually 587 for TLS, 465 for SSL
|
||||
username: info@99tales.net # SMTP username
|
||||
password: !secret EMAIL_PASSWORD # SMTP password
|
||||
use_tls: false # Use STARTTLS
|
||||
use_ssl: true # Use SSL/TLS from start
|
||||
|
||||
# Email addresses
|
||||
from_address: "info@99tales.net" # Sender address
|
||||
from_name: "AlpineBits Monitor" # Sender display name
|
||||
|
||||
api_tokens:
|
||||
- tLTI8wXF1OVEvUX7kdZRhSW3Qr5feBCz0mHo-kbnEp0
|
||||
from_address: "info@99tales.net" # Sender address
|
||||
from_name: "AlpineBits Monitor" # Sender display name
|
||||
|
||||
# Monitoring and alerting
|
||||
monitoring:
|
||||
# Daily report configuration
|
||||
daily_report:
|
||||
enabled: false # Set to true to enable daily reports
|
||||
enabled: false # Set to true to enable daily reports
|
||||
recipients:
|
||||
- "jonas@vaius.ai"
|
||||
#- "dev@99tales.com"
|
||||
send_time: "08:00" # Time to send daily report (24h format, local time)
|
||||
include_stats: true # Include reservation/customer stats
|
||||
include_errors: true # Include error summary
|
||||
send_time: "08:00" # Time to send daily report (24h format, local time)
|
||||
include_stats: true # Include reservation/customer stats
|
||||
include_errors: true # Include error summary
|
||||
|
||||
# Error alert configuration (hybrid approach)
|
||||
error_alerts:
|
||||
enabled: false # Set to true to enable error alerts
|
||||
enabled: false # Set to true to enable error alerts
|
||||
recipients:
|
||||
- "jonas@vaius.ai"
|
||||
#- "oncall@99tales.com"
|
||||
# Alert is sent immediately if threshold is reached
|
||||
error_threshold: 5 # Send immediate alert after N errors
|
||||
error_threshold: 5 # Send immediate alert after N errors
|
||||
# Otherwise, alert is sent after buffer time expires
|
||||
buffer_minutes: 15 # Wait N minutes before sending buffered errors
|
||||
buffer_minutes: 15 # Wait N minutes before sending buffered errors
|
||||
# Cooldown period to prevent alert spam
|
||||
cooldown_minutes: 15 # Wait N min before sending another alert
|
||||
cooldown_minutes: 15 # Wait N min before sending another alert
|
||||
# Error severity levels to monitor
|
||||
log_levels:
|
||||
- "ERROR"
|
||||
|
||||
@@ -299,10 +299,11 @@ async def lifespan(app: FastAPI):
|
||||
report_scheduler.set_stats_collector(stats_collector.collect_stats)
|
||||
_LOGGER.info("Stats collector initialized and hooked up to report scheduler")
|
||||
|
||||
# Send a test daily report on startup for testing
|
||||
_LOGGER.info("Sending test daily report on startup")
|
||||
# Send a test daily report on startup for testing (with 24-hour lookback)
|
||||
_LOGGER.info("Sending test daily report on startup (last 24 hours)")
|
||||
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(
|
||||
recipients=report_scheduler.recipients,
|
||||
stats=stats,
|
||||
|
||||
@@ -492,16 +492,27 @@ class ReservationStatsCollector:
|
||||
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.
|
||||
|
||||
Args:
|
||||
lookback_hours: Optional override to look back N hours from now.
|
||||
If None, uses time since last report.
|
||||
|
||||
Returns:
|
||||
Dictionary with statistics including reservations per hotel
|
||||
|
||||
"""
|
||||
now = datetime.now()
|
||||
period_start = self._last_report_time
|
||||
period_end = 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_end = now
|
||||
|
||||
_LOGGER.info(
|
||||
"Collecting reservation stats from %s to %s",
|
||||
@@ -538,8 +549,9 @@ class ReservationStatsCollector:
|
||||
# Sort by reservation count descending
|
||||
hotels_stats.sort(key=lambda x: x["reservations"], reverse=True)
|
||||
|
||||
# Update last report time
|
||||
self._last_report_time = now
|
||||
# Update last report time only in normal mode (not lookback mode)
|
||||
if lookback_hours is None:
|
||||
self._last_report_time = now
|
||||
|
||||
stats = {
|
||||
"reporting_period": {
|
||||
|
||||
294
test_smtp.py
Normal file
294
test_smtp.py
Normal 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)
|
||||
Reference in New Issue
Block a user