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 from dotenv import load_dotenv
import mysql.connector import mysql.connector
from datetime import datetime, timedelta 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 from ktvmanager.lib.database import get_push_subscriptions, _execute_query
# Add the project root to the Python path # Add the project root to the Python path
@ -33,10 +33,11 @@ def get_all_accounts(db_connection: MySQLConnection) -> List[Dict[str, Any]]:
return accounts 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. Sends notifications to users with accounts expiring in the next 30 days.
""" """
with app.app_context():
now = datetime.now() now = datetime.now()
thirty_days_later = now + timedelta(days=30) thirty_days_later = now + timedelta(days=30)
now_timestamp = int(now.timestamp()) now_timestamp = int(now.timestamp())
@ -51,6 +52,10 @@ def send_expiry_notifications() -> None:
expiring_accounts = _execute_query(query, (now_timestamp, thirty_days_later_timestamp)) expiring_accounts = _execute_query(query, (now_timestamp, thirty_days_later_timestamp))
for account in expiring_accounts: for account in expiring_accounts:
expiry_date = datetime.fromtimestamp(account['expiaryDate'])
days_to_expiry = (expiry_date - now).days
if days_to_expiry == 30 or days_to_expiry == 7:
user_id = account['user_id'] user_id = account['user_id']
subscriptions = get_push_subscriptions(user_id) subscriptions = get_push_subscriptions(user_id)
for sub in subscriptions: for sub in subscriptions:
@ -59,10 +64,10 @@ def send_expiry_notifications() -> None:
last_notified_result = _execute_query(last_notified_query, (sub['id'],)) 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 last_notified = last_notified_result[0]['last_notified'] if last_notified_result and last_notified_result[0]['last_notified'] else None
if last_notified and last_notified > now - timedelta(days=1): if last_notified and last_notified.date() == now.date():
continue continue
message = f"Your account {account['username']} is due to expire on {datetime.fromtimestamp(account['expiaryDate']).strftime('%d-%m-%Y')}." message = f"Your account {account['username']} is due to expire in {days_to_expiry} days."
send_notification(sub['subscription_json'], message) send_notification(sub['subscription_json'], message)
# Update the last notified timestamp # Update the last notified timestamp

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 ktvmanager.config import DevelopmentConfig, ProductionConfig
from routes.api import api_blueprint from routes.api import api_blueprint
from ktvmanager.lib.database import initialize_db_pool 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(): def create_app():
app = Flask(__name__) app = Flask(__name__)
@ -17,6 +19,12 @@ def create_app():
with app.app_context(): with app.app_context():
initialize_db_pool() 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 # Register blueprints
app.register_blueprint(api_blueprint) app.register_blueprint(api_blueprint)

View File

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

View File

@ -18,7 +18,7 @@ import re
import base64 import base64
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import ec
from pywebpush import webpush, WebPushException from ktvmanager.lib.notifications import send_notification
api_blueprint = Blueprint("api", __name__) api_blueprint = Blueprint("api", __name__)
@ -196,20 +196,6 @@ def save_subscription(username: str, password: str) -> Response:
return jsonify({"message": "Subscription saved."}) 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"]) @api_blueprint.route("/send-expiry-notifications", methods=["POST"])