List of commits:
Subject Hash Author Date (UTC)
Modified Knowledge process. Fixed indent 28e52095a297cbb0e6b9b6d799bb80ebe52828c1 Detche 2016-10-23 12:11:30
Updated README 8e298151286ec3cb4df29633aaca90fc871f2e80 Detche 2016-10-23 10:10:45
Update README.rst 7965df28cbef557fd3819919b4d44f0733dcecf0 Guillaume 2016-10-23 00:06:05
Added README page 8339ac5b13803b026dbfc5edd77fa50a24a70ecb Detche 2016-10-22 23:47:05
Added comments to sources. ce6491ef1337ea34d33b7e4ede0eb9f634e7ab1a Detche 2016-10-22 23:32:26
Added examples. Modified reaction class to add triggers. 545a35d1c5bb36e1b4e4d818b0e6a8f6642a222f Detche 2016-10-22 20:56:54
Initial commit 2cd79df6224290a994b1453f0327740816b0f632 Detche 2016-10-21 16:21:02
Commit 28e52095a297cbb0e6b9b6d799bb80ebe52828c1 - Modified Knowledge process. Fixed indent
Author: Detche
Author date (UTC): 2016-10-23 12:11
Committer name: Detche
Committer date (UTC): 2016-10-23 12:11
Parent(s): 8e298151286ec3cb4df29633aaca90fc871f2e80
Signing key:
Tree: 0f09b6052baf7c841cd3050cdadbb96248a77015
File Lines added Lines deleted
botly/behaviour.py 2 2
botly/comm.py 1 1
botly/db.py 10 4
botly/knowledge.py 96 35
botly/reaction.py 16 3
File botly/behaviour.py changed (mode: 100644) (index d5908fd..5221f53)
... ... class Behaviour:
11 11 # they react on. # they react on.
12 12 print('Loading reactions into behaviour...') print('Loading reactions into behaviour...')
13 13 for reaction in reactions: for reaction in reactions:
14 ename = reaction.get_event_name()
14 ename = reaction.eventName
15 15 print("Loading reaction '" print("Loading reaction '"
16 + reaction.get_module_name()
16 + reaction.moduleName
17 17 + "' into event table '" + "' into event table '"
18 18 + ename + "'.") + ename + "'.")
19 19 if not ename in self.reactions: if not ename in self.reactions:
File botly/comm.py changed (mode: 100644) (index 6fd9842..f2576da)
... ... import discord
7 7
8 8
9 9 class Comm(discord.Client): class Comm(discord.Client):
10 """Inherits from discord.client. Used for communication with server."""
10 """Inherits from discord.client. Used for communication with server."""
11 11
12 12 def __init__(self, botly): def __init__(self, botly):
13 13 self.botly = botly self.botly = botly
File botly/db.py changed (mode: 100644) (index ec32975..ede5918)
... ... class Db:
14 14 def is_loaded(self): def is_loaded(self):
15 15 return Db.dbxml != False return Db.dbxml != False
16 16
17 def get_all_items(self, elementName):
18 return Db.dbxml.xpath('{0}'.format(elementName))
19
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
24
17 25 def get_value(self, element, childName): def get_value(self, element, childName):
18 26 child = element.xpath(childName + '/text()') child = element.xpath(childName + '/text()')
19 if len(child) > 0:
20 return child[0]
21 else:
22 return 'None'
27 return child[0] if child else None
28
23 29
24 30
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)
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/detche/Botly

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/detche/Botly

Clone this repository using git:
git clone git://git.rocketgit.com/user/detche/Botly

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main