浏览代码

Got basic Kofi membership functional. No tier support yet.

allens 2 周之前
父节点
当前提交
76e158e0a0

+ 12 - 10
ownchatbot/__init__.py

@@ -44,14 +44,11 @@ def create_app(test_config=None):
     db.init_app(app)
     db.init_app(app)
 
 
     def announce():
     def announce():
-        if app.config['ANNOUNCE_ENABLE']:  # If announcements are enabled
-            global current_index
-            announcements = app.config['ANNOUNCEMENTS']
-            message = announcements[current_index]
-            send_system_chat(message)
-            current_index = (current_index + 1) % len(announcements)
-        else:
-            app.logger.info(f'Not live, so not sending announcement.')
+        global current_index
+        announcements = app.config['ANNOUNCEMENTS']
+        message = announcements[current_index]
+        send_system_chat(message)
+        current_index = (current_index + 1) % len(announcements)
                 
                 
     def award_job():
     def award_job():
         with app.app_context():
         with app.app_context():
@@ -60,8 +57,13 @@ def create_app(test_config=None):
                 
                 
     def announce_job():
     def announce_job():
         with app.app_context():
         with app.app_context():
-            if live_now():  # If stream is live
-                announce()
+            if app.config['ANNOUNCE_ENABLE']:  # If announcements are enabled
+                if live_now():  # If stream is live
+                    announce()
+                else:
+                    app.logger.debug(f'Not live, so not sending announcement.')
+            else:
+                app.logger.debug(f'Announcements not enabled.')
 
 
     jorel_master_of_scheduling = BackgroundScheduler()
     jorel_master_of_scheduling = BackgroundScheduler()
     points_seconds = app.config['POINTS_INTERVAL'] * 60
     points_seconds = app.config['POINTS_INTERVAL'] * 60

+ 15 - 3
ownchatbot/bot_messages.py

@@ -136,11 +136,23 @@ def help_message(user_id):
             <b>{prefix}rewards</b> to see a list of currently active rewards.'
             <b>{prefix}rewards</b> to see a list of currently active rewards.'
     if kofi_integration:
     if kofi_integration:
         message = f'{message}<br><br>\
         message = f'{message}<br><br>\
-            Kofi is enabled! <br>\
+            <b><u>Kofi is enabled!</b></u><br>\
             Authenticate with Owncast, and enter your email address into the Stream Rewards Info page to get Kofi perks.'
             Authenticate with Owncast, and enter your email address into the Stream Rewards Info page to get Kofi perks.'
-        if kofi_settings['tips']:
+        if kofi_settings['donations']:
+            if kofi_settings["donation_points"] == 1:
+                d_points_label = 'point'
+            else:
+                d_points_label = 'points'
+            if kofi_settings["sub_points"] == 1:
+                s_points_label = 'point'
+            else:
+                s_points_label = 'points'
             message = f'{message}<br>\
             message = f'{message}<br>\
-            You\'ll recieve {kofi_settings["tip_points"]} points for every dollar you tip on Kofi.'
+            You\'ll recieve {kofi_settings["donation_points"]} {d_points_label} for every dollar you donate on Kofi'
+        if kofi_settings['subs']:
+            message = f'{message}, and {kofi_settings["sub_points"]} {s_points_label} every month for subscribing to my Kofi page.'
+        else:
+            message = f'{message}.'
     send_private_chat(user_id, message)
     send_private_chat(user_id, message)
 
 
 
 

+ 17 - 16
ownchatbot/db.py

@@ -5,6 +5,7 @@ from flask.cli import with_appcontext
 import click
 import click
 from time import time
 from time import time
 import os
 import os
+import logging
 
 
 
 
 def rem_from_queue(reward_name):  # Remove a reward from the queue
 def rem_from_queue(reward_name):  # Remove a reward from the queue
@@ -16,7 +17,7 @@ def rem_from_queue(reward_name):  # Remove a reward from the queue
         )
         )
         db.commit()
         db.commit()
     except sqlite3.Error as rfqerror:
     except sqlite3.Error as rfqerror:
-        print(f'Couldn\'t remove {reward_name} from reward queue: {rfqerror.args[0]}')
+        current_app.logger.error(f'Couldn\'t remove {reward_name} from reward queue: {rfqerror.args[0]}')
         return False
         return False
     return True
     return True
 
 
@@ -95,7 +96,7 @@ def clear_reward_queue():  # Completely clear the reward queue
         )
         )
         db.commit()
         db.commit()
     except sqlite3.Error as serror:
     except sqlite3.Error as serror:
