Exemple #1
0
    def df2_get(self, args, f_type) -> any:
        """
        Return value stored in user variable

        :param args:
        :param f_type:
        :return:
        """
        try:
            var_name = self.bot.resolve_arg(args[0], f_type, True)
        except IndexError:
            raise BBotException({
                'code': 120,
                'function': 'get',
                'arg': 0,
                'message': 'Variable name missing.'
            })
        if type(var_name) is not str:
            raise BBotException({
                'code': 121,
                'function': 'get',
                'arg': 0,
                'message': 'Variable name should be a string.'
            })

        return self.bot.session.get_var(self.bot.user_id, var_name)
Exemple #2
0
    def df2_lte(self, args, f_type):
        """
        Evaluates lesser-than or equal

        :param args:
        :param f_type:
        :return:
        """
        try:
            value1 = self.bot.resolve_arg(args[0], f_type)
            tmp = float(value1)
        except IndexError:
            raise BBotException({
                'code': 150,
                'function': 'lte',
                'arg': 0,
                'message': 'Value in arg 0 is missing.'
            })

        try:
            value2 = self.bot.resolve_arg(args[1], f_type)
        except IndexError:
            raise BBotException({
                'code': 151,
                'function': 'lte',
                'arg': 1,
                'message': 'Value in arg 1 is missing.'
            })

        return value1 <= value2
Exemple #3
0
    def suggestedActions(self, args, f_type):
        errors = []
        try:
            actions = self.core.resolve_arg(args[0], f_type)
        except IndexError:
            errors.append({
                'code': 240,
                'function': 'suggestedAction',
                'arg': 0,
                'message': 'suggestedAction actions missing.'
            })
        if errors:
            raise BBotException(errors)

        if type(actions) is not list:
            actions = [actions]

        for i in range(len(actions)):
            if type(
                    actions[i]
            ) is not box.Box:  # if it's not an object (dict are box.Box class here)
                if type(actions[i]
                        ) is str:  # and it's a string, apply it as imBack
                    actions[i] = self.imBack(actions[i])
                else:
                    raise BBotException(
                        "suggestedActions function accepts strings or imBack objects only"
                    )

        bbot_response = {'suggestedActions': {'actions': actions}}
        self.core.add_output(bbot_response,
                             True)  # Adds suggestion to last output
Exemple #4
0
    def df2_eq(self, args, f_type):
        """
        Evaluates equivalency between two values

        :return:
        """
        try:
            value1 = self.bot.resolve_arg(args[0], f_type)
        except IndexError:
            raise BBotException({
                'code': 140,
                'function': 'eq',
                'arg': 0,
                'message': 'Value in arg 0 is missing.'
            })

        try:
            value2 = self.bot.resolve_arg(args[1], f_type)
        except IndexError:
            raise BBotException({
                'code': 141,
                'function': 'eq',
                'arg': 0,
                'message': 'Value in arg 1 is missing.'
            })

        return value1 == value2
Exemple #5
0
    def df2_gt(self, args, f_type):
        """
        Evaluates greater-than

        :param args:
        :param f_type:
        :return:
        """
        try:
            value1 = self.bot.resolve_arg(args[0], f_type)
        except IndexError:
            raise BBotException({
                'code': 150,
                'function': 'gt',
                'arg': 0,
                'message': 'Value in arg 0 is missing.'
            })

        try:
            value2 = self.bot.resolve_arg(args[1], f_type)
        except IndexError:
            raise BBotException({
                'code': 151,
                'function': 'gt',
                'arg': 1,
                'message': 'Value in arg 1 is missing.'
            })

        return value1 > value2
Exemple #6
0
    def df2_or(self, args, f_type):
        """
        Logical operator OR.
        It accepts unlimited values

        :param args:
        :param f_type:
        :return:
        """
        if len(args) == 0:
            raise BBotException({
                'code': 170,
                'function': 'or',
                'arg': 0,
                'message': 'Value in arg 0 is missing.'
            })

        if len(args) == 1:
            raise BBotException({
                'code': 171,
                'function': 'or',
                'arg': 1,
                'message': 'Value in arg 1 is missing.'
            })

        for arg in args:
            resolved_arg = self.bot.resolve_arg(arg, f_type)
            if resolved_arg:
                return True

        return False
