extend notifications

This commit is contained in:
Karl 2025-07-18 17:51:48 +01:00
parent 93e9a1990a
commit c79a908281
5 changed files with 61 additions and 44 deletions

View File

@ -3,7 +3,7 @@ import sys
from dotenv import load_dotenv
import mysql.connector
from datetime import datetime, timedelta
from routes.api import send_notification
from ktvmanager.lib.notifications import send_notification
from ktvmanager.lib.database import get_push_subscriptions, _execute_query
# Add the project root to the Python path
@ -33,41 +33,46 @@ def get_all_accounts(db_connection: MySQLConnection) -> List[Dict[str, Any]]:
return accounts
def send_expiry_notifications() -> None:
def send_expiry_notifications(app) -> None:
"""
Sends notifications to users with accounts expiring in the next 30 days.
"""
now = datetime.now()
thirty_days_later = now + timedelta(days=30)
now_timestamp = int(now.timestamp())
thirty_days_later_timestamp = int(thirty_days_later.timestamp())
with app.app_context():
now = datetime.now()
thirty_days_later = now + timedelta(days=30)
now_timestamp = int(now.timestamp())
thirty_days_later_timestamp = int(thirty_days_later.timestamp())
query = """
SELECT u.id as user_id, ua.username, ua.expiaryDate
FROM users u
JOIN userAccounts ua ON u.id = ua.userID
WHERE ua.expiaryDate BETWEEN %s AND %s
"""
expiring_accounts = _execute_query(query, (now_timestamp, thirty_days_later_timestamp))
query = """
SELECT u.id as user_id, ua.username, ua.expiaryDate
FROM users u
JOIN userAccounts ua ON u.id = ua.userID
WHERE ua.expiaryDate BETWEEN %s AND %s
"""
expiring_accounts = _execute_query(query, (now_timestamp, thirty_days_later_timestamp))
for account in expiring_accounts:
user_id = account['user_id']
subscriptions = get_push_subscriptions(user_id)
for sub in subscriptions:
# Check if a notification has been sent recently
last_notified_query = "SELECT last_notified FROM push_subscriptions WHERE id = %s"
last_notified_result = _execute_query(last_notified_query, (sub['id'],))
last_notified = last_notified_result[0]['last_notified'] if last_notified_result and last_notified_result[0]['last_notified'] else None
for account in expiring_accounts:
expiry_date = datetime.fromtimestamp(account['expiaryDate'])
days_to_expiry = (expiry_date - now).days
if last_notified and last_notified > now - timedelta(days=1):
continue
if days_to_expiry == 30 or days_to_expiry == 7:
user_id = account['user_id']
subscriptions = get_push_subscriptions(user_id)
for sub in subscriptions:
# Check if a notification has been sent recently
last_notified_query = "SELECT last_notified FROM push_subscriptions WHERE id = %s"
last_notified_result = _execute_query(last_notified_query, (sub['id'],))
last_notified = last_notified_result[0]['last_notified'] if last_notified_result and last_notified_result[0]['last_notified'] else None
message = f"Your account {account['username']} is due to expire on {datetime.fromtimestamp(account['expiaryDate']).strftime('%d-%m-%Y')}."
send_notification(sub['subscription_json'], message)
if last_notified and last_notified.date() == now.date():
continue
# Update the last notified timestamp
update_last_notified_query = "UPDATE push_subscriptions SET last_notified = %s WHERE id = %s"
_execute_query(update_last_notified_query, (now, sub['id']))
message = f"Your account {account['username']} is due to expire in {days_to_expiry} days."
send_notification(sub['subscription_json'], message)
# Update the last notified timestamp
update_last_notified_query = "UPDATE push_subscriptions SET last_notified = %s WHERE id = %s"
_execute_query(update_last_notified_query, (now, sub['id']))
def main() -> None:

View File

@ -0,0 +1,17 @@
from flask import current_app
from pywebpush import webpush, WebPushException
def send_notification(subscription_info, message_body):
try:
webpush(
subscription_info=subscription_info,
data=message_body,
vapid_private_key=current_app.config["VAPID_PRIVATE_KEY"],
vapid_claims={"sub": current_app.config["VAPID_CLAIM_EMAIL"]},
)
except WebPushException as ex:
print(f"Web push error: {ex}")
# You might want to remove the subscription if it's invalid
if ex.response and ex.response.status_code == 410:
print("Subscription is no longer valid, removing from DB.")
# Add logic to remove the subscription from your database

View File

@ -4,6 +4,8 @@ from dotenv import load_dotenv
from ktvmanager.config import DevelopmentConfig, ProductionConfig
from routes.api import api_blueprint
from ktvmanager.lib.database import initialize_db_pool
from ktvmanager.account_checker import send_expiry_notifications
from apscheduler.schedulers.background import BackgroundScheduler
def create_app():
app = Flask(__name__)
@ -17,6 +19,12 @@ def create_app():
with app.app_context():
initialize_db_pool()
# Schedule the daily account check
scheduler = BackgroundScheduler()
# Pass the app instance to the job
scheduler.add_job(func=lambda: send_expiry_notifications(app), trigger="interval", days=1)
scheduler.start()
# Register blueprints
app.register_blueprint(api_blueprint)

View File

@ -39,3 +39,4 @@ python-dotenv
python-dotenv
pywebpush==1.13.0
stem==1.8.2
APScheduler==3.10.4

View File

@ -18,7 +18,7 @@ import re
import base64
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from pywebpush import webpush, WebPushException
from ktvmanager.lib.notifications import send_notification
api_blueprint = Blueprint("api", __name__)
@ -196,20 +196,6 @@ def save_subscription(username: str, password: str) -> Response:
return jsonify({"message": "Subscription saved."})
def send_notification(subscription_info, message_body):
try:
webpush(
subscription_info=subscription_info,
data=message_body,
vapid_private_key=current_app.config["VAPID_PRIVATE_KEY"],
vapid_claims={"sub": current_app.config["VAPID_CLAIM_EMAIL"]},
)
except WebPushException as ex:
print(f"Web push error: {ex}")
# You might want to remove the subscription if it's invalid
if ex.response and ex.response.status_code == 410:
print("Subscription is no longer valid, removing from DB.")
# Add logic to remove the subscription from your database
@api_blueprint.route("/send-expiry-notifications", methods=["POST"])