Exemple #1
0
    def handle_handover(self, event):
        # from event payload determine the next node
        # convert this node into message
        # send this message to user
        # update state with new_node_id as current_node_id
        # respond with 200 ok status success
        current_node_id = self.state.get("current_node_id")
        next_node_data = AnaNode(current_node_id).get_next_node_data(self.message_data, self.state)
        next_node_id = next_node_data.get("node_id")

        self.state["current_node_id"] = next_node_id
        node_data = AnaNode(next_node_id).get_contents()
        data = AnaConverter(self.state).get_messages_data(node_data, self.message_data)
        messages_data = data.get("user_messages")

        self.meta_data["id"] = ""
        messages = [Message(meta=self.meta_data, data=data).trim() for data in messages_data]

        # message = Message(meta=self.meta_data, data=message_data).trim()
        user_response = Util.send_messages(messages=messages, sending_to="USER")


        if user_response:
            Util.update_state(meta_data=self.meta_data, state=self.state, is_handover=True)
            Util.log_events(meta_data=self.meta_data, state=self.state, events=data.get("publish_events", []))
        return []
Exemple #2
0
 def register(self, reload: bool = False) -> None:
     """Find plugins in plugins directory and register them"""
     plugin_directory = os.path.join(os.path.dirname(__file__), "plugins")
     plugin_modules = ['src.plugins.' + os.path.splitext(path)[0] for path in os.listdir(plugin_directory)
                       if os.path.isfile(os.path.join(plugin_directory, path)) and path.endswith(".py")]
     private_plugin_directory = os.path.join(os.path.dirname(__file__), "plugins", "private")
     plugin_modules += [Util.path_to_module(
         f"src.plugins.private.{os.path.relpath(path, private_plugin_directory)}."
         f"{os.path.splitext(file)[0]}")
         for path, _, files in os.walk(private_plugin_directory) for file in files
         if os.path.isfile(os.path.join(private_plugin_directory, path, file)) and file.endswith(".py")]
     importlib.invalidate_caches()
     for module in plugin_modules:
         log.debug2(f"Processing plugins from module: {module}")
         plugins_file = importlib.import_module(module)
         if reload:
             importlib.reload(plugins_file)
         plugins = [obj[1] for obj in inspect.getmembers(plugins_file, inspect.isclass)
                    if (obj[1].__module__ == module) and issubclass(obj[1], BasePlugin)]
         if len(plugins) == 1:
             plugin = plugins[0]
             actual_functions_list = [
                 func[0] for func in inspect.getmembers(plugin, inspect.isfunction)
                 if not func[0].startswith('_')
             ]
             if all(x in actual_functions_list for x in self._plugin_functions_interface):
                 p = plugin()
                 self._plugins[p.get_classname()] = p
                 log.debug(f"Registered plugin '{p.get_classname()}'")
             else:
                 log.error(f"Class '{p.get_classname()}' does comply with BasePlugin interface")
         elif len(plugins) > 1:
             log.error(f"Module '{module}' have more than 1 class in it")
         else:
             log.error(f"Module '{module}' have no classes in it")
Exemple #3
0
    def __get_next_node_id(cls, data, state, node_data):

        next_node_id = node_data.get('NextNodeId', '')  # Fallback node id

        for button in node_data.get('Buttons', []):
            try:
                root_key = re.split(r'\.|\[',
                                    button.get("ConditionMatchKey"))[0]

                if data.get(root_key) is None:
                    data[root_key] = None

                logger.debug("rootKey " + root_key)

                path = button.get("ConditionMatchKey")
                obj = {root_key: data[root_key]}
                variable_value = Util.deep_find(obj, path)

                match_operator = button.get("ConditionOperator")
                match_value = AnaHelper.verb_replacer(text=button.get(
                    "ConditionMatchValue", ""),
                                                      state=state)

                condition_matched = AnaHelper.is_condition_match(
                    variable_value, match_operator, match_value)

                if condition_matched:
                    next_node_id = button["NextNodeId"]
                    break
            except:
                logger.error("error in btn " + str(button))
                pass

        return next_node_id
