Browse Source

Got customizable alert boxes working

deadtom 1 week ago
parent
commit
645a897461

+ 3 - 0
ownchatbot/__init__.py

@@ -33,6 +33,9 @@ def create_app(test_config=None):
     app.config.from_pyfile('announce.py', silent=True)
     app.config.from_object('ownchatbot.defaults.todo')
     app.config.from_pyfile('todo.py', silent=True)
+    app.config.from_object('ownchatbot.defaults.alerts')
+    app.config.from_pyfile('alerts.py', silent=True)
+    app.config['ALERTS_FOLDER'] = os.path.join(app.instance_path, 'alerts')
 
     if app.config['GUNICORN']:  # Gunicorn logging integration
         gunicorn_logger = logging.getLogger('gunicorn.error')

+ 1 - 0
ownchatbot/owncast_com.py

@@ -31,6 +31,7 @@ def live_now():  # Check if stream is live
         url = f'{owncast_url}/api/status'
         try:
             response = requests.get(url)
+            current_app.logger.info(response)
         except requests.exceptions.RequestException as cserror:
             current_app.logger.error(f'Couldn\'t check if stream is live: {cserror.args[0]}')
             return False

+ 52 - 17
ownchatbot/reward_handlers.py

@@ -135,10 +135,10 @@ def was_milestone_reached(db, reward_name):  # Check if a milestone was reached
     try:
         all_rewards = current_app.config['REWARDS']  # Get milestone numbers from REWARDS
         milestones_info = all_rewards[reward_name]['milestones']
-        
+
         ms1_goal = ms2_goal = ms3_goal = float('inf')  # To avoid referencing unassigned variables, in case there aren't any milestones set
         ms1_reward = ms2_reward = ms3_reward = None  # Initialize to None
-        
+
         if len(milestones_info['milestone1']) > 1 and milestones_info['milestone1'][1]:  # Check that there is a value for the milestone
             ms1_goal = milestones_info['milestone1'][1]
             ms1_reward = f'🚩 \"{milestones_info["milestone1"][0]}\"'
@@ -146,23 +146,23 @@ def was_milestone_reached(db, reward_name):  # Check if a milestone was reached
         if len(milestones_info['milestone2']) > 1 and milestones_info['milestone2'][1]:
             ms2_goal = milestones_info['milestone2'][1]
             ms2_reward = f'🚩🚩 \"{milestones_info["milestone2"][0]}\"'
-        
+
         if len(milestones_info['milestone3']) > 1 and milestones_info['milestone3'][1]:
             ms3_goal = milestones_info['milestone3'][1]
             ms3_reward = f'🚩🚩🚩 \"{milestones_info["milestone3"][0]}\"'
-        
+
         cursor = db.execute(
             "SELECT progress, milestones FROM goals WHERE name = ?",
             (reward_name,)
         )  # Get progress and milestones info from database
         row = cursor.fetchone()
-        
+
         if row is None:
             current_app.logger.error(f'{reward_name} not found in Goal table.')
         else:
             progress = int(row['progress'])
             milestones = int(row['milestones'])
-            
+
             if progress >= ms1_goal and progress < ms2_goal:
                 new_milestones = 1
                 ms_reward = ms1_reward
@@ -174,7 +174,7 @@ def was_milestone_reached(db, reward_name):  # Check if a milestone was reached
                 ms_reward = ms3_reward
             else:
                 new_milestones = 0