Exemple #7
0
    def df2_code(self, args, f_type):
        """
        This function is used to run any python code on conditions and responses objects.
        Of course you can use DotFlow2 functions inside it
        :param args:    0: string with python code
                        1: string. 'C' to get expression result (only expressions works). 'R' to run any kind of python code (no result is returned)
        :return:
        """
        try:
            code = self.bot.resolve_arg(args[0], f_type)
        except IndexError:
            raise BBotException({
                'code': 130,
                'function': 'code',
                'arg': 0,
                'message': 'Code missing.'
            })

        if type(code) is not str:
            raise BBotException({
                'code': 131,
                'function': 'code',
                'arg': 0,
                'message': 'Argument 0 should be string'
            })

        self.logger.debug('$code: Running python code: "' + code + "'...")

        codeblock = self._get_codeblock(code, f_type)
        self.logger.debug('$code: Running template code block: "' + codeblock +
                          "'...")
        response = self.bot.template_engine.render(codeblock)
        response = self._get_boolean(response, f_type)
        self.logger.debug('$code: Returning: ' + str(response))
        return response
Exemple #8
0
    def chatscriptMatch(self, args, f_type):
        """
        Evaluates ChatScript pattern
        @TODO add caching

        :param args:
        :param f_type:
        :return:
        """

        try:
            pattern = self.bot.resolve_arg(args[0], f_type)
        except IndexError:
            raise BBotException({
                'code': 190,
                'function': 'chatscriptMatch',
                'arg': 0,
                'message': 'Pattern in arg 0 is missing.'
            })

        try:
            input_text = self.bot.resolve_arg(args[1], f_type)
        except IndexError:
            raise BBotException({
                'code': 191,
                'function': 'chatscriptMatch',
                'arg': 1,
                'message': 'Text in arg 1 is missing.'
            })

        try:
            entities_var_names = self.bot.resolve_arg(args[2], f_type)
        except IndexError:
            entities_var_names = []  # entities are optional

        result = False
        if len(input_text) > 0:
            # clear match variables first (ChatScript does not reset them when running testpattern)
            self.send(':do ^clearmatch()')
            # test the pattern
            cs_req = f":testpattern ({pattern}) {input_text}"  #@TODO try sending direct text and running ^match later (it's faster. sends input text once)
            self.logger.debug("ChatScript request: " + cs_req)
            cs_res = self.send(cs_req)
            self.logger.debug('ChatScript response: \n' + str(cs_res))

            if not self.has_error(cs_res):
                result = self.is_match(cs_res)
                if result:
                    self.logger.info('It\'s a match!')
                else:
                    self.logger.info('No match')
                # check if there are match variables set
                if self.has_match_variables(cs_res):
                    self.store_variables_from_matched_variables(
                        entities_var_names)
            else:
                self.logger.warning('Response returned with error')

        return result
Exemple #9
0
 def get_server_type(self):
     if self.dotbot.chatbot_engine['serverUrl'].startswith('http'):
         return 'rest'
     elif self.dotbot.chatbot_engine['serverUrl'].startswith('ws'):
         return 'socketio'
     else:
         raise BBotException('Wrong Rasa Server url')
Exemple #10
0
    def authenticate(self):
        # first check if we have access_token in database
        self.logger.debug("Looking for Azure AD access token in database...")
        stored_token = self.dotdb.get_azure_ad_access_token(self.dotbot.bot_id)        
        if stored_token:
            expire_date = stored_token['expire_date']
            if  expire_date >= datetime.datetime.utcnow():
                # got valid token
                self.access_token = stored_token['access_token']                
                self.logger.debug('Got valid token from db. Will expire in ' + str(stored_token['expire_date']))
                return
            else:
                self.logger.debug('Got expired token. Will request new one')
        else:
            self.logger.debug('There is no token in database. Will request one')

        url = "https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token"
        payload = {
            "grant_type": "client_credentials",
            "client_id": self.app_id,
            "client_secret": self.app_password,
            "scope": "https://api.botframework.com/.default"
        }
        self.logger.debug("Sending request to Microsoft OAuth with payload: " + str(payload))
        response = requests.post(url, data=payload)    
        msg = "Response from Microsoft OAuth: http code: " + str(response.status_code) + " message: " + str(response.text)
        if response.status_code != 200:
            raise BBotException(msg)
        self.logger.debug(msg)
        json_response = response.json()
        self.access_token = json_response['access_token']
        expire_date = datetime.datetime.utcnow() + datetime.timedelta(0, json_response['expires_in']) # now plus x seconds to get expire date 

        self.dotdb.set_azure_ad_access_token(self.dotbot.bot_id, self.access_token, expire_date)
    def imBack(self, args, f_type):
        """
        Returns BBot imBack object (must be used within suggestedAction)

        :param args:
        :return:
        """
        errors = []
        try:
            title = self.core.resolve_arg(args[0], f_type)
        except IndexError:
            errors.append({'code': 240, 'function': 'imBack', 'arg': 0, 'message': 'imBack title missing.'})

        try:
            value = self.core.resolve_arg(args[1], f_type)
        except IndexError:
            value = title

        if errors:
            raise BBotException(errors)

        bbot_response = {
            "type": "Action.Submit",
            "title": title,
            "data": {
                "imBack": value
            }
        }                    
        return bbot_response
