from flask import Flask, request, json, Blueprint, current_app, render_template, jsonify, request, g
from ownchatbot.db import get_db, clear_fulfilled_rewards
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
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
import json
import random
ocb = Blueprint('webhooks', __name__)
def format(rawjson): # Make data legible
formatted_data = json.dumps(rawjson, indent=4)
return formatted_data
@ocb.route('/ocbHook', methods=['POST'])
def chat_hook():
prefix = current_app.config['PREFIX']
data = request.json
db = get_db()
if data['type'] in ['CHAT', 'NAME_CHANGED', 'USER_JOINED']: # Check if the viewer is in the chatbot database
user_id = data['eventData']['user']['id']
authed = data['eventData']['user']['authenticated']
display_name = data['eventData']['user']['displayName']
if add_user_to_points(db, user_id, display_name, authed):
current_app.logger.debug(f'Added/updated {user_id} database.')
current_app.logger.debug(f'{display_name}/{user_id}: {data["eventData"]}') # Log all chat messages
if data['type'] == 'STREAM_STARTED':
current_app.logger.info('Starting a new stream.')
if clear_fulfilled_rewards():
current_app.logger.info('Cleared fulfilled rewards.')
if data['type'] == 'USER_JOINED': # Do username house cleaning when a viewer joins
if data['eventData']['user']['authenticated']:
remove_duplicates(db, user_id, display_name)
elif data['type'] == 'FEDIVERSE_ENGAGEMENT_FOLLOW':
alerts_dict = current_app.config['ALERTS']
data = request.json
current_app.logger.debug(f'\n\n_______________\n/followHook triggered!\n_______________')
alerts_dict['follower'] = data['eventData']['name']
save_alerts(alerts_dict)
return jsonify({'status': 'success'}), 200
elif data['type'] == 'NAME_CHANGE':
user_id = data['eventData']['user']['id']
new_name = data['eventData']['newName']
change_name(db, user_id, new_name)
if data['eventData']['user']['authenticated']:
remove_duplicates(db, user_id, new_name)
elif data['type'] == 'CHAT': # If a chat message, sort out what command it is
user_id = data['eventData']['user']['id']
display_name = data['eventData']['user']['displayName']
current_app.logger.info(f'{display_name}/{user_id}: {data["eventData"]["rawBody"]}') # Log all chat messages
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(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_private_chat(user_id, f'{display_name}, couldn\'t get your points, for some highly technical reason.')
else:
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']:
rewards_msg = f'Currently active rewards:'
for reward, details in current_app.config['REWARDS'].items():
if details.get('categories'):
if not (set(details['categories']) & set(current_app.config['ACTIVE_CAT'])): # If there are no common categories, continue
continue
if 'type' in details and details['type'] == 'goal':
rewards_msg = f'{rewards_msg}
* {prefix}{reward} goal at {details["target"]} contributed points.'
else:
rewards_msg = f'{rewards_msg}
* {prefix}{reward} for {details["price"]} points.'
if 'info' in details:
rewards_msg = f'{rewards_msg}
{details["info"]}'
else:
rewards_msg = f'{rewards_msg}'
else:
rewards_msg = 'There are currently no active rewards.'
send_private_chat(user_id, rewards_msg)
elif lowercase_msg.startswith(f'{prefix}'): # Send to handle rewards
do_reward(lowercase_msg, user_id)
return data
@ocb.route('/kofiHook', methods=["POST"])
def kofi_hook():
current_app.logger.info(f'----------------------------------------------------------------------------')
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']
accept_donation(donation_info, donation_points)
if is_public:
alert_info = {'name': from_name, 'amount': amount}
else:
alert_info = {'name': 'Anonymous Hero', 'amount': amount}
donations.append(alert_info) # Append info to be displayed in alert
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.')
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)
if is_public:
alert_info = {'name': from_name, 'tiername': tier_name}
else:
alert_info = {'name': 'Anonymous Hero', 'teirname': tier_name}
subscribers.append(alert_info) # Append info to be displayed in alert
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
@ocb.route('/checkFollows') # Polled by follower.html template to check for new followers
def check_follows():
alerts_dict = current_app.config['ALERTS']
follower = {'name': alerts_dict['follower'], 'reward': 'New Follow!'}
if follower['name']:
current_app.logger.info(f'New follower: \"{follower["name"]}\"')
alerts_dict['follower'] = ''
save_alerts(alerts_dict)
return jsonify(follower)
else:
current_app.logger.debug(f'No new followers')
return jsonify(None)
@ocb.route('/checkGoals') # Polled by ocbalert.html template to check for new followers
def check_goals():
alerts_dict = current_app.config['ALERTS']
rgoals = {'name': alerts_dict['g_name'], 'reward': 'GOAL!!'}
if rgoals['name']:
current_app.logger.debug(rgoals)
alerts_dict['g_name'] = ''
alerts_dict['g_reward'] = ''
save_alerts(alerts_dict)
return jsonify(rgoals)
else:
current_app.logger.debug(f'No new goals reached')
return jsonify(None)
@ocb.route('/checkMilestones') # Polled by ocbalert.html template to check for new followers
def check_milestones():
alerts_dict = current_app.config['ALERTS']
rmilestones = {'name': alerts_dict['m_name'], 'reward': 'Milestone!'}
if rmilestones['name']:
current_app.logger.info(rmilestones)
alerts_dict['m_name'] = ''
alerts_dict['m_reward'] = ''
save_alerts(alerts_dict)
return jsonify(rmilestones)
else:
current_app.logger.debug(f'No new milestones passed')
return jsonify(None)