Pārlūkot izejas kodu

Basic GiveButter integration is functional. Still needs gui settings tab.

deadtom 1 mēnesi atpakaļ
vecāks
revīzija
12d0876c6e

+ 2 - 0
ownchatbot/__init__.py

@@ -35,6 +35,8 @@ def create_app(test_config=None):
     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.from_object('ownchatbot.defaults.givebutter')
+    app.config.from_pyfile('givebutter.py', silent=True)
     app.config['ASSETS_FOLDER'] = os.path.join(app.instance_path, 'assets')
 
     gunicorn_logger = logging.getLogger('gunicorn.error')

+ 2 - 0
ownchatbot/defaults/config.py

@@ -11,3 +11,5 @@ SECRET_KEY = ''  # Needed for internal flask stuff. Generated during setup. DO N
 PREFIX = '!'  # Preceeds commands, so OwnchatBot knows what is a command
 KOFI_TOKEN = ''  # Needed to validate Ko-fi with OCB webhook. Get from Ko-fi Settings -> More -> API -> Webhooks -> Advanced - Verification Token.
 KOFI_INTEGRATION = False  # Integrate OwnchatBot with Ko-fi
+GB_SECRET = 'breadandbutter'  # GiveButter verification signature
+GB_INTEGRATION = True  # Integrate OwnchatBot with GiveBUtter

+ 8 - 6
ownchatbot/donation_handlers.py

@@ -8,7 +8,7 @@ import json
 import os
 
 
-def accept_donation(donation_info, donation_points):
+def accept_donation(donation_info, donation_points, donation_service):
     try:
         db = get_db()
         is_public = donation_info[0]
@@ -27,13 +27,15 @@ def accept_donation(donation_info, donation_points):
                 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]
+                        if not name:
+                            name = 'Someone (not yet registered with OCB)'
                     current_app.logger.info(f'Granted user id {id[0]} {porps(points)} for their ${amount} donation.')
         if is_public:
-            message = f'{name} got {porps(points)} for donating ${amount} on Kofi!'
-            current_app.logger.info(f'Public donation of ${amount} received from {name}')
+            message = f'{name} got {porps(points)} for donating ${amount} on {donation_service}!'
+            current_app.logger.info(f'Public donation of ${amount} received from {name} via {donation_service}')
         else:
             message = None
-            current_app.logger.info(f'Private donation of ${amount} received from {name}')
+            current_app.logger.info(f'Private donation of ${amount} received from {name} via {donation_service}')
         if message is not None:  # Only send chat message if it's a public donation
             send_chat(message)
         return True
@@ -41,7 +43,7 @@ def accept_donation(donation_info, donation_points):
         current_app.logger.error(f'General Exception: {aderror}')
 
 
-def accept_sub(sub_info, sub_points):
+def accept_kofi_sub(sub_info, sub_points):
     try:
         db = get_db()
         is_public = sub_info[0]
@@ -102,7 +104,7 @@ def save_kofi_settings(ksettings_info):  # Write rewards to kofi.py
     except Exception as sks_error:
         current_app.logger.error(f'Couldn\'t save kofi.py: {sks_error.args[0]}')
         return False
-    
+
     return True
 
 

+ 1 - 1
ownchatbot/web_panels.py

@@ -7,7 +7,7 @@ from ownchatbot.bot_messages import save_announce, porps
 from ownchatbot.owncast_com import send_private_chat, send_chat
 import json
 import emoji
-from ownchatbot.kofi_handlers import save_kofi_settings, kofi_pngs
+from ownchatbot.donation_handlers import save_kofi_settings, kofi_pngs
 import random
 import pkce
 import requests

+ 95 - 51
ownchatbot/webhooks.py

@@ -4,9 +4,11 @@ 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, award_chat_points, user_in_points, get_all_users_with_user_id
 from ownchatbot.bot_messages import do_reward, help_message, porps
 from ownchatbot.reward_handlers import all_active_goals, all_active_votes, all_active_rewards, save_alerts
-from ownchatbot.kofi_handlers import accept_donation, accept_sub
+from ownchatbot.donation_handlers import accept_donation, accept_kofi_sub
 import json
 import random
+import hmac
+import hashlib
 
 
 ocb = Blueprint('webhooks', __name__)
@@ -111,64 +113,106 @@ def kofi_hook():
     current_app.logger.info(f'Kofi request')
     if request.content_type == 'application/x-www-form-urlencoded':
         raw_data = request.form.get('data')  # Get the kofi data