Exemple #12
0
    def openUrl(self, args, f_type):
        """
        Returns BBot openUrl object (must be used within suggestedAction)

        :param args:
        :return:
        """
        errors = []
        try:
            title = self.core.resolve_arg(args[0], f_type)
        except IndexError:
            errors.append({
                'code': 240,
                'function': 'openUrl',
                'arg': 0,
                'message': 'openUrl title missing.'
            })
        try:
            value = self.core.resolve_arg(args[1], f_type)
        except IndexError:
            value = title

        if errors:
            raise BBotException(errors)

        response = {"type": "openUrl", "title": title, "value": value}
        return response
Exemple #13
0
    def df2_text(self, args, f_type):
        """
        Returns BBot text output object

        :param args:
        :return:
        """

        if len(args) == 0:
            raise BBotException({
                'code': 200,
                'function': 'text',
                'arg': 0,
                'message': 'Text in arg 0 is missing.'
            })

        msg_count = len(args)
        if msg_count > 1:  # multiple output, choose a random one
            msg_idx = random.randint(0, msg_count - 1)
        else:
            msg_idx = 0

        msg = self.bot.resolve_arg(args[msg_idx], f_type,
                                   True)  # no need to resolve arg before this
        bbot_response = {'text': str(msg)}
        self.bot.add_output(bbot_response)
        return bbot_response
Exemple #14
0
    def df2_regexMatch(self, args, f_type):
        """
        Evaluates regular expression.
        Supports named groups

        :param args:
        :param f_type:
        :return:
        """
        try:
            pattern = self.bot.resolve_arg(args[0], f_type)
        except IndexError:
            raise BBotException({
                'code': 180,
                'function': 'regexMatch',
                'arg': 0,
                'message': 'Pattern in arg 1 is missing.'
            })

        try:
            text = self.bot.resolve_arg(args[1], f_type)
        except IndexError:
            raise BBotException({
                'code': 181,
                'function': 'regexMatch',
                'arg': 1,
                'message': 'Text in arg 1 is missing.'
            })

        m = re.search(pattern, text)
        if m:
            # look for named groups and store them
            for var_name, var_value in m.groupdict().items():
                if var_value is not None:
                    self.bot.session.set_var(self.bot.user_id, var_name,
                                             var_value)
                    self.bot.detected_entities[var_name] = var_value
                    self.logger.debug('$regexMatch: Storing named group "' +
                                      var_name + '" with value "' + var_value +
                                      '"')
                else:
                    self.logger.debug('$regexMatch: Named group "' + var_name +
                                      '" not found.')

            return True
        else:
            return False