Exemple #4
0
def main(args):
    log.info("Reading config.yaml")
    config = Util.read_config_file(const.CONFIG_PATH)
    if config is None:
        config = Config()
    config.commands.update()
    log.info(f"Exporting help to {args.out_file}")
    config.commands.export_help(args.out_file)
Exemple #5
0
 def _save_yaml_file(self, path, config):
     _, yaml_dumper = Util.get_yaml()
     with open(path, 'wb') as f:
         f.write(
             yaml.dump(config,
                       Dumper=yaml_dumper,
                       encoding='utf-8',
                       allow_unicode=True))
    def feed_forward(record, node):
        if node.leaf:
            return node.label

        seq = record['Sequence']
        if Util.check_rule(seq, node.rule):
            return Id3DecisionTree.feed_forward(record, node.positive_child)
        else:
            return Id3DecisionTree.feed_forward(record, node.negative_child)
    def id3(self, node, rules, data):
        assert data

        #print(f'NODE: {node.debug_name}')

        if Util.entrophy(data) == 0:
            node.leaf = True
            node.label = data[0]['Cut']
            self.leaves.append(node)
            return

        if len(rules) == 0:
            label = Util.set_to_label(data)
            node.leaf = True
            node.label = label
            self.leaves.append(node)
            return

        best_rule = Util.best_rule(rules, data)
        if not best_rule:
            label = Util.set_to_label(data)
            node.leaf = True
            node.label = label
            self.leaves.append(node)
            return

        node.rule = best_rule

        positive_child, negative_child = Node(parent=node), Node(parent=node)
        node.positive_child = positive_child
        node.negative_child = negative_child

        self.nodes.append(node.positive_child)
        self.nodes.append(node.negative_child)

        node.positive_child.set, node.negative_child.set = Util.divide_set(
            data, rule=node.rule)

        self.id3(node=node.positive_child,
                 rules=copy.deepcopy(rules),
                 data=node.positive_child.set)
        self.id3(node=node.negative_child,
                 rules=copy.deepcopy(rules),
                 data=node.negative_child.set)
Exemple #8
0
 def start(self) -> int:
     print("Reading markov.yaml...")
     self.markov = Util.read_config_file(const.MARKOV_PATH)
     print("markov.yaml is loaded")
     while not self.quit:
         try:
             command = input("> ")
             self._process(command.split())
         except (EOFError, KeyboardInterrupt):
             self._quit(None)
Exemple #9
0
    def __handle_delivery_events(self, event):
        sender = SenderType.get_name(self.meta_data["senderType"])

        sending_to = None

        if sender == "AGENT":
            sending_to = "USER"
        elif sender == "USER":
            node_key = self.state.get("current_node_id")
            is_agent_node = AnaNode(node_key).check_if_agent_node()
            if is_agent_node:
                sending_to = "AGENT"

        logger.debug(f"Typing event forwarded to {sending_to} meta {self.meta_data} event {event}")

        message = Message(meta=self.meta_data, events=[event]).trim()

        Util.send_messages(messages=[message], sending_to=sending_to)
        return []
    def get_next_node(self, node_data):

        next_node_id = node_data.get('NextNodeId', '')  #Fallback node id
        variable_data = self.state.get("var_data", {})
        buttons = node_data.get("Buttons")

        for button in buttons:

            root_key = re.split(r'\.|\[', button.get("ConditionMatchKey"))[0]
            logger.debug(
                f"Variable Data received for condition call is {variable_data}"
            )

            # if isinstance(variable_data, str):
            # try:
            # variable_data = json.loads(variable_data)
            # except Exception as err:
            # logger.error(f"Error parsing variable_data {variable_data}")
            # variable_data = {}

            logger.debug(
                f"Variable Data after dict conversion is {variable_data}")
            if variable_data.get(root_key) is None:
                continue

            path = button.get("ConditionMatchKey")
            obj = {root_key: variable_data[root_key]}
            variable_value = Util.deep_find(obj, path)

            match_operator = button.get("ConditionOperator")
            match_value = AnaHelper.verb_replacer(text=button.get(
                "ConditionMatchValue", ""),
                                                  state=self.state)

            logger.debug(
                f"variable_value {variable_value} {variable_value.__class__} match_operator {match_operator} match_value {match_value}"
            )

            condition_matched = AnaHelper.is_condition_match(
                variable_value, match_operator, match_value)
            logger.debug(f"Condition matched is {condition_matched}")
            if condition_matched:
                variable_data = self.state.get("var_data", {})
                node_variable_name = node_data.get("VariableName")
                if node_variable_name:
                    button_variable_value = button.get("VariableValue")
                    button_variable_value = AnaHelper.verb_replacer(
                        text=button_variable_value, state=self.state)
                    variable_data[node_variable_name] = button_variable_value
                next_node_id = button["NextNodeId"]
                break

        next_node_key = self.state.get("flow_id", "") + "." + next_node_id
        node_data = AnaNode(next_node_key).get_contents()
        return {"id": next_node_key, "data": node_data}