-                
+
             if new_milestones > milestones:  # If we're passing a milestone, get the reward
                 cursor = db.execute(
                     "UPDATE goals SET milestones = ? WHERE name = ?",
@@ -290,20 +290,55 @@ def all_active_rewards():  # Get only active rewards
     return all_active_rewards
 
 
+def save_alerts(alerts_dict):  # Write alerts to alerts.py
+    alerts_dict = json.dumps(alerts_dict, indent=4)
+    alerts_file = os.path.join(current_app.instance_path, 'alerts.py')
+    new_alerts = f"ALERTS = {alerts_dict}"
+
+    try:
+        with open(alerts_file, 'w') as af:
+            af.write(new_alerts)
+        af.close
+        current_app.logger.info('Saved new alerts.py.')
+    except Exception as saerror:
+        current_app.logger.error(f'Couldn\'t save alerts.py: {saerror.args[0]}')
+        return False
+
+    current_app.config.from_pyfile('alerts.py', silent=True)  # Reread alerts.py into the app
+    return True
+
+
+def del_alert_file(alert_file):
+    filepath = os.path.join(current_app.config['ALERTS_FOLDER'], alert_file)
+    try:
+        os.remove(filepath)
+        current_app.logger.info(f"Successfully removed \"{alert_file}\" from alerts folder.")
+        return True
+    except FileNotFoundError:
+        current_app.logger.error(f"Couldn't delet \"{alert_file}\": File not found.")
+        return False
+    except PermissionError:
+        current_app.logger.error(f"No permission to delete file: \"{alert_file}\"")
+        return False
+    except Exception as daferror:
+        current_app.logger.error(f"An error occurred: {daferror}")
+        return False
+
+
 def save_rewards(reward_info):  # Write rewards to rewards.py
     sorted_rewards = dict(sorted(reward_info.items(), key=sort_key))
-    
+
     new_rewards = json.dumps(sorted_rewards, indent=4)
     rewards_file = os.path.join(current_app.instance_path, 'rewards.py')
-    
+
     try:
-        with open(rewards_file, 'w') as f:
-            f.write(f'REWARDS = {new_rewards}')
-            f.close()
+        with open(rewards_file, 'w') as rf:
+            rf.write(f'REWARDS = {new_rewards}')
+            rf.close()
     except Exception as srerror:
         current_app.logger.error(f'Couldn\'t save rewards.py: {srerror.args[0]}')
         return False
-    
+
     return True
 
 
@@ -325,11 +360,11 @@ KOFI_TOKEN = '{config_dict['KOFI_TOKEN']}'  # Needed to validate Ko-fi with OCB
 KOFI_INTEGRATION = {config_dict['KOFI_INTEGRATION']}  # Integrate OwnchatBot with Ko-fi"
 
     try:
-        with open(settings_file, 'w') as f:
-            f.write(new_settings)
-        f.close
+        with open(settings_file, 'w') as cf:
+            cf.write(new_settings)
+        cf.close
     except Exception as scerror:
-        current_app.logger.error(f'Couldn\'t save config.py: {saerror.args[0]}')
+        current_app.logger.error(f'Couldn\'t save config.py: {scerror.args[0]}')
         return False
 
     current_app.config.from_pyfile('config.py', silent=True)  # Reread config.py into the app

+ 90 - 0
ownchatbot/templates/mgmt.html

@@ -15,6 +15,7 @@
             <button class="tablinks" data-tab="accounts" onclick="openTab(event, 'accounts')">Manage Accounts</button>
             <button class="tablinks" data-tab="announcements" onclick="openTab(event, 'announcements')">Announcements</button>
             <button class="tablinks" data-tab="todolist" onclick="openTab(event, 'todolist')">To-Do List</button>
+            <button class="tablinks" data-tab="alerts" onclick="openTab(event, 'alerts')">Alerts</button>
             <button class="tablinks" data-tab="settings" onclick="openTab(event, 'settings')">Settings</button>
             {% if kofi_integration %}
                 <button class="tablinks" data-tab="kofi-settings" onclick="openTab(event, 'kofi-settings')">Kofi Settings</button>
@@ -462,5 +463,94 @@
         </form>
     </div>
     
+    <div id='alerts' class="tabcontent">
+        <body>
+        <h1>Customize Your Alerts</h1>
+        <table>
+            <thead>
+                <tr style="border-bottom: none;">
+                    <th style="width: 20%;">Type</th>
+                    <th style="width: 20%;">Image</th>
+                    <th></th>
+                    <th style="width: 15%;"></th>
+                    <th style="width: 25%;">Browser Source</th>
+                </tr>
+            </thead>
+                <tr>
+                    <form id="nf_upload" action="/mgmt/alertupload/FOLLOWER_ALERT" method="post" enctype="multipart/form-data">
+                    <td>New Follower:</td>
+                    {% set follower_alert = alerts_dict["FOLLOWER_ALERT"] %}
+                    {% if follower_alert %}
+                        {% if "webm" in follower_alert %}
+                            <td><video height="100" autoplay loop><source src="{{ url_for('web_panels.alerts', alert_name=follower_alert) }}" type="video/webm"></video></td>
+                        {% else %}
+                            <td><img src="{{ url_for('web_panels.alerts', alert_name=follower_alert) }}"></td>
+                        {% endif %}
+                    {% else %}
+                        <td>Empty</td>
+                    {% endif %}
+                    <td><input type="file" name="FOLLOWER_ALERT" accept=".gif, .jpg, .jpeg, .png, .webm" required></td>
+                    <td><button id="nf_upload" class="button button2" type="submit">Upload</button>
+                    </form>&nbsp
+                        <a href="{{ url_for('web_panels.del_alert', alert_type='FOLLOWER_ALERT') }}"><button class="button button2" onclick="openTab(event, 'alerts')"><span style="color: red;">Clear</span></button></a></td>
+                    <td>/ocbalert/follower_alert</td>
+                </tr>
+                <tr>
+                    <form id="nms_upload" action="/mgmt/alertupload/MILESTONE_ALERT" method="post" enctype="multipart/form-data">
+                    <td>Milestone Reached:</td>
+                    {% set milestone_alert = alerts_dict["MILESTONE_ALERT"] %}
+                    {% if milestone_alert %}
+                        {% if "webm" in milestone_alert %}
+                            <td><video height="100" autoplay loop><source src="{{ url_for('web_panels.alerts', alert_name=milestone_alert) }}" type="video/webm"></video></td>
+                        {% else %}
+                            <td><img src="{{ url_for('web_panels.alerts', alert_name=milestone_alert) }}"></td>
+                        {% endif %}
+                    {% else %}
+                        <td>Empty</td>
+                    {% endif %}
+                    <td><input type="file" name="MILESTONE_ALERT" accept=".gif, .jpg, .jpeg, .png, .webm" required></td>
+                    <td><button id="nms_upload" class="button button2" type="submit">Upload</button></form>&nbsp
+                        <a href="{{ url_for('web_panels.del_alert', alert_type='MILESTONE_ALERT') }}"><button class="button button2" onclick="openTab(event, 'alerts')"><span style="color: red;">Clear</span></button></a></td>
+                    <td>/ocbalert/milestone_alert</td>
+                </tr>
+                <tr>
+                    <form id="ng_upload" action="/mgmt/alertupload/GOAL_ALERT" method="post" enctype="multipart/form-data">
+                    <td>Goal Reached:</td>
+                    {% set goal_alert = alerts_dict["GOAL_ALERT"] %}
+                    {% if goal_alert %}
+                        {% if "webm" in goal_alert %}
+                            <td><video height="100" autoplay loop><source src="{{ url_for('web_panels.alerts', alert_name=goal_alert) }}" type="video/webm"></video></td>
+                        {% else %}
+                            <td><img src="{{ url_for('web_panels.alerts', alert_name=goal_alert) }}"></td>
+                        {% endif %}
+                    {% else %}
+                        <td>Empty</td>
+                    {% endif %}
+                    <td><input type="file" name="GOAL_ALERT" accept=".gif, .jpg, .jpeg, .png, .webm" required></td>
+                    <td><button id="ng_upload" class="button button2" type="submit">Upload</button></form>&nbsp
+                        <a href="{{ url_for('web_panels.del_alert', alert_type='GOAL_ALERT') }}"><button class="button button2" onclick="openTab(event, 'alerts')"><span style="color: red;">Clear</span></button></a></td>
+                    <td>/ocbalert/goal_alert</td>
+                </tr>
+                <tr>
+                    <form id="nd_upload" action="/mgmt/alertupload/DONATION_ALERT" method="post" enctype="multipart/form-data">
+                    <td>Donation:</td>
+                    {% set donation_alert = alerts_dict["DONATION_ALERT"] %}
+                    {% if donation_alert %}
+                        {% if "webm" in donation_alert %}
+                            <td><video height="100" autoplay loop><source src="{{ url_for('web_panels.alerts', alert_name=donation_alert) }}" type="video/webm"></video></td>
+                        {% else %}
+                            <td><img src="{{ url_for('web_panels.alerts', alert_name=donation_alert) }}"></td>
+                        {% endif %}
+                    {% else %}
+                        <td>Empty</td>
+                    {% endif %}
+                    <td><input type="file" name="DONATION_ALERT" accept=".gif, .jpg, .jpeg, .png, .webm" required></td>
+                    <td><button id="nd_upload" class="button button2" type="submit">Upload</button></form>&nbsp
+                        <a href="{{ url_for('web_panels.del_alert', alert_type='DONATION_ALERT') }}"><button class="button button2" onclick="openTab(event, 'alerts')"><span style="color: red;">Clear</span></button></a></td>
+                    <td>/ocbalert/donation_alert</td>
+                </tr>
+        </table>
+        <br><br>
+    </div>    
     
 </html>

+ 65 - 2
ownchatbot/web_panels.py

@@ -1,7 +1,7 @@
-from flask import flash, render_template, Blueprint, current_app, redirect, request, url_for, session, g
+from flask import flash, render_template, Blueprint, current_app, redirect, request, url_for, session, g, send_from_directory
 from datetime import timezone
 from ownchatbot.db import get_db, reread_goals, reread_votes, rem_vote, reset_vote, reset_goal, clear_fulfilled_rewards, clear_reward_queue, rem_cool, rem_from_queue
-from ownchatbot.reward_handlers import all_active_votes, all_active_goals, all_active_rewards, get_queue, fulfill_reward, save_rewards, activate_category, deactivate_category, refund_reward, reread_categories, save_config
+from ownchatbot.reward_handlers import all_active_votes, all_active_goals, all_active_rewards, get_queue, fulfill_reward, save_rewards, activate_category, deactivate_category, refund_reward, reread_categories, save_config, save_alerts, del_alert_file
 from ownchatbot.user_handlers import get_all_users, get_all_users_by_name, refund_points, adjust_points, change_email, get_email_code, del_email_code, save_todolist, save_todocss
 from ownchatbot.bot_messages import save_announce
 from ownchatbot.owncast_com import send_private_chat
@@ -12,6 +12,7 @@ import random
 import pkce
 import requests
 from functools import wraps
+import os
 
 ocb = Blueprint('web_panels', __name__)
 
@@ -106,6 +107,7 @@ def mgmt():
     todolist_items = current_app.config['LIST']
     todo_css = current_app.config['CSS']
     active_tab = request.args.get('activeTab')
+    alerts_dict = current_app.config['ALERTS']
     settings_info = [
         access_id,
         points_interval,
@@ -138,6 +140,7 @@ def mgmt():
                            settings_info=settings_info,
                            items=todolist_items,
                            todo_css=todo_css,
+                           alerts_dict=alerts_dict,
                            activeTab=active_tab)
 
 
@@ -496,18 +499,21 @@ def set_viewer_email():
 
 
 @ocb.route('/mgmt/activate/<category>', methods=['GET', 'POST'])
+@requires_login
 def activate(category):
     activate_category(category)
     return redirect(url_for('web_panels.mgmt', activeTab='categories'))
 
 
 @ocb.route('/mgmt/deactivate/<category>', methods=['GET', 'POST'])
+@requires_login
 def deactivate(category):
     deactivate_category(category)
     return redirect(url_for('web_panels.mgmt', activeTab='categories'))
 
 
 @ocb.route('/mgmt/delcat/<cat_name>/<cat_act>', methods=['GET', 'POST'])
+@requires_login
 def delcat(cat_name, cat_act):
     active_categories = current_app.config['ACTIVE_CAT']
     inactive_categories = current_app.config['INACTIVE_CAT']
@@ -525,6 +531,7 @@ def delcat(cat_name, cat_act):
 
 
 @ocb.route('/mgmt/reset/<reward_name>/<reward_type>', methods=['GET', 'POST'])  # Reset votes and goals to zero
+@requires_login
 def reset(reward_name, reward_type):
     if reward_type == "goal":
         reset_goal(reward_name)
@@ -540,18 +547,21 @@ def rereadv():
 
 
 @ocb.route('/mgmt/clearfulfilled', methods=['GET', 'POST'])
+@requires_login
 def clearfulfilled():
     clear_fulfilled_rewards()
     return redirect(url_for('web_panels.mgmtqueue'))
 
 
 @ocb.route('/mgmt/clearqueue', methods=['GET', 'POST'])
+@requires_login
 def clear_queue():
     clear_reward_queue()
     return redirect(url_for('web_panels.mgmtqueue'))
 
 
 @ocb.route('/mgmt/addtodocss', methods=['POST'])
+@requires_login
 def add_todo_css():
     if request.method == 'POST':
         new_css = request.form.get('todo_css')
@@ -567,6 +577,7 @@ def add_todo_css():
 
 
 @ocb.route('/mgmt/addtodoitem', methods=['POST'])
+@requires_login
 def add_todo_item():
     if request.method == 'POST':
         todolist_items = current_app.config['LIST']
@@ -580,6 +591,7 @@ def add_todo_item():
 
 
 @ocb.route('/mgmt/cross/<int:item_id>')
+@requires_login
 def cross(item_id):
     todolist_items = current_app.config['LIST']
     if 0 <= item_id < len(todolist_items):
@@ -590,6 +602,7 @@ def cross(item_id):
 
 
 @ocb.route('/mgmt/uncross/<int:item_id>')
+@requires_login
 def uncross(item_id):
     todolist_items = current_app.config['LIST']
     if 0 <= item_id < len(todolist_items):
@@ -600,6 +613,7 @@ def uncross(item_id):
 
 
 @ocb.route('/mgmt/clearlist')
+@requires_login
 def clear_list():
     todolist_items = current_app.config['LIST']
     todolist_items = []  # Clear the list
@@ -608,6 +622,55 @@ def clear_list():
     return redirect(url_for('web_panels.mgmt', activeTab='todolist'))
 
 
+@ocb.route('/mgmt/alertupload/<alert_type>', methods=['POST'])
+@requires_login
+def alert_upload(alert_type):
+    alerts_dict = current_app.config['ALERTS']
+    alert_file = request.files[alert_type]
+    if alert_type in alerts_dict.keys():  # If the alert already exists, delete the existing alert file
+        old_file = alerts_dict[alert_type.upper()]
+        current_app.logger.info(f'{alert_type} already set.')
+        if del_alert_file(old_file):
+            pass
+    filepath = os.path.join(current_app.config['ALERTS_FOLDER'], alert_file.filename)
+    alert_file.save(filepath)
+    alerts_dict[alert_type] = alert_file.filename
+    if save_alerts(alerts_dict):  # Save new alerts.py
+        return redirect(url_for('web_panels.mgmt', activeTab='alerts'))
+
+    return redirect(url_for('web_panels.mgmt', activeTab='alerts'))
+
+
+@ocb.route('/mgmt/delalert/<alert_type>')
+@requires_login
+def del_alert(alert_type):
+    alerts_dict = current_app.config['ALERTS']
+    try:
+        alert_file = alerts_dict[alert_type]
+        alerts_dict.pop(alert_type)
+        if save_alerts(alerts_dict):
+            current_app.logger.info(f'Removed {alert_type} from alerts.py.')
+            if del_alert_file(alert_file):
+                pass
+    except KeyError:
+        current_app.logger.info(f'No {alert_type} alert set. Nothing to do.')
+    return redirect(url_for('web_panels.mgmt', activeTab='alerts'))
+
+
+@ocb.route('/alerts/<alert_name>')  # Route for alerts overlay
+def alerts(alert_name):
+    return send_from_directory(current_app.config['ALERTS_FOLDER'], alert_name)
+
+
+@ocb.route('/ocbalert/<alert_type>')  # Route for alerts overlay
+def ocb_alerts(alert_type):
+    alert_type = alert_type.upper()
+    alerts_dict = current_app.config['ALERTS']
+    alert_name = alerts_dict[alert_type]
+    return render_template('ocbalert.html',
+                           alert_name=alert_name)
+
+
 @ocb.route('/goals', methods=['GET'])  # Route for goals overlay
 def goals():
     db = get_db()