import os from flask import current_app from sqlite3 import Error from ownchatbot.user_handlers import spend_points import subprocess import json def check_vote(db, vote_name, user_id): # Check if user has already voted on this vote try: cursor = db.execute( "SELECT voters FROM votes WHERE name = ?", (vote_name,) ) row = cursor.fetchone() if row is None: current_app.logger.warning(f'\"{vote_name}\" not found in vote table.') return False if row[0] == user_id: return True except Error as cverror: current_app.logger.error(f'Couldn\'t check if {user_id} already voted on \"{vote_name}\": {cverror.args[0]}') return False def add_to_vote(db, vote_name, user_id): # Add a count to a vote try: # Check if vote exists in the database cursor = db.execute( "SELECT count FROM votes WHERE name = ?", (vote_name,) ) vote = cursor.fetchone() if vote is None: current_app.logger.warning(f'{vote_name} not found in vote table.') return False else: # If vote exists, add a count db.execute( "UPDATE votes SET count = count + 1, voters = ? WHERE name = ?", (user_id, vote_name,) ) db.commit() return True except Error as terror: current_app.logger.error(f'Couldn\'t add to \"{vote_name}\" vote: {terror.args[0]}') return False def add_to_queue(db, user_id, reward_name): # Add a reward to the queue try: db.execute( "INSERT INTO reward_queue(reward, user_id, fulfilled, refunded) VALUES(?, ?, ?, ?)", (reward_name, user_id, 0, 0) ) db.commit() return True except Error as qerror: current_app.logger.error(f'Couldn\'t add to reward \"{reward_name}\" for {user_id} queue: {qerror.args[0]}') return False def run_script(reward_name, script_cmd): # Run a script form a special reward try: subprocess.check_call(script_cmd, shell=True) except Exception as scerror: current_app.logger.error(f'Couldn\'t run script \"{reward_name}\": {scerror.args[0]}') return False return True def add_to_goal(db, user_id, reward_name, points_contributed): # Add a contribution to a goal try: cursor = db.execute( "SELECT progress, target FROM goals WHERE name = ?", (reward_name,) ) row = cursor.fetchone() if row is None: current_app.logger.warning(f'\"{reward_name}\" not found in goal table.') return False progress, target = row if progress + points_contributed > target: points_contributed = target - progress if points_contributed < 0: points_contributed = 0 if spend_points(db, user_id, points_contributed): cursor = db.execute( "UPDATE goals SET progress = ? WHERE name = ?", (progress + points_contributed, reward_name) ) db.commit() return True except Error as gerror: current_app.logger.error(f'Couldn\'t update goal: {gerror.args[0]}') return False def goal_left(db, reward_name): try: cursor = db.execute( "SELECT progress, target FROM goals WHERE name = ?", (reward_name,) ) row = cursor.fetchone() if row is None: current_app.logger.warning(f'{reward_name} not found in Goal table.') else: progress, target = row left = target - progress return left except Error as glerror: current_app.logger.error(f'Couldn\'t check progress for \"{reward_name}\" goal: {glerror.args[0]}') def goal_reached(db, reward_name): # Set a goal as completed try: cursor = db.execute( "SELECT complete FROM goals WHERE name = ?", (reward_name,) ) row = cursor.fetchone() if row is None: current_app.logger.warning(f'{reward_name} not found in goal table.') else: return row[0] except Error as grerror: current_app.logger.error(f'Couldn\'t check if goal was met: {grerror.args[0]}') def was_goal_reached(db, reward_name): # Check if a goal was reached try: cursor = db.execute( "SELECT progress, target FROM goals WHERE name = ?", (reward_name,) ) row = cursor.fetchone() if row is None: current_app.logger.warning(f'{reward_name} not found in Goal table.') else: progress, target = row if progress == target: cursor = db.execute( "UPDATE goals SET complete = TRUE WHERE name = ?", (reward_name,) ) db.commit() return True return False except Error as wgrerror: current_app.logger.error(f'Couldn\'t mark goal met: {wgrerror.args[0]}') return False def all_votes(db): # Get all the votes try: cursor = db.execute( "SELECT votes.name, votes.count, votes.info FROM votes" ) return cursor.fetchall() except Error as aterror: current_app.logger.error(f'Couldn\'t select all votes: {aterror.args[0]}') def refund_reward(db, reward_id): # Refund a user for a particular reward reward_id = reward_id try: cursor = db.execute( "UPDATE reward_queue SET refunded = 1 WHERE id = ?", (reward_id,) ) db.commit() except Error as rferror: current_app.logger.error(f'Couldn\'t refund reward id {reward_id}: {rferror.args[0]}') return False def fulfill_reward(db, reward_id): # Mark a reward as fulfilled in the database reward_id = reward_id try: cursor = db.execute( "UPDATE reward_queue SET fulfilled = 1 WHERE id = ?", (reward_id,) ) db.commit() except Error as frerror: current_app.logger.error(f'Couldn\'t fulfill reward id {reward_id}: {frerror.args[0]}') return False def all_active_votes(db): # Get only active votes votes = all_votes(db) all_active_votes = [] for name, count, info in votes: if is_reward_active(name): all_active_votes.append((name, count, info)) return all_active_votes def all_goals(db): # Get all the goals try: cursor = db.execute( """SELECT name, progress, target, info FROM goals""" ) return cursor.fetchall() except Error as agerror: current_app.logger.error(f'Couldn\'t select all goals: {agerror.args[0]}') def all_active_goals(db): # Get only active goals goals = all_goals(db) all_active_goals = [] for name, progress, target, info in goals: if is_reward_active(name): all_active_goals.append((name, progress, target, info)) return all_active_goals def all_active_rewards(): # Get only active rewards rewards = current_app.config['REWARDS'] all_active_rewards = {} for reward_name, reward_dict in rewards.items(): if reward_dict.get('categories'): # If reward has empty categories list for category in reward_dict['categories']: # Compare each category to ACTIVE_CAT list if category in current_app.config['ACTIVE_CAT']: all_active_rewards[reward_name] = reward_dict break return all_active_rewards def save_rewards(reward_info): # Write rewards to rewards.py new_rewards = json.dumps(reward_info, 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 except Exception as srerror: current_app.logger.error(f'Couldn\'t save rewards.py: {serror.args[0]}') return False return True def save_config(config_dict): # Write settings to config.py settings_file = os.path.join(current_app.instance_path, 'config.py') secret_key = current_app.config['SECRET_KEY'] new_settings = f"# Owncast stuff. Needed to interact with your Owncast instance\n\ ACCESS_TOKEN = '{config_dict['ACCESS_TOKEN']}'\n\ OWNCAST_URL = '{config_dict['OWNCAST_URL']}'\n\ \n\ # OwnchatBot Configuration \n\ SECRET_KEY = '{secret_key}' # Needed for internal Flask stuff. DO NOT DELETE.\n\ POINTS_INTERVAL = {config_dict['POINTS_INTERVAL']} # How long, in seconds, between points awards\n\ POINTS_AWARD = {config_dict['POINTS_AWARD']} # How many points awarded each interval?\n\ GUNICORN = {config_dict['GUNICORN']} # Integrate OwnchatBot logging into Gunicorn\n\ PREFIX = '{config_dict['PREFIX']}' # Preceeds commands, so OwnchatBot knows what is a command\n\ MGMT_AUTH = '{config_dict['MGMT_AUTH']}' # Needed to access the OwnchatBot management panel. See README.md for more details.\n" with open(settings_file, 'w') as f: f.write(new_settings) f.close current_app.config.from_pyfile('config.py', silent=True) # Reread config.py into the app def reread_categories(): # Read _CAT varaibles and write to categories.py categories_file = os.path.join(current_app.instance_path, 'categories.py') active_categories = current_app.config['ACTIVE_CAT'] inactive_categories = current_app.config['INACTIVE_CAT'] try: with open(categories_file, 'r', encoding='utf-8') as f: # Read categories.py, and set up lines to change category_data = f.readlines() category_data[0] = f'ACTIVE_CAT = {active_categories}\n' category_data[1] = f'INACTIVE_CAT = {inactive_categories}\n' f.close with open(categories_file, 'w', encoding='utf-8') as f: # Write changes to categories.py f.writelines(category_data) f.close current_app.config.from_pyfile('categories.py', silent=True) # Reread categories into the app except Error as rcerror: current_app.logger.error(f'Couldn\'t reread categories: {rcerror.args[0]}') def activate_category(category): # Move an item from the ACTIVE_CAT list to the INACTIVE_CAT list try: categories_file = os.path.join(current_app.instance_path, 'categories.py') active_categories = current_app.config['ACTIVE_CAT'] inactive_categories = current_app.config['INACTIVE_CAT'] active_categories.append(category) # Add to ACTIVE_CAT inactive_categories.remove(category) # Remove from INACTIVE_CAT reread_categories() except Error as acerror: current_app.logger.error(f'Couldn\'t activate {category}: {acerror.args[0]}') def deactivate_category(category): # Move an item from the INACTIVE_CAT list to the ACTIVE_CAT list try: categories_file = os.path.join(current_app.instance_path, 'categories.py') active_categories = current_app.config['ACTIVE_CAT'] inactive_categories = current_app.config['INACTIVE_CAT'] active_categories.remove(category) # Remove from ACTIVE_CAT inactive_categories.append(category) # Add to INACTIVE_CAT reread_categories() except Error as dcerror: current_app.logger.error(f'Couldn\'t deactivate {category}: {dcerror.args[0]}') def get_queue(db): # Get the reward queue and the username try: cursor = db.execute( """SELECT reward_queue.id, reward_queue.created, reward_queue.reward, reward_queue.user_id, reward_queue.fulfilled, reward_queue.refunded, points.name FROM reward_queue INNER JOIN points on reward_queue.user_id = points.id""" ) return cursor.fetchall() except Error as gqerror: current_app.logger.error(f'Couldn\'t get queue: {gqerror.args[0]}') def is_reward_active(reward_name): # Check if reward is active active_categories = current_app.config['ACTIVE_CAT'] reward_dict = current_app.config['REWARDS'].get(reward_name, None) try: if reward_dict: if 'categories' in reward_dict: # Is there a categories key at all? for category in reward_dict['categories']: # Cycle through categories and compare to active_categories if category in active_categories: return True return False elif reward_dict['categories'] == []: # If categories key is there but empty, return False return False else: return True return None except Error as iraerror: current_app.logger.error(f'Couldn\'t check if {reward_name} is active: {iraerror.args[0]}')