def format(self, event): """ Prepares an outbound line :param event: an inbound event :type event: Event or Message or Join or Leave :return: outbound line :rtype: str This function adapts inbound events to the appropriate format. It turns an object with multiple attributes to a single string that can be pushed to a Cisco Spark room. """ if event.type == 'message': text = _(u"{}: {}") if event.content == event.text: return text.format(event.from_label, event.text) return Vibes(text=text.format(event.from_label, event.text), content=text.format(event.from_label, event.content), file=None) if event.type == 'join': return _(u"{} has joined").format(event.actor_label) if event.type == 'leave': return _(u"{} has left").format(event.actor_label) return _(u"an unknown event has been received")
def on_init(self): """ Localize strings for this command """ self.keyword = _(u'sleep') self.information_message = _(u'Sleep for a while') self.usage_message = _(u'sleep <n>')
def execute(self, bot, arguments=None, **kwargs): """ Lists available commands and related usage information :param bot: The bot for this execution :type bot: Shellbot :param arguments: The arguments for this command :type arguments: str or ``None`` """ if self.engine.shell.commands == []: bot.say(_(u"No command has been found.")) elif not arguments: lines = [] for key in self.engine.shell.commands: command = self.engine.shell.command(key) if command.is_hidden: pass elif self.allow(bot, command): lines.append(u"{} - {}".format( command.keyword, command.information_message)) if not bot.channel.is_direct: # cannot turn 1:1 to group for name in self.engine.list_factory.list_commands(): item = self.engine.list_factory.get_list(name) first = next(iter(item)) lines.append(u"{} - {}".format( name, _(u"add participants ({}, ...)").format(first))) if lines: bot.say(_('Available commands:') + '\n' + '\n'.join(lines)) else: command = self.engine.shell.command(arguments) if not command: bot.say(_(u"This command is unknown.")) elif not self.allow(bot, command): bot.say(_(u"This command is unknown.")) else: lines = [] lines.append(u"{} - {}".format(command.keyword, command.information_message)) if command.usage_message: lines.append( self.usage_template.format(command.usage_message)) else: lines.append(self.usage_template.format(command.keyword)) if lines: bot.say('\n'.join(lines))
def __init__(self, attributes, index): """ Represents a step in a linear process :param attributes: the dict to use :type attributes: dict :param index: index of this step :type index: int """ self.label = attributes.get('label', _(u'Step {}').format(index)) self.message = attributes.get('message', _(u'(no description is available)')) self.content = attributes.get('content', None) self.file = attributes.get('file', None) self.participants = attributes.get('participants', []) if isinstance(self.participants, string_types): self.participants = [self.participants] self.machine = attributes.get('machine', None)
def on_init(self): """ Localize strings for this command """ self.keyword = _(u'*upload') self.information_message = _(u'Handle file upload') self.feedback_message = _(u"Thank you for the information shared!")
def on_init(self): """ Localize strings for this command """ self.keyword = _(u'help') self.information_message = _(u'Show commands and usage') self.usage_message = _(u'help <command>') self.usage_template = _(u"usage: {}")
def on_init(self): """ Localize strings for this command """ self.keyword = _(u'input') self.information_message = _(u'Display all input') self.no_input_message = _(u'There is nothing to display') self.input_header = _(u'Input:')
def on_init(self): """ Localize strings for this command """ self.keyword = _(u'*default') self.information_message = _(u'Handle unmatched commands') self.participants_message = _(u"Adding participants from '{}'") self.default_message = _(u"Sorry, I do not know how to handle '{}'")
def on_init(self): """ Localize strings for this command """ self.keyword = _(u'update') self.information_message = _(u'Update input content') self.no_arg = _(u'Thanks to provide the key and the data') self.no_input = _(u'There is nothing to update, input is empty') self.ok_msg = _(u'Update successfuly done')
def trigger(self, bot): """ Triggers a step :param bot: the bot to use :type bot: ShellBot This function does everything that is coming with a step: - send a message to the chat space, - maybe in MarkDown or HTML, - maybe with some attachment, - add participants to the channel, - reset and start a state machine Example:: step.trigger(bot) """ bot.say(_(u"New state: {} - {}").format(self.label, self.message)) if self.content or self.file: bot.say(' ', content=self.content, file=self.file) bot.add_participants(self.participants) if self.machine: self.machine.stop() self.machine.reset() self._step_process = self.machine.start()
def on_start(self): """ Adds processing on engine start """ sys.stdout.write( _(u"Type 'help' for guidance, or Ctl-C to exit.") + '\n') sys.stdout.flush()
def execute(self, bot, arguments=None, **kwargs): """ Checks and changes audit status :param bot: The bot for this execution :type bot: Shellbot :param arguments: either 'on' or 'off' :type arguments: str """ if not self.has_been_enabled: bot.say(self.disabled_message) elif arguments == 'on': self.audit_on(bot) elif arguments == 'off': self.audit_off(bot) elif not arguments: self.audit_status(bot) else: bot.say(_(u"usage: {}").format(self.usage_message))
def on_init(self, space=None, speaker=None, **kwargs): """ Replicates messages to a secondary space :param space: the target space to use (optional) :type space: Space :param speaker: the speaker instance to use (optional) :type speaker: Speaker Parameters are provided mainly for test injection. """ # create a secondary room # self.space = space if space else SparkSpace( context=self.engine.context) self.space.connect() title = u"{} - {}".format(self.space.configured_title(), _(u"Audited content")) self.space.bond(title=title) # speak incoming updates # self.context = Context() self.mouth = Queue() self.speaker = speaker if speaker else Speaker(engine=self.engine) self.speaker.start()
def execute(self, bot, arguments=None, **kwargs): """ Handles empty command :param bot: The bot for this execution :type bot: Shellbot :param arguments: The arguments for this command :type arguments: str or ``None`` """ if not hasattr(self, 'help_command'): self.help_command = self.engine.shell.command(_('help')) if self.help_command is None: bot.say(_(u"No help command has been found.")) else: self.help_command.execute(bot)
def test_global(self): logging.info('*** global ***') settings = { 'localized': { 'hello world': "What'up, Doc?", 'another string': 'Bye!', }, 'space': { 'title': 'space name', 'participants': ['*****@*****.**'], }, 'server': { 'url': 'http://to.no.where', 'hook': '/hook', 'binding': '0.0.0.0', 'port': 8080, }, } context = Context(settings) l10n.set_context(context) self.assertEqual(l10n.actual_strings, {}) self.assertEqual(_('hello world'), "What'up, Doc?") self.assertEqual(l10n.actual_strings, {'hello world': "What'up, Doc?"}) self.assertEqual(_('not localized'), 'not localized') self.assertEqual(l10n.actual_strings, { 'hello world': "What'up, Doc?", 'not localized': 'not localized' }) self.assertEqual(_('another string'), 'Bye!') self.assertEqual( l10n.actual_strings, { 'hello world': "What'up, Doc?", 'another string': 'Bye!', 'not localized': 'not localized' })
def execute(self, bot, arguments=None, **kwargs): """ Displays software version :param bot: The bot for this execution :type bot: Shellbot :param arguments: The arguments for this command :type arguments: str or ``None`` """ bot.say(_(u"{} version {}").format(self.engine.name, self.engine.version))
def execute(self, bot, arguments=None, **kwargs): """ Restarts the underlying state machine :param bot: The bot for this execution :type bot: Shellbot :param arguments: The arguments for this command :type arguments: str or ``None`` This function calls the ``restart()`` function of the underlying state machine. It also transmits text typed by the end user after the command verb, and any other parameters received from the shell, e.g., attachment, etc. Note: this command has no effect on a running machine. """ if not bot.machine: bot.say(_(u"No state machine is available")) elif not bot.machine.restart(arguments=arguments, **kwargs): bot.say(_(u"Cannot restart the state machine"))
def on_init(self): """ Localize strings for this command and register event """ self.keyword = _(u'audit') self.information_message = _(u'Check and change audit status') self.usage_message = _(u'audit [on|off]') self.disabled_message = _(u'Audit has not been enabled.') self.on_message = _(u'Chat interactions are currently audited.') self.off_message = _(u'Chat interactions are not audited.') self.already_on_message = _(u'Chat interactions are already audited.') self.already_off_message = _(u'Chat interactions are already private.') self.temporary_off_message = _( u"Please note that auditing will restart after {}") self.engine.register('bond', self)
def on_inbound(self, **kwargs): """ Updates the chat on inbound message """ if kwargs.get('event') != 'inbound': return logging.debug(u"Receiving inbound message") message = kwargs('message') self.bot.say( _(u"Received {}: {} (from {})").format(message['input']['key'], message['input']['value'], message['from']))
def say(self, bot): """ Reports on this step :param bot: the bit to use :type bot: ShellBot This function posts to the chat space some information on this step. Example:: step.say(bot) """ bot.say(_(u"Current state: {} - {}").format(self.label, self.message))
def execute(self, bot, arguments=None, **kwargs): """ Closes the space :param bot: The bot for this execution :type bot: Shellbot :param arguments: The arguments for this command :type arguments: str or ``None`` This function should report on progress by sending messages with one or multiple ``bot.say("Whatever response")``. """ bot.say(_(u"Closing this channel")) bot.dispose()
def execute(self, bot, arguments=None, **kwargs): """ Moves underlying state machine to the next step :param bot: The bot for this execution :type bot: Shellbot :param arguments: The arguments for this command :type arguments: str or ``None`` This function calls the ``step()`` function of the underlying state machine and provides a static event. It also transmits text typed by the end user after the command verb, and any other parameters received from the shell, e.g., attachment, etc. """ if bot.machine: bot.machine.step(event=self.event, arguments=arguments, **kwargs) else: bot.say(_(u"No state machine is available"))
def check(self): """ Check settings This function reads key ``local`` and below, and update the context accordingly. This function also selects the right input for this local space. If some content has been provided during initialisation, it is used to simulate user input. Else stdin is read one line at a time. """ self.context.check('space.title', _(u'Collaboration space'), filter=True) self.context.check('space.participants', '$CHANNEL_DEFAULT_PARTICIPANTS', filter=True) self.context.set('server.binding', None) # no web server at all if self.input: def read_list(): for line in self.input: sys.stdout.write(line + '\n') sys.stdout.flush() yield line self._lines = read_list() # yield creates an iterator else: def read_stdin(): readline = sys.stdin.readline() while readline: yield readline.rstrip('\n') readline = sys.stdin.readline() self._lines = read_stdin() # yield creates an iterator
def put(self, event): """ Processes one event :param event: an inbound event :type event: Event or Message or Join or Leave With this class a string representation of the received event is forwarded to the speaker queue of a chat space. """ logging.debug(u"- updating with a {} event".format(event.type)) logging.debug(event.attributes) if event.get('url'): attachment = self.space.download_attachment(event.url) message = _(u"{}: {}").format(event.from_label, os.path.basename(attachment)) self.mouth.put(Vibes(text=message, file=attachment)) else: self.mouth.put(self.format(event))
class Input(Machine): """ Asks for some input This implements a state machine that can get one piece of input from chat participants. It can ask a question, wait for some input, check provided data and provide guidance when needed. Example:: machine = Input(bot=bot, question="PO Number?", key="order.id") machine.start() ... In normal operation mode, the machine asks a question in the chat space, then listen for an answer, captures it, and stops. When no adequate answer is provided, the machine will provide guidance in the chat space after some delay, and ask for a retry. Multiple retries can take place, until correct input is provided, or the machine is timed out. The machine can also time out after a (possibly) long duration, and send a message in the chat space when giving up. If correct input is mandatory, no time out will take place and the machine will really need a correct answer to stop. Data that has been captured can be read from the machine itself. For example:: value = machine.get('answer') If the machine is given a key, this is used for feeding the bot store. For example:: machine.build(key='my_field', ...) ... value = bot.recall('input')['my_field'] The most straightforward way to process captured data in real-time is to subclass ``Input``, like in the following example:: class MyInput(Input): def on_input(self, value): mail.send_message(value) machine = MyInput(...) machine.start() """ ANSWER_MESSAGE = _(u"Ok, this has been noted") RETRY_MESSAGE = _(u"Invalid input, please retry") CANCEL_MESSAGE = _(u"Ok, forget about it") RETRY_DELAY = 20.0 # amount of seconds between retries CANCEL_DELAY = 40.0 # amount of seconds before time out def on_init(self, question=None, question_content=None, mask=None, regex=None, on_answer=None, on_answer_content=None, on_answer_file=None, on_retry=None, on_retry_content=None, on_retry_file=None, retry_delay=None, on_cancel=None, on_cancel_content=None, on_cancel_file=None, cancel_delay=None, is_mandatory=False, key=None, **kwargs): """ Asks for some input :param question: Message to ask for some input :type question: str :param question_content: Rich message to ask for some input :type question_content: str :param mask: A mask to filter the input :type mask: str :param regex: A regular expression to filter the input :type regex: str :param on_answer: Message on successful data capture :type on_answer: str :param on_answer_content: Rich message on successful data capture :type on_answer_content: str in Markdown or HTML format :param on_answer_file: File to be uploaded on successful data capture :type on_answer_file: str :param on_retry: Message to provide guidance and ask for retry :type on_retry: str :param on_retry_content: Rich message on retry :type on_retry_content: str in Markdown or HTML format :param on_retry_file: File to be uploaded on retry :type on_retry_file: str :param retry_delay: Repeat the on_retry message after this delay in seconds :type retry_delay: int :param on_cancel: Message on time out :type on_cancel: str :param on_cancel_content: Rich message on time out :type on_cancel_content: str in Markdown or HTML format :param on_cancel_file: File to be uploaded on time out :type on_cancel_file: str :param is_mandatory: If the bot will insist and never give up :type is_mandatory: boolean :param cancel_delay: Give up on this input after this delay in seconds :type cancel_delay: int :param key: The label associated with data captured in bot store :type key: str If a mask is provided, it is used to filter provided input. Use following conventions to build the mask: * ``A`` - Any kind of unicode symbol such as ``g`` or ``ç`` * ``9`` - A digit such as ``0`` or ``2`` * ``+`` - When following ``#`` or ``9``, indicates optional extensions of the same type * Any other symbol, including punctuation or white space, has to match exactly. For example: * ``9999A`` will match 4 digits and 1 additional character * ``#9-A+`` will match ``#3-June 2017`` Alternatively, you can provide a regular expression (regex) to extract useful information from the input. You can use almost every regular expression that is supported by python. If parenthesis are used, the function returns the first matching group. For example, you can capture an identifier with a given prefix:: machine.build(question="What is the identifier?", regex=r'ID-\d\w\d+', ...) ... id = machine.filter('The id is ID-1W27 I believe') assert id == 'ID-1W27' As a grouping example, you can capture a domain name by asking for some e-mail address like this:: machine.build(question="please enter your e-mail address", regex=r'@([\w.]+)', ...) ... domain_name = machine.filter('my address is [email protected]') assert domain_name == 'acme.com' """ assert question or question_content self.question = question self.question_content = question_content assert regex is None or mask is None # use only one of them self.regex = regex self.mask = mask self.on_answer = on_answer self.on_answer_content = on_answer_content self.on_answer_file = on_answer_file self.on_retry = on_retry self.on_retry_content = on_retry_content self.on_retry_file = on_retry_file self.on_cancel = on_cancel self.on_cancel_content = on_cancel_content self.on_cancel_file = on_cancel_file assert is_mandatory in (True, False) self.is_mandatory = is_mandatory if retry_delay is not None: assert float(retry_delay) > 0 self.RETRY_DELAY = float(retry_delay) if cancel_delay is not None: assert float(cancel_delay) > 0 self.CANCEL_DELAY = float(cancel_delay) assert self.CANCEL_DELAY > self.RETRY_DELAY self.key = key states = ['begin', 'waiting', 'delayed', 'end'] transitions = [ { 'source': 'begin', 'target': 'waiting', 'action': self.ask }, { 'source': 'waiting', 'target': 'end', 'condition': lambda **z: self.get('answer') is not None, 'action': self.stop }, { 'source': 'waiting', 'target': 'delayed', 'condition': lambda **z: self.elapsed > self.RETRY_DELAY, 'action': self.say_retry, }, { 'source': 'delayed', 'target': 'end', 'condition': lambda **z: self.get('answer') is not None, 'action': self.stop }, { 'source': 'delayed', 'target': 'end', 'condition': lambda **z: self.elapsed > self.CANCEL_DELAY and not self. is_mandatory, 'action': self.cancel }, ] during = { 'begin': self.on_inbound, 'waiting': self.on_inbound, 'delayed': self.on_inbound, 'end': self.on_inbound, } self.build(states=states, transitions=transitions, initial='begin') self.start_time = time.time() @property def elapsed(self): """ Measures time since the question has been asked Used in the state machine for repeating the question and on time out. """ return time.time() - self.start_time def say_answer(self, input): """ Responds on correct capture :param input: the text that has been noted :type input: str """ text = self.on_answer.format(input) if self.on_answer else None content = self.on_answer_content.format( input) if self.on_answer_content else None file = self.on_answer_file if self.on_answer_file else None if not text and not content: text = self.ANSWER_MESSAGE.format(input) self.bot.say(text) if content or file: self.bot.say(' ', content=content, file=file) def say_retry(self): """ Provides guidance on retry """ text = self.on_retry if self.on_retry else None content = self.on_retry_content if self.on_retry_content else None file = self.on_retry_file if self.on_retry_file else None if not text and not content: text = self.RETRY_MESSAGE self.bot.say(text) if content or file: self.bot.say(' ', content=content, file=file) def say_cancel(self): """ Says that input has been timed out """ text = self.on_cancel if self.on_cancel else None content = self.on_cancel_content if self.on_cancel_content else None file = self.on_cancel_file if self.on_cancel_file else None if not text and not content: text = self.CANCEL_MESSAGE self.bot.say(text) if content or file: self.bot.say(' ', content=content, file=file) def ask(self): """ Asks the question in the chat space """ text = self.question if self.question else None content = self.question_content if self.question_content else None self.bot.say(text) if content: self.bot.say(' ', content=content) self.start_time = time.time() self.listen() def listen(self): """ Listens for data received from the chat space This function starts a separate process to scan the ``bot.fan`` queue until time out. """ p = Process(target=self.receive) p.daemon = True p.start() return p def receive(self): """ Receives data from the chat space This function implements a loop until some data has been actually captured, or until the state machine stops for some reason. The loop is also stopped when the parameter ``general.switch`` is changed in the context. For example:: engine.set('general.switch', 'off') """ logging.info(u"Receiving input") self.set('answer', None) try: while self.bot.engine.get('general.switch', 'on') == 'on': if self.get('answer'): break # on good answer if not self.is_running: break # on machine stop try: if self.bot.fan.empty(): label = 'fan.' + self.bot.id self.bot.engine.set(label, time.time()) time.sleep(self.TICK_DURATION) continue item = self.bot.fan.get(True, self.TICK_DURATION) if item is None: break logging.debug(u"Input has been received") self.execute(arguments=item) except Exception as feedback: logging.exception(feedback) break except KeyboardInterrupt: pass logging.info(u"Input receiver has been stopped") def execute(self, arguments=None, **kwargs): """ Receives data from the chat :param arguments: data captured from the chat space :type arguments: str This function checks data that is provided, and provides guidance if needed. It can extract information from the provided mask or regular expression, and save it for later use. """ if not arguments: self.say_retry() return arguments = self.filter(text=arguments) if not arguments: self.say_retry() return # store at machine level self.set('answer', arguments) # store at bot level if self.key: self.bot.update('input', self.key, arguments) # use the input in this instance as well self.on_input(value=arguments, **kwargs) # advertise subscribers if self.key: self.bot.publisher.put( self.bot.id, { 'from': self.bot.id, 'input': { 'key': self.key, 'value': arguments } }) self.say_answer(arguments) self.step(event='tick') def filter(self, text): """ Filters data from user input :param text: Text coming from the chat space :type text: str :return: Data to be captured, or None If a mask is provided, or a regular expression, they are used to extract useful information from provided data. Example to read a PO mumber:: machine.build(mask='9999A', ...) ... po = machine.filter('PO Number is 2413v') assert po == '2413v' """ if self.mask: return self.search_mask(self.mask, text) if self.regex: return self.search_expression(self.regex, text) return text def search_mask(self, mask, text): """ Searches for structured data in text :param mask: A simple expression to be searched :type mask: str :param text: The string from the chat space :type text: str :return: either the matching expression, or None Use following conventions to build the mask: * ``A`` - Any kind of unicode symbol, such as ``g`` or ``ç`` * ``9`` - A digit, such as ``0`` or ``2`` * ``+`` - When following ``#`` or ``9``, indicates optional extensions of the same type * Any other symbol, including punctuation or white space, has to match exactly. Some mask examples: * ``9999A`` will match 4 digits and 1 additional character * ``#9-A+`` will match ``#3-June 2017`` Example to read a PO mumber:: machine.build(question="What is the PO number?", mask='9999A', ...) ... po = machine.filter('PO Number is 2413v') assert po == '2413v' """ assert mask assert text logging.debug(u"Searching for mask '{}'".format(mask)) mask = mask.replace('+', 'pLuS') mask = re.escape(mask) mask = mask.replace('pLuS', '+').replace('A', '\S').replace('9', '\d').replace( 'Z', '[^0-9]') logging.debug(u"- translated to expression '{}'".format(mask)) pattern = re.compile(mask, re.U) searched = pattern.search(text) if not searched: logging.debug(u"- no match") return None matched = searched.group() logging.debug(u"- found '{}'".format(matched)) return matched def search_expression(self, regex, text): """ Searches for a regular expression in text :param regex: A regular expression to be matched :type regex: str :param text: The string from the chat space :type text: str :return: either the matching expression, or None You can use almost every regular expression that is supported by python. If parenthesis are used, the function returns the first matching group. For example, you can capture an identifier with a given prefix:: machine.build(question="What is the identifier?", regex=r'ID-\d\w\d+', ...) ... id = machine.filter('The id is ID-1W27 I believe') assert id == 'ID-1W27' As a grouping example, you can capture a domain name by asking for some e-mail address like this:: machine.build(question="please enter your e-mail address", regex=r'@([\w.]+)', ...) ... domain_name = machine.filter('my address is [email protected]') assert domain_name == 'acme.com' """ assert regex assert text logging.debug(u"Searching for expression '{}'".format(regex)) pattern = re.compile(regex, re.I) searched = pattern.search(text) if not searched: logging.debug(u"- no match") return None matched = searched.group() if len(searched.groups()) > 0: # return the first matching group matched = searched.groups()[0] logging.debug(u"- found '{}'".format(matched)) return matched def on_input(self, value, **kwargs): """ Processes input data :param value: data that has been captured :type value: str This function is called as soon as some input has been captured. It can be overlaid in subclass, as in the following example:: class MyInput(Input): def on_input(self, value): mail.send_message(value) machine = MyInput(...) machine.start() The extra parameters wil be used in case of attachment with the value. """ pass def on_inbound(self, **kwargs): """ Updates the chat on inbound message """ if kwargs.get('event') != 'inbound': return logging.debug(u"Receiving inbound message") message = kwargs('message') self.bot.say( _(u"Received {}: {} (from {})").format(message['input']['key'], message['input']['value'], message['from'])) def cancel(self): """ Cancels the question Used by the state machine on time out """ self.say_cancel() self.stop()
def do(self, line, received=None): """ Handles one line of text :param line: a line of text to parse and to handle :type line: str :param received: the message that contains the command :type received: Message This function uses the first token as a verb, and looks for a command of the same name in the shell. If the command does not exist, the command ``*default`` is used instead. Default behavior is implemented in ``shellbot.commands.default`` yet you can load a different command for customization. If an empty line is provided, the command ``*empty`` is triggered. Default implementation is provided in ``shellbot.commands.empty``. When a file has been uploaded, the information is given to the command that is executed. If no message is provided with the file, the command ``*upload`` is triggered instad of ``*empty``. Default implementation is provided in ``shellbot.commands.upload``. Following parameters are used for the execution of a command: - ``bot`` - A bot instance is retrieved from the channel id mentioned in ``received``, and provided to the command. - ``arguments`` - This is a string that contains everything after the command verb. When ``hello How are you doing?`` is submitted to the shell, ``hello`` is the verb, and ``How are you doing?`` are the arguments. This is the regular case. If there is no command ``hello`` then the command ``*default`` is used instead, and arguments provided are the full line ``hello How are you doing?``. - ``attachment`` - When a file has been uploaded, this attribute provides its external name, e.g., ``picture024.png``. This can be used in the executed command, if you keep in mind that the same name can be used multiple times in a conversation. - ``url`` - When a file has been uploaded, this is the handle by which actual content can be retrieved. Usually, ask the underlying space to get a local copy of the document. """ line = str(line) if line else '' # sanity check logging.info(u"Handling: {}".format(line)) self.line = line self.count += 1 tokens = line.split(' ') verb = tokens.pop(0) if len(verb) < 1: if received and received.url: verb = _(u'*upload') else: verb = _(u'*empty') verb = verb.lower() # align across devices kwargs = {} if len(tokens) > 0: kwargs['arguments'] = ' '.join(tokens) else: kwargs['arguments'] = '' if received and received.attachment: kwargs['attachment'] = received.attachment if received and received.url: kwargs['url'] = received.url channel_id = received.channel_id if received else None bot = self.engine.get_bot(channel_id) if bot.channel is None: return sorry_message = _(u"Sorry, I do not know how to handle '{}'") try: if verb in self._commands.keys(): command = self._commands[verb] if ( (command.in_direct and bot.channel.is_direct) or (command.in_group and not bot.channel.is_direct) ): self.verb = verb command.execute(bot, **kwargs) else: logging.debug(u"- command cannot be used in this channel") bot.say(sorry_message.format(verb)) elif _(u'*default') in self._commands.keys(): kwargs['arguments'] = line # provide full input line command = self._commands[_(u'*default')] command.execute(bot, **kwargs) else: bot.say(sorry_message.format(verb)) except Exception: bot.say(sorry_message.format(verb)) raise
def on_init(self): """ Localize strings for this command """ self.keyword = _(u'step') self.information_message = _(u'Move process to next step')
def test_configure(self): logging.info('*** configure ***') self.engine.configure({}) self.assertEqual(self.engine.space.ears, self.engine.ears) self.assertTrue(self.engine.list_factory is not None) self.engine.context.clear() settings = { 'bot': { 'on_enter': 'Hello!', 'on_exit': 'Bye!', }, 'localized': { 'hello world': "What'up, Doc?", 'another string': 'Bye!', }, 'space': { 'title': 'space name', 'participants': ['*****@*****.**'], }, 'server': { 'url': 'http://to.no.where', 'hook': '/hook', 'binding': '0.0.0.0', 'port': 8080, }, } self.engine.configure(settings) self.assertEqual(self.engine.get('bot.on_enter'), 'Hello!') self.assertEqual(self.engine.get('bot.on_exit'), 'Bye!') self.assertEqual(self.engine.get('space.title'), 'space name') self.assertEqual(self.engine.get('space.participants'), ['*****@*****.**']) self.assertEqual(self.engine.get('server.url'), 'http://to.no.where') self.assertEqual(self.engine.get('server.hook'), '/hook') self.assertEqual(_('hello world'), "What'up, Doc?") self.assertEqual(_('not localized'), 'not localized') self.engine.context.clear() self.engine.configure_from_path( os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_settings', 'regular.yaml')) self.assertEqual(self.engine.get('bot.on_enter'), 'How can I help you?') self.assertEqual(self.engine.get('bot.on_exit'), 'Bye for now') self.assertEqual(self.engine.get('space.title'), 'Support channel') self.assertEqual(self.engine.get('space.participants'), ['*****@*****.**', '*****@*****.**']) self.assertEqual(self.engine.get('server.url'), None) self.assertEqual(self.engine.get('server.hook'), None) self.assertEqual(self.engine.get('server.binding'), None) self.assertEqual(self.engine.get('server.port'), None) items = [x for x in self.engine.list_factory.get_list('SupportTeam')] self.assertEqual(items, ['*****@*****.**', '*****@*****.**']) items = [x for x in self.engine.list_factory.get_list('*unknown*list')] self.assertEqual(items, []) names = self.engine.list_factory.list_commands() self.assertEqual(sorted(names), ['SupportTeam'])
def on_init(self): """ Localize strings for this command """ self.keyword = _(u'pass') self.information_message = _(u'Do absolutely nothing')
def on_init(self): """ Localize strings for this command """ self.keyword = _(u'echo') self.information_message = _(u'Echo input string')