db.py 12 KB

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