-        print(f'Couldn\'t clear reward queue: {serror.args[0]}')
+        current_app.logger.error(f'Couldn\'t clear reward queue: {serror.args[0]}')
         return False
         return False
     return True
     return True
 
 
@@ -109,7 +110,7 @@ def clear_fulfilled_rewards():  # Clears only fulfilled rewards from the queue
         )
         )
         db.commit()
         db.commit()
     except sqlite3.Error as serror:
     except sqlite3.Error as serror:
-        print(f'Couldn\'t clear fulfilled rewards: {serror.args[0]}')
+        current_app.logger.error(f'Couldn\'t clear fulfilled rewards: {serror.args[0]}')
         return False
         return False
     return True
     return True
 
 
@@ -121,7 +122,7 @@ def rem_all_votes():  # USED TO BE "clear_votes" Clear all votes from the databa
         db.execute("DELETE FROM votes")
         db.execute("DELETE FROM votes")
         db.commit()
         db.commit()
     except sqlite3.Error as cverror:
     except sqlite3.Error as cverror:
-        print(f'Couldn\'t clear all votes: {cverror.args[0]}')
+        current_app.logger.error(f'Couldn\'t clear all votes: {cverror.args[0]}')
         return False
         return False
     if put_votes(db):
     if put_votes(db):
         return True
         return True
@@ -134,7 +135,7 @@ def rem_vote():  # Remove a single vote from the database
         db.execute("DELETE FROM votes WHERE name = ?", ('vote',))
         db.execute("DELETE FROM votes WHERE name = ?", ('vote',))
         db.commit()
         db.commit()
     except sqlite3.Error as rverror:
     except sqlite3.Error as rverror:
-        print(f'Couldn\'t remove \"{vote}\" from database: {rverror.args[0]}')
+        current_app.logger.error(f'Couldn\'t remove \"{vote}\" from database: {rverror.args[0]}')
         return False
         return False
     if put_votes(db):
     if put_votes(db):
         return True
         return True
@@ -152,7 +153,7 @@ def is_cool(reward_name):  # Check if a reward is cooling down.
                 )
                 )
             current_cds = cursor.fetchall()
             current_cds = cursor.fetchall()
         except sqlite3.Error as icerror:
         except sqlite3.Error as icerror:
-            print(f'Couldn\'t get \"{reward_name}\" from database: {icerror.args[0]}')
+            current_app.logger.error(f'Couldn\'t get \"{reward_name}\" from database: {icerror.args[0]}')
         if current_cds:
         if current_cds:
             last_time = current_cds[0][0]
             last_time = current_cds[0][0]
             hot_time = current_time - last_time
             hot_time = current_time - last_time
@@ -168,7 +169,7 @@ def is_cool(reward_name):  # Check if a reward is cooling down.
                     db.commit()
                     db.commit()
                     return True, 0
                     return True, 0
                 except sqlite3.Error as scerror:
                 except sqlite3.Error as scerror:
-                    print(f'Couldn\'t update \"{reward_name}\"\'s cooldown time in the database: {scerror.args[0]}')
+                    current_app.logger.error(f'Couldn\'t update \"{reward_name}\"\'s cooldown time in the database: {scerror.args[0]}')
         else:  # If it is not in the database, add it and return True
         else:  # If it is not in the database, add it and return True
             try:
             try:
                 db.execute(
                 db.execute(
@@ -178,7 +179,7 @@ def is_cool(reward_name):  # Check if a reward is cooling down.
                 db.commit()
                 db.commit()
                 return True, 0
                 return True, 0
             except sqlite3.Error as scerror:
             except sqlite3.Error as scerror:
-                print(f'Couldn\'t add \"{reward_name}\" to database: {scerror.args[0]}')
+                current_app.logger.error(f'Couldn\'t add \"{reward_name}\" to database: {scerror.args[0]}')
     else:  # If the redeem has no cooldown
     else:  # If the redeem has no cooldown
         return True, 0
         return True, 0
 
 
@@ -191,7 +192,7 @@ def rem_cool(reward_name):  # Remove a reward from the database
             )
             )
         current_cds = cursor.fetchall()
         current_cds = cursor.fetchall()
     except sqlite3.Error as icerror:
     except sqlite3.Error as icerror:
-        print(f'Couldn\'t remove \"{reward_name}\" from database: {icerror.args[0]}')
+        current_app.logger.error(f'Couldn\'t remove \"{reward_name}\" from database: {icerror.args[0]}')
         return False
         return False
     return True
     return True
 
 
@@ -207,7 +208,7 @@ def put_votes(db):  # Reread votes from rewards.py, and sync with database
                 )
                 )
                 db.commit()
                 db.commit()
             except sqlite3.Error as serror:
             except sqlite3.Error as serror:
-                print(f'Couldn\'t insert \"{vote}\" into database: {serror.args[0]}')
+                current_app.logger.error(f'Couldn\'t insert \"{vote}\" into database: {serror.args[0]}')
                 return False
                 return False
     return True
     return True
 
 
@@ -229,7 +230,7 @@ def reread_votes():  # Reread votes from rewards.py, and sync with database
             cursor.execute("DELETE FROM votes WHERE name = ?", (vote,))
             cursor.execute("DELETE FROM votes WHERE name = ?", (vote,))
         db.commit()
         db.commit()
     except sqlite3.Error as serror:
     except sqlite3.Error as serror:
-        print(f'Couldn\'t clear deleted votes from database: {serror.args[0]}')
+        current_app.logger.error(f'Couldn\'t clear deleted votes from database: {serror.args[0]}')
         return False
         return False
 
 
     try:  # Add new votes found in rewards.py
     try:  # Add new votes found in rewards.py
@@ -254,7 +255,7 @@ def reread_votes():  # Reread votes from rewards.py, and sync with database
                     )
                     )
                 db.commit()
                 db.commit()
     except sqlite3.Error as serror:
     except sqlite3.Error as serror:
-        print(f'Couldn\'t insert \"{vote}\" into database: {serror.args[0]}')
+        current_app.logger.error(f'Couldn\'t insert \"{vote}\" into database: {serror.args[0]}')
         return False
         return False
     return True
     return True
 
 
@@ -276,7 +277,7 @@ def reread_goals():  # Reread goals from rewards.py, and sync with database
             cursor.execute("DELETE FROM goals WHERE name = ?", (goal,))
             cursor.execute("DELETE FROM goals WHERE name = ?", (goal,))
         db.commit()
         db.commit()
     except sqlite3.Error as serror:
     except sqlite3.Error as serror:
-        print(f'Couldn\'t clear removed goals from database: {serror.args[0]}')
+        current_app.logger.error(f'Couldn\'t clear removed goals from database: {serror.args[0]}')
         return False
         return False
 
 
     try:  # Add new goals found in rewards.py
     try:  # Add new goals found in rewards.py
@@ -299,14 +300,14 @@ def reread_goals():  # Reread goals from rewards.py, and sync with database
                     )
                     )
         db.commit()
         db.commit()
     except sqlite3.Error as serror:
     except sqlite3.Error as serror:
-        print(f'Couldn\'t insert \"{reward}\" into database: {serror.args[0]}')
+        current_app.logger.error(f'Couldn\'t insert \"{reward}\" into database: {serror.args[0]}')
         return False
         return False
     return True
     return True
 
 
 
 
 def reset_goal(goal):  # Set goal progress back to zero
 def reset_goal(goal):  # Set goal progress back to zero
     if goal not in current_app.config['REWARDS']:  # If it doesn't exist in rewards.py
     if goal not in current_app.config['REWARDS']:  # If it doesn't exist in rewards.py
-        print(f'Couldn\'t reset goal, {goal} not in rewards file.')
+        current_app.logger.error(f'Couldn\'t reset goal, {goal} not in rewards file.')
         return False
         return False
     try:
     try:
         db = get_db()
         db = get_db()
@@ -326,7 +327,7 @@ def reset_goal(goal):  # Set goal progress back to zero
 
 
 def reset_vote(vote):
 def reset_vote(vote):
     if vote not in current_app.config['REWARDS']:  # Check if it exists in rewards.py
     if vote not in current_app.config['REWARDS']:  # Check if it exists in rewards.py
-        print(f'Couldn\'t reset vote, {vote} not in rewards file.')
+        current_app.logger.error(f'Couldn\'t reset vote, {vote} not in rewards file.')
         return False
         return False
     else:
     else:
         try:
         try:

+ 4 - 2
ownchatbot/defaults/kofi.py

@@ -1,6 +1,8 @@
 KOFI_SETTINGS = {
 KOFI_SETTINGS = {
-    "tips": True,  # Reward tips with points
-    "tip_points": 100,  # How many points per dollar tipped?
+    "donations": True,  # Reward donations with points
+    "donation_points": 100,  # How many points per dollar donated?
+    "subs": False,  # Reward subscriptions with points
+    "sub_points": 1000,  # How many points per month?
     "kofi_url": "https://",  # What is the URL of your Kofi page?
     "kofi_url": "https://",  # What is the URL of your Kofi page?
     "kofi_logo": "kofi_symbol.png"  # Which Kofi logo are we using in the viewer panel?
     "kofi_logo": "kofi_symbol.png"  # Which Kofi logo are we using in the viewer panel?
 }
 }