Exemple #15
0
    def _mediaCard(self, args, f_type, media_elm: str = "media"):
        """
        Returns BBot video object

        :param args:
        :param f_type:
        :return:
        """
        try:
            media_url = self.core.resolve_arg(args[0], f_type, True)
        except IndexError:
            raise BBotException({
                'code': 210,
                'function': 'mediaCard',
                'arg': 0,
                'message': 'Media URL is missing.'
            })
        try:
            title = self.core.resolve_arg(args[1], f_type, True)
        except IndexError:
            title = ""
        try:
            subtitle = self.core.resolve_arg(args[2], f_type, True)
        except IndexError:
            subtitle = ""
        try:
            text = self.core.resolve_arg(args[3], f_type, True)
        except IndexError:
            text = ""
        try:
            buttons = self.core.resolve_arg(args[4], f_type, True)
        except IndexError:
            buttons = []
        try:
            image_url = self.core.resolve_arg(args[5], f_type, True)
        except IndexError:
            image_url = ""

        bbot_response = {
            'type':
            'message',
            'attachments': [{
                "contentType": "",
                "content": {
                    "subtitle": subtitle,
                    "text": text,
                    "image": image_url,
                    "title": title,
                    media_elm: [{
                        "url": media_url
                    }],
                    "buttons": buttons
                }
            }]
        }

        return bbot_response
Exemple #16
0
    def heroCard(self, args, f_type):
        """
        Returns BBot hero card 

        :param args:
        :return:
        """
        errors = []
        try:
            image = self.core.resolve_arg(args[0], f_type)
        except IndexError:
            errors.append({
                'code': 240,
                'function': 'heroCard',
                'arg': 0,
                'message': 'heroCard image missing.'
            })
        try:
            title = self.core.resolve_arg(args[1], f_type)
        except IndexError:
            title = None
        try:
            subtitle = self.core.resolve_arg(args[2], f_type, True)
        except IndexError:
            subtitle = None
        try:
            text = self.core.resolve_arg(args[3], f_type, True)
        except IndexError:
            text = None
        try:
            buttons = self.core.resolve_arg(args[4], f_type)
        except IndexError:
            buttons = None
        if errors:
            raise BBotException(errors)

        content = {}
        content['images'] = [{'url': image}]
        if title:
            content['title'] = title
        if subtitle:
            content['subtitle'] = subtitle
        if text:
            content['text'] = text
        if buttons:
            content['buttons'] = buttons

        bbot_response = {
            'type':
            'message',
            'attachments': [{
                "contentType": "application/vnd.microsoft.card.hero",
                "content": content
            }]
        }
        self.core.add_output(bbot_response)
Exemple #17
0
    def df2_weather(self, args, f_type):
        """
        Returns weather report
        @TODO return forecast based on args[1] date

        :param args:
        :param f_type:
        :return:
        """
        try:
            location = self.bot.resolve_arg(args[0], f_type)
        except IndexError:
            raise BBotException({
                'code': 0,
                'function': 'sendEmail',
                'arg': 0,
                'message': 'Location is missing.'
            })

        try:
            date = args[1]
        except:
            date = 'today'  # optional. default 'today'

        self.logger.debug(f'Retrieving weather for {location}')
        st = self.search_text(location)

        if not st:
            self.logger.debug("Location not found. Invalid location")
            return {
                'text':
                '<No weather data or invalid location>',  #@TODO should raise a custom exception which will be used for flow exceptions
                'canonicalLocation': location
            }

        location_key = st[0].get('Key', None)

        self.logger.debug("Accuweather Location Key: " + location_key)

        canonical_location = st[0]['LocalizedName'] + ', ' + st[0]['Country'][
            'LocalizedName']
        self.logger.debug("Canonical location: " + canonical_location)

        r = requests.get(
            f'http://dataservice.accuweather.com/currentconditions/v1/{location_key}?apikey={self.accuweather_api_key}&details=false'
        )

        if r.status_code == 200:
            aw = r.json()
            return {
                'text': aw[0]['WeatherText'],
                'canonicalLocation': canonical_location
            }

            self.logger.error(r.text)
Exemple #18
0
    def directline_get_new_conversation_id(self):
        url = self.base_url + '/conversations'
        self.logger.debug('DirectLine requesting new conversation id')
        response = requests.post(url, headers=self.directline_get_headers())
        self.logger.debug('DirectLine response: ' + response.text)
        if response.status_code == 201 or response.status_code == 200:
            jsonresponse = response.json()
            return jsonresponse['conversationId']

        raise BBotException('Response error code: ' +
                            str(response.status_code))
Exemple #19
0
 def rest_server(self, msg):
     server_url = self.dotbot.chatbot_engine['serverUrl']
     self.logger.debug('Querying to Rasa Server ' + server_url)
     params = {"sender": self.user_id, "message": msg}
     r = requests.post(server_url + '/webhooks/rest/webhook', json=params)
     self.logger.debug('Rasa Server response code: ' + str(r.status_code) +
                       ' - message: ' + str(r.text)[0:300])
     if r.status_code == 200:
         aw = r.json()
     else:
         raise BBotException(r.text)
     return aw