-        if raw_data:
-            raw_data = json.loads(raw_data)
-            is_authed = raw_data['verification_token']
-            if is_authed == current_app.config['KOFI_TOKEN']:
-                type = raw_data['type']
-                is_public = raw_data['is_public']
-                new_sub = raw_data['is_first_subscription_payment']
-                message = raw_data['message']
-                shop_items = raw_data['shop_items']
-                from_name = raw_data['from_name']
-                email = raw_data['email']
-                amount = raw_data['amount']
-                sub_payment = raw_data['is_subscription_payment']
-                first_sub = raw_data['is_first_subscription_payment']
-                tier_name = raw_data['tier_name']
-                if type == 'Shop Order':
-                    current_app.logger.info(f'{from_name} purchased {format(shop_items)}\nMessage: {message}\n')
-                if type == 'Donation':
-                    donation_info = [is_public, from_name, email, amount, message]
-                    donation_points = current_app.config['KOFI_SETTINGS']['donation_points']
-                    if accept_donation(donation_info, donation_points):
-                        if is_public:
-                            alert_info = {'name': from_name, 'amount': amount}
-                        else:
-                            alert_info = {'name': 'Anonymous Hero', 'amount': amount}
-                if type == 'Subscription':
-                    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:
-                            if tier_name:
-                                current_app.logger.info(f'{from_name} <{email}> renewed their {tier_name} tier membership.')
+        if current_app.config['KOFI_INTEGRATION']:
+            if raw_data:
+                raw_data = json.loads(raw_data)
+                is_authed = raw_data['verification_token']
+                if is_authed == current_app.config['KOFI_TOKEN']:
+                    type = raw_data['type']
+                    is_public = raw_data['is_public']
+                    new_sub = raw_data['is_first_subscription_payment']
+                    message = raw_data['message']
+                    shop_items = raw_data['shop_items']
+                    from_name = raw_data['from_name']
+                    email = raw_data['email']
+                    amount = raw_data['amount']
+                    sub_payment = raw_data['is_subscription_payment']
+                    first_sub = raw_data['is_first_subscription_payment']
+                    tier_name = raw_data['tier_name']
+                    if type == 'Shop Order':
+                        current_app.logger.info(f'{from_name} purchased {format(shop_items)}\nMessage: {message}\n')
+                    if type == 'Donation':
+                        donation_info = [is_public, from_name, email, amount, message]
+                        donation_points = current_app.config['KOFI_SETTINGS']['donation_points']
+                        if accept_donation(donation_info, donation_points, 'Kofi'):
+                            current_app.logger.info('Donation processed.')
+                    if type == 'Subscription':
+                        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:
-                                current_app.logger.info(f'{from_name} <{email}> renewed their membership.')
+                                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']
-                        if accept_sub(sub_info, sub_points):
-                            if is_public:
-                                alert_info = {'name': from_name, 'tiername': tier_name}
-                            else:
-                                alert_info = {'name': 'Anonymous Hero', 'teirname': tier_name}
-                    else:
-                        current_app.logger.info(f'Kofi membership received, but subscriptions are not enabled. Doing nothing.')
-                return jsonify({'status': 'success'}), 200
+                            sub_info = [is_public, from_name, email, amount, message, first_sub, tier_name]
+                            sub_points = current_app.config['KOFI_SETTINGS']['sub_points']
+                            if accept_kofi_sub(sub_info, sub_points):
+                                current_app.logger.info('Subscription processed.')
+                        else:
+                            current_app.logger.info(f'Kofi membership received, but subscriptions are not enabled. Doing nothing.')
+                    return jsonify({'status': 'success'}), 200
+                else:
+                    current_app.logger.info(f'Token invalid. Rejecting.')
+                    return jsonify({'status': 'unauthorized'}), 401
             else:
-                current_app.logger.info(f'Token invalid. Rejecting.')
-                return jsonify({'status': 'unauthorized'}), 401
+                return jsonify({'status': 'Failed. No data'}), 400
+            return jsonify({'status': 'success'}), 200
         else:
-            return jsonify({'status': 'Failed. No data'}), 400
-        return jsonify({'status': 'success'}), 200
+            current_app.logger.error(f'Kofi donation recieved, but Kofi integration is turned off. Rejected.')
+            return jsonify({'status': 'Failed. Not accepting Kofi donations.'}), 400
     else:
         return jsonify({'status': 'Failed. Invalid content type'}), 400
 
 
+def verify_gbhook_signature(payload, signature, secret):
+    expected_signature = hmac.new(
+        secret.encode(),
+        payload.encode(),
+        hashlib.sha256
+    ).hexdigest()
+
+
+@ocb.route('/gbHook', methods=['POST'])
+def gb_hook():
+    current_app.logger.info(f'----------------------------------------------------------------------------')
+    current_app.logger.info(f'GiveButter request')
+    signature = request.headers.get('X-Givebutter-Signature')
+    gb_secret = current_app.config['GB_SECRET']
+
+    raw_data = request.get_data(as_text=True)
+    event = request.json
+
+    # if not verify_gbhook_signature(raw_data, signature, gb_secret):  # TESTING
+    #     return jsonify({'error': 'Invalid signature'}), 401
+    if current_app.config['GB_INTEGRATION']:
+        try:
+            event_type = event['event']
+
+            if event_type == 'transaction.succeeded':
+                transaction = event['data']
+                from_name = f'{transaction["first_name"]} {transaction["last_name"]}'
+                email = transaction['email']
+                amount = transaction['amount']
+                donation_info = [True, from_name, email, amount, '']
+                donation_points = current_app.config['GB_SETTINGS']['donation_points']
+                if accept_donation(donation_info, donation_points, 'GiveButter'):
+                    current_app.logger.info(f'Donation processed.')
+            else:
+                current_app.logger.info(f'Unhandled event type: {event_type}')
+        except Exception as pgberror:
+            current_app.logger.error(f'General exception processing gbhook: {pgberror}')
+    else:
+        current_app.logger.error(f'GiveButter donation recieved, but GiveButter integration is turned off. Rejected.')
+        return jsonify({'status': 'Failed. Not accepting GiveButter donations.'}), 400
+
+    return jsonify({'received': True}), 200
+
+
 @ocb.route('/checkFollows')  # Polled by follower.html template to check for new followers
 def check_follows():
     alerts_dict = current_app.config['ALERTS']