File botly/bot.py changed (mode: 100644) (index 8e6311b..b0047a2) |
... |
... |
import asyncio |
5 |
5 |
|
|
6 |
6 |
import discord |
import discord |
7 |
7 |
|
|
|
8 |
|
from botly.db import Db |
8 |
9 |
from botly.settings import Settings |
from botly.settings import Settings |
9 |
10 |
from botly.knowledge import Knowledge |
from botly.knowledge import Knowledge |
10 |
11 |
from botly.comm import Comm |
from botly.comm import Comm |
|
... |
... |
class Bot: |
19 |
20 |
def __init__(self, rootModule, dbPath): |
def __init__(self, rootModule, dbPath): |
20 |
21 |
"""Initialization loads database, reactions and tasks from drive.""" |
"""Initialization loads database, reactions and tasks from drive.""" |
21 |
22 |
self.me = False |
self.me = False |
22 |
|
|
|
|
23 |
|
|
|
24 |
|
# Load database and models |
|
25 |
|
Db.load(dbPath) |
23 |
26 |
self.settings = Settings() |
self.settings = Settings() |
24 |
|
self.settings.load(dbPath) |
|
25 |
27 |
self.knowledge = Knowledge() |
self.knowledge = Knowledge() |
26 |
28 |
|
|
27 |
29 |
# Create discord client object for communication and event reception |
# Create discord client object for communication and event reception |
|
... |
... |
class Bot: |
41 |
43 |
print("Connecting to Discord...") |
print("Connecting to Discord...") |
42 |
44 |
self.comm.run() |
self.comm.run() |
43 |
45 |
print("\nBotly exited.") |
print("\nBotly exited.") |
44 |
|
|
|
|
46 |
|
|
|
47 |
|
def print(self, message, msgtype='INFO'): |
|
48 |
|
print('[' + msgtype + ']' + message) |
|
49 |
|
|
45 |
50 |
async def leave(self): |
async def leave(self): |
46 |
51 |
"""Coroutine that disconnects the robot from Discord.""" |
"""Coroutine that disconnects the robot from Discord.""" |
47 |
52 |
await self.comm.logout() |
await self.comm.logout() |
|
... |
... |
class Bot: |
60 |
65 |
if reactions: |
if reactions: |
61 |
66 |
for reaction in reactions: |
for reaction in reactions: |
62 |
67 |
if reaction.trigger.is_triggered(self, **eventInfo): |
if reaction.trigger.is_triggered(self, **eventInfo): |
63 |
|
print("Reaction '" + reaction.moduleName |
|
64 |
|
+ "' triggered by event '" + eventName + "'.") |
|
65 |
68 |
reaction.prepare_react(**eventInfo) |
reaction.prepare_react(**eventInfo) |
66 |
69 |
await reaction.react() |
await reaction.react() |
67 |
70 |
|
|
File botly/db.py changed (mode: 100644) (index ede5918..5789619) |
... |
... |
class Db: |
8 |
8 |
|
|
9 |
9 |
dbxml = False |
dbxml = False |
10 |
10 |
|
|
11 |
|
def load(self, filePath): |
|
12 |
|
Db.dbxml = etree.parse(filePath) |
|
|
11 |
|
def __init__(self): |
|
12 |
|
assert False, "No instance of this class should be created." |
|
13 |
|
|
|
14 |
|
@classmethod |
|
15 |
|
def load(cls, filePath): |
|
16 |
|
"""Loads the database XML file at given path.""" |
|
17 |
|
cls.dbxml = etree.parse(filePath) |
|
18 |
|
|
|
19 |
|
def get_node(self, path): |
|
20 |
|
"""Returns the node at the given absolute path""" |
|
21 |
|
return Db.dbxml.xpath(path)[0] |
13 |
22 |
|
|
14 |
23 |
def is_loaded(self): |
def is_loaded(self): |
|
24 |
|
"""Returns whether or not the database was loaded.""" |
15 |
25 |
return Db.dbxml != False |
return Db.dbxml != False |
16 |
26 |
|
|
17 |
|
def get_all_items(self, elementName): |
|
18 |
|
return Db.dbxml.xpath('{0}'.format(elementName)) |
|
|
27 |
|
def query_value(self, valueName, parentPath = None): |
|
28 |
|
"""Loads a single value from the given parent.""" |
|
29 |
|
self._pre_check() |
|
30 |
|
|
|
31 |
|
# Define parent: Either the root elem or given parent path |
|
32 |
|
parent = None |
|
33 |
|
if parentPath is None: |
|
34 |
|
parent = self.root |
|
35 |
|
else: |
|
36 |
|
parent = self.root.xpath(parentXpath)[0] |
|
37 |
|
|
|
38 |
|
if parent is not None: |
|
39 |
|
print(parent) |
|
40 |
|
valueElem = parent.find(valueName) |
|
41 |
|
if valueElem is not None and valueElem.text is not None: |
|
42 |
|
return valueElem.text |
|
43 |
|
return None |
|
44 |
|
|
|
45 |
|
def query_object(self, parentXpath, objectId): |
|
46 |
|
"""Loads a single object identified by its Id. None if not found.""" |
|
47 |
|
self._pre_check() |
|
48 |
|
|
|
49 |
|
parent = self.root.xpath(parentXpath)[0] |
|
50 |
|
if parent is not None: |
|
51 |
|
for child1 in parent.iterchildren(): |
|
52 |
|
if child1.get('Id') != objectId: |
|
53 |
|
continue |
|
54 |
|
return self._load_object_values(child1) |
|
55 |
|
return None |
|
56 |
|
|
|
57 |
|
def query_object_list(self, parentXpath): |
|
58 |
|
"""Loads a list of objects, represented by a dict with its values""" |
|
59 |
|
self._pre_check() |
19 |
60 |
|
|
20 |
|
def get_item(self, elementName, attributeName, value): |
|
21 |
|
item = Db.dbxml.xpath('{0}[@{1}="{2}"]'.format(elementName, |
|
22 |
|
attributeName, value)) |
|
23 |
|
return item[0] if item else None |
|
|
61 |
|
parent = self.root.xpath(parentXpath)[0] |
|
62 |
|
if parent is not None: |
|
63 |
|
objects = [] |
|
64 |
|
for child1 in parent.iterchildren(): |
|
65 |
|
objects.append(self._load_object_values(child1)) |
|
66 |
|
return objects |
|
67 |
|
return None |
24 |
68 |
|
|
25 |
|
def get_value(self, element, childName): |
|
26 |
|
child = element.xpath(childName + '/text()') |
|
27 |
|
return child[0] if child else None |
|
|
69 |
|
def _load_object_values(self, xmlobj): |
|
70 |
|
"""Loads the given xml node in a dictionary, considered as an object. |
28 |
71 |
|
|
|
72 |
|
Objects are suspected to have an Id specified as an attribute on their |
|
73 |
|
root xml element. It will be loaded in the dictionary as 'Id'. |
|
74 |
|
|
|
75 |
|
Elements containing no children are considered as simple values. |
|
76 |
|
They are loaded in the dict using their tagname and their value. |
|
77 |
|
|
|
78 |
|
Elements that contains children are considered a list of value. A list |
|
79 |
|
is created in the dict under that element's tag name containing the |
|
80 |
|
value of its children. Those children's tag name is ignored.""" |
|
81 |
|
|
|
82 |
|
obj = {} |
|
83 |
|
obj['Id'] = xmlobj.get('Id') |
|
84 |
|
for child in xmlobj.iterchildren(): |
|
85 |
|
if len(child) == 0: |
|
86 |
|
# We have no children, this node is value |
|
87 |
|
obj[child.tag] = child.text |
|
88 |
|
else: |
|
89 |
|
# We have children. We consider this node as a list |
|
90 |
|
obj[child.tag] = [] |
|
91 |
|
for child2 in child.iterchildren(): |
|
92 |
|
if child2.text is not None: |
|
93 |
|
obj[child.tag].append(child2.text) |
|
94 |
|
return obj |
29 |
95 |
|
|
|
96 |
|
def _pre_check(self): |
|
97 |
|
assert self.is_loaded(), "The DB is not loaded." |
|
98 |
|
assert self.root is not None, "Root node not defined." |
30 |
99 |
|
|
File botly/knowledge.py changed (mode: 100644) (index 4d59510..4127b2d) |
2 |
2 |
"""Class Knowledge represents what bot knows, such as users and their info.""" |
"""Class Knowledge represents what bot knows, such as users and their info.""" |
3 |
3 |
|
|
4 |
4 |
from botly.db import Db |
from botly.db import Db |
|
5 |
|
from botly.users import Users |
|
6 |
|
from botly.groups import Groups |
5 |
7 |
|
|
6 |
8 |
|
|
7 |
9 |
class Knowledge(Db): |
class Knowledge(Db): |
8 |
10 |
"""Represents what bot knows. Use accessors to access data.""" |
"""Represents what bot knows. Use accessors to access data.""" |
9 |
11 |
|
|
10 |
|
userValues=['Alias', 'BotAffinity', 'IsMaster'] |
|
11 |
|
|
|
12 |
12 |
def __init__(self): |
def __init__(self): |
13 |
|
self.users = [] |
|
14 |
|
|
|
15 |
|
# Shortcut accessors for built in values: |
|
16 |
|
|
|
17 |
|
def get_user_value(self, userId, valueName, default=False): |
|
18 |
|
"""Returns a precise value for given user. Try to load from XML.""" |
|
19 |
|
user = self.get_user(userId) |
|
20 |
|
if not user: |
|
21 |
|
return default |
|
22 |
|
if valueName not in user: |
|
23 |
|
# Try to load this value as it may not have been loaded yet |
|
24 |
|
self._load_user_value(userId, valueName) |
|
25 |
|
return user[valueName] |
|
26 |
|
|
|
27 |
|
def get_user_affinity(self, userId): |
|
28 |
|
"""Returns the affinity of the user designated by given id.""" |
|
29 |
|
user = self.get_user(userId) |
|
30 |
|
if not user or user['BotAffinity'] is None: |
|
31 |
|
# We have an affinity of 0 (neutral) for unknown users |
|
32 |
|
return 0 |
|
33 |
|
return int(user['BotAffinity']) |
|
34 |
|
|
|
35 |
|
def get_user_alias(self, userId): |
|
36 |
|
"""Returns the alias of the user designated by given id.""" |
|
37 |
|
user = self.get_user(userId) |
|
38 |
|
if not user or user['Alias'] is None: |
|
39 |
|
return '' |
|
40 |
|
return user['Alias'] |
|
41 |
|
|
|
42 |
|
def is_user_master(self, userId): |
|
43 |
|
"""Returns whether or not user is a master (can command bot).""" |
|
44 |
|
user = self.get_user(userId) |
|
45 |
|
if not user or user['IsMaster'] is None: |
|
46 |
|
return False |
|
47 |
|
return user['IsMaster'] == '1' |
|
48 |
|
|
|
49 |
|
# User methods: |
|
50 |
|
|
|
51 |
|
def add_user_values(self, *valueNames): |
|
52 |
|
for name in valueNames: |
|
53 |
|
Knowledge.userValues.append(name) |
|
54 |
|
# Empty the table, as we now have new values for each users |
|
55 |
|
self.users.clear() |
|
56 |
|
|
|
57 |
|
def get_users(self): |
|
58 |
|
"""Returns the whole users table. Loads it from XML if not done yet.""" |
|
59 |
|
if not self.users: |
|
60 |
|
self._load_all_users() |
|
61 |
|
return self.users |
|
62 |
|
|
|
63 |
|
def get_user(self, userId): |
|
64 |
|
"""Return all botly values for given user id.""" |
|
65 |
|
user = self._has_loaded_user(userId, getuser=True) |
|
66 |
|
if not user: |
|
67 |
|
user = self._load_user(userId) |
|
68 |
|
if not user: |
|
69 |
|
return False |
|
70 |
|
return user |
|
71 |
|
|
|
72 |
|
def user_exists(self, userId): |
|
73 |
|
"""Returns whether or not we have this user in our knowledge db.""" |
|
74 |
|
return True if self.get_user(userId) else False |
|
75 |
|
|
|
76 |
|
# Internal methods: |
|
77 |
|
|
|
78 |
|
def _load_user_value(self, userId, valueName): |
|
79 |
|
assert self.is_loaded(), 'Xml document is not loaded' |
|
80 |
|
userxml = self.get_item('/BotDb/Knowledge/Users/User', 'Id', userId) |
|
81 |
|
if userxml is not None: |
|
82 |
|
value = self.get_value(userxml, valueName) |
|
83 |
|
user = self._has_loaded_user(userId, getuser=True) |
|
84 |
|
user[valueName] = value # Value could still be None |
|
85 |
|
return None |
|
86 |
|
|
|
87 |
|
def _load_user(self, userId): |
|
88 |
|
assert self.is_loaded(), 'Xml document is not loaded' |
|
89 |
|
userxml = self.get_item('/BotDb/Knowledge/Users/User', 'Id', userId) |
|
90 |
|
if userxml is not None: |
|
91 |
|
user = {} |
|
92 |
|
user['Id'] = userxml.get('Id') # Id is a XML attribute |
|
93 |
|
for valueName in Knowledge.userValues: |
|
94 |
|
user[valueName] = self.get_value(userxml, valueName) |
|
95 |
|
# Remove already loaded user. Does nothing if it doesn't exist yet |
|
96 |
|
self._remove_loaded_user(userId) |
|
97 |
|
self.users.append(user) |
|
98 |
|
return user |
|
99 |
|
return False |
|
|
13 |
|
self.users = Users() |
|
14 |
|
self.groups = Groups() |
100 |
15 |
|
|
101 |
|
def _load_all_users(self): |
|
102 |
|
assert self.is_loaded(), 'Xml document is not loaded' |
|
103 |
|
# Clear the user table, in case we are reloading users |
|
104 |
|
self.users.clear() |
|
105 |
|
for userxml in self.get_all_items('/BotDb/Knowledge/Users/User'): |
|
106 |
|
user = {} |
|
107 |
|
user['Id'] = userxml.get('Id') |
|
108 |
|
# Retrieve user values |
|
109 |
|
for valueName in Knowledge.userValues: |
|
110 |
|
user[valueName] = self.get_value(userxml, valueName) |
|
111 |
|
self.users.append(user) |
|
112 |
|
|
|
113 |
|
def _remove_loaded_user(self, userId): |
|
114 |
|
for user in self.users: |
|
115 |
|
if user['Id'] == userId: |
|
116 |
|
self.users.remove(user) |
|
117 |
|
break |
|
118 |
|
return False |
|
119 |
|
|
|
120 |
|
def _has_loaded_user(self, userId, getuser=False): |
|
121 |
|
for user in self.users: |
|
122 |
|
if user['Id'] == userId: |
|
123 |
|
return user if getuser else True |
|
124 |
|
return False |
|
125 |
|
|
|
126 |
|
|
|
File botly/reaction.py changed (mode: 100644) (index cdd185c..99c136c) |
... |
... |
class ReactionBase: |
49 |
49 |
|
|
50 |
50 |
def print(self, message): |
def print(self, message): |
51 |
51 |
"""Helper for console printing. Will prepend current Reaction name.""" |
"""Helper for console printing. Will prepend current Reaction name.""" |
52 |
|
print('[{0}] {1}'.format(self.moduleName, message)) |
|
|
52 |
|
self.bot.print('[Reaction][{0}] {1}'.format(self.moduleName, message)) |
53 |
53 |
|
|
54 |
54 |
def is_mentioned(self): |
def is_mentioned(self): |
55 |
55 |
"""Returns whether or not our bot was mentioned in the message.""" |
"""Returns whether or not our bot was mentioned in the message.""" |
|
... |
... |
class ReactionBase: |
66 |
66 |
"""Coroutine that send message to given channel.""" |
"""Coroutine that send message to given channel.""" |
67 |
67 |
await self.bot.say(channel, message) |
await self.bot.say(channel, message) |
68 |
68 |
|
|
69 |
|
def set_bot_instance(self, bot): |
|
|
69 |
|
def set_instance_info(self, bot): |
70 |
70 |
"""Saves botly instance. Only meant to becalled upon module import.""" |
"""Saves botly instance. Only meant to becalled upon module import.""" |
71 |
71 |
self.bot = bot |
self.bot = bot |
72 |
72 |
self.knowledge = bot.knowledge |
self.knowledge = bot.knowledge |
|
... |
... |
def load_reactions(bot, reactionsParent): |
122 |
122 |
assert len(reaction.eventName), \ |
assert len(reaction.eventName), \ |
123 |
123 |
'Loaded a reaction linked to no event.' |
'Loaded a reaction linked to no event.' |
124 |
124 |
# Inject instance info in reaction object |
# Inject instance info in reaction object |
125 |
|
reaction.set_bot_instance(bot) |
|
|
125 |
|
reaction.set_instance_info(bot) |
126 |
126 |
reaction.set_module_name(module) |
reaction.set_module_name(module) |
127 |
127 |
print('Loaded ' + str(len(reactions)) + ' reactions from drive.') |
print('Loaded ' + str(len(reactions)) + ' reactions from drive.') |
128 |
128 |
return reactions |
return reactions |
File botly/settings.py changed (mode: 100644) (index bad6ea3..84d336b) |
... |
... |
from botly.db import Db |
7 |
7 |
class Settings(Db): |
class Settings(Db): |
8 |
8 |
|
|
9 |
9 |
def __init__(self): |
def __init__(self): |
10 |
|
self.settings = {} |
|
|
10 |
|
assert self.is_loaded(), "Db is not loaded before loading settings." |
|
11 |
|
self.root = self.get_node('/BotDb/Settings') |
|
12 |
|
self.settingsCache = {} |
11 |
13 |
|
|
12 |
|
def get_string(self, key, default='None'): |
|
|
14 |
|
def get_string(self, key, default=''): |
13 |
15 |
"""Returns the string value if it exists.""" |
"""Returns the string value if it exists.""" |
14 |
|
if not key in self.settings: |
|
15 |
|
self.settings[key] = self._load_setting(key, default) |
|
16 |
|
return self.settings[key] |
|
|
16 |
|
if key not in self.settingsCache: |
|
17 |
|
self.settingsCache[key] = self._load_setting(key, default) |
|
18 |
|
return self.settingsCache[key] |
17 |
19 |
|
|
18 |
20 |
def get_int(self, key, default='0'): |
def get_int(self, key, default='0'): |
19 |
21 |
"""Returns the int value if it exists.""" |
"""Returns the int value if it exists.""" |
|
... |
... |
class Settings(Db): |
23 |
25 |
return int(v) |
return int(v) |
24 |
26 |
|
|
25 |
27 |
def _load_setting(self, key, default): |
def _load_setting(self, key, default): |
26 |
|
assert self.is_loaded(), "Database is not loaded." |
|
27 |
|
setting = Db.dbxml.xpath('/BotDb/Settings/' + key) |
|
28 |
|
if setting: |
|
29 |
|
return setting[0].text |
|
30 |
|
else: |
|
31 |
|
return default |
|
|
28 |
|
setting = self.query_value(key) |
|
29 |
|
return setting if setting is not None else default |
|
30 |
|
|
File botly/task.py changed (mode: 100644) (index cbc32e0..6cf8f59) |
... |
... |
import discord |
12 |
12 |
class TaskBase: |
class TaskBase: |
13 |
13 |
"""Base class for bot's background tasks""" |
"""Base class for bot's background tasks""" |
14 |
14 |
|
|
15 |
|
def __init__(self): |
|
16 |
|
self.endTask = False |
|
17 |
|
|
|
18 |
15 |
def set_instance_info(self, bot): |
def set_instance_info(self, bot): |
|
16 |
|
self.endTask = False |
19 |
17 |
self.bot = bot |
self.bot = bot |
20 |
18 |
self.knowledge = bot.knowledge |
self.knowledge = bot.knowledge |
21 |
19 |
self.settings = bot.settings |
self.settings = bot.settings |
|
... |
... |
class TaskBase: |
24 |
22 |
self.mainChannel = discord.Object( |
self.mainChannel = discord.Object( |
25 |
23 |
id=bot.settings.get_string('DefaultChannelId')) |
id=bot.settings.get_string('DefaultChannelId')) |
26 |
24 |
|
|
|
25 |
|
def print(self, message): |
|
26 |
|
self.bot.print('[Tasks][' + self.moduleName + ']' + ' ' + message) |
|
27 |
|
|
27 |
28 |
async def run(self): |
async def run(self): |
28 |
29 |
"""Entry point of task. Do not overwrite.""" |
"""Entry point of task. Do not overwrite.""" |
29 |
30 |
|
|
30 |
|
# Wait until the bot is ready before running |
|
|
31 |
|
# Wait until the bot is ready before running the task |
31 |
32 |
await self.comm.wait_until_ready() |
await self.comm.wait_until_ready() |
32 |
33 |
|
|
33 |
34 |
while not self.comm.is_closed or not self.endTask: |
while not self.comm.is_closed or not self.endTask: |
34 |
35 |
await asyncio.sleep(self.get_next_timeout()) |
await asyncio.sleep(self.get_next_timeout()) |
|
36 |
|
if self.comm is None or self.comm.is_closed or self.endTask: |
|
37 |
|
break |
35 |
38 |
await self.do() |
await self.do() |
36 |
39 |
|
|
37 |
40 |
def get_next_timeout(self): |
def get_next_timeout(self): |
|
... |
... |
class TaskBase: |
42 |
45 |
"""Proceed with the task's action. To be overwritten when inherited""" |
"""Proceed with the task's action. To be overwritten when inherited""" |
43 |
46 |
pass |
pass |
44 |
47 |
|
|
|
48 |
|
def set_module_name(self, name): |
|
49 |
|
self.moduleName = name |
45 |
50 |
|
|
46 |
51 |
def load_tasks(bot, tasksParent): |
def load_tasks(bot, tasksParent): |
47 |
52 |
"""Function that loads the tasks from given paren module directory.""" |
"""Function that loads the tasks from given paren module directory.""" |
|
... |
... |
def load_tasks(bot, tasksParent): |
63 |
68 |
tasks.append(task) |
tasks.append(task) |
64 |
69 |
# Inject instance info in task object |
# Inject instance info in task object |
65 |
70 |
task.set_instance_info(bot) |
task.set_instance_info(bot) |
|
71 |
|
task.set_module_name(module) |
66 |
72 |
print('Loaded ' + str(len(tasks)) + ' tasks from drive.') |
print('Loaded ' + str(len(tasks)) + ' tasks from drive.') |
67 |
73 |
return tasks |
return tasks |
68 |
74 |
|
|
File botly/trigger.py changed (mode: 100644) (index 6a06731..29e6975) |
... |
... |
class Trigger: |
54 |
54 |
self.advConditions.append(callback) |
self.advConditions.append(callback) |
55 |
55 |
|
|
56 |
56 |
def set_trigger_chance(self, percent): |
def set_trigger_chance(self, percent): |
|
57 |
|
"""Adds a chance for the trigger to activate. 100% is default value.""" |
|
58 |
|
|
57 |
59 |
assert isinstance(value, int), 'Int expected for trigger chance.' |
assert isinstance(value, int), 'Int expected for trigger chance.' |
58 |
60 |
percent = percent if percent >= 1 else 1 |
percent = percent if percent >= 1 else 1 |
59 |
61 |
percent = percent if percent <= 100 else 100 |
percent = percent if percent <= 100 else 100 |
|
... |
... |
class Trigger: |
65 |
67 |
self.requireMention = value |
self.requireMention = value |
66 |
68 |
|
|
67 |
69 |
def is_triggered(self, botly, **eventInfo): |
def is_triggered(self, botly, **eventInfo): |
68 |
|
"""This should only be called from Botly class. |
|
|
70 |
|
"""This should only be called from Bot class. |
69 |
71 |
|
|
70 |
72 |
Checks whether or not the trigger object activates based on the |
Checks whether or not the trigger object activates based on the |
71 |
73 |
given information. This is meant to be called from the Botly class |
given information. This is meant to be called from the Botly class |
File botly/users.py added (mode: 100644) (index 0000000..c125b44) |
|
1 |
|
#!/usr/bin/env python |
|
2 |
|
"""Contains Users class which represents the users database""" |
|
3 |
|
|
|
4 |
|
from botly.db import Db |
|
5 |
|
|
|
6 |
|
|
|
7 |
|
class Users(Db): |
|
8 |
|
|
|
9 |
|
def __init__(self): |
|
10 |
|
assert self.is_loaded(), "Db is not loaded upon Users object creation." |
|
11 |
|
self.userCache = [] |
|
12 |
|
self.root = self.get_node('/BotDb/Knowledge') |
|
13 |
|
|
|
14 |
|
def is_master(self, userid): |
|
15 |
|
"""Returns whether or not this user can control the bot.""" |
|
16 |
|
user = self.get(userid) |
|
17 |
|
if user is not None: |
|
18 |
|
if user['IsMaster'] == '1': |
|
19 |
|
return True |
|
20 |
|
return False |
|
21 |
|
|
|
22 |
|
def affinity(self, userid): |
|
23 |
|
"""Returns the bot affinity to the user.""" |
|
24 |
|
user = self.get(userid) |
|
25 |
|
if user is not None: |
|
26 |
|
return int(user['BotAffinity']) |
|
27 |
|
return 0 |
|
28 |
|
|
|
29 |
|
def get(self, userid): |
|
30 |
|
"""Returns the user with given ID or None if it does not exist.""" |
|
31 |
|
user = self._get_cached_user(userid) |
|
32 |
|
if user is None: |
|
33 |
|
user = self.query_object('Users', userid) |
|
34 |
|
return user |
|
35 |
|
|
|
36 |
|
def get_all(self): |
|
37 |
|
"""Returns all users from the database.""" |
|
38 |
|
users = self.query_object_list('Users') |
|
39 |
|
self.userCache.clear() |
|
40 |
|
self.userCache = users |
|
41 |
|
return users |
|
42 |
|
|
|
43 |
|
def _get_cached_user(self, userid): |
|
44 |
|
"""Returns the cached user if it exists, None otherwise.""" |
|
45 |
|
for user in self.userCache: |
|
46 |
|
if user['Id'] == userid: |
|
47 |
|
return user |
|
48 |
|
return None |
|
49 |
|
|
|
50 |
|
|