Exemple #11
0
    def __init__(self, message):

        self.meta_data = message["meta"]
        self.message_data = message.get("data", {})
        self.events = message.get("events", [])

        self.sender_id = self.meta_data["sender"]["id"]
        self.recipient_id = self.meta_data["recipient"]["id"]

        self.state = Util.get_current_state(self.meta_data)
        logger.info(f"Current state of user is {self.state}")
        self.meta_data["sessionId"] = self.state.get("session_id")
Exemple #12
0
    def handle_set_session_data(self, event):

        data = event.get("data", "{}")

        try:
            dict_data = json.loads(data)
            var_data = self.state.get("var_data", {})
            final_var_data = Util.merge_dicts(var_data, dict_data)
            self.state["var_data"] = final_var_data
        except ValueError:
            logger.error(f"Set session data payload is not in json format {data}")

        return []
Exemple #13
0
 def update(self):
     """Perform update"""
     yaml_path = self.config_name + '.yaml'
     if os.path.isfile(yaml_path):
         # .yaml file path
         config = Util.read_config_file(yaml_path)
         getattr(self, self.config_name + "_yaml")(config)
     else:
         if FF.is_enabled("WALBOT_FEATURE_NEW_CONFIG") == "1":
             getattr(self, self.config_name + "_db")()
         else:
             log.error(f"File '{self.config_name}.yaml' does not exist")
             sys.exit(const.ExitStatus.CONFIG_FILE_ERROR)
     if self.modified:
         self._save_yaml_file(yaml_path, config)
Exemple #14
0
    def verb_replacer(text, state):
        if text is None:
            return text
        variable_data = state.get("var_data", {})

        logger.debug(
            f"variable_data {variable_data} {variable_data.__class__}")
        logger.debug(f"text received for replacing verbs is {text}")

        # if isinstance(variable_data, str):
        # variable_data = json.loads(variable_data)

        all_matches = re.findall(r"\[~(.*?)\]|{{(.*?)}}", text)

        for matches in all_matches:
            for match in matches:
                logger.debug(f"match: {match}")
                if variable_data.get(match, None) is not None:
                    logger.debug(
                        f"Match exists in variable_data {variable_data[match]}"
                    )
                    variable_value = variable_data[match]
                    variable_value = str(
                        AnaHelper.escape_json_text(variable_value))
                    text = text.replace("[~" + match + "]",
                                        variable_value).replace(
                                            "{{" + match + "}}",
                                            variable_value)
                    logger.debug(f"Text just after replacing is {text}")
                else:
                    logger.debug("No exact match")
                    root_key = re.split(r"\.|\[", match)[0]
                    logger.debug(f"match: {match}")
                    logger.debug(f"root_key: {root_key}")
                    if variable_data.get(root_key, None) is None:
                        continue
                    variable_value = Util.deep_find(
                        {root_key: variable_data[root_key]}, match)
                    variable_value = str(
                        AnaHelper.escape_json_text(variable_value))
                    logger.debug(f"match: {match}")
                    logger.debug(f"variable_value: {variable_value}")
                    text = text.replace("[~" + match + "]",
                                        str(variable_value)).replace(
                                            "{{" + match + "}}",
                                            str(variable_value))
        logger.debug(f"Text after replacing verbs is {text}")
        return text
