<reponame>davidwagnerkc/mycroft-core<gh_stars>0
# Copyright 2017 Mycroft AI Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import json
import re

from mycroft.util.parse import normalize


class Message:
    """Holds and manipulates data sent over the websocket

        Message objects will be used to send information back and forth
        between processes of Mycroft.

    Attributes:
        type (str): type of data sent within the message.
        data (dict): data sent within the message
        context: info about the message not part of data such as source,
            destination or domain.
    """
    # TODO: For 119.08, change the name of the "type" argument to msg_type
    # The name "type" shadows a Python built-in name
    def __init__(self, type, data=None, context=None):
        """Used to construct a message object

        Message objects will be used to send information back and fourth
        bettween processes of mycroft service, voice, skill and cli
        """
        self.type = type
        self.data = data or {}
        self.context = context or {}

    def serialize(self):
        """This returns a string of the message info.

        This makes it easy to send over a websocket. This uses
        json dumps to generate the string with type, data and context

        Returns:
            str: a json string representation of the message.
        """
        return json.dumps(dict(
            type=self.type,
            data=self.data,
            context=self.context
        ))

    @staticmethod
    def deserialize(value):
        """This takes a string and constructs a message object.

        This makes it easy to take strings from the websocket and create
        a message object.  This uses json loads to get the info and generate
        the message object.

        Args:
            value(str): This is the json string received from the websocket

        Returns:
            Message: message object constructed from the json string passed
            int the function.
            value(str): This is the string received from the websocket
        """
        obj = json.loads(value)
        return Message(obj.get('type') or '',
                       obj.get('data') or {},
                       obj.get('context') or {})

    def reply(self, type, data=None, context=None):
        """Construct a reply message for a given message

        This will take the same parameters as a message object but use
        the current message object as a reference.  It will copy the context
        from the existing message object and add any context passed in to
        the function.  Check for a target passed in to the function from
        the data object and add that to the context as a target.  If the
        context has a client name then that will become the target in the
        context.  The new message will then have data passed in plus the
        new context generated.

        Args:
            type (str): type of message
            data (dict): data for message
            context: intended context for new message

        Returns:
            Message: Message object to be used on the reply to the message
        """
        data = data or {}
        context = context or {}

        new_context = self.context
        for key in context:
            new_context[key] = context[key]
        if 'target' in data:
            new_context['target'] = data['target']
        elif 'client_name' in context:
            context['target'] = context['client_name']
        return Message(type, data, context=new_context)

    def response(self, data=None, context=None):
        """Construct a response message for the message

        Constructs a reply with the data and appends the expected
        ".response" to the message

        Args:
            data (dict): message data
            context (dict): message context
        Returns
            (Message) message with the type modified to match default response
        """
        response_message = self.reply(self.type, data or {}, context)
        response_message.type += '.response'
        return response_message

    def publish(self, type, data, context=None):
        """
        Copy the original context and add passed in context.  Delete
        any target in the new context. Return a new message object with
        passed in data and new context.  Type remains unchanged.

        Args:
            type (str): type of message
            data (dict): date to send with message
            context: context added to existing context

        Returns:
            Message: Message object to publish
        """
        context = context or {}
        new_context = self.context.copy()
        for key in context:
            new_context[key] = context[key]

        if 'target' in new_context:
            del new_context['target']

        return Message(type, data, context=new_context)

    def utterance_remainder(self):
        """
        For intents get the portion not consumed by Adapt.

        For example: if they say 'Turn on the family room light' and there are
        entity matches for "turn on" and "light", then it will leave behind
        " the family room " which is then normalized to "family room".

        Returns:
            str: Leftover words or None if not an utterance.
        """
        utt = normalize(self.data.get("utterance", ""))
        if utt and "__tags__" in self.data:
            for token in self.data["__tags__"]:
                # Substitute only whole words matching the token
                utt = re.sub(r'\b' + token.get("key", "") + r"\b", "", utt)
        return normalize(utt)

# Copyright 2017 Mycroft AI Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import json
import re

from mycroft.util.parse import normalize