Exemple #20
0
    def render(self, string: str) -> str:
        """
        Renders any string with configured bbot custom functions and bot session vars
        """
        # We still need a way to know if a string is a template or not, but Templator don't need enclosing
        # So for Templator, just enclose the whole string with {{ }} for BBot to know it is a template
        if re.search('({%).*(%})|({{.*}})', string) is None:
            self.logger.debug('Nothing to render')
            return string
        string = string.replace('{{', '')
        string = string.replace('}}', '')
        string = string.replace('{%', '')
        string = string.replace('%}', '')

        session_vars = {}
        if hasattr(self.core.bot, 'session'):
            session_vars = self.core.bot.session.get_var(self.core.bot.user_id)

        t_globals = session_vars
        # add predefined vars from publishers
        if hasattr(self.dotbot, 'botsubscription'):
            if hasattr(self.dotbot.botsubscription, 'predefined_vars'):
                t_globals = {
                    **t_globals,
                    **self.dotbot.botsubscription.predefined_vars
                }

        # get custom functions from extensions
        c_functions = self.get_functions()
        t_globals = {**t_globals, **c_functions}

        self.logger.debug('Rendering template: "' + str(string) + '"')
        templator_obj = Template(string, globals=t_globals)
        try:
            response = str(templator_obj())
        except NameError as e:
            err_msg = 'Template error: ' + str(e)
            self.core.logger.debug(err_msg)
            raise BBotException({'message': err_msg})

        if response[
                -1:] == '\n':  # Templator seems to add a trailing \n, remove it
            response = response[:-1]

        self.logger.debug('Template response: "' + response + '"')

        if response.find('<function BBotFunctionsProxy') is not -1:
            self.logger.error(
                'Templator returned an invalid response. Botdev forgot to escape $?'
            )
            response = '<TEMPLATE RENDERING ERROR. CHECK DEBUG DATA>'  # @TODO add debug data in context to the instruction executed

        return response
Exemple #21
0
 def atalk(self, input_txt: str):
     params = {
         'botkey': self.botkey,
         'input': input_txt,
     }
     response = requests.post(self.url + '/atalk', params)
     self.logger.debug("Response status code: " + str(response.status_code))
     if response.status_code == 200:
         return response.json()
     elif response.status_code == 401:
         raise BBotException(response.text)
     else:
         raise Exception(response.text)
 def imageCard(self, args, f_type):
     try:
         image = self.core.resolve_arg(args[0], f_type, True)
     except IndexError:
         raise BBotException({'code': 210, 'function': 'imageCard', 'arg': 0, 'message': 'imageCard image is missing.'})        
     try:
         title = self.core.resolve_arg(args[1], f_type, True)
     except IndexError:
         title = ""
     try:
         buttons = self.core.resolve_arg(args[2], f_type, True)
     except IndexError:
         buttons = []
         
     self.heroCard([image, title, buttons], 'R')        
Exemple #23
0
    def df2_set(self, args, f_type):
        """
        Stores values on user variables.

        :param args:
        :param f_type:
        :return:
        """
        try:
            var_name = self.bot.resolve_arg(args[0], f_type, True)
        except IndexError:
            raise BBotException({
                'code': 110,
                'function': 'set',
                'arg': 0,
                'message': 'Variable name missing.'
            })
        if type(var_name) is not str:
            raise BBotException({
                'code': 111,
                'function': 'set',
                'arg': 0,
                'message': 'Variable name should be a string.'
            })

        try:
            var_value = self.bot.resolve_arg(args[1], f_type, True)
        except IndexError:
            raise BBotException({
                'code': 112,
                'function': 'set',
                'arg': 1,
                'message': 'Variable value missing.'
            })

        self.bot.session.set_var(self.bot.user_id, var_name, var_value)