Exemple #15
0
    def handle_no_agent_found(self, event):

        messages = []
        self.meta_data["id"] = ""

        message_text = "Apologies, our agents are busy at the moment"
        data = CustomMessage.get_simple_text(text=message_text)
        message = Message(meta=self.meta_data, data=data).trim()
        messages.append(message)

        message_text = "If you have already shared your information, we will get back to you"
        data = CustomMessage.get_simple_text(text=message_text)
        message = Message(meta=self.meta_data, data=data).trim()
        messages.append(message)

        user_response = Util.send_messages(messages=messages, sending_to="USER")
        return []
Exemple #16
0
    def __process_repeatable_item(ana_repeatable, state, is_carousel):
        variable_data = state.get("var_data", {})

        repeat_on_varname = ana_repeatable.get("RepeatOn", None)
        repeat_on = variable_data.get(repeat_on_varname, None)

        if repeat_on is None:
            logger.debug(f"No exact repeat_on_varname")
            root_key = re.split(r"\.|\[", repeat_on_varname)[0]
            if variable_data.get(root_key, None) is not None:
                repeat_on = Util.deep_find({root_key:variable_data[root_key]}, repeat_on_varname)
        
        logger.info("repeat_on: " + json.dumps(repeat_on))
        
        repeat_as_varname = ana_repeatable.get("RepeatAs", None)
        start_position = ana_repeatable.get("StartPosition", 0)
        max_repeats = ana_repeatable.get("MaxRepeats", None)
        end_position = None
        if max_repeats:
            end_position = start_position + max_repeats
            pass
        resulting_items = []
        if isinstance(repeat_on, list):
            for item in repeat_on[start_position:end_position]:
                tempState = {}
                tempState['var_data'] = copy.deepcopy(variable_data)
                tempState['var_data'][repeat_as_varname] = item
                item_json = json.dumps(ana_repeatable)
                replaced_item_json = AnaHelper.verb_replacer(text=item_json, state=tempState)
                replaced_item = json.loads(replaced_item_json)
                if is_carousel:
                    car_buttons = replaced_item.get("Buttons", [])
                    car_buttons = AnaHelper.process_repeatable(car_buttons, tempState, False)
                    for car_button in car_buttons:
                        car_button["DoesRepeat"] = False
                    replaced_item["Buttons"] = car_buttons
                    pass
                replaced_item['_id'] = replaced_item.get('_id', '') + "--" + str(repeat_on.index(item))
                resulting_items.append(replaced_item)
                pass
            pass
        pass
        return resulting_items
Exemple #17
0
 def __make_conspicuity_map(self, srcs):
     util = Util()
     intensity = self.__scale_add(
         list(map(util.normalize, srcs['intensity'])))
     for key in srcs['colors'].keys():
         srcs['colors'][key] = list(map(util.normalize,
                                        srcs['colors'][key]))
     color = self.__scale_add([
         srcs['colors']['bg'][x] + srcs['colors']['ry'][x]
         for x in range(len(srcs['colors']['bg']))
     ])
     orientation = np.zeros(intensity.shape)
     for key in srcs['orientations'].keys():
         orientation += self.__scale_add(
             list(map(util.normalize, srcs['orientations'][key])))
     return {
         'intensity': intensity,
         'color': color,
         'orientation': orientation
     }
    def __get_node(self, message_data):
        """
        Get next_node(ANA output node) to send to user depending on current_node
        and the incoming message. If it's a first time user, next_node is first_node
        """

        get_started_node = self.state.get(
            "flow_id", "") + "." + flow_config["first_node_key"]
        next_node_id = get_started_node
        event_data = []

        if bool(self.state.get("current_node_id")):
            # user already in ana flow
            current_node_id = self.state.get(
                "current_node_id",
                get_started_node)  # give first_node as default
            next_node_data = AnaNode(current_node_id).get_next_node_data(
                message_data, self.state)

            event_data = next_node_data.get("publish_events", [])
            next_node_id = next_node_data["node_id"]

            var_data = self.state.get("var_data", {})
            new_var_data = next_node_data.get("input_data", {})
            logger.debug(f"var_data type is {type(var_data)} {var_data}")
            logger.debug(
                f"new_var_data type is {type(var_data)} {new_var_data}")
            logger.debug(f"var_data type is {type(var_data)} {var_data}")
            logger.debug(
                f"new_var_data type is {type(new_var_data)} {new_var_data}")
            final_var_data = Util.merge_dicts(var_data, new_var_data)
            self.state["var_data"] = final_var_data
            self.state["new_var_data"] = new_var_data

        # self.state["current_node_id"] = next_node_id
        node = AnaNode(next_node_id).get_contents()

        return {"node": node, "publish_events": event_data}