+ 50 - 5
ownchatbot/kofi_handlers.py

@@ -2,19 +2,20 @@ from flask import current_app
 from sqlite3 import Error
 from sqlite3 import Error
 from ownchatbot.db import get_db
 from ownchatbot.db import get_db
 from ownchatbot.user_handlers import get_id_by_email, award_chat_points, add_email_to_points, get_all_users_with_user_id
 from ownchatbot.user_handlers import get_id_by_email, award_chat_points, add_email_to_points, get_all_users_with_user_id
-from ownchatbot.owncast_com import send_chat
+from ownchatbot.owncast_com import send_chat, send_private_chat
+from ownchatbot.bot_messages import porps
 import json
 import json
 import os
 import os
 
 
 
 
-def accept_donation(donation_info, tip_points):
+def accept_donation(donation_info, donation_points):
     db = get_db()
     db = get_db()
     is_public = donation_info[0]
     is_public = donation_info[0]
     email = donation_info[2]
     email = donation_info[2]
     amount = donation_info[3]
     amount = donation_info[3]
     amount = int(float(amount))  # Convert from str to int
     amount = int(float(amount))  # Convert from str to int
     message = donation_info[4]
     message = donation_info[4]
-    points = amount * tip_points  # Multiply by streamers tip point award
+    points = amount * donation_points  # Multiply by streamers donation points award
     ids = get_id_by_email(db, email)
     ids = get_id_by_email(db, email)
     if not ids:  # If no id found with that email address
     if not ids:  # If no id found with that email address
         if add_email_to_points(db, email, points):  # Create empty account with email and points
         if add_email_to_points(db, email, points):  # Create empty account with email and points
@@ -25,9 +26,9 @@ def accept_donation(donation_info, tip_points):
             if award_chat_points(db, id[0], points):  # Grant points
             if award_chat_points(db, id[0], points):  # Grant points
                 for user in get_all_users_with_user_id(db, id[0]):
                 for user in get_all_users_with_user_id(db, id[0]):
                     name = user[1]
                     name = user[1]
-                current_app.logger.info(f'Granted user id {id[0]} {points} points for their ${amount} donation.')
+                current_app.logger.info(f'Granted user id {id[0]} {porps(points)} for their ${amount} donation.')
     if is_public:
     if is_public:
-        message = f'{name} got {points} points for tipping ${amount} on Kofi!'
+        message = f'{name} got {porps(points)} for donating ${amount} on Kofi!'
         current_app.logger.info(f'Public donation of ${amount} received from {name}')
         current_app.logger.info(f'Public donation of ${amount} received from {name}')
     else:
     else:
         message = None
         message = None
@@ -36,6 +37,50 @@ def accept_donation(donation_info, tip_points):
         send_chat(message)
         send_chat(message)
 
 
 
 
+def accept_sub(sub_info, sub_points):
+    db = get_db()
+    is_public = sub_info[0]
+    name = sub_info[1]
+    email = sub_info[2]
+    amount = sub_info[3]
+    amount = int(float(amount))  # Convert from str to int
+    message = sub_info[4]
+    first_sub = sub_info[5]
+    tier_name = sub_info[6]
+    points = sub_points
+    ids = get_id_by_email(db, email)
+    if not ids:  # If no id found with that email address
+        if add_email_to_points(db, email, points):  # Create empty account with email and points
+            name = 'Someone'
+            current_app.logger.info(f'No user with email \"{email}\" found in database, created empty account.')
+    else:  # Grant points to the corresponding id
+        for id in ids:
+            if award_chat_points(db, id[0], points):  # Grant points
+                for user in get_all_users_with_user_id(db, id[0]):
+                    name = user[1]  # Assign name from points table
+                current_app.logger.info(f'Awarded user id {id[0]} {porps(points)} for their subscription.')                
+    if is_public:
+        if not name:  # If no name in points table
+            name = 'Someone'
+        if first_sub:
+            message = f'{name} got {porps(points)} for their one month membership on Kofi!'
+            current_app.logger.info(f'Public subscription received from {name}')
+        else:
+            message = f'{name} got {porps(points)} for renewing their membership on Kofi!'
+            current_app.logger.info(f'Public subscription renewal received from {name}')
+        send_chat(message)  # Send message publicly if a public membership
+    else:
+        if not name:  # If no name in points table
+            name = sub_info[1]  # Assign name from Kofi response
+        if first_sub:
+            message = f'Thanks so much for your subscribing to my Kofi! You\'ve been awarded {porps(points)}!'
+            current_app.logger.info(f'Private subscription received from {name}')
+        else:
+            message = f'Thanks so much for renewing your membership to my Kofi! You\'ve been awarded {porps(points)}!'
+            current_app.logger.info(f'Private subscription renewal received from {name}')
+        send_private_chat(id[0], message)
+            
+
 def save_kofi_settings(ksettings_info):  # Write rewards to kofi.py
 def save_kofi_settings(ksettings_info):  # Write rewards to kofi.py
     settings_file = os.path.join(current_app.instance_path, 'kofi.py')
     settings_file = os.path.join(current_app.instance_path, 'kofi.py')
     try:
     try:

+ 26 - 19
ownchatbot/owncast_com.py

@@ -13,11 +13,16 @@ def get_client_id(user_id):
     try:
     try:
         response = requests.get(url, headers=headers)
         response = requests.get(url, headers=headers)
         response = response.json()
         response = response.json()
-        client_id = response['connectedClients'][0]['id']
+        if response["connectedClients"]:  # Make sure this is an actual, connected client
+            client_id = response['connectedClients'][0]['id']
+            current_app.logger.debug(f'Got client id {client_id}')
+            return client_id
+        else:
+            current_app.logger.info('Not a connected client. Can\'t get client ID.')
+            return False
     except requests.exceptions.RequestException as gcierror:
     except requests.exceptions.RequestException as gcierror:
         current_app.logger.error(f'Couldn\'t get client id from Owncast: {gcierror.args[0]}')
         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
+        return False
 
 
 
 
 def live_now():  # Check if stream is live
 def live_now():  # Check if stream is live
@@ -62,30 +67,32 @@ def send_chat(message):  # Send message to owncast chat
     try:
     try:
         response = requests.post(url, headers=headers, json={'body': message})
         response = requests.post(url, headers=headers, json={'body': message})
     except requests.exceptions.RequestException as scerror:
     except requests.exceptions.RequestException as scerror:
-        current_app.logger.error(f'Couldn\'t send {message} to Owncast: {scerror.args[0]}')
+        current_app.logger.error(f'Couldn\'t send \"{message}\" to Owncast: {scerror.args[0]}')
         return
         return
     if response.status_code != 200:
     if response.status_code != 200:
-        current_app.logger.error(f'Couldn\'t send {message} to Owncast: {response.status_code}.')
+        current_app.logger.error(f'Couldn\'t send \"{message}\" to Owncast: {response.status_code}.')
         return
         return
     return response.json()
     return response.json()
 
 
 
 
 def send_private_chat(user_id, message):
 def send_private_chat(user_id, message):
     client_id = get_client_id(user_id)
     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()
+    if client_id or client_id == 0:  # If no client ID is found, don't try to send
+        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]}')
+        except Exception as swerror:
+            current_app.logger.error(f'Couldn\'t send \"{message}\" to Owncast: {swerror.args[0]}')
+        if response.status_code != 200:
+            current_app.logger.error(f'Couldn\'t send \"{message}\" to Owncast: {response.status_code}.')
+    else:
+        current_app.logger.info(f'Couldn\'t send \"{message}\" to Owncast. Not a connected client.')
 
 
 
 
 def send_system_chat(message):
 def send_system_chat(message):

+ 33 - 9
ownchatbot/templates/mgmt.html

@@ -463,6 +463,8 @@
             <form method="POST" action="/mgmt/ksettings">
             <form method="POST" action="/mgmt/ksettings">
                 <table>
                 <table>
                 <h3>Kofi Settings</h3>
                 <h3>Kofi Settings</h3>
+                
+                <h4> Donations </h4>
                     <thead>
                     <thead>
                         <tr style="border-bottom: none;">
                         <tr style="border-bottom: none;">
                             <th style="width: 20%;"></th>
                             <th style="width: 20%;"></th>
@@ -471,18 +473,18 @@
                         </tr>
                         </tr>
                     </thead>
                     </thead>
                     <tr>
                     <tr>
-                        <td> <label for="enable_tips">Enable points for tips:</label> </td>
-                        {% if kofi_settings['tips'] %}
-                        <td> <input type="checkbox" name="enable_tips" value="{{ kofi_settings['tips'] }}" checked> </td>
+                        <td> <label for="enable_donations">Enable points for donations:</label> </td>
+                        {% if kofi_settings['donations'] %}
+                        <td> <input type="checkbox" name="enable_donations" value="{{ kofi_settings['donations'] }}" checked> </td>
                         {% else %}
                         {% else %}
