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 []
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")
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
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)
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)
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)
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}
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")
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 []
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)
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
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 []
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
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}
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
# -*- 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)
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.
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)