def error_handler(update: Update, context: CallbackContext): formatted_error = "HomeBot: Error encountered!\n" formatted_error += f"Command sent: {update.message.text}\n\n" formatted_error += format_exception(context.error) LOGE(formatted_error) update.message.reply_text(formatted_error) LOGE("End error handling")
def error_handler(update: Update, context: CallbackContext): formatted_error = "HomeBot: Error encountered!\n" formatted_error += f"Command sent: {update.message.text}\n\n" formatted_error += ''.join( traceback.format_exception(type(context.error), context.error, context.error.__traceback__, limit=None, chain=True)) LOGE(formatted_error) update.message.reply_text(formatted_error) LOGE("End error handling")
def ci(update: Update, context: CallbackContext): if not user_is_admin(update.message.from_user.id): update.message.reply_text("Error: You are not authorized to use CI function of this bot.\n" "Ask to who host this bot to add you to the authorized people list") return if get_config("CI_CHANNEL_ID") == "": update.message.reply_text("Error: CI channel or user ID not defined") LOGE("CI channel or user ID not defined") return parser = CIParser(prog="/ci") parser.set_output(update.message.reply_text) parser.add_argument('project', help='CI project', nargs='?', default=None,) parser.add_argument('-s', '--status', action='store_true', help='show queue status') args, project_args = parser.parse_known_args(context.args) if args.status: update.message.reply_text(queue_manager.get_formatted_queue_list()) return if args.project is None: parser.error("Please specify a project") try: project_class = import_module(f"homebot.modules.ci.projects.{args.project}", package="Project").Project except ModuleNotFoundError: update.message.reply_text(f"Error: Project script not found") return except Exception as e: text = "Error: Error while importing project:" text += format_exception(e) update.message.reply_text(text) LOGE(text) return try: project = project_class(update, context, project_args) except Exception as e: text = "Error: Project class initialization failed:\n" text += format_exception(e) update.message.reply_text(text) LOGE(text) return workflow = Workflow(project) queue_manager.put(workflow) update.message.reply_text("Workflow added to the queue") LOGI("Workflow added to the queue")
def __init__(self, bot: 'Bot', update_queue: Queue, workers: int = 4, exception_event: Event = None, job_queue: 'JobQueue' = None): """ Initialize the dispatcher and its modules. """ super().__init__(bot, update_queue, workers=workers, exception_event=exception_event, job_queue=job_queue) self.add_error_handler(error_handler, True) self.modules = [] LOGI("Parsing modules") for module in modules: try: module_instance = module() except Exception as e: LOGE(f"Error initializing module {module.name}, will be skipped\n" f"Error: {e}") else: self.modules.append(module_instance) LOGI("Modules parsed") LOGI("Loading modules") for module in self.modules: self.load_module(module) LOGI("Modules loaded")
def disable_module(self: Dispatcher, module_name: str): """ Unload a provided module and remove its command handler from the bot's dispatcher. """ LOGI(f"Loading module {module_name}") module = get_module(module_name) if module is None: raise ModuleNotFoundError(f"Module {module_name} not found") with self.modules_status_lock: if not module_name in self.modules_status: self.modules_status[module_name] = MODULE_STATUS_DISABLED if self.modules_status[module_name] == MODULE_STATUS_DISABLED: raise AttributeError("Module is already disabled") self.modules_status[module_name] = MODULE_STATUS_DISABLING try: for command in module.commands: self.add_handler(command.handler) except: LOGE(f"Failed to add handler for module {module_name}") self.modules_status[module_name] = MODULE_STATUS_ERROR self.modules_status[module_name] = MODULE_STATUS_DISABLED LOGI(f"Module {module_name} disabled")
def register_modules(modules_path: Path): # Import all the modules and let them execute register_module() for module_name in [ name for _, name, _ in iter_modules([str(modules_path)]) ]: try: import_module(f'homebot.modules.{module_name}') except Exception as e: LOGE(f"Error importing module {module_name}:\n" f"{format_exception(e)}")
def get_bot_modules(): modules = [] for module_name in [name for _, name, _ in iter_modules([str(modules_path)])]: try: module_class = import_module(f'homebot.modules.{module_name}', package="Module").Module except Exception as e: LOGE(f"Error importing module {module_name}, will be skipped\n" f"Error: {e}") else: modules.append(module_class) return modules
def run(self): while True: try: self.current_workflow = self.queue.get() workflow_name = self.current_workflow.project_name LOGI(f"CI workflow started, project: {workflow_name}") try: self.current_workflow.run() except Exception as e: message = "Unhandled exception from CI workflow:\n" message += format_exception(e) LOGE(message) self.current_workflow.update.message.reply_text( f"Error: {message}") except Exception as e: message = "Unhandled exception from QueueManager:\n" message += format_exception(e) LOGE(message) finally: LOGI(f"CI workflow finished, project: {workflow_name}") self.current_workflow = None
def run(self): while True: self.current_workflow = self.queue.get() self.running = True workflow_name = self.current_workflow.project_name LOGI(f"CI workflow started, project: {workflow_name}") try: self.current_workflow.run() except Exception as e: message = f"Unhandled exception from CI workflow: {type(e)}: {e}" LOGE(message) self.current_workflow.update.message.reply_text( f"Error: {message}") self.running = False LOGI(f"CI workflow finished, project: {workflow_name}") self.current_workflow = None
def weather(update: Update, context: CallbackContext): try: city = update.message.text.split(' ', 1)[1] except IndexError: update.message.reply_text("City not provided") return if get_config("WEATHER_API_KEY", None) == None: update.message.reply_text("OpenWeatherMap API key not specified\n" "Ask the bot hoster to configure it") LOGE("OpenWeatherMap API key not specified, get it at https://home.openweathermap.org/api_keys") return parameters = { "appid": get_config("WEATHER_API_KEY", None), "q": city, "units": get_config("WEATHER_TEMP_UNIT", "metric"), } temp_unit = TEMP_UNITS.get(get_config("WEATHER_TEMP_UNIT", None), "K") wind_unit = WIND_UNITS.get(get_config("WEATHER_TEMP_UNIT", None), "km/h") response = requests.get(url=URL, params=parameters).json() if response["cod"] != 200: update.message.reply_text(f"Error: {response['message']}") return city_name = response["name"] city_country = response["sys"]["country"] city_lat = response["coord"]["lat"] city_lon = response["coord"]["lon"] weather_type = response["weather"][0]["main"] weather_type_description = response["weather"][0]["description"] temp = response["main"]["temp"] temp_min = response["main"]["temp_min"] temp_max = response["main"]["temp_max"] humidity = response["main"]["humidity"] wind_speed = response["wind"]["speed"] update.message.reply_text( f"Current weather for {city_name}, {city_country} ({city_lat}, {city_lon}):\n" f"Weather: {weather_type} ({weather_type_description})\n" f"Temperature: {temp}{temp_unit} (Min: {temp_min}{temp_unit} Max: {temp_max}{temp_unit})\n" f"Humidity: {humidity}%\n" f"Wind: {wind_speed}{wind_unit}" )
def weather(update: Update, context: CallbackContext): try: city = update.message.text.split(' ', 1)[1] except IndexError: update.message.reply_text("City not provided") return try: weather = Weather() except AssertionError: update.message.reply_text("OpenWeatherMap API key not specified\n" "Ask the bot hoster to configure it") LOGE( "OpenWeatherMap API key not specified, get it at https://home.openweathermap.org/api_keys" ) return try: response = weather.current_weather(city) except ConnectionError as err: update.message.reply_text(err.strerror) return city_name = response["name"] city_country = response["sys"]["country"] city_lat = response["coord"]["lat"] city_lon = response["coord"]["lon"] weather_type = response["weather"][0]["main"] weather_type_description = response["weather"][0]["description"] temp = response["main"]["temp"] temp_min = response["main"]["temp_min"] temp_max = response["main"]["temp_max"] humidity = response["main"]["humidity"] wind_speed = response["wind"]["speed"] update.message.reply_text( f"Current weather for {city_name}, {city_country} ({city_lat}, {city_lon}):\n" f"Weather: {weather_type} ({weather_type_description})\n" f"Temperature: {temp}{weather.temp_unit} (Min: {temp_min}{weather.temp_unit} Max: {temp_max}{weather.temp_unit})\n" f"Humidity: {humidity}%\n" f"Wind: {wind_speed}{weather.wind_unit}")
def build(self): project_dir = Path( f"{get_config('ci.main_dir', '')}/{self.name}-{self.version}") device_out_dir = project_dir / "out" / "target" / "product" / self.parsed_args.device artifacts = Artifacts(device_out_dir, self.artifacts) post_manager = PostManager(self, self.parsed_args.device, artifacts) if self.parsed_args.clean is True: clean_type = "clean" elif self.parsed_args.installclean is True: clean_type = "installclean" else: clean_type = "none" post_manager.update("Building") command = [ bot_path / "modules" / "ci" / "projects" / "aosp" / "tools" / "building.sh", "--sources", project_dir, "--lunch_prefix", self.lunch_prefix, "--lunch_suffix", self.lunch_suffix, "--build_target", self.build_target, "--clean", clean_type, "--device", self.parsed_args.device ] last_edit = datetime.now() process = subprocess.Popen(command, encoding="UTF-8", stdout=subprocess.PIPE, stderr=subprocess.STDOUT) while True: output = process.stdout.readline() if output == '' and process.poll() is not None: break if not output: continue now = datetime.now() if (now - last_edit).seconds < 150: continue result = re.search(r"\[ +([0-9]+% [0-9]+/[0-9]+)\]", output.strip()) if result is None: continue result_split = str(result.group(1)).split() if len(result_split) != 2: continue percentage, targets = re.split(" +", result.group(1)) post_manager.update(f"Building: {percentage} ({targets})") last_edit = now returncode = process.poll() # Process return code build_result = ERROR_CODES.get(returncode, "Build failed: Unknown error") post_manager.update(build_result) needs_logs_upload = NEEDS_LOGS_UPLOAD.get(returncode, False) if needs_logs_upload != False: log_file = open(project_dir / needs_logs_upload, "rb") self.context.bot.send_document(get_config("ci.channel_id"), log_file) log_file.close() if returncode != SUCCESS or get_config("ci.upload_artifacts", False) is not True: return # Upload artifacts try: uploader = Uploader() except Exception as e: post_manager.update(f"{build_result}\n" f"Upload failed: {type(e)}: {e}") return artifacts.update() post_manager.update(build_result) for artifact in artifacts.artifacts: artifact.status = STATUS_UPLOADING post_manager.update(build_result) try: uploader.upload( artifact.path, Path(self.category) / self.parsed_args.device / self.name / self.android_version) except Exception as e: artifact.status = f"{STATUS_NOT_UPLOADED}: {type(e)}: {e}" LOGE(f"Error while uploading artifact {artifact.name}:\n" f"{format_exception(e)}") else: artifact.status = STATUS_UPLOADED post_manager.update(build_result)