web_panels.py 33 KB

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