-                        <td> <input type="checkbox" name="enable_tips" value="{{ kofi_settings['tips'] }}"> </td>
+                        <td> <input type="checkbox" name="enable_donations" value="{{ kofi_settings['donations'] }}"> </td>
                         {% endif %}
                         {% endif %}
-                        <td>Enable awarding points for tips</td>
+                        <td>Enable awarding points for donations</td>
                     </tr>
                     </tr>
                     <tr>
                     <tr>
-                        <td> <label for="set_tip_points">Points per dollar:</label> </td>
-                        <td> <input type="number" name="set_tip_points" value="{{ kofi_settings['tip_points'] }}" size="5" required> points</td>
-                        <td>How many points should viewers recieve, for every dollar they tip?</td>
+                        <td> <label for="set_donation_points">Points per dollar:</label> </td>
+                        <td> <input type="number" name="set_donation_points" value="{{ kofi_settings['donation_points'] }}" size="5" required> points</td>
+                        <td>How many points should viewers recieve, for every dollar they donate?</td>
                     </tr>
                     </tr>
                     <tr>
                     <tr>
                         <td> <label for="kofi_url">Kofi Page:</label> </td>
                         <td> <label for="kofi_url">Kofi Page:</label> </td>
@@ -491,6 +493,28 @@
                     </tr>
                     </tr>
                     <tr>
                     <tr>
                         <table>
                         <table>
+                        <h4> Subscriptions </h4>
+                            <tbody>
+                                <tr>
+                                    <td> <label for="enable_subs">Enable points for subscriptions:</label> </td>
+                                    {% if kofi_settings['subs'] %}
+                                    <td> <input type="checkbox" name="enable_subs" value="{{ kofi_settings['subs'] }}" checked> </td>
+                                    {% else %}
+                                    <td> <input type="checkbox" name="enable_subs" value="{{ kofi_settings['subs'] }}"> </td>
+                                    {% endif %}
+                                    <td>Enable awarding points for monthly subscriptions</td>
+                                </tr>
+                                <tr>
+                                    <td> <label for="sub_points">Points per month:</label> </td>
+                                    <td> <input type="number" name="sub_points" value="{{ kofi_settings['sub_points'] }}" size="6" required> points</td>
+                                    <td>How many points should subscribers recieve every month?</td>
+                                </tr>
+                                        </tbody>
+                                    </table>
+                                </tr>
+                                <tr>
+                        <table>
+                        <h4> Logo </h4>
                             <tbody>
                             <tbody>
                                 <tr style="border-bottom: none;">
                                 <tr style="border-bottom: none;">
                                 Which logo would you like to use?
                                 Which logo would you like to use?
@@ -518,7 +542,7 @@
                 <br><button class="button button2" type="submit">Save Changes</button><br>
                 <br><button class="button button2" type="submit">Save Changes</button><br>
             </form>
             </form>
             <br><br>
             <br><br>
-	    <i>Kofi subscription support coming soon.</i>
+	    <i>Kofi subscription tier support coming soon.</i>
         </body>
         </body>
     </div>
     </div>
     
     

+ 17 - 9
ownchatbot/templates/userpanel.html

@@ -35,16 +35,23 @@
         {% if kofi_integration %}
         {% if kofi_integration %}
             <h4>Kofi Integration</h4>
             <h4>Kofi Integration</h4>
             You must be authenticated with Owncast to take advantage of Kofi integration.<br>
             You must be authenticated with Owncast to take advantage of Kofi integration.<br>
-            {% if kofi_settings['tips'] %}
-                {% set points_label = 'point' if tip_points == 1 else 'points' %}
-                You are awarded {{ kofi_settings['tip_points'] }} {{ points_label }} for every dollar you tip on Kofi.<br>
+            
+            {% if kofi_settings['donations'] %}
+                {% set d_points_label = 'point' if donation_points == 1 else 'points' %}
+                You are awarded {{ kofi_settings['donation_points'] }} {{ d_points_label }} for every dollar you donate on Kofi.<br>
+            {% endif %}
+            
+            {% if kofi_settings['subs'] %}
+                {% set s_points_label = 'point' if sub_points == 1 else 'points' %}
+                Subscribers get {{ kofi_settings['sub_points'] }} {{ s_points_label }} every month.<br>
             {% endif %}
             {% endif %}
+            
             {% for user in users %}
             {% for user in users %}
             
             
             <div>
             <div>
                 {% if user['user_authed'] %}
                 {% if user['user_authed'] %}