Exemple #19
0
    def respond_to_message(self):
        """
        This method is responsible for getting the messages to respond with
        Also covers analytics events for those messages for e.g. click, view
        """

        MessageEventHandler(self.state, self.meta_data, self.message_data).handle_events(events=self.events)
        data = Converter(self.state).get_messages(meta_data=self.meta_data, message_data=self.message_data)

        outgoing_messages = data.get("messages", [])
        events_to_publish = data.get("publish_events", [])

        agent_messages = [message["message"] for message in outgoing_messages if message["sending_to"] == "AGENT"]
        user_messages = [message["message"] for message in outgoing_messages if message["sending_to"] == "USER"]

        agent_response = Util.send_messages(messages=agent_messages, sending_to="AGENT")
        user_response = Util.send_messages(messages=user_messages, sending_to="USER")

        if agent_response or user_response:

            Util.update_state(meta_data=self.meta_data, state=self.state)
            Util.log_events(meta_data=self.meta_data, state=self.state, events=events_to_publish)

        return 1
Exemple #20
0
# -*- coding: utf-8 -*-

import uvicorn
from fastapi import FastAPI, Request
from pydantic import BaseModel
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from typing import List
from src.config import dpath, taginfo, tag_color_mapping
from src.utils import Util

app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")

util = Util()


class Item(BaseModel):
    idx: int
    senetnce: str = None
    label_sen_arr: List[str] = None


@app.get('/')
async def index(request: Request):
    return templates.TemplateResponse("hello.html", {
        "request": request,
        "message": '欢迎使用'
    })
 def to_leaf(self):
     label = Util.set_to_label(self.set)
     return Node(parent=self.parent, leaf=True, label=label, set=self.set)
Exemple #22
0
 def __make_saliency_map(self, srcs):
     util = Util()
     srcs = list(map(util.normalize, [srcs[key] for key in srcs.keys()]))
     return srcs[0] / 3. + srcs[1] / 3. + srcs[2] / 3.