class Message:
    """Holds and manipulates data sent over the websocket

        Message objects will be used to send information back and forth
        between processes of Mycroft.

    Attributes:
        type (str): type of data sent within the message.
        data (dict): data sent within the message
        context: info about the message not part of data such as source,
            destination or domain.
    """
    # TODO: For 119.08, change the name of the "type" argument to msg_type
    # The name "type" shadows a Python built-in name
    def __init__(self, type, data=None, context=None):
        """Used to construct a message object

        Message objects will be used to send information back and fourth
        bettween processes of mycroft service, voice, skill and cli
        """
        self.type = type
        self.data = data or {}
        self.context = context or {}

    def serialize(self):
        """This returns a string of the message info.

        This makes it easy to send over a websocket. This uses
        json dumps to generate the string with type, data and context

        Returns:
            str: a json string representation of the message.
        """
        return json.dumps(dict(
            type=self.type,
            data=self.data,
            context=self.context
        ))

    @staticmethod
    def deserialize(value):
        """This takes a string and constructs a message object.

        This makes it easy to take strings from the websocket and create
        a message object.  This uses json loads to get the info and generate
        the message object.

        Args:
            value(str): This is the json string received from the websocket

        Returns:
            Message: message object constructed from the json string passed
            int the function.
            value(str): This is the string received from the websocket
        """
        obj = json.loads(value)
        return Message(obj.get('type') or '',
                       obj.get('data') or {},
                       obj.get('context') or {})

    def reply(self, type, data=None, context=None):
        """Construct a reply message for a given message

        This will take the same parameters as a message object but use
        the current message object as a reference.  It will copy the context
        from the existing message object and add any context passed in to
        the function.  Check for a target passed in to the function from
        the data object and add that to the context as a target.  If the
        context has a client name then that will become the target in the
        context.  The new message will then have data passed in plus the
        new context generated.

        Args:
            type (str): type of message
            data (dict): data for message
            context: intended context for new message

        Returns:
            Message: Message object to be used on the reply to the message
        """
        data = data or {}
        context = context or {}

        new_context = self.context
        for key in context:
            new_context[key] = context[key]
        if 'target' in data:
            new_context['target'] = data['target']
        elif 'client_name' in context:
            context['target'] = context['client_name']
        return Message(type, data, context=new_context)

    def response(self, data=None, context=None):
        """Construct a response message for the message

        Constructs a reply with the data and appends the expected
        ".response" to the message

        Args:
            data (dict): message data
            context (dict): message context
        Returns
            (Message) message with the type modified to match default response
        """
        response_message = self.reply(self.type, data or {}, context)
        response_message.type += '.response'
        return response_message

    def publish(self, type, data, context=None):
        """
        Copy the original context and add passed in context.  Delete
        any target in the new context. Return a new message object with
        passed in data and new context.  Type remains unchanged.

        Args:
            type (str): type of message
            data (dict): date to send with message
            context: context added to existing context

        Returns:
            Message: Message object to publish
        """
        context = context or {}
        new_context = self.context.copy()
        for key in context:
            new_context[key] = context[key]

        if 'target' in new_context:
            del new_context['target']

        return Message(type, data, context=new_context)

    def utterance_remainder(self):
        """
        For intents get the portion not consumed by Adapt.

        For example: if they say 'Turn on the family room light' and there are
        entity matches for "turn on" and "light", then it will leave behind
        " the family room " which is then normalized to "family room".

        Returns:
            str: Leftover words or None if not an utterance.
        """
        utt = normalize(self.data.get("utterance", ""))
        if utt and "__tags__" in self.data:
            for token in self.data["__tags__"]:
                # Substitute only whole words matching the token
                utt = re.sub(r'\b' + token.get("key", "") + r"\b", "", utt)
        return normalize(utt)
# Copyright 2017 Mycroft AI Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # #    http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Holds and manipulates data sent over the websocket          Message objects will be used to send information back and forth         between processes of Mycroft.      Attributes:         type (str): type of data sent within the message.         data (dict): data sent within the message         context: info about the message not part of data such as source,             destination or domain. # TODO: For 119.08, change the name of the "type" argument to msg_type # The name "type" shadows a Python built-in name Used to construct a message object          Message objects will be used to send information back and fourth         bettween processes of mycroft service, voice, skill and cli This returns a string of the message info.          This makes it easy to send over a websocket. This uses         json dumps to generate the string with type, data and context          Returns:             str: a json string representation of the message. This takes a string and constructs a message object.          This makes it easy to take strings from the websocket and create         a message object.  This uses json loads to get the info and generate         the message object.          Args:             value(str): This is the json string received from the websocket          Returns:             Message: message object constructed from the json string passed             int the function.             value(str): This is the string received from the websocket Construct a reply message for a given message          This will take the same parameters as a message object but use         the current message object as a reference.  It will copy the context         from the existing message object and add any context passed in to         the function.  Check for a target passed in to the function from         the data object and add that to the context as a target.  If the         context has a client name then that will become the target in the         context.  The new message will then have data passed in plus the         new context generated.          Args:             type (str): type of message             data (dict): data for message             context: intended context for new message          Returns:             Message: Message object to be used on the reply to the message Construct a response message for the message          Constructs a reply with the data and appends the expected         ".response" to the message          Args:             data (dict): message data             context (dict): message context         Returns             (Message) message with the type modified to match default response Copy the original context and add passed in context.  Delete         any target in the new context. Return a new message object with         passed in data and new context.  Type remains unchanged.          Args:             type (str): type of message             data (dict): date to send with message             context: context added to existing context          Returns:             Message: Message object to publish For intents get the portion not consumed by Adapt.          For example: if they say 'Turn on the family room light' and there are         entity matches for "turn on" and "light", then it will leave behind         " the family room " which is then normalized to "family room".          Returns:             str: Leftover words or None if not an utterance. # Substitute only whole words matching the token