-                    <a href="{{ kofi_settings['kofi_url'] }}/tip" target="new"><img src="/static/img/kofi/{{ kofi_settings['kofi_logo'] }}"></a><br>
-                    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>
+                    <a href="{{ kofi_settings['kofi_url'] }}/donate" target="new"><img src="/static/img/kofi/{{ kofi_settings['kofi_logo'] }}"></a><br>
+                    OwnchatBot recognizes your Kofi account by your email address. In order for OwnchatBot to award your donation points, you must enter the email address associated with your Kofi account here.<br><br>
                     
                     
                     {% with messages = get_flashed_messages(with_categories=true) %}
                     {% with messages = get_flashed_messages(with_categories=true) %}
                         {% if messages %}
                         {% if messages %}
@@ -79,7 +86,7 @@
         <h4>Other stuff</h4>
         <h4>Other stuff</h4>
         <div>
         <div>
             OwnchatBot can be downloaded from <a href=https://git.deadtom.me/deadtom/OwnchatBot>https://git.deadtom.me/deadtom/OwnchatBot</a>.<br>
             OwnchatBot can be downloaded from <a href=https://git.deadtom.me/deadtom/OwnchatBot>https://git.deadtom.me/deadtom/OwnchatBot</a>.<br>
-            If you are thrilled to death with OwnchatBot, and want to throw a little monetary love his way, <a href=https://ko-fi.com/deadtom>he's on Kofi</a>.<br>
+            If you are thrilled to death with OwnchatBot, and want to throw a little monetary love the developer's way, <a href=https://ko-fi.com/deadtom>he's on Kofi</a>.<br>
             OwnchatBot © 2025 by <a href=https://www.deadtom.me>DeadTOm</a> is licensed under <a href=https://creativecommons.org/licenses/by-sa/4.0/>Creative Commons Attribution-ShareAlike 4.0 International</a>.
             OwnchatBot © 2025 by <a href=https://www.deadtom.me>DeadTOm</a> is licensed under <a href=https://creativecommons.org/licenses/by-sa/4.0/>Creative Commons Attribution-ShareAlike 4.0 International</a>.
         </div>
         </div>
 	
 	
@@ -97,9 +104,10 @@
                 &nbsp;&nbsp;{{ user[1] }}, you currently have {{ user[2] }} {{ points_label }}.<br>
                 &nbsp;&nbsp;{{ user[1] }}, you currently have {{ user[2] }} {{ points_label }}.<br>
                 &nbsp;&nbsp;You are accruing {{ points_award }} {{ points_label }} every {{ points_interval }} {{ minutes_label }}.
                 &nbsp;&nbsp;You are accruing {{ points_award }} {{ points_label }} every {{ points_interval }} {{ minutes_label }}.
                 {% if kofi_integration %}
                 {% if kofi_integration %}
-                    {% if kofi_settings['tips'] %}
-                        {% set points_label = 'point' if tip_points == 1 else 'points' %}
-                        <br>&nbsp;&nbsp;You can also get {{ kofi_settings['tip_points'] }} {{ points_label }} for every dollar you tip me on Kofi. 🤑
+                    {% if kofi_settings['donations'] %}
+                        {% set d_points_label = 'point' if donation_points == 1 else 'points' %}
+                        {% set s_points_label = 'point' if sub_points == 1 else 'points' %}
+                        <br>&nbsp;&nbsp;You can also get {{ kofi_settings['donation_points'] }} {{ d_points_label }} for every dollar you donate on Kofi{% if kofi_settings['subs'] %}, and {{ kofi_settings['sub_points'] }} {{ s_points_label }} every month for subscribing to my Kofi page{% endif %}. 🤑
                     {% endif %}
                     {% endif %}
                 {% endif %}
                 {% endif %}
             {% endfor %}
             {% endfor %}

+ 7 - 4
ownchatbot/web_panels.py

@@ -277,13 +277,16 @@ def announcements():
 def ksettings():
 def ksettings():
     kofi_settings_dict = current_app.config['KOFI_SETTINGS']
     kofi_settings_dict = current_app.config['KOFI_SETTINGS']
     if request.method == 'POST':
     if request.method == 'POST':
-        enable_tips = 'enable_tips' in request.form
-        set_tip_points = request.form['set_tip_points']
+        enable_donations = 'enable_donations' in request.form
+        set_donation_points = request.form['set_donation_points']
+        enable_subs = 'enable_subs' in request.form
+        sub_points = int(request.form['sub_points'])
         kofi_url = request.form['kofi_url']
         kofi_url = request.form['kofi_url']
         kofi_logo = request.form.get('kofi_logo')
         kofi_logo = request.form.get('kofi_logo')
         
         
