db.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. import sqlite3
  2. from flask import current_app, g
  3. from ownchatbot.reward_handlers import goal_reached, get_queue
  4. from flask.cli import with_appcontext
  5. import click
  6. from time import time
  7. import os
  8. import logging
  9. def rem_from_queue(reward_name): # Remove a reward from the queue
  10. db = get_db()
  11. try:
  12. cursor = db.execute(
  13. "DELETE FROM reward_queue WHERE reward = ?", (reward_name,)
  14. )
  15. db.commit()
  16. except sqlite3.Error as rfqerror:
  17. current_app.logger.error(f'Couldn\'t remove {reward_name} from reward queue: {rfqerror.args[0]}')
  18. return False
  19. return True
  20. def init_app(app):
  21. app.teardown_appcontext(close_db)
  22. app.cli.add_command(init_db_command)
  23. app.cli.add_command(update_db_command)
  24. def get_db():
  25. if 'db' not in g:
  26. g.db = sqlite3.connect(
  27. current_app.config['DATABASE'],
  28. detect_types=sqlite3.PARSE_DECLTYPES
  29. )
  30. g.db.row_factory = sqlite3.Row
  31. return g.db
  32. def close_db(e=None):
  33. db = g.pop('db', None)
  34. if db is not None:
  35. db.close()
  36. def init_db(): # Setup the database. If it already exists, apply new schema
  37. db = get_db()
  38. with current_app.open_resource('schema.sql') as f: # Set up database
  39. db.executescript(f.read().decode('utf8'))
  40. reread_votes()
  41. reread_goals()
  42. if get_queue(db): # Make sure database is initialized
  43. return True
  44. def update_db():
  45. db = get_db()
  46. update_db_path = os.path.join(current_app.root_path, 'update_db.sql') # Check if the update_db.sql file exists
  47. if os.path.exists(update_db_path): # Update it if it does
  48. with current_app.open_resource('update_db.sql') as f:
  49. db.executescript(f.read().decode('utf8'))
  50. if get_queue(db): # Make sure database is initialized
  51. return True
  52. @click.command('init-db')
  53. @with_appcontext
  54. def init_db_command(): # If there is already a database, clear it and do setup
  55. if init_db():
  56. click.echo('Database initialized.')
  57. @click.command('update-db')
  58. @with_appcontext
  59. def update_db_command(): # If there is already a database, clear it and do setup
  60. if update_db():
  61. click.echo('Database initialized.')
  62. def clear_reward_queue(): # Completely clear the reward queue
  63. db = get_db()
  64. try:
  65. cursor = db.execute(
  66. "DELETE FROM reward_queue"
  67. )
  68. cursor.execute(
  69. "UPDATE votes SET count = 0"
  70. )
  71. db.commit()
  72. except sqlite3.Error as serror:
  73. current_app.logger.error(f'Couldn\'t clear reward queue: {serror.args[0]}')
  74. return False
  75. return True
  76. def clear_fulfilled_rewards(): # Clears only fulfilled rewards from the queue
  77. db = get_db()
  78. try:
  79. cursor = db.execute(
  80. "DELETE FROM reward_queue WHERE fulfilled = 1"
  81. )
  82. db.commit()
  83. except sqlite3.Error as serror:
  84. current_app.logger.error(f'Couldn\'t clear fulfilled rewards: {serror.args[0]}')
  85. return False
  86. return True
  87. def rem_all_votes(): # USED TO BE "clear_votes" Clear all votes from the database
  88. db = get_db()
  89. try:
  90. db.execute("DELETE FROM votes")
  91. db.commit()
  92. except sqlite3.Error as cverror:
  93. current_app.logger.error(f'Couldn\'t clear all votes: {cverror.args[0]}')
  94. return False
  95. if put_votes(db):
  96. return True
  97. def rem_vote(): # Remove a single vote from the database
  98. db = get_db(vote)
  99. try:
  100. db.execute("DELETE FROM votes WHERE name = ?", ('vote',))
  101. db.commit()
  102. except sqlite3.Error as rverror:
  103. current_app.logger.error(f'Couldn\'t remove \"{vote}\" from database: {rverror.args[0]}')
  104. return False
  105. if put_votes(db):
  106. return True
  107. def is_cool(reward_name): # Check if a reward is cooling down.
  108. current_time = time() # In seconds
  109. rewards = current_app.config['REWARDS']
  110. this_cool_down = rewards[reward_name]['cooldown']
  111. if this_cool_down > 0: # If this reward has a cooldown
  112. try: # Get the cooldown time for it
  113. db = get_db()
  114. cursor = db.execute(
  115. "SELECT created FROM cool_downs WHERE reward = ?", (reward_name,)
  116. )
  117. current_cds = cursor.fetchall()
  118. except sqlite3.Error as icerror:
  119. current_app.logger.error(f'Couldn\'t get \"{reward_name}\" from database: {icerror.args[0]}')
  120. if current_cds:
  121. last_time = current_cds[0][0]
  122. hot_time = current_time - last_time
  123. if hot_time < this_cool_down * 60: # If it is in the database and still within the cooldown time, return False
  124. hot_time = this_cool_down - (hot_time / 60) # Get remaining minutes in cooldown
  125. return False, hot_time # Return False, and the time left on the cooldown
  126. else: # If it is in the database and the cooldown has expired, update the cooldown time, and return True
  127. try:
  128. db.execute(
  129. "UPDATE cool_downs SET created = ? WHERE reward = ?",
  130. (current_time, reward_name)
  131. )
  132. db.commit()
  133. return True, 0
  134. except sqlite3.Error as scerror:
  135. current_app.logger.error(f'Couldn\'t update \"{reward_name}\"\'s cooldown time in the database: {scerror.args[0]}')
  136. else: # If it is not in the database, add it and return True
  137. try:
  138. db.execute(
  139. "INSERT INTO cool_downs(created, reward) VALUES(?, ?)",
  140. (current_time, reward_name)
  141. )
  142. db.commit()
  143. return True, 0
  144. except sqlite3.Error as scerror:
  145. current_app.logger.error(f'Couldn\'t add \"{reward_name}\" to database: {scerror.args[0]}')
  146. else: # If the redeem has no cooldown
  147. return True, 0
  148. def rem_cool(reward_name): # Remove a reward from the database
  149. try:
  150. db = get_db()
  151. cursor = db.execute(
  152. "DELETE FROM cool_downs WHERE reward = ?", (reward_name,)
  153. )
  154. current_cds = cursor.fetchall()
  155. except sqlite3.Error as icerror:
  156. current_app.logger.error(f'Couldn\'t remove \"{reward_name}\" from database: {icerror.args[0]}')
  157. return False
  158. return True
  159. def put_votes(db): # Reread votes from rewards.py, and sync with database
  160. info = details['info']
  161. for reward, details in current_app.config['REWARDS'].items():
  162. if details['type'] == 'vote':
  163. try:
  164. db.execute(
  165. "INSERT INTO votes(name, count, info) VALUES(?, 0, ?)",
  166. (reward, info)
  167. )
  168. db.commit()
  169. except sqlite3.Error as serror:
  170. current_app.logger.error(f'Couldn\'t insert \"{vote}\" into database: {serror.args[0]}')
  171. return False
  172. return True
  173. def reread_votes(): # Reread votes from rewards.py, and sync with database
  174. db = get_db()
  175. try: # Remove votes not in rewards.py
  176. cursor = db.execute("SELECT name FROM votes")
  177. votes = cursor.fetchall()
  178. chopping_block = []
  179. for vote in votes:
  180. vote = vote[0]
  181. if vote not in current_app.config['REWARDS'].keys():
  182. chopping_block.append(vote)
  183. elif current_app.config['REWARDS'][vote]['type'] != 'vote':
  184. chopping_block.append(vote)
  185. for vote in chopping_block:
  186. cursor.execute("DELETE FROM votes WHERE name = ?", (vote,))
  187. db.commit()
  188. except sqlite3.Error as serror:
  189. current_app.logger.error(f'Couldn\'t clear deleted votes from database: {serror.args[0]}')
  190. return False
  191. try: # Add new votes found in rewards.py
  192. for reward, details in current_app.config['REWARDS'].items():
  193. if details['type'] == 'vote': # Check if the vote is already in the database
  194. cursor = db.execute(
  195. "SELECT count FROM votes WHERE name = ?",
  196. (reward,)
  197. )
  198. result = cursor.fetchone()
  199. if result is None: # If it isn't, add it
  200. info = details['info']
  201. if details['type'] == 'vote':
  202. db.execute(
  203. "INSERT INTO votes(name, count, info) VALUES(?, 0, ?)",
  204. (reward, details["info"])
  205. )
  206. else: # If it is, update the info
  207. cursor.execute(
  208. "UPDATE votes SET info = ? WHERE name = ?",
  209. (details['info'], reward)
  210. )
  211. db.commit()
  212. except sqlite3.Error as serror:
  213. current_app.logger.error(f'Couldn\'t insert \"{vote}\" into database: {serror.args[0]}')
  214. return False
  215. return True
  216. def reread_goals(): # Reread goals from rewards.py, and sync with database
  217. db = get_db()
  218. try: # Remove goals not in rewards.py
  219. cursor = db.execute("SELECT name FROM goals")
  220. goals = cursor.fetchall()
  221. chopping_block = []
  222. for goal in goals:
  223. goal = goal[0]
  224. if goal not in current_app.config['REWARDS'].keys():
  225. chopping_block.append(goal)
  226. elif current_app.config['REWARDS'][goal]['type'] != 'goal':
  227. chopping_block.append(goal)
  228. for goal in chopping_block:
  229. cursor.execute("DELETE FROM goals WHERE name = ?", (goal,))
  230. db.commit()
  231. except sqlite3.Error as serror:
  232. current_app.logger.error(f'Couldn\'t clear removed goals from database: {serror.args[0]}')
  233. return False
  234. try: # Add new goals found in rewards.py
  235. for reward, details in current_app.config['REWARDS'].items():
  236. if details['type'] == 'goal': # Check if the goal is already in the database
  237. cursor = db.execute(
  238. "SELECT target FROM goals WHERE name = ?",
  239. (reward,)
  240. )
  241. result = cursor.fetchone()
  242. if result is None: # If it isn't, add it
  243. cursor.execute(
  244. "INSERT INTO goals(name, progress, target, info, complete, milestones) VALUES(?, 0, ?, ?, FALSE, 0)",
  245. (reward, details['target'], details['info'])
  246. )
  247. elif result != details['target']: # If it is, update the target
  248. cursor.execute(
  249. "UPDATE goals SET target = ?, info = ? WHERE name = ?",
  250. (details["target"], details["info"], reward)
  251. )
  252. db.commit()
  253. except sqlite3.Error as serror:
  254. current_app.logger.error(f'Couldn\'t insert \"{reward}\" into database: {serror.args[0]}')
  255. return False
  256. return True
  257. def reset_goal(goal): # Set goal progress back to zero
  258. if goal not in current_app.config['REWARDS']: # If it doesn't exist in rewards.py
  259. current_app.logger.error(f'Couldn\'t reset goal, {goal} not in rewards file.')
  260. return False
  261. try:
  262. db = get_db()
  263. target = current_app.config['REWARDS'][goal]['target']
  264. info = current_app.config['REWARDS'][goal]['info']
  265. cursor = db.cursor()
  266. cursor.execute(
  267. "UPDATE goals SET progress = 0, target = ?, complete = FALSE, info = ?, milestones = 0 WHERE name = ?",
  268. (target, info, goal)
  269. )
  270. db.commit()
  271. return True
  272. except sqlite3.Error as rgerror:
  273. current_app.logger.error(f'Couldn\'t reset {goal} in the goals table: {rgerror.args[0]}')
  274. return False
  275. def reset_vote(vote):
  276. if vote not in current_app.config['REWARDS']: # Check if it exists in rewards.py
  277. current_app.logger.error(f'Couldn\'t reset vote, {vote} not in rewards file.')
  278. return False
  279. else:
  280. try:
  281. db = get_db()
  282. cursor = db.cursor()
  283. cursor.execute(
  284. "UPDATE votes SET count = 0, voters = '' WHERE name = ?",
  285. (vote,)
  286. )
  287. db.commit()
  288. except sqlite3.Error as rverror:
  289. current_app.logger.error(f'Couldn\'t reset {vote} in the votes table: {rverror.args[0]}')
  290. return False
  291. return True