File botly/knowledge.py changed (mode: 100644) (index cb5b41d..78325c2) |
... |
... |
from botly.db import Db |
5 |
5 |
|
|
6 |
6 |
|
|
7 |
7 |
class Knowledge(Db): |
class Knowledge(Db): |
8 |
|
"""Represents what bot knows. Use accessors to access data.""" |
|
|
8 |
|
"""Represents what bot knows. Use accessors to access data.""" |
|
9 |
|
|
|
10 |
|
userValues=['Alias', 'BotAffinity', 'IsMaster'] |
9 |
11 |
|
|
10 |
12 |
def __init__(self): |
def __init__(self): |
11 |
13 |
self.users = [] |
self.users = [] |
12 |
14 |
|
|
13 |
|
def get_users(self): |
|
14 |
|
"""Returns the whole users table. Loads it from XML if not done yet.""" |
|
15 |
|
if len(self.users) == 0: |
|
16 |
|
self._load_users() |
|
17 |
|
return self.users |
|
|
15 |
|
# Shortcut accessors for built in values: |
18 |
16 |
|
|
19 |
|
def user_exists(self, id): |
|
20 |
|
"""Returns whether or not we have this user in our knowledge db.""" |
|
21 |
|
return True if self.get_user(id) else False |
|
|
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] |
22 |
26 |
|
|
23 |
|
def get_user(self, id): |
|
24 |
|
"""Return all botly values for given user id.""" |
|
25 |
|
for user in self.get_users(): |
|
26 |
|
if user['Id'] == id: |
|
27 |
|
return user |
|
28 |
|
return False |
|
29 |
|
|
|
30 |
|
def get_user_affinity(self, id): |
|
|
27 |
|
def get_user_affinity(self, userId): |
31 |
28 |
"""Returns the affinity of the user designated by given id.""" |
"""Returns the affinity of the user designated by given id.""" |
32 |
|
user = self.get_user(id) |
|
33 |
|
if user == False: |
|
|
29 |
|
user = self.get_user(userId) |
|
30 |
|
if not user or user['BotAffinity'] is None: |
34 |
31 |
# We have an affinity of 0 (neutral) for unknown users |
# We have an affinity of 0 (neutral) for unknown users |
35 |
32 |
return 0 |
return 0 |
36 |
33 |
return int(user['BotAffinity']) |
return int(user['BotAffinity']) |
37 |
34 |
|
|
38 |
|
def get_user_alias(self, id): |
|
|
35 |
|
def get_user_alias(self, userId): |
39 |
36 |
"""Returns the alias of the user designated by given id.""" |
"""Returns the alias of the user designated by given id.""" |
40 |
|
user = self.get_user(id) |
|
41 |
|
if user == False: |
|
42 |
|
return False |
|
|
37 |
|
user = self.get_user(userId) |
|
38 |
|
if not user or user['Alias'] is None: |
|
39 |
|
return '' |
43 |
40 |
return user['Alias'] |
return user['Alias'] |
44 |
41 |
|
|
45 |
|
def is_user_master(self, id): |
|
|
42 |
|
def is_user_master(self, userId): |
46 |
43 |
"""Returns whether or not user is a master (can command bot).""" |
"""Returns whether or not user is a master (can command bot).""" |
47 |
|
user = self.get_user(id) |
|
48 |
|
if user == False: |
|
|
44 |
|
user = self.get_user(userId) |
|
45 |
|
if not user or user['IsMaster'] is None: |
49 |
46 |
return False |
return False |
50 |
47 |
return user['IsMaster'] == '1' |
return user['IsMaster'] == '1' |
51 |
48 |
|
|
52 |
|
def _load_users(self): |
|
53 |
|
# TODO, Change how users are loaded. Don't load them all at start, but |
|
54 |
|
# only when requested. Also include non-hardcoded possible value. |
|
|
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: |
|
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 |
|
100 |
|
|
|
101 |
|
def _load_all_users(self): |
55 |
102 |
assert self.is_loaded(), 'Xml document is not loaded' |
assert self.is_loaded(), 'Xml document is not loaded' |
56 |
|
self.users = [] |
|
57 |
|
for userxml in Db.dbxml.xpath('/BotDb/Knowledge/Users/User'): |
|
|
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'): |
58 |
106 |
user = {} |
user = {} |
59 |
|
user['Id'] = self.get_value(userxml, 'Id') |
|
60 |
|
user['Alias'] = self.get_value(userxml, 'Alias') |
|
61 |
|
user['BotAffinity'] = self.get_value(userxml, 'BotAffinity') |
|
62 |
|
user['IsMaster'] = self.get_value(userxml, 'IsMaster') |
|
|
107 |
|
user['Id'] = userxml.get('Id') |
|
108 |
|
# Retrieve user values |
|
109 |
|
for valueName in Knowledge.userValues: |
|
110 |
|
user[valueName] = self.get_value(userxml, valueName) |
63 |
111 |
self.users.append(user) |
self.users.append(user) |
64 |
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 |
|
|
65 |
126 |
|
|
File botly/reaction.py changed (mode: 100644) (index 258432e..0520ccf) |
1 |
1 |
#!/usr/bin/env python |
#!/usr/bin/env python |
2 |
|
"""Contains the ReactionBase class, meant to be subclassed.""" |
|
|
2 |
|
"""Contains the ReactionBase class and load_reactions function.""" |
3 |
3 |
|
|
4 |
4 |
import asyncio |
import asyncio |
5 |
5 |
from importlib import import_module |
from importlib import import_module |
|
... |
... |
from botly.trigger import Trigger |
10 |
10 |
|
|
11 |
11 |
|
|
12 |
12 |
class ReactionBase: |
class ReactionBase: |
13 |
|
"""Meant""" |
|
|
13 |
|
"""Subclass this class in a reaction module to create a new Reaction. |
|
14 |
|
|
|
15 |
|
When sublcassed, it should pass the event name hook through the constructor |
|
16 |
|
and should redefine these two methods: |
|
17 |
|
def prepare_trigger(self, trigger) |
|
18 |
|
async def react(self) |
|
19 |
|
|
|
20 |
|
The first method is called when the object is created. It should defines |
|
21 |
|
the trigger's conditions. |
|
22 |
|
|
|
23 |
|
The second one is called when an event triggers this reaction. Place the |
|
24 |
|
actions there. |
|
25 |
|
""" |
14 |
26 |
|
|
15 |
27 |
def __init__(self, eventName): |
def __init__(self, eventName): |
16 |
28 |
"""When subclassed, pass the event name to the mother class.""" |
"""When subclassed, pass the event name to the mother class.""" |
|
... |
... |
class ReactionBase: |
88 |
100 |
self.before = eventInfo['before'] |
self.before = eventInfo['before'] |
89 |
101 |
self.after = eventInfo['after'] |
self.after = eventInfo['after'] |
90 |
102 |
|
|
|
103 |
|
|
91 |
104 |
def load_reactions(botly, reactionsParent): |
def load_reactions(botly, reactionsParent): |
92 |
105 |
"""Function that loads reactions from given parent module directory.""" |
"""Function that loads reactions from given parent module directory.""" |
93 |
106 |
# Convert module path to drive path. |
# Convert module path to drive path. |
|
... |
... |
def load_reactions(botly, reactionsParent): |
106 |
119 |
_reaction = import_module(reactionsParent + '.' + module) |
_reaction = import_module(reactionsParent + '.' + module) |
107 |
120 |
reaction = _reaction.Reaction() |
reaction = _reaction.Reaction() |
108 |
121 |
reactions.append(reaction) |
reactions.append(reaction) |
109 |
|
assert len(reaction.get_event_name()), \ |
|
|
122 |
|
assert len(reaction.eventName), \ |
110 |
123 |
'Loaded a reaction linked to no event.' |
'Loaded a reaction linked to no event.' |
111 |
124 |
# Inject instance info in reaction object |
# Inject instance info in reaction object |
112 |
125 |
reaction.set_botly_instance(botly) |
reaction.set_botly_instance(botly) |