Exemple #24
0
    def to_botframework(self, bbot_response):
        
        response_payload = copy.deepcopy(self.response_payload)

        for br in bbot_response['output']:
            
            r = {**response_payload, **br}

            self.logger.debug("Response sent back to BotFramework: " + str(r))        
            url = self.service_url + 'v3/conversations/' + r['conversation']['id'] + '/activities/' + r['id']
            self.logger.debug("To url: " + url)
            response = requests.post(url, headers=self.directline_get_headers(), json=r)
            msg = "BotFramework response: http code: " + str(response.status_code) + " message: " + str(response.text)
            if response.status_code != 200:
                raise BBotException(msg)
            self.logger.debug(msg)
Exemple #25
0
 def talk(self, input_txt: str, sessionid: str, client_name: str):
     params = {
         'botkey': self.botkey,
         'input': input_txt,
         'extra': True,
         'sessionid': sessionid,
         'client_name': client_name
     }
     response = requests.post(self.url + '/talk', params)
     self.logger.debug("Response status code: " + str(response.status_code))
     if response.status_code == 200:
         return response.json()
     elif response.status_code == 401:
         raise BBotException(response.text)
     else:
         raise Exception(response.text)
    def suggestedAction(self, args, f_type):
        errors = []
        try:
            actions = self.core.resolve_arg(args[0], f_type)
        except IndexError:
            errors.append({'code': 240, 'function': 'suggestedAction', 'arg': 0, 'message': 'suggestedAction actions missing.'})
        if errors:
            raise BBotException(errors)

        bbot_response = {
            "contentType": "application/vnd.microsoft.card.adaptive",
            "content": {
                "$schema": "https://adaptivecards.io/schemas/adaptive-card.json",
                "type": "AdaptiveCard",
                "version": "1.0",
                "actions": actions
            }
        }
        self.core.add_output(bbot_response)        
Exemple #27
0
    def df2_button(self, args, f_type):
        """
        Returns BBot button output object

        :param args:
        :return:
        """
        errors = []
        try:
            button_id = self.bot.resolve_arg(args[0], f_type)
        except IndexError:
            errors.append({
                'code': 240,
                'function': 'button',
                'arg': 0,
                'message': 'Button ID missing.'
            })

        try:
            text = self.bot.resolve_arg(args[1], f_type)
        except IndexError:
            errors.append({
                'code': 241,
                'function': 'button',
                'arg': 1,
                'message': 'Button text missing.'
            })

        try:
            postback = self.bot.resolve_arg(args[2], f_type)
        except IndexError:
            postback = None  # postback is optional #@TODO revisit this. buttonId is not a good idea after all

        if errors:
            raise BBotException(errors)

        bbot_response = {'button': {'id': button_id, 'text': text}}
        if postback:
            bbot_response['button']['postback'] = postback

        self.bot.add_output(bbot_response)
        return bbot_response
Exemple #28
0
    def df2_image(self, args, f_type):
        """
        Returns BBot image output object

        :param args:
        :param f_type:
        :return:
        """
        try:
            image_url = self.bot.resolve_arg(args[0], f_type, True)
        except IndexError:
            raise BBotException({
                'code': 210,
                'function': 'image',
                'arg': 0,
                'message': 'Image URL in arg 0 is missing.'
            })

        bbot_response = {'image': {'url': image_url}}
        self.bot.add_output(bbot_response)
        return bbot_response
Exemple #29
0
    def df2_goto(self, args, f_type):
        # @TODO if target node of the goto does not need to wait for user input, it should execute it directly. If botdev doesn't want this, they should use $gotoAndWait
        """
        Sets internal follow-up context.

        :param args: 0: Node ID where to go next
        :param f_type:
        :return:
        """
        # Existence of node id should be done previously. Bot engine will raise an exception if can't find it
        try:
            node_id = self.bot.resolve_arg(args[0], f_type)
        except IndexError:
            raise BBotException({
                'code': 100,
                'function': 'goto',
                'arg': 0,
                'message': 'Node ID missing.'
            })

        self.bot.set_followup_context(node_id)
Exemple #30
0
    def df2_video(self, args, f_type):
        """
        Returns BBot video object

        :param args:
        :param f_type:
        :return:
        """
        try:
            video_url = self.bot.resolve_arg(args[0], f_type, True)
        except IndexError:
            raise BBotException({
                'code': 220,
                'function': 'video',
                'arg': 0,
                'message': 'Video URL in arg 0 is missing.'
            })

        bbot_response = {'video': {'url': video_url}}
        self.bot.add_output(bbot_response)
        return bbot_response