-
Notifications
You must be signed in to change notification settings - Fork 0
/
storage.py
371 lines (327 loc) · 14 KB
/
storage.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Storage handling component for LamiaDMBot
import os, sqlite3, logging
# Current database version (To be used in future updates for automated migrations
DBVER = (4, )
class LamiaDB:
"""Database handler for LamiaDMBot"""
def __init__(self, dbname='lamia.db'):
"""Constructor for the LamiaDB database handler"""
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
self.logger = logging.getLogger(__name__)
if dbname == "":
dbname = 'lamia.db'
if not os.path.isfile(dbname):
#No previous database file, we'll create one
self.logger.info("Initializing database...")
self.conn = sqlite3.connect(dbname)
self.conn.isolation_level = None #Hahahaha REAL FUCKING FUNNY PYTHON
cur = self.conn.cursor()
cur.execute('PRAGMA foreign_keys = ON')
cur.execute('CREATE TABLE config (version integer)')
cur.execute('INSERT INTO config (version) VALUES (?)', DBVER)
cur.execute('CREATE TABLE users (userid integer primary key)')
cur.execute('CREATE TABLE rolls (id integer primary key autoincrement, users_id integer, name text not null, roll text not null, foreign key(users_id) references users(userid))')
cur.execute('CREATE TABLE characters (id integer primary key autoincrement, users_id integer, name text not null, foreign key(users_id) references users(userid))')
cur.execute('CREATE TABLE attributes (id integer primary key autoincrement, characters_id integer, attributename text not null, attributevalue text, foreign key(characters_id) references characters(id))')
cur.execute('CREATE TABLE places (id integer primary key autoincrement, users_id integer, placeshortname text not null, placename text, placedesc text, placeurl text, foreign key(users_id) references users(userid))')
cur.execute('CREATE TABLE rolltables (id integer primary key autoincrement, users_id integer, name text not null, is_public integer not null, foreign key(users_id) references users(userid))')
cur.execute('CREATE TABLE rolltable_entries (id integer primary key autoincrement, rolltables_id integer, description text not null, rangemin integer not null, rangemax integer not null, foreign key(rolltables_id) references rolltables(id))')
self.conn.commit()
self.logger.info("Done.")
else:
self.conn = sqlite3.connect(dbname)
self.conn.execute('PRAGMA foreign_keys = ON')
self.conn.isolation_level = None #Hahahaha REAL, REAL FUCKING FUNNY PYTHON
# Users section
def is_user_registered(self, userid):
"""Returns True if the user with specified userid is already registered, otherwise it
returns False"""
cur = self.conn.cursor()
t = (userid, )
cur.execute('SELECT * FROM users WHERE userid=?', t)
retrieved_user = cur.fetchone()
if not (retrieved_user == None): #No such user
return True
else:
return False
def register_user(self, userid):
"""Registers a new user in the database, returning True if it was successful or False if
the user already exists or the register failed."""
try:
#First, we'll test if an user already exists
cur = self.conn.cursor()
t = (userid, )
if not (self.is_user_registered(userid)): #No such user, we'll register it
cur.execute('begin')
cur.execute('INSERT INTO users (userid) VALUES (?)', t)
self.conn.commit()
return True
else:
return False
except Exception as e:
self.logger.exception("Exception in call to 'register_user'")
self.conn.rollback()
return False
# Rolls section
def is_roll_registered(self, userid, rollname):
"""Returns True if a given roll has already been registered in the database, otherwise
it returns False"""
cur = self.conn.cursor()
t = (userid, rollname, )
cur.execute('SELECT * FROM rolls WHERE users_id=? AND name=?', t)
retrieved_roll = cur.fetchone()
if not (retrieved_roll == None):
return True
else:
return False
def register_roll(self, userid, rollname, rollparam):
"""Registers a new roll in the database, returning True if it was successful and False if
it wasn't"""
try:
cur = self.conn.cursor()
t = (rollname, rollparam, userid, )
if not (self.is_roll_registered(userid, rollname)):
cur.execute('begin')
cur.execute('INSERT INTO rolls (name, roll, users_id) VALUES (?, ?, ?)', t)
self.conn.commit()
return True
else:
return False
except Exception as e:
self.logger.exception("Exception in call to 'register_roll'")
self.conn.rollback()
return False
def fetch_roll(self, userid, rollname):
"""Recalls a single roll stored in the database, returning the roll's parameters (value)
if found and None if it wasn't"""
cur = self.conn.cursor()
t = (rollname, userid, )
cur.execute('SELECT roll FROM rolls where name=? AND users_id=?', t)
retrieved_roll = cur.fetchone()
return retrieved_roll
def fetch_all_rolls(self, userid):
"""Recalls all rolls of a given user stored in the database, returning a dictionary in
the format {rollname=rollparam}"""
retrieved_rolls = {}
cur = self.conn.cursor()
t = (userid, )
for row in cur.execute('SELECT name, roll FROM rolls WHERE users_id=?', t):
retrieved_rolls[row[0]] = row[1]
return retrieved_rolls
def delete_roll(self, userid, rollname):
"""Deletes a roll parametrized by userid and name. Returns True if the delete was
successful and False if it wasn't."""
try:
cur = self.conn.cursor()
t = (userid, rollname, )
if self.is_roll_registered(userid, rollname):
cur.execute('begin')
cur.execute('DELETE FROM rolls WHERE users_id=? AND name=?', t)
self.conn.commit()
return True
else:
return False
except Exception as e:
self.logger.exception("Exception in call to 'delete_roll'")
self.conn.rollback()
return False
# Rolltables section
def is_rolltable_registered(self, userid, rolltablename):
pass
def is_public_rolltable_registered(self, rolltablename):
pass
def fetch_rolltable(self, userid, rolltablename):
pass
def fetch_public_rolltable(self, rolltablename):
pass
def register_rolltable(self, userid, rolltablename, entries):
pass
def change_rolltable(self, userid, rolltablename):
pass
def delete_rolltable(self, userid, rolltablename):
pass
def rolltable_id_from_name(self, rolltablename):
pass
# Characters section
def is_character_registered(self, userid, charactername):
"""Returns True if the character with specified associated userid is already registered,
otherwise it returns False"""
cur = self.conn.cursor()
t = (charactername, userid, )
cur.execute('SELECT * FROM characters WHERE name=? AND users_id=?', t)
retrieved_character = cur.fetchone()
if not (retrieved_character == None): #No such character
return True
else:
return False
def register_character(self, userid, charactername):
"""Registers a new character in the database, returning True if it was successful and False
if it wasn't"""
try:
cur = self.conn.cursor()
t = (charactername, userid, )
if not (self.is_character_registered(userid, charactername)):
cur.execute('begin')
cur.execute('INSERT INTO characters (name, users_id) VALUES (?, ?)', t)
self.conn.commit()
return True
else:
return False
except Exception as e:
self.logger.exception("Exception in call to 'register_character'")
self.conn.rollback()
return False
def fetch_character(self, userid, charactername):
"""Returns a dictionary with all of a given character's attributes in the format
{attribute=value} with the first attribute being its name.
If such character does not exist, return an empty dictionary."""
character_attributes = {}
cur = self.conn.cursor()
if(self.is_character_registered(userid, charactername)): #Character registered exists
t = (userid, charactername, )
character_attributes['name'] = charactername
current_character = self.character_id_from_name(userid, charactername)
t = (current_character, )
for row in cur.execute('SELECT attributename, attributevalue FROM attributes WHERE characters_id=?', t):
character_attributes[row[0]] = row[1] #This sets the dictionary to use attributename as key and attributevalue as value
return character_attributes
def fetch_all_characters(self, userid):
"""Returns a list with all characters registered under the same user. If a given user does
not exist or has no characters registered, return an empty list."""
character_list = []
cur = self.conn.cursor()
t = (userid, )
if(self.is_user_registered(userid)):
for row in cur.execute('SELECT name FROM characters WHERE users_id=?', t):
character_list.append(row[0])
return character_list
def character_id_from_name(self, userid, charactername):
"""Returns a given character's id if it can be found"""
cur = self.conn.cursor()
t = (userid, charactername, )
cur.execute('SELECT id FROM characters WHERE users_id=? AND name=?', t) #First, we'll get the character's id
current_character = cur.fetchone()
return current_character[0]
def delete_character(self, userid, charactername):
"""Removes a character belonging to a given user, returning True if it was a success or False if it wasn't."""
try:
cur = self.conn.cursor()
current_character = self.character_id_from_name(userid, charactername)
t = (current_character, )
cur.execute('begin')
cur.execute('DELETE FROM attributes WHERE characters_id=?', t) #With this, we'll delete all the attributes first
cur.execute('DELETE FROM characters WHERE id=?', t) #And then the character itself
self.conn.commit()
return True #Looking good? Commit and return True
except Exception as e: #No? We'll undo the mess we made and return False
self.logger.exception("Exception in call to 'delete_character'")
self.conn.rollback()
return False
def add_attribute(self, userid, charactername, attributename, attributevalue):
"""Adds an attribute to a character, returning True if it was a success and False if
it wasn't."""
try:
cur = self.conn.cursor()
current_character = self.character_id_from_name(userid, charactername)
t = (attributename, current_character, )
cur.execute('SELECT * FROM attributes WHERE attributename=? AND characters_id=?', t)
current_attribute = cur.fetchone()
if(current_attribute == None):
t = (attributename, attributevalue, current_character, )
cur.execute('begin')
cur.execute('INSERT INTO attributes (attributename, attributevalue, characters_id) values (?, ?, ?)', t)
self.conn.commit()
return True
else:
self.conn.rollback()
return False
except Exception as e:
self.logger.exception("Exception in call to 'add_attribute'")
self.conn.rollback()
return False
def change_attribute(self, userid, charactername, attributename, attributevalue):
"""Changes the current value of a given attribute, returning True if it was a success and
False if it wasn't."""
try:
cur = self.conn.cursor()
current_character = self.character_id_from_name(userid, charactername)
t = (attributevalue, current_character, attributename, )
cur.execute('begin')
cur.execute('UPDATE attributes SET attributevalue=? WHERE characters_id=? AND attributename=?', t)
self.conn.commit()
return True
except Exception as e:
self.logger.exception("Exception in call to 'change_attribute'")
self.conn.rollback()
return False
def remove_attribute(self, userid, charactername, attributename):
"""Deletes a given character's attribute, returning True if it was a success and False if it wasn't."""
try:
cur = self.conn.cursor()
current_character = self.character_id_from_name(userid, charactername)
t = (attributename, current_character, )
cur.execute('begin')
cur.execute('DELETE FROM attributes WHERE attributename=? AND characters_id=?', t)
self.conn.commit()
return True
except Exception as e:
self.logger.exception("Exception in call to 'remove_attribute'")
self.conn.rollback()
return False
# Places section
def is_place_registered(self, userid, placeshortname):
"""Returns True if the place with specified associated userid is already registered,
otherwise it returns False"""
cur = self.conn.cursor()
t = (placeshortname, userid, )
cur.execute('SELECT * FROM places WHERE placeshortname=? AND users_id=?', t)
retrieved_place = cur.fetchone()
if not (retrieved_place == None): #No such place
return True
else:
return False
def register_place(self, userid, placeshortname, placename, placedesc="", placeurl=""):
"""Adds a place (A text describing a scene or place in a game), returning True if it could
be created and False if it wasn't."""
try:
cur = self.conn.cursor()
t = (userid, placeshortname, placename, placedesc, placeurl, )
cur.execute('begin')
cur.execute('INSERT INTO places (users_id, placeshortname, placename, placedesc, placeurl) values (?, ?, ?, ?, ?)', t)
self.conn.commit()
return True
except Exception as e:
self.logger.exception("Exception in call to 'register_place'")
self.conn.rollback()
return False
def fetch_all_places(self, userid):
"""Returns a list with all characters registered under the same user. If a given user does
not exist or has no characters registered, return an empty list."""
place_list = []
cur = self.conn.cursor()
t = (userid, )
if(self.is_user_registered(userid)):
for row in cur.execute('SELECT name FROM places WHERE users_id=?', t):
place_list.append(row[0])
return place_list
def change_place(self, userid, placeshortname, placename="", placedesc="", placeurl=""):
pass
def delete_place(self, userid, placeshortname):
"""Removes a place belonging to a given user, returning True if it was a success or False if it wasn't."""
try:
cur = self.conn.cursor()
t = (placeshortname, userid, )
cur.execute('begin')
cur.execute('DELETE FROM places WHERE placeshortname=? and users_id=?', t)
self.conn.commit()
return True #Looking good? Commit and return True
except Exception as e: #No? We'll undo the mess we made and return False
self.logger.exception("Exception in call to 'delete_place'")
self.conn.rollback()
return False
if __name__ == '__main__':
print "This is not meant to be used directly! Run 'main.py' instead."