def __init__(self): super().__init__() self.log = ConsoleLogger() self.inventory_collection = None self.inventory_collection_name = None self.collections = {} self.monitoring_setup_manager = None
class AndroidAudioUtils(): def __init__(self): self._logger = ConsoleLogger() self._droid = sl4a.Android() def speak(self, text): self._droid.ttsSpeak(text) def stop_listening(self): self._listening = False def listen(self, callback): self._listening = True self._run_listening_loop(callback) def _run_listening_loop(self, callback): while self._listening: if self._droid.ttsIsSpeaking()[1]: sleep(1) continue try: result = self._droid.recognizeSpeech()[1] callback(result) sleep(1) except Exception as e: self._logger.error(e)
def __init__(self): self._logger = ConsoleLogger() self._recognizer = sr.Recognizer() self._microphone = sr.Microphone() with self._microphone as source: self._recognizer.adjust_for_ambient_noise(source)
def __init__(self, messenger): super(MusicAgent, self).__init__(messenger=messenger, name=NAME, comparison_commands=COMPARISON_COMMANDS) self._logger = ConsoleLogger() self._player = None self._messenger.register_callback(self.process)
def __init__(self, platform="osx"): self._logger = ConsoleLogger() self._similarity_utils = SimilarityUtilsFactory().get_similarity_utils( ) self._audio_utils = AudioUtilsFactory( platform=platform).get_audio_utils() self._topic_agents = [] self._default_agent = None self._messenger_dict = {} self._init_agents()
def setUp(self): self.log = ConsoleLogger() self.env = ENV_CONFIG self.collection = COLLECTION_CONFIG self.item_ids = [] self.inv_patcher = patch('discover.events.event_base.InventoryMgr') self.inv_class = self.inv_patcher.start() self.inv = self.inv_class.return_value self.log_patcher = patch('discover.fetcher.FullLogger') self.log_patcher.start()
class PcAudioUtils(): def __init__(self): self._logger = ConsoleLogger() self._recognizer = sr.Recognizer() self._microphone = sr.Microphone() with self._microphone as source: self._recognizer.adjust_for_ambient_noise(source) def speak(self, text, language="en"): if text is not None: call(['python3', 'utils/audio/pc_text_to_voice.py', text, language]) def stop_listening(self): raise NotImplementedError("listen has to be called first!") def listen(self, callback): listener_callback = partial( self._listen_for_command, callback) stop_recognizer = self._recognizer.listen_in_background( self._microphone, listener_callback) self.stop_listening = stop_recognizer def _listen_for_command(self, callback, _, audio): try: command = self._recognizer.recognize_google(audio) callback(command) except sr.UnknownValueError: self._logger.warning("A Value error occurred. Please repeat!") except sr.RequestError as e: self._logger.error( "Could not request results from Google Speech Recognition service; {0}".format(e))
class CalipsoApiException(Exception): log = ConsoleLogger() def __init__(self, status, body="", message=""): super().__init__(message) self.message = message self.status = status self.body = body @staticmethod def handle(ex, req, resp, params): CalipsoApiException.log.error(ex.message) resp.status = ex.status resp.body = ex.body
def __init__(self): super().__init__() self.log_file = os.path.join(FileLogger.LOG_DIRECTORY, MongoAccess.LOG_FILENAME) try: self.log = FileLogger(self.log_file) except OSError as e: ConsoleLogger().warning("Couldn't use file {} for logging. " "Using default location: {}.\n" "Error: {}".format(self.log_file, self.DEFAULT_LOG_FILE, e)) self.log_file = self.DEFAULT_LOG_FILE self.log = FileLogger(self.log_file) self.connect_params = {} self.mongo_connect(self.config_file)
class MoneyPenny(): def __init__(self, platform="osx"): self._logger = ConsoleLogger() self._similarity_utils = SimilarityUtilsFactory().get_similarity_utils( ) self._audio_utils = AudioUtilsFactory( platform=platform).get_audio_utils() self._topic_agents = [] self._default_agent = None self._messenger_dict = {} self._init_agents() def _init_agents(self): self._init_topic_agents() self._init_default_agent() def _init_topic_agents(self): for AgentClass in TOPIC_AGENTS: messenger = QueueMessenger() agent = AgentClass(messenger) self._topic_agents.append(agent) self._messenger_dict[agent.name] = messenger def _init_default_agent(self): messenger = QueueMessenger() agent = DEFAULT_AGENT(messenger) self._default_agent = agent self._messenger_dict[agent.name] = messenger def _listen(self, command): self._logger.info(f"Understood '{command}'") valid = self._evaluate_command(command) if not valid: return command = self._preprocess_command(command) agent = self._get_most_probable_agent(command, threshold=0.5) self._send_command_to_agent(agent, command) def _evaluate_command(self, command): command = command.lower() if 'moneypenny' in command or 'money penny' in command: return True else: self._logger.warning('The command has to include "moneypenny"!') return False def _preprocess_command(self, command): command = command.lower() if 'moneypenny' in command: command = command.split("moneypenny")[-1] elif 'money penny' in command: command = command.split("money penny")[-1] return command def _get_most_probable_agent(self, command, threshold=0): highest_similarities = [] for agent in self._topic_agents: highest_similarity = 0 for comparison_command in agent.comparison_commands: similarity = self._similarity_utils.get_similarity( command, comparison_command) if similarity > highest_similarity: highest_similarity = similarity highest_similarities.append(highest_similarity) if len([ similarity for similarity in highest_similarities if similarity > threshold ]) > 0: agent_index = np.argmax(highest_similarities) return self._topic_agents[agent_index] else: return self._default_agent def _send_command_to_agent(self, agent, command): messenger = self._messenger_dict[agent.name] messenger.send(command) def start(self): self._audio_utils.listen(self._listen) self._audio_utils.speak("I am ready to listen to you.") self._default_agent.start() for agent in self._topic_agents: agent.start() def stop(self): self._audio_utils.stop_listening() self._default_agent.stop() for agent in self._topic_agents: agent.stop() self._logger.info('stopped moneypenny')
def __init__(self): super().__init__() self.log = ConsoleLogger()
class CliAccess(BinaryConverter, Fetcher): connections = {} ssh_cmd = "ssh -q -o StrictHostKeyChecking=no " call_count_per_con = {} max_call_count_per_con = 100 cache_lifetime = 60 # no. of seconds to cache results cached_commands = {} def __init__(self): super().__init__() self.log = ConsoleLogger() @staticmethod def is_gateway_host(ssh_to_host): ssh_conn = SshConn(ssh_to_host) return ssh_conn.is_gateway_host(ssh_to_host) def run_on_gateway(self, cmd, ssh_to_host="", enable_cache=True, use_sudo=True): self.run(cmd, ssh_to_host=ssh_to_host, enable_cache=enable_cache, on_gateway=True, use_sudo=use_sudo) def run(self, cmd, ssh_to_host="", enable_cache=True, on_gateway=False, ssh=None, use_sudo=True): ssh_conn = ssh if ssh else SshConn(ssh_to_host) commands = self.adapt_cmd_to_env(ssh_conn, cmd, use_sudo, on_gateway, ssh_to_host) out = '' for c in commands: out += self.run_single_command(c, ssh_conn, ssh_to_host, enable_cache=enable_cache) return out def run_single_command(self, cmd, ssh_conn, ssh_to_host="", enable_cache=True): curr_time = time.time() cmd_path = ssh_to_host + ',' + cmd if enable_cache and cmd_path in self.cached_commands: # try to re-use output from last call cached = self.cached_commands[cmd_path] if cached["timestamp"] + self.cache_lifetime < curr_time: # result expired self.cached_commands.pop(cmd_path, None) else: # result is good to use - skip the SSH call self.log.info('CliAccess: ****** using cached result, ' + 'host: ' + ssh_to_host + ', cmd: %s ******', cmd) return cached["result"] self.log.info('CliAccess: host: %s, cmd: %s', ssh_to_host, cmd) ret = ssh_conn.exec(cmd) self.cached_commands[cmd_path] = {"timestamp": curr_time, "result": ret} return ret def run_fetch_lines(self, cmd, ssh_to_host="", enable_cache=True): out = self.run(cmd, ssh_to_host, enable_cache) if not out: return [] # first try to split lines by whitespace ret = out.splitlines() # if split by whitespace did not work, try splitting by "\\n" if len(ret) == 1: ret = [line for line in out.split("\\n") if line != ""] return ret MULTI_COMMAND_SEPARATOR = ';;;' @staticmethod def handle_split_cmd(cmd: str): if CliAccess.MULTI_COMMAND_SEPARATOR in cmd: return cmd.split(CliAccess.MULTI_COMMAND_SEPARATOR) return [cmd] def adapt_cmd_to_env(self, ssh_conn, cmd, use_sudo, on_gateway, ssh_to_host): cmd = self.adapt_cmd_to_dist(cmd) commands = self.handle_split_cmd(cmd) return [self.adapt_cmd_to_environment(c, use_sudo, on_gateway, ssh_to_host, ssh_conn) for c in commands] def adapt_cmd_to_environment(self, cmd, use_sudo, on_gateway, ssh_to_host, ssh_conn): if self.configuration.environment["distribution"] == "Mercury": use_sudo = False if use_sudo and not cmd.strip().startswith("sudo "): cmd = "sudo " + cmd if not on_gateway and ssh_to_host \ and not ssh_conn.is_gateway_host(ssh_to_host): cmd = self.ssh_cmd + ssh_to_host + " " + cmd return cmd def adapt_cmd_to_dist(self, cmd): env_conf = self.configuration.get_env_config() dist = env_conf.get('distribution') dist_version = env_conf.get('distribution_version') translator = CliDistTranslator(dist, dist_version=dist_version) cmd = translator.translate(cmd) return cmd # parse command output columns separated by whitespace # since headers can contain whitespace themselves, # it is the caller's responsibility to provide the headers def parse_cmd_result_with_whitespace(self, lines, headers, remove_first): if remove_first: # remove headers line del lines[:1] results = [self.parse_line_with_ws(line, headers) for line in lines] return results # parse command output with "|" column separators and "-" row separators def parse_cmd_result_with_separators(self, lines): headers = self.parse_headers_line_with_separators(lines[1]) # remove line with headers and formatting lines above it and below it del lines[:3] # remove formatting line in the end lines.pop() results = [self.parse_content_line_with_separators(line, headers) for line in lines] return results # parse a line with columns separated by whitespace def parse_line_with_ws(self, line, headers): s = line if isinstance(line, str) else self.binary2str(line) parts = [word.strip() for word in s.split() if word.strip()] ret = {} for i, p in enumerate(parts): header = headers[i] ret[header] = p return ret # parse a line with "|" column separators def parse_line_with_separators(self, line): s = self.binary2str(line) parts = [word.strip() for word in s.split("|") if word.strip()] # remove the ID field del parts[:1] return parts def parse_headers_line_with_separators(self, line): return self.parse_line_with_separators(line) def parse_content_line_with_separators(self, line, headers): content_parts = self.parse_line_with_separators(line) content = {} for i in range(0, len(content_parts)): content[headers[i]] = content_parts[i] return content @staticmethod def merge_ws_spillover_lines(lines): # with WS-separated output, extra output sometimes spills to next line # detect that and add to the end of the previous line for our procesing pending_line = None fixed_lines = [] # remove headers line for l in lines: if l[0] == '\t': # this is a spill-over line if pending_line: # add this line to the end of the previous line pending_line = pending_line.strip() + "," + l.strip() else: # add the previous pending line to the fixed lines list if pending_line: fixed_lines.append(pending_line) # make current line the pending line pending_line = l if pending_line: fixed_lines.append(pending_line) return fixed_lines """ given output lines from CLI command like 'ip -d link show', find lines belonging to section describing a specific interface parameters: - lines: list of strings, output of command - header_regexp: regexp marking the start of the section - end_regexp: regexp marking the end of the section """ @staticmethod def get_section_lines(lines, header_regexp, end_regexp): if not lines: return [] header_re = re.compile(header_regexp) start_pos = None # find start_pos of section line_count = len(lines) for line_num in range(0, line_count-1): matches = header_re.match(lines[line_num]) if matches: start_pos = line_num break if not start_pos: return [] # find end of section end_pos = line_count end_re = re.compile(end_regexp) for line_num in range(start_pos+1, end_pos-1): matches = end_re.match(lines[line_num]) if matches: end_pos = line_num break return lines[start_pos:end_pos] def get_object_data(self, o, lines, regexps): """ find object data in output lines from CLI command parameters: - o: object (dict), to which we'll add attributes with the data found - lines: list of strings - regexps: dict, keys are attribute names, values are regexp to match for finding the value of the attribute """ for line in lines: self.find_matching_regexps(o, line, regexps) for regexp_tuple in regexps: name = regexp_tuple['name'] if 'name' not in o and 'default' in regexp_tuple: o[name] = regexp_tuple['default'] @staticmethod def find_matching_regexps(o, line, regexps): for regexp_tuple in regexps: name = regexp_tuple['name'] regex = regexp_tuple['re'] regex = re.compile(regex) matches = regex.search(line) if matches and name not in o: o[name] = matches.group(1)
class InventoryMgr(MongoAccess, metaclass=Singleton): def __init__(self): super().__init__() self.log = ConsoleLogger() self.inventory_collection = None self.inventory_collection_name = None self.collections = {} self.monitoring_setup_manager = None def set_collection(self, collection_type: str = None, use_default_name: bool = False): # do not allow setting the collection more than once if not self.collections.get(collection_type): collection_name = collection_type \ if use_default_name \ else self.get_coll_name(collection_type) self.log.info("Using {} collection: {}".format( collection_type, collection_name)) self.collections[collection_type] = MongoAccess.db[collection_name] def set_inventory_collection(self, collection_name: str = None): if not self.inventory_collection: if not collection_name: collection_name = "inventory" self.log.info( "Using inventory collection: {}".format(collection_name)) collection = MongoAccess.db[collection_name] self.collections["inventory"] = collection self.inventory_collection = collection self.inventory_collection_name = collection_name def get_coll_name(self, coll_name): if not self.inventory_collection_name: raise TypeError("inventory_collection_name is not set") return self.inventory_collection_name.replace("inventory", coll_name) \ if self.inventory_collection_name.startswith("inventory") \ else self.inventory_collection_name + "_" + coll_name def set_collections(self, inventory_collection: str = None): self.set_inventory_collection(inventory_collection) self.set_collection("links") self.set_collection("link_types") self.set_collection("clique_types") self.set_collection("clique_constraints") self.set_collection("cliques") self.set_collection("monitoring_config") self.set_collection("scans") self.set_collection("messages") self.set_collection("environments_config") self.set_collection("supported_environments") self.set_collection("connection_tests") self.set_collection("constants", use_default_name=True) self.set_collection("monitoring_config_templates", use_default_name=True) self.set_collection("api_tokens", use_default_name=True) def clear(self, scan_plan): if scan_plan.inventory_only: collections = {"inventory"} elif scan_plan.links_only: collections = {"links"} elif scan_plan.cliques_only: collections = {"cliques"} else: collections = { "inventory", "links", "cliques", "monitoring_config" } env_cond = {} if scan_plan.clear_all else { "environment": scan_plan.env } for collection_name in collections: collection = self.collections[collection_name] self.log.info("clearing collection: " + collection.full_name) # delete docs from the collection, # either all or just for the specified environment collection.delete_many(env_cond) # return single match def get_by_id(self, environment, item_id): return self.find({ "environment": environment, "id": item_id }, get_single=True) # return matches for ID in list of values def get_by_ids(self, environment, ids_list): return self.find({"environment": environment, "id": {"$in": ids_list}}) def get_by_field(self, environment, item_type, field_name, field_value, get_single=False): if field_value: return self.find( { "environment": environment, "type": item_type, field_name: field_value }, get_single=get_single) else: return self.find({ "environment": environment, "type": item_type }, get_single=get_single) def get(self, environment, item_type, item_id, get_single=False): return self.get_by_field(environment, item_type, "id", item_id, get_single=get_single) def get_children(self, environment, item_type, parent_id): if parent_id: if not item_type: return self.find({ "environment": environment, "parent_id": parent_id }) else: return self.find({ "environment": environment, "type": item_type, "parent_id": parent_id }) else: return self.find({"environment": environment, "type": item_type}) def get_single(self, environment, item_type, item_id): matches = self.find({ "environment": environment, "type": item_type, "id": item_id }) if len(matches) > 1: raise ValueError("Found multiple matches for item: " + "type=" + item_type + ", id=" + item_id) if len(matches) == 0: raise ValueError("No matches for item: " + "type=" + item_type + ", id=" + item_id) return matches[0] # item must contain properties 'environment', 'type' and 'id' def set(self, item, collection=None): col = collection mongo_id = None projects = None if "_id" in item: mongo_id = item.pop("_id", None) if not collection or collection == self.collections['inventory']: # make sure we have environment, type & id self.check(item, "environment") self.check(item, "type") self.check(item, "id") item["last_scanned"] = datetime.now() item.pop("projects", []) obj_name = item["name_path"] obj_name = obj_name[obj_name.rindex('/') + 1:] if 'object_name' not in item: item['object_name'] = obj_name self.set_collections() # make sure we have all collections set if not col: col = self.collections['inventory'] find_tuple = { "environment": item["environment"], "type": item["type"], "id": item["id"] } else: find_tuple = {'_id': bson.ObjectId(mongo_id)} doc = col.find_one(find_tuple) if not doc: raise ValueError('set(): could not find document with _id=' + mongo_id) col.update_one(find_tuple, {'$set': self.encode_mongo_keys(item)}, upsert=True) if mongo_id: # restore original mongo ID of document, in case we need to use it item['_id'] = mongo_id if projects: col.update_one(find_tuple, {'$addToSet': { "projects": { '$each': projects } }}, upsert=True) @staticmethod def check(obj, field_name): arg = obj[field_name] if not arg or not str(arg).rstrip(): raise ValueError("Inventory item - " + "the following field is not defined: " + field_name) # note: to use general find, call find_items(), # which also does process_results @inv_initialization_required def find(self, search, projection=None, collection=None, get_single=False): coll = self.inventory_collection if not collection \ else self.collections[collection] if get_single is True: return self.decode_object_id( self.decode_mongo_keys( coll.find_one(search, projection=projection))) else: return list( map( self.decode_object_id, map(self.decode_mongo_keys, coll.find(search, projection=projection)))) def find_one(self, search, projection=None, collection=None) -> dict: return self.find(search, projection, collection, True) def find_items(self, search, projection=None, get_single=False, collection=None): return self.find(search, projection, collection, get_single) # record a link between objects in the inventory, to be used in graphs # returns - the new link document # parameters - # environment: name of environment # host: name of host # source: node mongo _id # source_id: node id value of source node # target: node mongo _id # target_id: node id value of target node # link_type: string showing types of connected objects, e.g. "instance-vnic" # link_name: label for the link itself # state: up/down # link_weight: integer, position/priority for graph placement # source_label, target_label: labels for the ends of the link (optional) def create_link(self, env, src, source_id, target, target_id, link_type, link_name, state, link_weight, source_label="", target_label="", host=None, switch=None, extra_attributes=None): s = bson.ObjectId(src) t = bson.ObjectId(target) link = { "environment": env, "source": s, "source_id": source_id, "target": t, "target_id": target_id, "link_type": link_type, "link_name": link_name, "state": state, "link_weight": link_weight, "source_label": source_label, "target_label": target_label, "attributes": extra_attributes if extra_attributes else {} } if host: link['host'] = host if switch: link['switch'] = switch return self.write_link(link) def write_link(self, link): find_tuple = { 'environment': link['environment'], 'source_id': link['source_id'], 'target_id': link['target_id'] } if "_id" in link: link.pop("_id", None) link_encoded = self.encode_mongo_keys(link) links_col = self.collections["links"] result = links_col.update_one(find_tuple, {'$set': link_encoded}, upsert=True) link['_id'] = result.upserted_id return link def values_replace_in_object(self, o, values_replacement): for k in values_replacement.keys(): if k not in o: continue repl = values_replacement[k] if 'from' not in repl or 'to' not in repl: continue o[k] = o[k].replace(repl['from'], repl['to']) self.set(o) # perform replacement of substring in values of objects in the inventory # input: # - search: dict with search parameters # - values_replacement: dict, # - keys: names of keys for which to replace the values # - values: dict with "from" (value to be replaced) and "to" (new value) @inv_initialization_required def values_replace(self, search, values_replacement): for doc in self.inventory_collection.find(search): self.values_replace_in_object(doc, values_replacement) def delete(self, coll, query_filter): collection = self.collections[coll] if not collection: self.log.warn('delete(): collection not found - ' + coll) return result = collection.delete_many(query_filter) count = result.deleted_count self.log.info('delete(): ' + ('deleted ' + str(count) + ' documents' if count else 'no matching documents')) return count def get_env_config(self, env: str): return self.find_one(search={'name': env}, collection='environments_config') def is_feature_supported(self, env: str, feature: EnvironmentFeatures)\ -> bool: env_config = self.get_env_config(env) if not env_config: return False # Workaround for mechanism_drivers field type mechanism_driver = env_config['mechanism_drivers'][0] \ if isinstance(env_config['mechanism_drivers'], list) \ else env_config['mechanism_drivers'] full_env = { 'environment.distribution': env_config['distribution'], 'environment.distribution_version': { "$in": [env_config['distribution_version']] }, 'environment.type_drivers': env_config['type_drivers'], 'environment.mechanism_drivers': mechanism_driver } return self.is_feature_supported_in_env(full_env, feature) def is_feature_supported_in_env(self, env_def: dict, feature: EnvironmentFeatures) -> bool: result = self.collections['supported_environments'].find_one(env_def) if not result: return False features_in_env = result.get('features', {}) return features_in_env.get(feature.value) is True def save_inventory_object(self, o: dict, parent: dict, environment: str, type_to_fetch: dict = None) \ -> bool: if not type_to_fetch: type_to_fetch = {} o["id"] = str(o["id"]) o["environment"] = environment if type_to_fetch.get("type"): o["type"] = type_to_fetch["type"] o["show_in_tree"] = type_to_fetch.get("show_in_tree", True) parent_id_path = parent.get("id_path", "/{}".format(environment)) parent_name_path = parent.get("name_path", "/{}".format(environment)) try: # case of dynamic folder added by need master_parent_type = o["master_parent_type"] master_parent_id = o["master_parent_id"] master_parent = self.get_by_id(environment, master_parent_id) if not master_parent: self.log.error("failed to find master parent " + master_parent_id) return False folder_id_path = "/".join( (master_parent["id_path"], o["parent_id"])) folder_name_path = "/".join( (master_parent["name_path"], o["parent_text"])) folder = { "environment": parent["environment"], "parent_id": master_parent_id, "parent_type": master_parent_type, "id": o["parent_id"], "id_path": folder_id_path, "show_in_tree": True, "name_path": folder_name_path, "name": o["parent_id"], "type": o["parent_type"], "text": o["parent_text"] } # remove master_parent_type & master_parent_id after use, # as they're there just ro help create the dynamic folder o.pop("master_parent_type", True) o.pop("master_parent_id", True) self.set(folder) except KeyError: pass if o.get("text"): o["name"] = o["text"] elif not o.get("name"): o["name"] = o["id"] if "parent_id" not in o and parent: parent_id = parent["id"] o["parent_id"] = parent_id o["parent_type"] = parent["type"] elif "parent_id" in o and o["parent_id"] != parent["id"]: # using alternate parent - fetch parent path from inventory parent_obj = self.get_by_id(environment, o["parent_id"]) if parent_obj: parent_id_path = parent_obj["id_path"] parent_name_path = parent_obj["name_path"] o["id_path"] = "/".join((parent_id_path, o["id"].strip())) o["name_path"] = "/".join((parent_name_path, o["name"])) # keep list of projects that an object is in associated_projects = [] keys_to_remove = [] for k in o: if k.startswith("in_project-"): proj_name = k[k.index('-') + 1:] associated_projects.append(proj_name) keys_to_remove.append(k) for k in keys_to_remove: o.pop(k) if len(associated_projects) > 0: projects = o["projects"] if "projects" in o.keys() else [] projects.extend(associated_projects) if projects: o["projects"] = projects if "create_object" not in o or o["create_object"]: # add/update object in DB self.set(o) if self.is_feature_supported(environment, EnvironmentFeatures.MONITORING): self.monitoring_setup_manager.create_setup(o) return True
def __init__(self): super().__init__() self.log_file = os.path.join(FileLogger.LOG_DIRECTORY, self.LOG_FILENAME) self.log = ConsoleLogger(level=Logger.INFO)
def __init__(self): self._logger = ConsoleLogger() self._droid = sl4a.Android()
class MusicAgent(TopicAgent): def __init__(self, messenger): super(MusicAgent, self).__init__(messenger=messenger, name=NAME, comparison_commands=COMPARISON_COMMANDS) self._logger = ConsoleLogger() self._player = None self._messenger.register_callback(self.process) def _get_url(self, title): # based on https://github.com/dashvinsingh/YoutubeAudio-Python-Stream/blob/master/youtube_stream.py query = urllib.parse.quote(title) url = "https://www.youtube.com/results?search_query=" + query response = urllib.request.urlopen(url) sleep(1) html = response.read() try: soup = BeautifulSoup(html, 'lxml') video_urls = soup.findAll(attrs={'class': 'yt-uix-tile-link'}) video_url = 'https://www.youtube.com' + video_urls[0]['href'] except IndexError: return self._get_url(title) video = pafy.new(video_url) best = video.getbestaudio() playurl = best.url return playurl def _play_audio_from_url(self, url): Instance = vlc.Instance() self._player = Instance.media_player_new() Media = Instance.media_new(url) Media.get_mrl() self._player.set_media(Media) self._player.play() while self._player and not self._player.is_playing(): sleep(0.2) while self._player is not None and self._player.is_playing(): sleep(1) def _play_audio(self, title): self._logger.info('play entered') if self._player and self._player.is_playing(): self._stop_audio() url = self._get_url(title) playing_thread = Thread(target=self._play_audio_from_url, args=[url]) playing_thread.start() def _pause_audio(self): print('pause entered') if self._player is not None: self._player.pause() def _stop_audio(self): self._logger.info('stop entered') if self._player is not None: self._player.stop() self._player = None def _resume_audio(self): self._logger.info('resume entered') if self._player is not None: self._player.pause() def _raise_volume(self): self._logger.info('raise volume entered') if self._player is not None: volume = self._player.audio_get_volume() raised_volume = max(volume + 10, 100) self._player.audio_set_volume(raised_volume) def _lower_volume(self): self._logger.info('lower volume entered') if self._player is not None: volume = self._player.audio_get_volume() raised_volume = min(volume - 10, 0) self._player.audio_set_volume(raised_volume) def process(self, command): if 'play' in command: # TODO: Use more elaborate nlp title = command.split("play")[-1] self._play_audio(title) elif 'pause' in command: self._pause_audio() elif 'resume' in command: self._resume_audio() elif 'stop' in command: self._stop_audio() elif 'raise' in command: self._raise_volume() elif 'lower' in command: self._raise_volume() return '' def run(self): self._messenger.start_listening() def stop(self): self._messenger.stop_listening()