shellbot.engine module¶
-
class
shellbot.engine.
Engine
(context=None, settings={}, configure=False, mouth=None, ears=None, fan=None, space=None, type=None, server=None, store=None, command=None, commands=None, driver=<class 'shellbot.bot.ShellBot'>, machine_factory=None, updater_factory=None, preload=0)[source]¶ Bases:
object
Powers multiple bots
The engine manages the infrastructure that is used accross multiple bots acting in multiple spaces. It is made of an extensible set of components that share the same context, that is, configuration settings.
Shellbot allows the creation of bots with a given set of commands. Each bot instance is bonded to a single chat space. The chat space can be either created by the bot itself, or the bot can join an existing space.
The first use case is adapted when a collaboration space is created for semi-automated interactions between human and machines. In the example below, the bot controls the entire life cycle of the chat space. A chat space is created when the program is launched. And it is deleted when the program is stopped.
Example of programmatic chat space creation:
from shellbot import Engine, ShellBot, Context, Command Context.set_logger() # create a bot and load command # class Hello(Command): keyword = 'hello' information_message = u"Hello, World!" engine = Engine(command=Hello(), type='spark') # load configuration # engine.configure() # create a chat space, or connect to an existing one # settings of the chat space are provided # in the engine configuration itself # engine.bond(reset=True) # run the engine # engine.run() # delete the chat channel when the engine is stopped # engine.dispose()
A second interesting use case is when a bot is invited to an existing chat space. On such an event, a new bot instance can be created and bonded to the chat space.
Example of invitation to a chat space:
def on_enter(self, channel_id): bot = engine.get_bot(channel_id=channel_id)
The engine is configured by setting values in the context that is attached to it. This is commonly done by loading the context with a dict before the creation of the engine itself, as in the following example:
context = Context({ 'bot': { 'on_enter': 'You can now chat with Batman', 'on_exit': 'Batman is now quitting the channel, bye', }, 'server': { 'url': 'http://d9b62df9.ngrok.io', 'hook': '/hook', }, }) engine = Engine(context=context) engine.configure()
Please note that the configuration is checked and actually used on the call
engine.configure()
, rather on the initialisation itself.When configuration statements have been stored in a separate text file in YAML format, then the engine can be initialised with an empty context, and configuration is loaded afterwards.
Example:
engine = Engine() engine.configure_from_path('/opt/shellbot/my_bot.yaml')
When no configuration is provided to the engine, then default settings are considered for the engine itself, and for various components.
For example, for a basic engine interacting in a Cisco Spark channel:
engine = Engine(type='spark') engine.configure()
When no indication is provided at all, the engine loads a space of type ‘local’.
So, in other terms:
engine = Engine() engine.configure()
is strictly equivalent to:
engine = Engine('local') engine.configure()
In principle, the configuration of the engine is set once for the full life of the instance. This being said, some settings can be changed globally with the member function set(). For example:
engine.set('bot.on_banner': 'Hello, I am here to help')
-
DEFAULT_SETTINGS
= {'bot': {'banner.content': '$BOT_BANNER_CONTENT', 'on_enter': '$BOT_ON_ENTER', 'on_exit': '$BOT_ON_EXIT', 'banner.text': '$BOT_BANNER_TEXT', 'banner.file': '$BOT_BANNER_FILE'}}¶
-
bond
(title=None, reset=False, participants=None, **kwargs)[source]¶ Bonds to a channel
Parameters: - title – title of the target channel
- reset (bool) – if True, delete previous channel and re-create one
- participants (list of str) – the list of initial participants (optional)
Type: title: str
Returns: Channel or None
This function creates a channel, or connect to an existing one. If no title is provided, then the generic title configured for the underlying space is used instead.
For example:
channel = engine.bond('My crazy channel') if channel: ...
Note: this function asks the listener to load a new bot in its cache on successful channel creation or lookup. In other terms, this function can be called safely from any process for the creation of a channel.
-
build_bot
(id=None, driver=<class 'shellbot.bot.ShellBot'>)[source]¶ Builds a new bot
Parameters: id (str) – The unique id of the target space Returns: a ShellBot instance, or None This function receives the id of a chat space, and returns the related bot.
-
build_machine
(bot)[source]¶ Builds a state machine for this bot
Parameters: bot (ShellBot) – The target bot Returns: a Machine instance, or None This function receives a bot, and returns a state machine bound to it.
-
build_store
(channel_id=None)[source]¶ Builds a store for this bot
Parameters: channel_id (str) – Identifier of the target chat space Returns: a Store instance, or None This function receives an identifier, and returns a store bound to it.
-
build_updater
(id)[source]¶ Builds an updater for this channel
Parameters: id (str) – The identifier of an audited channel Returns: an Updater instance, or None This function receives a bot, and returns a state machine bound to it.
-
check
()[source]¶ Checks settings of the engine
Parameters: settings (dict) – a dictionary with some statements for this instance This function reads key
bot
and below, and update the context accordingly.Example:
context = Context({ 'bot': { 'on_enter': 'You can now chat with Batman', 'on_exit': 'Batman is now quitting the channel, bye', }, 'server': { 'url': 'http://d9b62df9.ngrok.io', 'hook': '/hook', }, }) engine = Engine(context=context) engine.check()
-
configure
(settings={})[source]¶ Checks settings
Parameters: settings (dict) – configuration information If no settings is provided, and the context is empty, then
self.DEFAULT_SETTINGS
andself.space.DEFAULT_SETTINGS
are used instead.
-
configure_from_file
(stream)[source]¶ Reads configuration information
Parameters: stream (file) – the handle that contains configuration information The function loads configuration from the file and from the environment. Port number can be set from the command line.
-
configure_from_path
(path='settings.yaml')[source]¶ Reads configuration information
Parameters: path (str) – path to the configuration file The function loads configuration from the file and from the environment. Port number can be set from the command line.
-
dispatch
(event, **kwargs)[source]¶ Triggers objects that have registered to some event
Parameters: event (str) – label of the event Example:
def on_bond(self): self.dispatch('bond', bot=this_bot)
For each registered object, the function will look for a related member function and call it. For example for the event ‘bond’ it will look for the member function ‘on_bond’, etc.
Dispatch uses weakref so that it affords the unattended deletion of registered objects.
-
dispose
(title=None, **kwargs)[source]¶ Destroys a named channel
Parameters: title – title of the target channel Type: title: str
-
get
(key, default=None)[source]¶ Retrieves the value of one configuration key
Parameters: - key (str) – name of the value
- default (any serializable type is accepted) – default value
Returns: the actual value, or the default value, or None
Example:
message = engine.get('bot.on_start')
This function is safe on multiprocessing and multithreading.
-
get_bot
(channel_id=None, **kwargs)[source]¶ Gets a bot by id
Parameters: channel_id (str) – The unique id of the target chat space Returns: a bot instance, or None This function receives the id of a chat space, and returns the related bot.
If no id is provided, then the underlying space is asked to provide with a default channel, as set in overall configuration.
Note: this function should not be called from multiple processes, because this would create one bot per process. Use the function
engine.bond()
for the creation of a new channel.
-
hook
(server=None)[source]¶ Connects this engine with back-end API
Parameters: server (Server) – web server to be used This function adds a route to the provided server, and asks the back-end service to send messages there.
-
load_command
(*args, **kwargs)[source]¶ Loads one commands for this bot
This function is a convenient proxy for the underlying shell.
-
load_commands
(*args, **kwargs)[source]¶ Loads commands for this bot
This function is a convenient proxy for the underlying shell.
-
name
¶ Retrieves the dynamic name of this bot
Returns: The value of bot.name
key in current contextReturn type: str
-
on_build
(bot)[source]¶ Extends the building of a new bot instance
Parameters: bot (ShellBot) – a new bot instance Provide your own implementation in a sub-class where required.
Example:
on_build(self, bot): bot.secondary_machine = Input(...)
-
on_enter
(join)[source]¶ Bot has been invited to a chat space
Parameters: join (Join) – The join event received from the chat space Provide your own implementation in a sub-class where required.
Example:
on_enter(self, join): mailer.post(u"Invited to {}".format(join.space_title))
-
on_exit
(leave)[source]¶ Bot has been kicked off from a chat space
Parameters: leave (Leave) – The leave event received from the chat space Provide your own implementation in a sub-class where required.
Example:
on_exit(self, leave): mailer.post(u"Kicked off from {}".format(leave.space_title))
-
on_start
()[source]¶ Does additional stuff when the engine is started
Provide your own implementation in a sub-class where required.
-
on_stop
()[source]¶ Does additional stuff when the engine is stopped
Provide your own implementation in a sub-class where required.
Note that some processes may have been killed at the moment of this function call. This is likely to happen when end-user hits Ctl-C on the keyboard for example.
-
register
(event, instance)[source]¶ Registers an object to process an event
Parameters: - event (str) – label, such as ‘start’ or ‘bond’
- instance (object) – an object that will handle the event
This function is used to propagate events to any module that may need it via callbacks.
On each event, the engine will look for a related member function in the target instance and call it. For example for the event ‘start’ it will look for the member function ‘on_start’, etc.
Following standard events can be registered:
- ‘bond’ - when the bot has connected to a chat channel
- ‘dispose’ - when resources, including chat space, will be destroyed
- ‘start’ - when the engine is started
- ‘stop’ - when the engine is stopped
- ‘join’ - when a person is joining a space
- ‘leave’ - when a person is leaving a space
Example:
def on_init(self): self.engine.register('bond', self) # call self.on_bond() self.engine.register('dispose', self) # call self.on_dispose()
If the function is called with an unknown label, then a new list of registered callbacks will be created for this event. Therefore the engine can be used for the dispatching of any custom event.
Example:
self.engine.register('input', processor) # for processor.on_input() ... received = 'a line of text' self.engine.dispatch('input', received)
Registration uses weakref so that it affords the unattended deletion of registered objects.
-
run
(server=None)[source]¶ Runs the engine
Parameters: server (Server) – a web server If a server is provided, it is ran in the background. A server could also have been provided during initialisation, or loaded during configuration check.
If no server instance is available, a loop is started to fetch messages in the background.
In both cases, this function does not return, except on interrupt.
-
set
(key, value)[source]¶ Changes the value of one configuration key
Parameters: - key (str) – name of the value
- value (any serializable type is accepted) – new value
Example:
engine.set('bot.on_start', 'hello world')
This function is safe on multiprocessing and multithreading.
-
start_processes
()[source]¶ Starts the engine processes
This function starts a separate process for each main component of the architecture: listener, speaker, etc.
-
stop
()[source]¶ Stops the engine
This function changes in the context a specific key that is monitored by bot components.
-
version
¶ Retrieves the version of this bot
Returns: The value of bot.version
key in current contextReturn type: str
-