9 Комити 5376628f01 ... a264e7b68b

Аутор SHA1 Порука Датум
  deadtom a264e7b68b Incremented version number пре 2 недеља
  deadtom 44d90c43f3 Added database upgrade script, to add code column to existing database пре 2 недеља
  deadtom 7b37e44517 Added success and failure messages when registering email address. Refined some bot messages. пре 2 недеља
  deadtom cc67ede724 Got basic account verification for email working. Needs some polishing. пре 3 недеља
  deadtom 099ed2cab5 Added code column to points table пре 3 недеља
  deadtom cdd15dca6f More message tuning пре 3 недеља
  deadtom 9a1f8e36c8 Changed access token verbage пре 3 недеља
  deadtom efdc906546 Set some messages to be sent only to the user, instead of chat пре 3 недеља
  deadtom 2de0181f40 Changed mouse over flag verbage пре 3 недеља

+ 1 - 0
.gitignore

@@ -4,6 +4,7 @@ instance/
 .kdev4/
 /*.kdev4
 .git/
+ownchatbot/snippets/
 
 __pycache__/
 *.py[cod]

+ 1 - 1
README.md

@@ -136,7 +136,7 @@ You need to create a button on your Owncast page, so your viewers can access the
     ```
     https://<your_external.ownchatbot.url>/userpanel
     ```
-4. Set the third field (action title) to whatever you want the button to be named.
+4. Set the third field (action title) to "Stream Rewards".
     Example:
     ```
     Action Title: Stream Rewards

+ 1 - 0
TODO.md

@@ -0,0 +1 @@
+

+ 3 - 3
ownchatbot/__init__.py

@@ -1,8 +1,8 @@
 import os
 import logging
-from flask import Flask
+from flask import Flask, g
 from ownchatbot.db import get_db
-from ownchatbot.owncast_com import live_now, award_points, send_chat
+from ownchatbot.owncast_com import live_now, award_points, send_system_chat
 from apscheduler.schedulers.background import BackgroundScheduler
 
 current_index = 0
@@ -49,7 +49,7 @@ def create_app(test_config=None):
         if app.config['ANNOUNCE_ENABLE']:  # If announcements are enabled
             global current_index
             message = announcements[current_index]
-            send_chat(message)
+            send_system_chat(message)
             current_index = (current_index + 1) % len(announcements)
         else:
             app.logger.info(f'Not live, so not sending announcement.')

+ 29 - 26
ownchatbot/bot_messages.py

@@ -1,6 +1,6 @@
 from flask import current_app
 from ownchatbot.db import get_db, is_cool
-from ownchatbot.owncast_com import send_chat
+from ownchatbot.owncast_com import send_chat, send_private_chat
 from ownchatbot.reward_handlers import run_script, add_to_queue, add_to_vote, add_to_goal, was_goal_reached, goal_reached, is_reward_active, check_vote, all_active_goals, goal_left, was_milestone_reached
 from ownchatbot.user_handlers import spend_points, get_users_points, refund_points, get_all_users_with_user_id
 import os
@@ -45,7 +45,7 @@ def do_reward(message, user_id):  # Parse the chat command
         contribution = split_message[1]
 
     if reward not in current_app.config['REWARDS']:  # Check if it's a command or a reward
-        send_chat(f'{username}, \"{prefix}{reward}\" is not a chat command or a reward. Check your spelling?')
+        send_private_chat(user_id, f'{username}, \"{prefix}{reward}\" is not a chat command or a reward. Check your spelling?')
         return
     if not is_reward_active(reward):  # If reward isn't active, say so
         send_chat(f'Sorry, {username}. \"{prefix}{reward}\" is not currently an active reward.')
@@ -56,20 +56,20 @@ def do_reward(message, user_id):  # Parse the chat command
 
     if reward_type == 'goal':  # If it's a goal contribution, do the thing
         if not contribution:
-            send_chat(f'{username}, you didn\'t tell me how many points you want to contribute.')
+            send_private_chat(user_id, f'{username}, you didn\'t tell me how many points you want to contribute.')
             return
         if goal_reached(db, reward):
-            send_chat(f'{username}, we already completed this goal.')
+            send_private_chat(user_id, f'{username}, we already completed this goal.')
             return
         if int(contribution) > goal_left(db, reward):  # If they're contributing more than they need to
             current_app.logger.info(f'{username} contributed more than what was needed to reach the target.')
             contribution = goal_left(db, reward)  # only spend what is needed to reach the goal.
         if int(contribution) > points:
-            send_chat(f'{username}, you don\'t have that many points.')
+            send_private_chat(user_id, f'{username}, you don\'t have that many points.')
         elif int(contribution) < 0:
-            send_chat(f'{username}, you can\'t contribute negative points.')
+            send_private_chat(user_id, f'{username}, you can\'t contribute negative points.')
         elif int(contribution) == 0:
-            send_chat(f'{username}, you can\'t contribute zero points.')
+            send_private_chat(user_id, f'{username}, you can\'t contribute zero points.')
         elif add_to_goal(db, user_id, reward, int(contribution)):
             send_chat(f'{username} contributed {porps(contribution)} to the \"{prefix}{reward}\" goal.')
             wmr = was_milestone_reached(db, reward)
@@ -78,22 +78,22 @@ def do_reward(message, user_id):  # Parse the chat command
             if was_goal_reached(db, reward):
                 send_chat(f'\"{prefix}{reward}\" target reached! 🎉')
         else:
-            send_chat(f'Couldn\'t contribute to the \"{prefix}{reward}\" goal for {username}, for some highly technical reason.')
+            send_private_chat(user_id, f'Couldn\'t contribute to the \"{prefix}{reward}\" goal for {username}, for some highly technical reason.')
         return
 
     price = current_app.config['REWARDS'][reward]['price']  # Do they have enough points?
     if not points or points < price:
-        send_chat(f'{username}, you don\'t have enough points for \"{prefix}{reward}\".')
+        send_private_chat(user_id, f'{username}, you don\'t have enough points for \"{prefix}{reward}\".')
         return
 
     if reward_type == 'vote':  # If it's a vote, do the thing
         if check_vote(db, reward, user_id):  # See if viewer already voted
-            send_chat(f'{username}, you already voted for \"{prefix}{reward}\".')
+            send_private_chat(user_id, f'{username}, you already voted for \"{prefix}{reward}\".')
         else:
             if add_to_vote(db, reward, user_id) and spend_points(db, user_id, price):
                 send_chat(f'{username} voted for \"{prefix}{reward}\" for {porps(price)}.')
             else:
-                send_chat(f'Couldn\'t vote for \"{prefix}{reward}\" for {username}, for some highly technical reason.')
+                send_private_chat(user_id, f'Couldn\'t vote for \"{prefix}{reward}\" for {username}, for some highly technical reason.')
             
     elif reward_type == 'redeem':  # If it's a redeem, do the thing
         if is_cool(reward)[0]:  # Is there an active cool down?
@@ -101,10 +101,10 @@ def do_reward(message, user_id):  # Parse the chat command
                     spend_points(db, user_id, price)):
                 send_chat(f'{username} redeemed \"{prefix}{reward}\" for {porps(price)}.')
             else:
-                send_chat(f'Couldn\'t redeem \"{prefix}{reward}\"for {username}, for some highly technical reason.')
+                send_private_chat(user_id, f'Couldn\'t redeem \"{prefix}{reward}\"for {username}, for some highly technical reason.')
         else:
             hot_time = is_cool(reward)[1]
-            send_chat(f'Couldn\'t redeem \"{prefix}{reward}\"for {username}.<br>That reward has {mas(hot_time)} left to cool down.')
+            send_chat(f'\"{prefix}{reward}\" has {mas(hot_time)} left to cool down.')
             
     elif reward_type == 'special':  # If it's a special, do the thing
         if is_cool(reward)[0]:  # Is there an active cool down?
@@ -115,30 +115,33 @@ def do_reward(message, user_id):  # Parse the chat command
                 send_chat(f'{username} redeemed \'{prefix}{reward}\' for {porps(price)}.')
             else:
                 if refund_points(db, user_id, price):
-                    send_chat(f'Couldn\'t redeem \"{prefix}{reward}\"for {username}, for some highly technical reason.')
+                    send_private_chat(user_id, f'Couldn\'t redeem \"{prefix}{reward}\"for {username}, for some highly technical reason.')
         else:
             hot_time = is_cool(reward)[1]
-            send_chat(f'Couldn\'t redeem \"{prefix}{reward}\"for {username}.<br>That reward has {mas(hot_time)} left to cool down.')       
+            send_chat(f'\"{prefix}{reward}\" has {mas(hot_time)} left to cool down.')
             
     else:  # If we can't find the reward, say so
-        send_chat(f'\"{prefix}{reward}\", {username}? No such reward.')
+        send_private_chat(user_id, f'\"{prefix}{reward}\", {username}? No such reward.')
 
 
-def help_message():
+def help_message(user_id):
     prefix = current_app.config['PREFIX']
     kofi_settings = current_app.config['KOFI_SETTINGS']
     kofi_integration = current_app.config['KOFI_INTEGRATION']
-    message = f'You get {current_app.config["POINTS_AWARD"]} points for every {current_app.config["POINTS_INTERVAL"]} seconds you\'re in chat.<br> \
+    message = f'You get {current_app.config["POINTS_AWARD"]} points for every {current_app.config["POINTS_INTERVAL"]} minutes you\'re in chat.<br> \
             You can see your points, the rewards queue, and other helpful information by clicking on the \"Points Rewards\" button.<br><br> \
-            Chat commands:<br> \
-            {prefix}help to see this help message.<br> \
-            {prefix}points to see your points.<br> \
-            {prefix}rewards to see a list of currently active rewards.<br>'
-    if kofi_integration and kofi_settings['tips']:
+            <b><u>Chat commands:</u></b><br> \
+            <b>{prefix}help</b> to see this help message.<br> \
+            <b>{prefix}points</b> to see your points.<br> \
+            <b>{prefix}rewards</b> to see a list of currently active rewards.'
+    if kofi_integration:
+        message = f'{message}<br><br>\
+            Kofi is enabled! <br>\
+            Authenticated with Owncast, and enter your email address into the Stream Rewards Info page to get Kofi perks.'
+    if kofi_settings['tips']:
         message = f'{message}<br>\
-        Kofi is enabled! Once you\'ve authenticated with Owncast, you\'ll recieve\
-        {kofi_settings["tip_points"]} points for every dollar you tip on Kofi.'
-    send_chat(message)
+        You\'ll recieve {kofi_settings["tip_points"]} points for every dollar you tip on Kofi.'
+    send_private_chat(user_id, message)
 
 
 def save_announce(announce_dict):  # Write rewards to announce.py

+ 52 - 0
ownchatbot/owncast_com.py

@@ -1,6 +1,23 @@
 from flask import current_app
 import requests
 from ownchatbot.user_handlers import award_chat_points
+import random
+
+
+def get_client_id(user_id):
+    owncast_url = current_app.config['OWNCAST_URL']
+    access_token = current_app.config['ACCESS_TOKEN']
+    url = f'{owncast_url}/api/integrations/moderation/chat/user/{user_id}'
+    auth_bearer = f'Bearer {access_token}'
+    headers = {'Authorization': auth_bearer}
+    try:
+        response = requests.get(url, headers=headers)
+        response = response.json()
+        client_id = response['connectedClients'][0]['id']
+    except requests.exceptions.RequestException as gcierror:
+        current_app.logger.error(f'Couldn\'t get client id from Owncast: {gcierror.args[0]}')
+    current_app.logger.debug(f'Got client id {client_id}')
+    return client_id
 
 
 def live_now():  # Check if stream is live
@@ -51,3 +68,38 @@ def send_chat(message):  # Send message to owncast chat
         current_app.logger.error(f'Couldn\'t send {message} to Owncast: {response.status_code}.')
         return
     return response.json()
+
+
+def send_private_chat(user_id, message):
+    client_id = get_client_id(user_id)
+    owncast_url = current_app.config['OWNCAST_URL']
+    access_token = current_app.config['ACCESS_TOKEN']
+    url = f'{owncast_url}/api/integrations/chat/system/client/{client_id}'
+    auth_bearer = f'Bearer {access_token}'
+    headers = {'Authorization': auth_bearer}
+    try:
+        response = requests.post(url, headers=headers, json={'body': message})
+    except requests.exceptions.RequestException as swerror:
+        current_app.logger.error(f'Couldn\'t send {message} to Owncast: {swerror.args[0]}')
+        sys.exit()
+    if response.status_code != 200:
+        current_app.logger.error(f'Couldn\'t send {message} to Owncast: {response.status_code}.')
+        sys.exit()
+    response = response.json()
+
+
+def send_system_chat(message):
+    owncast_url = current_app.config['OWNCAST_URL']
+    access_token = current_app.config['ACCESS_TOKEN']
+    url = f'{owncast_url}/api/integrations/chat/system'
+    auth_bearer = f'Bearer {access_token}'
+    headers = {'Authorization': auth_bearer}
+    try:
+        response = requests.post(url, headers=headers, json={'body': message})
+    except requests.exceptions.RequestException as swerror:
+        current_app.logger.error(f'Couldn\'t send {message} to Owncast: {swerror.args[0]}')
+        sys.exit()
+    if response.status_code != 200:
+        current_app.logger.error(f'Couldn\'t send {message} to Owncast: {response.status_code}.')
+        sys.exit()
+    response = response.json()

+ 2 - 1
ownchatbot/schema.sql

@@ -8,7 +8,8 @@ CREATE TABLE IF NOT EXISTS points (
   name TEXT,
   points INTEGER,  
   user_authed BOOLEAN NOT NULL,
-  email TEXT
+  email TEXT,
+  code INTEGER
 );
 
 CREATE TABLE goals (

+ 2 - 2
ownchatbot/templates/mgmt.html

@@ -104,7 +104,7 @@
                         <td>
                         </td>
                         <td style="font-size: small;">
-                            (Mouse over flags for details)
+                            (Mouse over flags for milestone details)
                         </td>
                     </tr>
                 </tbody>
@@ -418,7 +418,7 @@
                     <tr>
                         <td> <label for="access_token">Access Token:</label> </td>
                         <td style="padding: 5px;"> <input type="text" name="access_token" value="{{ settings_info[5] }}" size="40"> </td>
-                        <td>Create in Owncast Admin panel. Integrations -> Access Tokens</td>
+                        <td>Create in Owncast Admin panel. Integrations -> Access Tokens (check all three boxes)</td>
                     </tr>
                     <tr>
                         <td> <label for="owncast_url">Your Owncast URL:</label> </td>

+ 19 - 3
ownchatbot/templates/userpanel.html

@@ -44,9 +44,25 @@
                         You are awarded {{ kofi_settings['tip_points'] }} {{ points_label }} for every dollar you tip on Kofi.<br>
                     {% endif %}
                     OwnchatBot recognizes your Kofi account by your email address. In order for OwnchatBot to award your tip points, you must enter the email address associated with your Kofi account here.<br><br>
+                    
+                    {% with messages = get_flashed_messages(with_categories=true) %}
+                        {% if messages %}
+                            {% for category, message in messages %}
+                                {% if category == 'failure' %}
+                                    <span style="color: red;">{{ message }}</span>
+                                {% endif %}
+                                {% if category == 'success' %}
+                                    <span style="color: green;">{{ message }}</span>
+                                {% endif %}
+                            {% endfor %}
+                        {% endif %}
+                    {% endwith %}
+                    
                     <form method="POST" action="/set_viewer_email">
-                        <label for="new_email">Your Kofi email address:</label>
-                        <input type="text" name="new_email" value="{{ user[4] }}" size="40">
+                        <label for="code">Type !reg_mail into the chat, and enter the code it gives you here:</label>
+                        <input type="number" name="code" size="6" required><br>                        
+                        <label for="new_email">Enter the email address associated with your Kofi account:</label>
+                        <input type="text" name="new_email" value="{{ user[4] }}" size="40" required>
                         <input type="hidden" name="instance" value="{{ instance }}">
                         <input type="hidden" name="user_name" value="{{ username }}">
                         <br>Email addresses are <b>ONLY</b> used for Kofi integration. They are not sent to any other individual or company, and will not be used to create or send mailing lists of any kind.<br>
@@ -162,7 +178,7 @@
                         <td></td>
                         <td></td>
                         <td style="font-size: small;">
-                            (Mouse over flags for details)
+                            (Mouse over flags for milestone details)
                         </td>
                     </tr>
                 </tbody>

+ 2 - 0
ownchatbot/update_db.sql

@@ -0,0 +1,2 @@
+ALTER TABLE points ADD COLUMN code INTEGER;
+

+ 37 - 0
ownchatbot/user_handlers.py

@@ -15,6 +15,17 @@ def get_users_points(db, user_id):  # Look up one user's points by user id
         current_app.logger.error(f'Couldn\'t look up points for {user_id}: {guperror.args[0]}')
 
 
+def get_email_code(db, user_id):  # Get user's verification code
+    try:
+        cursor = db.execute(
+            "SELECT code FROM points WHERE id = ?",
+            (user_id,)
+        )
+        return cursor.fetchone()[0]
+    except Error as gecerror:
+        current_app.logger.error(f'Couldn\'t look up points for {user_id}: {gecerror.args[0]}')
+
+
 def get_id_by_email(db, email):  # Look up all users' points by username
     try:
         cursor = db.execute(
@@ -75,6 +86,32 @@ def award_chat_points(db, user_id, points):  # Award points to user by user id
         return False
 
 
+def set_email_code(db, user_id, reg_code):  # Set verification code
+    try:
+        db.execute(
+            "UPDATE points SET code = ? WHERE id = ?",
+            (reg_code, user_id,)
+        )
+        db.commit()
+        return True
+    except Error as secerror:
+        current_app.logger.error(f'Couldn\'t set reg code \"{reg_code}\" for {user_id}: {secerror.args[0]}')
+        return False
+
+
+def del_email_code(db, user_id):  # Delete verification code
+    try:
+        db.execute(
+            "UPDATE points SET code = NULL WHERE id = ?",
+            (user_id,)
+        )
+        db.commit()
+        return True
+    except Error as decerror:
+        current_app.logger.error(f'Couldn\'t remove reg code for {user_id}: {decerror.args[0]}')
+        return False
+
+
 def adjust_points(db, user_id, points):  # For streamer to manually adjust a user's points
     try:
         db.execute(

+ 15 - 4
ownchatbot/web_panels.py

@@ -1,12 +1,14 @@
-from flask import flash, render_template, Blueprint, current_app, redirect, request, url_for, session
+from flask import flash, render_template, Blueprint, current_app, redirect, request, url_for, session, g
 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.user_handlers import get_all_users, get_all_users_by_name, refund_points, adjust_points, change_email
+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
 from ownchatbot.bot_messages import save_announce
+from ownchatbot.owncast_com import send_private_chat
 import json
 import emoji
 from ownchatbot.kofi_handlers import save_kofi_settings, kofi_pngs
+import random
 
 ocb = Blueprint('web_panels', __name__)
 
@@ -382,12 +384,21 @@ def add(reward_type):
 @ocb.route('/set_viewer_email', methods=['GET', 'POST'])
 def set_viewer_email():
     db = get_db()
+    mail_reg_code = int(request.form['code'])
     user_id = request.form['user_id']
+    db_mail_reg_code = get_email_code(db, user_id)
     new_email = request.form['new_email']
     instance = request.form['instance']
     user_name = request.form['user_name']
-    if change_email(db, user_id, new_email):
-        current_app.logger.info(f'Changed {user_id}\'s email to {new_email}')
+    if mail_reg_code == db_mail_reg_code:
+        if change_email(db, user_id, new_email):
+            del_email_code(db, user_id)
+            flash(f"Email Address \"{new_email}\" successfully registered.", "success")
+            send_private_chat(user_id, f'{user_name}, thanks for registering for Kofi perks! I appreciate your support!')
+            current_app.logger.info(f'Changed {user_id}\'s email to {new_email}')
+    else:
+        flash(f"Incorrect code. Email Address \"{new_email}\" was not registered.", "failure")
+        current_app.logger.info(f'The code entered, \"{mail_reg_code}\", does not match \"{db_mail_reg_code}\" found in database.')
     return redirect(url_for('web_panels.user_panel', instance=instance, username=user_name))
 
 

+ 20 - 7
ownchatbot/webhooks.py

@@ -1,11 +1,12 @@
-from flask import Flask, request, json, Blueprint, current_app, render_template, jsonify, request
+from flask import Flask, request, json, Blueprint, current_app, render_template, jsonify, request, g
 from ownchatbot.db import get_db
-from ownchatbot.owncast_com import send_chat
-from ownchatbot.user_handlers import add_user_to_points, change_name, get_users_points, remove_duplicates
+from ownchatbot.owncast_com import send_chat, send_private_chat
+from ownchatbot.user_handlers import add_user_to_points, change_name, get_users_points, remove_duplicates, get_email_code, set_email_code
 from ownchatbot.bot_messages import do_reward, help_message
 from ownchatbot.reward_handlers import all_active_goals, all_active_votes, all_active_rewards
 from ownchatbot.kofi_handlers import accept_donation
 import json
+import random
 
 
 ocb = Blueprint('webhooks', __name__)
@@ -93,14 +94,26 @@ def chat_hook():
         
         lowercase_msg = data['eventData']['rawBody'].lower()  # Convert body to lower case to match reward case
         if lowercase_msg.startswith(f'{prefix}help'):  # Send the help message
-            help_message()
+            help_message(user_id)
             
         elif lowercase_msg.startswith(f'{prefix}points'):  # Get the viewer's current points
             points = get_users_points(db, user_id)
             if points is None:
-                send_chat('Couldn\'t get your points, for some highly technical reason.')
+                send_private_chat(user_id, f'{display_name}, couldn\'t get your points, for some highly technical reason.')
             else:
-                send_chat(f'{display_name}, you have {points} points.')
+                send_private_chat(user_id, f'{display_name}, you have {points} points.')
+            
+        elif lowercase_msg.startswith(f'{prefix}reg_mail'):  # Generate a code to verify users account for email registration
+            if current_app.config['KOFI_INTEGRATION']:
+                mail_reg_code = get_email_code(db, user_id)
+                if mail_reg_code:  # If the viewer already has a code waiting
+                    send_private_chat(user_id, f'{display_name}, your code is {mail_reg_code}. Enter it into the form on the Stream Rewards Info page, with your email address, to enable Kofi perks!')
+                else:  # if not
+                    mail_reg_code = random.randint(100000, 999999)
+                    if set_email_code(db, user_id, mail_reg_code):
+                        send_private_chat(user_id, f'{display_name}, your code is {mail_reg_code}. Enter it into the form on the Stream Rewards Info page, with your email address, to enable Kofi perks!')
+            else:
+                send_chat(f'{display_name}, Kofi integration is not enabled on this stream.')                
                 
         elif lowercase_msg.startswith(f'{prefix}rewards'):  # Send rewards list
             if current_app.config['REWARDS']:
@@ -119,7 +132,7 @@ def chat_hook():
                         rewards_msg = f'{rewards_msg}'
             else:
                 rewards_msg = 'There are currently no active rewards.'
-            send_chat(rewards_msg)
+            send_private_chat(user_id, rewards_msg)
                     
         elif lowercase_msg.startswith(f'{prefix}'):  # Send to handle rewards
             do_reward(lowercase_msg, user_id)

+ 1 - 1
pyproject.toml

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "ownchatbot"
-version = "1.0.1"
+version = "1.0.2"
 authors = [
     {name = "DeadTOm", email = "deadtom@deadtom.me"},
 ]

+ 1 - 1
setup.py

@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
 
 setup(
     name='ownchatbot',
-    version='1.0.1',
+    version='1.0.2',
     packages=find_packages(),
     include_package_data=True,
     install_requires=[

+ 19 - 1
update.sh

@@ -1,4 +1,22 @@
 #!/bin/bash
 #
 
-echo "No updates to make this time around."
+set -e  #  Exit immediately if any command exits with a non-zero status
+
+activate_venv() {
+    source env/bin/activate
+}
+
+update_db() {  # Create the database. This also populates it with some goals and votes from the default rewards.py
+    export FLASK_APP=ownchatbot
+    if python -m flask update-db; then
+        echo "Database updated successfully."
+    else
+        echo "Failed to update the database. Please check for errors."
+        exit 1  # Exit the script with a non-zero status
+    fi
+}
+
+activate_venv
+update_db
+deactivate