Exemple #23
0
 def start(self, args, main_bot=True):
     # Check whether bot is already running
     bot_cache = BotCache(main_bot).parse()
     if bot_cache is not None:
         pid = bot_cache["pid"]
         if pid is not None and psutil.pid_exists(pid):
             return log.error("Bot is already running!")
     # Some variable initializations
     config = None
     secret_config = None
     bc.restart_flag = False
     bc.args = args
     # Handle --nohup flag
     if sys.platform in ("linux", "darwin") and args.nohup:
         fd = os.open(const.NOHUP_FILE_PATH,
                      os.O_WRONLY | os.O_CREAT | os.O_APPEND)
         log.info(f"Output is redirected to {const.NOHUP_FILE_PATH}")
         os.dup2(fd, sys.stdout.fileno())
         os.dup2(sys.stdout.fileno(), sys.stderr.fileno())
         os.close(fd)
         signal.signal(signal.SIGHUP, signal.SIG_IGN)
     # Selecting YAML parser
     bc.yaml_loader, bc.yaml_dumper = Util.get_yaml(verbose=True)
     # Saving application pd in order to safely stop it later
     BotCache(main_bot).dump_to_file()
     # Executing patch tool if it is necessary
     if args.patch:
         cmd = f"'{sys.executable}' '{os.path.dirname(__file__) + '/../tools/patch.py'}' all"
         log.info("Executing patch tool: " + cmd)
         subprocess.call(cmd)
     # Read configuration files
     config = Util.read_config_file(const.CONFIG_PATH)
     if config is None:
         config = Config()
     secret_config = Util.read_config_file(const.SECRET_CONFIG_PATH)
     if secret_config is None:
         secret_config = SecretConfig()
     bc.markov = Util.read_config_file(const.MARKOV_PATH)
     if bc.markov is None and os.path.isdir("backup"):
         # Check available backups
         markov_backups = sorted([
             x for x in os.listdir("backup")
             if x.startswith("markov_") and x.endswith(".zip")
         ])
         if markov_backups:
             # Restore Markov model from backup
             with zipfile.ZipFile("backup/" + markov_backups[-1],
                                  'r') as zip_ref:
                 zip_ref.extractall(".")
             log.info(
                 f"Restoring Markov model from backup/{markov_backups[-1]}")
             shutil.move(markov_backups[-1][:-4], "markov.yaml")
             bc.markov = Util.read_config_file(const.MARKOV_PATH)
             if bc.markov is None:
                 bc.markov = Markov()
                 log.warning(
                     "Failed to restore Markov model from backup. Creating new Markov model..."
                 )
     if bc.markov is None:
         bc.markov = Markov()
         log.info("Created empty Markov model")
     # Check config versions
     ok = True
     ok &= Util.check_version(
         "discord.py",
         discord.__version__,
         const.DISCORD_LIB_VERSION,
         solutions=[
             "execute: python -m pip install -r requirements.txt",
         ])
     ok &= Util.check_version(
         "Config",
         config.version,
         const.CONFIG_VERSION,
         solutions=[
             "run patch tool",
             "remove config.yaml (settings will be lost!)",
         ])
     ok &= Util.check_version(
         "Markov config",
         bc.markov.version,
         const.MARKOV_CONFIG_VERSION,
         solutions=[
             "run patch tool",
             "remove markov.yaml (Markov model will be lost!)",
         ])
     ok &= Util.check_version(
         "Secret config",
         secret_config.version,
         const.SECRET_CONFIG_VERSION,
         solutions=[
             "run patch tool",
             "remove secret.yaml (your Discord authentication token will be lost!)",
         ])
     if main_bot and not ok:
         sys.exit(const.ExitStatus.CONFIG_FILE_ERROR)
     config.commands.update()
     # Checking authentication token
     if secret_config.token is None:
         secret_config = SecretConfig()
         if not FF.is_enabled("WALBOT_FEATURE_NEW_CONFIG"):
             secret_config.token = input("Enter your token: ")
     # Constructing bot instance
     if main_bot:
         intents = discord.Intents.all()
         walbot = WalBot(args.name, config, secret_config, intents=intents)
     else:
         walbot = importlib.import_module("src.minibot").MiniWalBot(
             args.name, config, secret_config, args.message)
     # Starting the bot
     try:
         walbot.run(secret_config.token)
     except discord.errors.PrivilegedIntentsRequired:
         log.error(
             "Privileged Gateway Intents are not enabled! Shutting down the bot..."
         )
     # After stopping the bot
     log.info("Bot is disconnected!")
     if main_bot:
         config.save(const.CONFIG_PATH,
                     const.MARKOV_PATH,
                     const.SECRET_CONFIG_PATH,
                     wait=True)
     BotCache(main_bot).remove()
     if bc.restart_flag:
         cmd = f"'{sys.executable}' '{os.path.dirname(os.path.dirname(__file__)) + '/walbot.py'}' start"
         log.info("Calling: " + cmd)
         if sys.platform in ("linux", "darwin"):
             fork = os.fork()
             if fork == 0:
                 subprocess.call(cmd)
             elif fork > 0:
                 log.info("Stopping current instance of the bot")
                 sys.exit(const.ExitStatus.NO_ERROR)
         else:
             subprocess.call(cmd)