-        kofi_settings_dict['tips'] = enable_tips
-        kofi_settings_dict['tip_points'] = int(set_tip_points)
+        kofi_settings_dict['donations'] = enable_donations
+        kofi_settings_dict['subs'] = enable_subs
+        kofi_settings_dict['sub_points'] = sub_points
         kofi_settings_dict['kofi_url'] = kofi_url
         kofi_settings_dict['kofi_url'] = kofi_url
         kofi_settings_dict['kofi_logo'] = kofi_logo
         kofi_settings_dict['kofi_logo'] = kofi_logo
         if save_kofi_settings(kofi_settings_dict):
         if save_kofi_settings(kofi_settings_dict):

+ 21 - 16
ownchatbot/webhooks.py

@@ -4,7 +4,7 @@ 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.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.bot_messages import do_reward, help_message
 from ownchatbot.reward_handlers import all_active_goals, all_active_votes, all_active_rewards
 from ownchatbot.reward_handlers import all_active_goals, all_active_votes, all_active_rewards
-from ownchatbot.kofi_handlers import accept_donation
+from ownchatbot.kofi_handlers import accept_donation, accept_sub
 import json
 import json
 import random
 import random
 
 
@@ -29,34 +29,39 @@ def kofiHook():
             if is_authed == current_app.config['KOFI_TOKEN']:
             if is_authed == current_app.config['KOFI_TOKEN']:
                 type = raw_data['type']
                 type = raw_data['type']
                 is_public = raw_data['is_public']
                 is_public = raw_data['is_public']
-                if is_public:
-                    from_name = raw_data['from_name']
                 new_sub = raw_data['is_first_subscription_payment']
                 new_sub = raw_data['is_first_subscription_payment']
                 message = raw_data['message']
                 message = raw_data['message']
                 shop_items = raw_data['shop_items']
                 shop_items = raw_data['shop_items']
-                name = raw_data['from_name']
+                from_name = raw_data['from_name']
                 email = raw_data['email']
                 email = raw_data['email']
                 amount = raw_data['amount']
                 amount = raw_data['amount']
                 sub_payment = raw_data['is_subscription_payment']
                 sub_payment = raw_data['is_subscription_payment']
                 first_sub = raw_data['is_first_subscription_payment']
                 first_sub = raw_data['is_first_subscription_payment']
                 tier_name = raw_data['tier_name']
                 tier_name = raw_data['tier_name']
                 if type == 'Shop Order':
                 if type == 'Shop Order':
-                    current_app.logger.info(f'\n{name} purchased {format(shop_items)}\nMessage: {message}\n')
+                    current_app.logger.info(f'{from_name} purchased {format(shop_items)}\nMessage: {message}\n')
                 if type == 'Donation':
                 if type == 'Donation':
-                    donation_info = [is_public, name, email, amount, message]
-                    tip_points = current_app.config['KOFI_SETTINGS']['tip_points']
-                    accept_donation(donation_info, tip_points)
+                    donation_info = [is_public, from_name, email, amount, message]
+                    donation_points = current_app.config['KOFI_SETTINGS']['donation_points']
+                    accept_donation(donation_info, donation_points)
                 if type == 'Subscription':
                 if type == 'Subscription':
-                    if first_sub:
-                        if tier_name:
-                            current_app.logger.info(f'\n{name} <{email}> subscribed as a {tier_name} tier member.')
+                    if current_app.config['KOFI_SETTINGS']['subs']:  # Check that subscriptions are enabled
+                        if first_sub:
+                            if tier_name:
+                                current_app.logger.info(f'{from_name} <{email}> subscribed as a {tier_name} tier member.')
+                            else:
+                                current_app.logger.info(f'{from_name} <{email}> subscribed.')
                         else:
                         else:
-                            current_app.logger.info(f'\n{name} <{email}> subscribed.')
+                            if tier_name:
+                                current_app.logger.info(f'{from_name} <{email}> renewed their {tier_name} tier membership.')
+                            else:
+                                current_app.logger.info(f'{from_name} <{email}> renewed their membership.')
+                        
+                        sub_info = [is_public, from_name, email, amount, message, first_sub, tier_name]
+                        sub_points = current_app.config['KOFI_SETTINGS']['sub_points']
+                        accept_sub(sub_info, sub_points)
                     else:
                     else:
-                        if tier_name:
-                            current_app.logger.info(f'\n{name} <{email}> renewed their {tier_name} tier membership.')
-                        else:
-                            current_app.logger.info(f'\n{name} <{email}> renewed their membership.')
+                        current_app.logger.info(f'Kofi membership received, but subscriptions are not enabled. Doing nothing.')
                         
                         
                 return jsonify({'status': 'success'}), 200
                 return jsonify({'status': 'success'}), 200
             else:
             else: