web_panels.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. from flask import flash, render_template, Blueprint, current_app, redirect, request, url_for, session, g, send_from_directory
  2. from datetime import timezone
  3. 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
  4. 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, save_alerts, del_alert_file
  5. 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, save_todolist, save_todocss
  6. from ownchatbot.bot_messages import save_announce
  7. from ownchatbot.owncast_com import send_private_chat
  8. import json
  9. import emoji
  10. from ownchatbot.kofi_handlers import save_kofi_settings, kofi_pngs
  11. import random
  12. import pkce
  13. import requests
  14. from functools import wraps
  15. import os
  16. ocb = Blueprint('web_panels', __name__)
  17. state_value = ''
  18. def requires_login(f):
  19. @wraps(f)
  20. def decorated_function(*args, **kwargs):
  21. if 'user' not in session:
  22. return redirect(url_for('web_panels.login'))
  23. return f(*args, **kwargs)
  24. return decorated_function
  25. @ocb.route('/login') # Remove this comment
  26. def login(): # Verify the streamer using indieauth, to their owncast instance
  27. code_verifier, code_challenge = pkce.generate_pkce_pair() # Generate a code verifier and code challenge
  28. global state_value
  29. state_value = code_verifier
  30. owncast_url = current_app.config['OWNCAST_URL']
  31. client_id = current_app.config['ACCESS_ID']
  32. redirect_url = f'{owncast_url}/api/auth/provider/indieauth?client_id={client_id}&redirect_uri={url_for("web_panels.auth_response", _external=True)}&response_type=code&code_challenge_method=S256&code_challenge={code_challenge}&state={code_verifier}'
  33. return redirect(redirect_url)
  34. @ocb.route('/auth_response')
  35. def auth_response():
  36. code = request.args.get('code')
  37. state = request.args.get('state')
  38. if state == state_value: # Check that the state value returned matches the state value sent
  39. current_app.logger.info(f'CSRF code is valid.')
  40. owncast_url = current_app.config['OWNCAST_URL']
  41. owncast_auth_url = f'{owncast_url}/api/auth/provider/indieauth'
  42. access_id = current_app.config['ACCESS_ID']
  43. access_token = current_app.config['ACCESS_TOKEN']
  44. # https://owncast.online/api/latest/#tag/Auth
  45. # https://aaronparecki.com/2021/04/13/26/indieauth
  46. token_response = requests.post(owncast_auth_url, data={
  47. 'client_id': access_id,
  48. 'client_secret': access_token,
  49. 'code': code,
  50. 'redirect_uri': url_for("web_panels.auth_response", _external=True),
  51. 'grant_type': 'authorization_code',
  52. 'code_verifier': state
  53. })
  54. return_data = token_response.json()
  55. f_return_data = json.dumps(return_data, indent=4)
  56. auth_photo = return_data['profile']['photo']
  57. session['user'] = f_return_data
  58. current_app.logger.info(f'Authenticated.')
  59. return redirect(url_for('web_panels.mgmt'))
  60. else:
  61. current_app.logger.info(f'Invalid CSRF Code.')
  62. return 'Not Authorized'
  63. @ocb.route('/logout')
  64. def logout():
  65. session.pop('user', None)
  66. return redirect(url_for('web_panels.user_panel'))
  67. @ocb.route('/mgmt', methods=['GET']) # The streamer's management panel
  68. @requires_login
  69. def mgmt():
  70. owncast_url = current_app.config['OWNCAST_URL']
  71. db = get_db()
  72. users = get_all_users(db)
  73. utc_timezone = timezone.utc
  74. rewards = current_app.config['REWARDS']
  75. active_rewards = []
  76. for each_reward in all_active_rewards(): # Get the name of all active rewards
  77. active_rewards.append(each_reward)
  78. active_categories = current_app.config['ACTIVE_CAT']
  79. inactive_categories = current_app.config['INACTIVE_CAT']
  80. all_cats = current_app.config['ALL_CAT']
  81. points_interval = current_app.config['POINTS_INTERVAL']
  82. points_award = current_app.config['POINTS_AWARD']
  83. gunicorn_logging = current_app.config['GUNICORN']
  84. prefix = current_app.config['PREFIX']
  85. access_id = current_app.config['ACCESS_ID']
  86. access_token = current_app.config['ACCESS_TOKEN']
  87. kofi_token = current_app.config['KOFI_TOKEN']
  88. kofi_integration = current_app.config['KOFI_INTEGRATION']
  89. kofi_logos = kofi_pngs()
  90. announce_enable = current_app.config['ANNOUNCE_ENABLE']
  91. announce_interval = current_app.config['ANNOUNCE_INTERVAL']
  92. announcements = current_app.config['ANNOUNCEMENTS']
  93. todolist_items = current_app.config['LIST']
  94. todo_css = current_app.config['CSS']
  95. active_tab = request.args.get('activeTab')
  96. alerts_dict = current_app.config['ALERTS']
  97. settings_info = [
  98. access_id,
  99. points_interval,
  100. points_award,
  101. gunicorn_logging,
  102. prefix,
  103. access_token,
  104. owncast_url,
  105. kofi_token,
  106. kofi_integration,
  107. announce_enable,
  108. announce_interval
  109. ]
  110. return render_template('mgmt.html',
  111. queue=get_queue(db),
  112. votes=all_active_votes(db),
  113. goals=all_active_goals(db),
  114. rewards=rewards,
  115. active_rewards=active_rewards,
  116. prefix=current_app.config['PREFIX'],
  117. kofi_settings=current_app.config['KOFI_SETTINGS'],
  118. kofi_integration=kofi_integration,
  119. kofi_logos=kofi_logos,
  120. announcements=announcements,
  121. users=users,
  122. utc_timezone=utc_timezone,
  123. active_categories=active_categories,
  124. inactive_categories=inactive_categories,
  125. settings_info=settings_info,
  126. items=todolist_items,
  127. todo_css=todo_css,
  128. alerts_dict=alerts_dict,
  129. activeTab=active_tab)
  130. @ocb.route('/mgmt_queue', methods=['GET']) # The streamer's management panel
  131. @requires_login
  132. def mgmtqueue():
  133. owncast_url = current_app.config['OWNCAST_URL']
  134. db = get_db()
  135. users = get_all_users(db)
  136. utc_timezone = timezone.utc
  137. rewards = current_app.config['REWARDS']
  138. active_rewards = []
  139. for each_reward in all_active_rewards(): # Get the name of all active rewards
  140. active_rewards.append(each_reward)
  141. return render_template('queue.html',
  142. queue=get_queue(db),
  143. votes=all_active_votes(db),
  144. goals=all_active_goals(db),
  145. rewards=rewards,
  146. utc_timezone=utc_timezone)
  147. @ocb.route('/userpanel', methods=['GET']) # The viewers panel
  148. def user_panel():
  149. db = get_db()
  150. instance = request.args.get('instance')
  151. all_rewards = rewards = current_app.config['REWARDS']
  152. username = request.args.get('username')
  153. points_interval = current_app.config['POINTS_INTERVAL']
  154. points_award = current_app.config['POINTS_AWARD']
  155. if username is not None:
  156. users = get_all_users_by_name(db, username)
  157. else:
  158. users = []
  159. utc_timezone = timezone.utc
  160. return render_template('userpanel.html',
  161. queue=get_queue(db),
  162. votes=all_active_votes(db),
  163. goals=all_active_goals(db),
  164. rewards=all_active_rewards(),
  165. all_rewards=all_rewards,
  166. prefix=current_app.config['PREFIX'],
  167. kofi_settings=current_app.config['KOFI_SETTINGS'],
  168. kofi_integration=current_app.config['KOFI_INTEGRATION'],
  169. points_interval=points_interval,
  170. points_award=points_award,
  171. username=username,
  172. users=users,
  173. instance=instance,
  174. utc_timezone=utc_timezone)
  175. @ocb.route('/mgmt/fulfill', methods=['GET'])
  176. @requires_login
  177. def fulfilled():
  178. db = get_db()
  179. reward_id = request.args.get('reward_id')
  180. username = request.args.get('username')
  181. fulfill_reward(db, reward_id)
  182. return redirect(url_for('web_panels.mgmtqueue'))
  183. @ocb.route('/mgmt/refund', methods=['GET'])
  184. @requires_login
  185. def refund():
  186. db = get_db()
  187. reward_id = request.args.get('reward_id')
  188. reward = request.args.get('reward')
  189. rewards = current_app.config['REWARDS']
  190. points = rewards[reward]['price']
  191. username = request.args.get('username')
  192. user_id = request.args.get('rewarder_id')
  193. refund_points(db, user_id, points) # resets points
  194. refund_reward(db, reward_id) # marks the reward as refunded
  195. return redirect(url_for('web_panels.mgmtqueue'))
  196. @ocb.route('/mgmt/edit_account/<user_id>', methods=['GET', 'POST']) # Streamer manually edit user's account
  197. @requires_login
  198. def edit_account(user_id):
  199. db = get_db()
  200. name = request.args.get('name')
  201. points = request.args.get('points')
  202. email = request.args.get('email')
  203. if request.method == 'POST':
  204. user_id = request.form['user_id']
  205. name = request.form['name']
  206. newpoints = request.form['newpoints']
  207. adjust_points(db, user_id, newpoints)
  208. newemail = request.form['newemail']
  209. if newemail == 'None':
  210. current_app.logger.info(f'No email change requested')
  211. else:
  212. if change_email(db, user_id, newemail):
  213. if newemail == '':
  214. current_app.logger.info(f'Removed {name}\'s email')
  215. else:
  216. current_app.logger.info(f'Changed {name}\'s email to {newemail}')
  217. return redirect(url_for('web_panels.mgmt', activeTab='accounts'))
  218. return render_template('edit_account.html',
  219. name=name,
  220. user_id=user_id,
  221. points=points,
  222. email=email)
  223. @ocb.route('/mgmt/delete/<reward_name>', methods=['GET', 'POST'])
  224. @requires_login
  225. def delete(reward_name):
  226. del_reward = current_app.config['REWARDS']
  227. del_reward.pop(reward_name)
  228. if save_rewards(del_reward):
  229. if rem_cool(reward_name):
  230. rem_from_queue(reward_name)
  231. if reread_votes():
  232. if reread_goals():
  233. pass
  234. return redirect(url_for('web_panels.mgmt', activeTab='managerewards'))
  235. @ocb.route('/mgmt/edit/<reward_name>', methods=['GET', 'POST'])
  236. @requires_login
  237. def edit(reward_name):
  238. active_categories = current_app.config['ACTIVE_CAT']
  239. all_the_rewards = current_app.config['REWARDS']
  240. reward_data = all_the_rewards[reward_name]
  241. all_cats = current_app.config['ALL_CAT']
  242. if request.method == 'POST':
  243. reward_data['cooldown'] = int(request.form['cooldown'])
  244. reward_data['type'] = request.form['type']
  245. if reward_data['type'] == 'goal':
  246. reward_data['target'] = int(request.form['target'])
  247. if "milestones" not in reward_data: # If using old rewards.py, and no milestones key exists, create one
  248. reward_data["milestones"] = {"milestone1": [], "milestone2": [], "milestone3": []}
  249. if request.form['milestone1_points'] == '':
  250. reward_data['milestones']['milestone1'] = []
  251. else:
  252. milestone1_points = int(request.form['milestone1_points'])
  253. reward_data['milestones']['milestone1'] = [request.form['milestone1_desc'], milestone1_points]
  254. if request.form['milestone2_points'] == '':
  255. reward_data['milestones']['milestone2'] = []
  256. else:
  257. milestone2_points = int(request.form['milestone2_points'])
  258. reward_data['milestones']['milestone2'] = [request.form['milestone2_desc'], milestone2_points]
  259. if request.form['milestone3_points'] == '':
  260. reward_data['milestones']['milestone3'] = []
  261. else:
  262. milestone3_points = int(request.form['milestone3_points'])
  263. reward_data['milestones']['milestone3'] = [request.form['milestone3_desc'], milestone3_points]
  264. else:
  265. reward_data['price'] = int(request.form['price'])
  266. reward_data['info'] = emoji.demojize(request.form['info'])
  267. if reward_data['type'] == 'special':
  268. reward_data['cmd'] = request.form['cmd']
  269. reward_data['categories'] = request.form.getlist('category')
  270. reward_data['cooldown'] = int(request.form['cooldown'])
  271. all_the_rewards[reward_name] = reward_data
  272. save_rewards(all_the_rewards)
  273. if reward_data['type'] == 'goal': # Sync goals and votes in the db with rewards.py
  274. reread_goals()
  275. if reward_data['type'] == 'vote':
  276. reread_votes()
  277. return redirect(url_for('web_panels.mgmt', activeTab='managerewards'))
  278. return render_template('edit.html',
  279. all_cats=all_cats,
  280. reward_name=reward_name,
  281. active_categories=active_categories,
  282. reward_data=reward_data)
  283. @ocb.route('/mgmt/settings', methods=['GET', 'POST']) # OwnchatBot settings panel
  284. @requires_login
  285. def settings():
  286. todolist_items = current_app.config['LIST']
  287. points_interval = int(request.form['points_interval'])
  288. points_award = int(request.form['points_award'])
  289. gunicorn_logging = 'gunicorn_logging' in request.form
  290. prefix = request.form['prefix']
  291. access_id = request.form['access_id']
  292. access_token = request.form['access_token']
  293. owncast_url = request.form['owncast_url']
  294. kofi_integration = 'kofi_integration' in request.form
  295. kofi_token = request.form['kofi_token']
  296. config_dict = {
  297. 'POINTS_INTERVAL': points_interval,
  298. 'POINTS_AWARD': points_award,
  299. 'GUNICORN': gunicorn_logging,
  300. 'PREFIX': prefix,
  301. 'ACCESS_ID': access_id,
  302. 'ACCESS_TOKEN': access_token,
  303. 'OWNCAST_URL': owncast_url,
  304. 'KOFI_TOKEN': kofi_token,
  305. 'KOFI_INTEGRATION': kofi_integration
  306. }
  307. if save_config(config_dict): # Save new config.py
  308. current_app.logger.info('Saved new config.')
  309. return redirect(url_for('web_panels.mgmt', activeTab='settings'))
  310. @ocb.route('/mgmt/announcements', methods=['GET', 'POST']) # OwnchatBot settings panel
  311. @requires_login
  312. def announcements():
  313. announce_enable = 'announce_enable' in request.form
  314. announce_interval = int(request.form['announce_interval'])
  315. new_announcements = []
  316. new_announcements = request.form['announcements'].strip().split('\n')
  317. announce_dict = {
  318. 'ANNOUNCEMENTS': new_announcements,
  319. 'ANNOUNCE_ENABLE': announce_enable,
  320. 'ANNOUNCE_INTERVAL': announce_interval
  321. }
  322. if save_announce(announce_dict): # Save new announce.py
  323. current_app.logger.info('Saved new announcements.')
  324. return redirect(url_for('web_panels.mgmt', activeTab='announcements'))
  325. @ocb.route('/mgmt/ksettings', methods=['GET', 'POST']) # OwnchatBot settings panel
  326. @requires_login
  327. def ksettings():
  328. kofi_settings_dict = current_app.config['KOFI_SETTINGS']
  329. if request.method == 'POST':
  330. enable_donations = 'enable_donations' in request.form
  331. set_donation_points = request.form['set_donation_points']
  332. enable_subs = 'enable_subs' in request.form
  333. sub_points = int(request.form['sub_points'])
  334. kofi_url = request.form['kofi_url']
  335. kofi_logo = request.form.get('kofi_logo')
  336. kofi_settings_dict['donations'] = enable_donations
  337. kofi_settings_dict['subs'] = enable_subs
  338. kofi_settings_dict['sub_points'] = sub_points
  339. kofi_settings_dict['kofi_url'] = kofi_url
  340. kofi_settings_dict['kofi_logo'] = kofi_logo
  341. if save_kofi_settings(kofi_settings_dict):
  342. current_app.logger.info(f'Saved Kofi settings')
  343. return redirect(url_for('web_panels.mgmt', activeTab='kofi-settings'))
  344. @ocb.route('/mgmt/add/<reward_type>', methods=['GET', 'POST'])
  345. @requires_login
  346. def add(reward_type):
  347. all_cats = current_app.config['ALL_CAT']
  348. active_categories = current_app.config['ACTIVE_CAT']
  349. all_the_rewards = current_app.config['REWARDS']
  350. if request.method == 'POST':
  351. name = request.form['name']
  352. name = name.lower() # Force the name to all lower case
  353. name = emoji.demojize(name) # Remove any emojis
  354. name = name.replace(" ", "") # Remove any spaces from the name
  355. type = request.form['type']
  356. if name in all_the_rewards: # Check for duplicate reward names
  357. flash("A reward with this name already exists.", "error") # Flash an error message
  358. return redirect(url_for('web_panels.add', reward_type=reward_type)) # Redirect back to the add page
  359. if type != 'category': # If we're only adding a category, skip all of this
  360. cooldown = int(request.form['cooldown'])
  361. if type == 'redeem' or type == 'special' or type == 'vote':
  362. price = int(request.form['price'])
  363. if type == 'goal':
  364. target = int(request.form['target'])
  365. milestone1_desc = request.form['milestone1_desc']
  366. if request.form['milestone1_points'] == '':
  367. milestone1_points = ''
  368. else:
  369. milestone1_points = int(request.form['milestone1_points'])
  370. milestone2_desc = request.form['milestone2_desc']
  371. if request.form['milestone2_points'] == '':
  372. milestone2_points = ''
  373. else:
  374. milestone2_points = int(request.form['milestone2_points'])
  375. milestone3_desc = request.form['milestone3_desc']
  376. if request.form['milestone3_points'] == '':
  377. milestone3_points = ''
  378. else:
  379. milestone3_points = int(request.form['milestone3_points'])
  380. info = request.form['info']
  381. info = emoji.demojize(info) # Remove any emojis
  382. if type == 'special':
  383. cmd = request.form['cmd']
  384. categories = request.form.getlist('category')
  385. if type == 'redeem':
  386. if categories == ['']:
  387. all_the_rewards[name] = {'price': price, 'type': type, 'info': info, 'cooldown': cooldown}
  388. else:
  389. all_the_rewards[name] = {'price': price, 'type': type, 'info': info, 'categories': categories, 'cooldown': cooldown}
  390. if type == 'goal':
  391. if categories == ['']:
  392. all_the_rewards[name] = {'target': target, 'type': type, 'info': info, 'cooldown': cooldown}
  393. else:
  394. all_the_rewards[name] = {'target': target, 'type': type, 'info': info, 'categories': categories, 'cooldown': cooldown}
  395. all_the_rewards[name]["milestones"] = {"milestone1": [], "milestone2": [], "milestone3": []} # Create empty milestones key
  396. if milestone1_points:
  397. all_the_rewards[name]["milestones"]["milestone1"] = [milestone1_desc, milestone1_points]
  398. if milestone2_points:
  399. all_the_rewards[name]["milestones"]["milestone2"] = [milestone2_desc, milestone2_points]
  400. if milestone3_points:
  401. all_the_rewards[name]["milestones"]["milestone3"] = [milestone3_desc, milestone3_points]
  402. if type == 'vote':
  403. if categories == ['']:
  404. all_the_rewards[name] = {'price': price, 'type': type, 'info': info}
  405. else:
  406. all_the_rewards[name] = {'price': price, 'type': type, 'info': info, 'categories': categories, 'cooldown': cooldown}
  407. if type == 'special':
  408. if categories == ['']:
  409. all_the_rewards[name] = {'price': price, 'type': type, 'info': info, 'cmd': cmd, 'cooldown': cooldown}
  410. else:
  411. all_the_rewards[name] = {'price': price, 'type': type, 'info': info, 'cmd': cmd, 'categories': categories, 'cooldown': cooldown}
  412. save_rewards(all_the_rewards)
  413. if type == 'goal': # Remove old goals and votes from the database
  414. reread_goals()
  415. if type == 'vote':
  416. reread_votes()
  417. else: # If we're only adding a category
  418. inactive_categories = current_app.config['INACTIVE_CAT']
  419. inactive_categories.append(name) # Add it to the INACTIVE_CAT variable
  420. reread_categories() # Write it to categories.py
  421. return redirect(url_for('web_panels.mgmt', activeTab='managerewards'))
  422. return render_template('add.html',
  423. all_cats=all_cats,
  424. reward_type=reward_type,
  425. active_categories=active_categories)
  426. @ocb.route('/set_viewer_email', methods=['GET', 'POST'])
  427. def set_viewer_email():
  428. db = get_db()
  429. mail_reg_code = int(request.form['code'])
  430. user_id = request.form['user_id']
  431. db_mail_reg_code = get_email_code(db, user_id)
  432. new_email = request.form['new_email']
  433. instance = request.form['instance']
  434. user_name = request.form['user_name']
  435. if mail_reg_code == db_mail_reg_code:
  436. if change_email(db, user_id, new_email):
  437. del_email_code(db, user_id)
  438. flash(f"Email Address \"{new_email}\" successfully registered.", "success")
  439. send_private_chat(user_id, f'{user_name}, thanks for registering for Kofi perks! I appreciate your support!')
  440. current_app.logger.info(f'Changed {user_id}\'s email to {new_email}')
  441. else:
  442. flash(f"Incorrect code. Email Address \"{new_email}\" was not registered.", "failure")
  443. current_app.logger.info(f'The code entered, \"{mail_reg_code}\", does not match \"{db_mail_reg_code}\" found in database.')
  444. return redirect(url_for('web_panels.user_panel', instance=instance, username=user_name))
  445. @ocb.route('/mgmt/activate/<category>', methods=['GET', 'POST'])
  446. @requires_login
  447. def activate(category):
  448. activate_category(category)
  449. return redirect(url_for('web_panels.mgmt', activeTab='categories'))
  450. @ocb.route('/mgmt/deactivate/<category>', methods=['GET', 'POST'])
  451. @requires_login
  452. def deactivate(category):
  453. deactivate_category(category)
  454. return redirect(url_for('web_panels.mgmt', activeTab='categories'))
  455. @ocb.route('/mgmt/delcat/<cat_name>/<cat_act>', methods=['GET', 'POST'])
  456. @requires_login
  457. def delcat(cat_name, cat_act):
  458. active_categories = current_app.config['ACTIVE_CAT']
  459. inactive_categories = current_app.config['INACTIVE_CAT']
  460. if cat_act == 'inactive':
  461. inactive_categories.remove(cat_name)
  462. else:
  463. active_categories.remove(cat_name)
  464. reread_categories()
  465. current_rewards = current_app.config['REWARDS']
  466. for reward, details in current_rewards.items(): # Remove from rewards.py as well
  467. if cat_name in details['categories']:
  468. details['categories'].remove(cat_name)
  469. save_rewards(current_rewards)
  470. return redirect(url_for('web_panels.mgmt', activeTab='categories'))
  471. @ocb.route('/mgmt/reset/<reward_name>/<reward_type>', methods=['GET', 'POST']) # Reset votes and goals to zero
  472. @requires_login
  473. def reset(reward_name, reward_type):
  474. if reward_type == "goal":
  475. reset_goal(reward_name)
  476. if reward_type == "vote":
  477. reset_vote(reward_name)
  478. return redirect(url_for('web_panels.mgmt', activeTab='managerewards'))
  479. @ocb.route('/mgmt/rereadvotes', methods=['GET', 'POST'])
  480. def rereadv():
  481. reread_votes()
  482. return redirect(url_for('web_panels.mgmt'))
  483. @ocb.route('/mgmt/clearfulfilled', methods=['GET', 'POST'])
  484. @requires_login
  485. def clearfulfilled():
  486. clear_fulfilled_rewards()
  487. return redirect(url_for('web_panels.mgmtqueue'))
  488. @ocb.route('/mgmt/clearqueue', methods=['GET', 'POST'])
  489. @requires_login
  490. def clear_queue():
  491. clear_reward_queue()
  492. return redirect(url_for('web_panels.mgmtqueue'))
  493. @ocb.route('/mgmt/addtodocss', methods=['POST'])
  494. @requires_login
  495. def add_todo_css():
  496. if request.method == 'POST':
  497. new_css = request.form.get('todo_css')
  498. if not new_css:
  499. new_css = """body {
  500. background-color: transparent;
  501. color: white;
  502. }"""
  503. if save_todocss(new_css): # Save todo list
  504. current_app.logger.info('Saved to-do CSS.')
  505. return redirect(url_for('web_panels.mgmt', activeTab='todolist'))
  506. return redirect(url_for('web_panels.mgmt', activeTab='todolist'))
  507. @ocb.route('/mgmt/addtodoitem', methods=['POST'])
  508. @requires_login
  509. def add_todo_item():
  510. if request.method == 'POST':
  511. todolist_items = current_app.config['LIST']
  512. item = request.form.get('item')
  513. if item:
  514. todolist_items.append({'name': item, 'crossed': 'no'})
  515. if save_todolist(todolist_items): # Save todo list
  516. current_app.logger.info('Saved to-do list.')
  517. return redirect(url_for('web_panels.mgmt', activeTab='todolist'))
  518. return redirect(url_for('web_panels.mgmt', activeTab='todolist'))
  519. @ocb.route('/mgmt/cross/<int:item_id>')
  520. @requires_login
  521. def cross(item_id):
  522. todolist_items = current_app.config['LIST']
  523. if 0 <= item_id < len(todolist_items): # Make sure the item exists
  524. todolist_items[item_id]['crossed'] = 'yes'
  525. if save_todolist(todolist_items): # Save todo list
  526. current_app.logger.info('Saved to-do list.')
  527. return redirect(url_for('web_panels.mgmt', activeTab='todolist'))
  528. @ocb.route('/mgmt/uncross/<int:item_id>')
  529. @requires_login
  530. def uncross(item_id):
  531. todolist_items = current_app.config['LIST']
  532. if 0 <= item_id < len(todolist_items):
  533. todolist_items[item_id]['crossed'] = 'no'
  534. if save_todolist(todolist_items): # Save todo list
  535. current_app.logger.info('Saved to-do list.')
  536. return redirect(url_for('web_panels.mgmt', activeTab='todolist'))
  537. @ocb.route('/mgmt/remtodoitem/<int:item_id>')
  538. @requires_login
  539. def rem_todo_item(item_id):
  540. todolist_items = current_app.config['LIST']
  541. if 0 <= item_id < len(todolist_items): # Make sure the item exists
  542. removed = todolist_items.pop(item_id)
  543. current_app.logger.info(f'Removed \"{removed}\" from the to-do list.')
  544. if save_todolist(todolist_items): # Save todo list
  545. current_app.logger.info('Saved to-do list.')
  546. return redirect(url_for('web_panels.mgmt', activeTab='todolist'))
  547. @ocb.route('/mgmt/clearlist')
  548. @requires_login
  549. def clear_list():
  550. todolist_items = current_app.config['LIST']
  551. todolist_items = [] # Clear the list
  552. if save_todolist(todolist_items): # Save todo list
  553. current_app.logger.info('Saved to-do list.')
  554. return redirect(url_for('web_panels.mgmt', activeTab='todolist'))
  555. @ocb.route('/mgmt/alertupload/<alert_type>', methods=['POST'])
  556. @requires_login
  557. def alert_upload(alert_type):
  558. alerts_dict = current_app.config['ALERTS']
  559. alert_file = request.files[alert_type]
  560. if alert_type in alerts_dict.keys(): # If the alert already exists, delete the existing alert file
  561. old_file = alerts_dict[alert_type.upper()]
  562. current_app.logger.info(f'{alert_type} already set.')
  563. if del_alert_file(old_file):
  564. pass
  565. filepath = os.path.join(current_app.config['ALERTS_FOLDER'], alert_file.filename)
  566. alert_file.save(filepath)
  567. alerts_dict[alert_type] = alert_file.filename
  568. if save_alerts(alerts_dict): # Save new alerts.py
  569. return redirect(url_for('web_panels.mgmt', activeTab='alerts'))
  570. return redirect(url_for('web_panels.mgmt', activeTab='alerts'))
  571. @ocb.route('/mgmt/delalert/<alert_type>')
  572. @requires_login
  573. def del_alert(alert_type):
  574. alerts_dict = current_app.config['ALERTS']
  575. try:
  576. alert_file = alerts_dict[alert_type]
  577. alerts_dict.pop(alert_type)
  578. if save_alerts(alerts_dict):
  579. current_app.logger.info(f'Removed {alert_type} from alerts.py.')
  580. if del_alert_file(alert_file):
  581. pass
  582. except KeyError:
  583. current_app.logger.info(f'No {alert_type} alert set. Nothing to do.')
  584. return redirect(url_for('web_panels.mgmt', activeTab='alerts'))
  585. @ocb.route('/alerts/<alert_name>') # Route for displaying alert images
  586. def alerts(alert_name):
  587. return send_from_directory(current_app.config['ALERTS_FOLDER'], alert_name)
  588. @ocb.route('/ocbalert/<alert_type>') # Route for alerts overlay
  589. def ocb_alert(alert_type):
  590. alert_type = alert_type.upper()
  591. alerts_dict = current_app.config['ALERTS']
  592. try:
  593. alert_name = alerts_dict[alert_type]
  594. except KeyError:
  595. current_app.logger.info(f'No {alert_type} alert set. Nothing to do.')
  596. return f'You have not configured alert type \"{alert_type}\"'
  597. if alert_type == 'FOLLOWER_ALERT':
  598. return render_template('follower.html',
  599. alert_name=alert_name)
  600. elif alert_type == 'DONATION_ALERT':
  601. return render_template('donation.html',
  602. alert_name=alert_name)
  603. elif alert_type == 'SUBSCRIBER_ALERT':
  604. return render_template('subscriber.html',
  605. alert_name=alert_name)
  606. elif alert_type == 'MILESTONE_ALERT':
  607. return render_template('rmilestone.html',
  608. alert_name=alert_name)
  609. elif alert_type == 'GOAL_ALERT':
  610. return render_template('rgoal.html',
  611. alert_name=alert_name)
  612. @ocb.route('/goals', methods=['GET']) # Route for goals overlay
  613. def goals():
  614. db = get_db()
  615. return render_template('goals.html',
  616. goals=all_active_goals(db),
  617. rewards=all_active_rewards())
  618. @ocb.route('/votes', methods=['GET']) # Route for votes overlay
  619. def votes():
  620. db = get_db()
  621. return render_template('votes.html',
  622. votes=all_active_votes(db))
  623. @ocb.route('/todo')
  624. def todo():
  625. todolist_items = current_app.config['LIST']
  626. todolist_css = current_app.config['CSS']
  627. return render_template('list.html', items=todolist_items, css=todolist_css)