async def info(self, ctx): """Shows you information about the bot owner, bot uptime, latency, memory and cpu usage.""" embed = Embed(color=0x000000) embed.set_thumbnail(url=ctx.bot.user.avatar_url) app = await ctx.bot.application_info() owner = app.owner embed.set_author(name=str(owner), icon_url=owner.avatar_url) proc = Process() with proc.oneshot(): uptime = datetime.utcnow() - ctx.bot.launched_at mem_total = virtual_memory().total / (1024**2) mem_of_total = proc.memory_percent() mem_usage = mem_total * (mem_of_total / 100) cpu_usage = proc.cpu_percent() / cpu_count() embed.add_field(name='Uptime', value=str(uptime), inline=False) embed.add_field(name='Latency', value=f'{round(ctx.bot.latency * 1000, 2)} ms', inline=False) embed.add_field( name='Memory usage', value= f'{int(mem_usage)} / {int(mem_total)} MiB ({round(mem_of_total)}%)', inline=False) embed.add_field(name='CPU usage', value=f'{round(cpu_usage)}%', inline=False) await ctx.send(embed=embed)
def get_resource_usage(self) -> ResourceUsage: """ Return various resource usage statistics about the running process. """ assert not self.returncode, "Can't collect data on stopped process" proc = Process(self.ps.pid) if 'bitcoind' in self.cmd: def find_process(proc_): """ Process graph looks like this: sh(327)───time(334)───bitcoind(335) """ name = proc_.name() # Recurse into child processes if need be. if name in ['sh', 'time']: assert len(proc_.children()) == 1 return find_process(proc_.children()[0]) assert (name.startswith('bitcoin') or name.startswith('b-')) return proc_ proc = find_process(proc) with proc.oneshot(): return ResourceUsage( cpu_percent=proc.cpu_percent(), memory_info=proc.memory_info(), num_fds=proc.num_fds(), )
def track_process(p: psutil.Process): pid = p.pid with p.oneshot(): try: res = p.memory_info() tracker.add({ f'process.{p.pid}.{p.name()}.rss': res.rss, f'process.{p.pid}.{p.name()}.vms': res.vms }) except psutil.AccessDenied: pass try: res = p.memory_percent() tracker.add({ f'process.{p.pid}.{p.name()}.mem': res, }) except psutil.AccessDenied: pass try: res = p.cpu_percent() tracker.add({ f'process.{p.pid}.{p.name()}.cpu': res, }) except psutil.AccessDenied: pass try: res = p.num_threads() tracker.add({ f'process.{p.pid}.{p.name()}.threads': res, }) except psutil.AccessDenied: pass
def worker(self, obj): if self.active_workers: for node, active_workers in self.active_workers.iteritems(): for worker in active_workers: if worker['id'] == obj.task_id: p = Process(worker['worker_pid']) return 'CPU:%.1f%% RAM:%.2f%%' % (p.cpu_percent(0.05), p.memory_percent()) return 'N/a'
def main(self, opts): if not opts.sound: mixer.quit() cpu_timer = 0 mem_timer = 0 pid = getpid() pu = Process(pid) # import after init, to load images # pygame.error: cannot convert without pygame.display initialized from sprite.common import Txt if opts.viewer: from scene.viewer import AnimationViewerScene scene = AnimationViewerScene(self.screen) display.set_caption('Barbarian - Animation viewer') else: from scene.battle import BattleScene scene = BattleScene(self.screen) display.set_caption('Barbarian') clock = time.Clock() cpu = Txt(FONT, 8, 'CPU: ', RED, 0, 566) # 'Resident Set Size', this is the non-swapped # physical memory a process has used. mem_rss = Txt(FONT, 8, 'Mem RSS: ', RED, 0, cpu.rect.bottom) # 'Virtual Memory Size', this is the total amount of # virtual memory used by the process. mem_vms = Txt(FONT, 8, 'Mem VMS: ', RED, 0, mem_rss.rect.bottom) fps = Txt(FONT, 8, 'FPS: ', RED, 0, mem_vms.rect.bottom) if opts.debug: scene.add(cpu, mem_rss, mem_vms, fps) while True: for evt in event.get(): scene.process_event(evt) # Update all the sprites current_time = time.get_ticks() if opts.debug: fps.msg = 'FPS: {0:.0f}'.format(clock.get_fps()) if current_time - cpu_timer > opts.cpu_time: cpu_timer = current_time cpu.msg = 'CPU: {0:.1f}%'.format(pu.cpu_percent()) if current_time - mem_timer > opts.mem_time: mem_timer = current_time mem = pu.memory_info() resident = 'Mem RSS: {0:>7,.0f} Kb'.format(mem.rss / 1024) mem_rss.msg = resident.replace(',', ' ') virtual = 'Mem VMS: {0:>7,.0f} Kb'.format(mem.vms / 1024) mem_vms.msg = virtual.replace(',', ' ') scene.update(current_time) # Draw the scene dirty = scene.draw(self.screen) display.update(dirty) clock.tick(60)
def add_new_information_from_process_object(self, process: psutil.Process, data_retrieval_timestamp: datetime.datetime) -> None: """ Adds the new information about the process to the application profile. This should be mainly used for applications with only one process. :raises TypeError if process is not of type psutil.Process or data_retrieval_timestamp is not of type datetime.datetime. :raises ValueError if data_retrieval_timestamp is newer than current time. :param process: Information about the specific process. :type process: psutil.Process :param data_retrieval_timestamp: The time the data was retrieved. :type data_retrieval_timestamp: datetime.datetime """ if not (isinstance(process, psutil.Process)): raise TypeError(expected_type_but_received_message.format("process", "psutil.Process", process)) if not (isinstance(data_retrieval_timestamp, datetime.datetime)): raise TypeError(expected_type_but_received_message.format("data_retrieval_timestamp", "datetime.datetime", data_retrieval_timestamp)) if data_retrieval_timestamp.replace(tzinfo=None) > datetime.datetime.now(): raise ValueError("Argument data_retrieval_timestamp cannot be newer than current time. Value receive: {}" .format(data_retrieval_timestamp)) # Get info from the process object. One of the following calls may raise an Error (OS, AccessDenied, etc). open_files = process.open_files() memory_info = process.memory_info() child_process_count = len(process.children()) username = process.username() threads_number = process.num_threads() process.cpu_percent() try: connections_num = len(process.connections()) except psutil.AccessDenied: connections_num = 0 time.sleep(0.1) # wait for cpu_percent to return a meaningful value. cpu_percentage = process.cpu_percent() self.add_open_files(open_files=open_files, data_retrieval_timestamp=data_retrieval_timestamp) self.__memory_usages.append(memory_info.rss) self.__data_retrieval_timestamp.append(data_retrieval_timestamp) self.__child_processes_count.append(child_process_count) self.__users.extend(username) self.__cpu_percent_usages.append(cpu_percentage) self.__threads_numbers.append(threads_number) self.__connections_numbers.append(connections_num)
def collect_status(pid, appname, site): ip_out = get_host_info()[0] ip_inner = get_host_info()[1] server_id = get_host_info()[2] physical_mem = psutil.virtual_memory().total / 1024 / 1024 #Unit of M cpu_count = psutil.cpu_count() its = int(time.time()) p_ins = Process(pid) pstatus = p_ins.status() create_time = time.strftime("%Y%m%d %H:%M:%S", time.localtime(p_ins.create_time())) memory_percent = p_ins.memory_percent() memory_used = memory_percent * physical_mem cpu_calc_list = [] for i in range(6): cpu_calc = p_ins.cpu_percent(interval=0.1) cpu_calc_list.append(cpu_calc) cpu_percent = float(sum(cpu_calc_list) / len(cpu_calc_list)) num_fds = p_ins.num_fds() connections = p_ins.connections() connections_num = len(connections) #appname=p_ins.cwd() if p_ins.name() == 'jsvc': app_path = p_ins.exe().split('/')[:-2] else: app_path = p_ins.cwd() #appname = app_path.split('/')[-1] appname = appname if p_ins.children(recursive=True): children_list = str(p_ins.children(recursive=True)) else: children_list = None message = { 'site': site, 'ip': ip_out, 'ip_inner': ip_inner, 'server_id': server_id, 'pstatus': pstatus, 'metric_name': 'app_monitor', 'its': its, 'pid': pid, 'physical_mem': physical_mem, 'memory_used': memory_used, 'memory_percent': memory_percent, 'cpu_count': cpu_count, 'cpu_percent': cpu_percent, 'num_fds': num_fds, 'connections_num': connections_num, 'create_time': create_time, 'appname': appname, 'app_path': app_path, 'children': children_list } return message
def _collect(self, index: int, process: psutil.Process, data: ValueSet): if self.load: data.add( Value(process.cpu_percent(), label_values=[str(index)], name='load_percent')) if self.memory: data.add( Value(process.memory_info().vms, label_values=[str(index)], name='virtual_memory')) return data
def track_memory_usage(out_path: str, write_frequency: float): """Track how busy the head node is Args: out_path: Path to the output file write_frequency: How often to write (s) """ while True: # Get a timestamp ts = datetime.now().timestamp() # Measure the thinker process proc = Process() my_usage = proc.cpu_percent() my_memory = proc.memory_full_info().pss # Measure all processes from my user my_name = getuser() all_cpu = all_memory = 0 for proc in process_iter(): if proc.username() != my_name: continue try: all_cpu += proc.cpu_percent() all_memory += proc.memory_full_info().pss except: continue with open(out_path, 'a') as fp: print(json.dumps({ 'time': ts, 'thinker_cpu': my_usage, 'thinker_mem': my_memory, 'all_cpu': all_cpu, 'all_mem': all_memory }), file=fp) time.sleep(write_frequency)
def _get_stats_record(proc_info: psutil.Process) -> ResourceStats: """ Read resources usage and create record. :param proc_info: process information to get cpu usage and memory usage from. :return: one time resource stats record """ return ResourceStats( time.time(), proc_info.cpu_percent(), memory_profiler.memory_usage(proc_info.pid, max_usage=True), )
def fetch_process(process: psutil.Process): cpu_percent = None name = None try: cpu_percent = process.cpu_percent() name = process.name() except Exception: pass return PsUtilProcess( process=process, name=name, cpu_percent=cpu_percent, )
def get_proc_data(proc: psutil.Process) -> Dict[str, Union[int, float]]: data: Dict[str, Union[int, float]] = {} try: with proc.oneshot(): data.update(proc.memory_full_info()._asdict()) data["cpu_percent"] = proc.cpu_percent() data["rss_percent"] = data["rss"] / TOTAL_MEMORY * 100 data["pss_percent"] = data["pss"] / TOTAL_MEMORY * 100 data["uss_percent"] = data["uss"] / TOTAL_MEMORY * 100 data["vms_percent"] = data["vms"] / TOTAL_MEMORY * 100 except psutil.NoSuchProcess: raise ValueError(f"The process no longer exists: {proc.pid}") else: return data
class TextUi(MpfController): """Handles the text-based UI.""" def __init__(self, machine: "MachineController") -> None: """Initialize TextUi.""" super().__init__(machine) self.screen = None if not machine.options['text_ui']: return self.start_time = datetime.now() self.machine = machine self._tick_task = self.machine.clock.schedule_interval(self._tick, 1) self.screen = Screen.open() self.mpf_process = Process() self.ball_devices = list() # type: List[BallDevice] self.switches = OrderedDict( ) # type: Dict[Switch, Tuple[str, int, int]] self.player_start_row = 0 self.column_positions = [0, .25, .5, .75] self.columns = [0] * len(self.column_positions) self.machine.events.add_handler('init_phase_2', self._init) self.machine.events.add_handler('init_phase_3', self._update_switches) # self.machine.events.add_handler('init_phase_3', self._init2) self.machine.events.add_handler('loading_assets', self._asset_load_change) self.machine.events.add_handler('bcp_connection_attempt', self._bcp_connection_attempt) self.machine.events.add_handler('asset_loading_complete', self._asset_load_complete) self.machine.events.add_handler('bcp_clients_connected', self._bcp_connected) self.machine.events.add_handler('shutdown', self.stop) self.machine.events.add_handler('player_number', self._update_player) self.machine.events.add_handler('player_ball', self._update_player) self.machine.events.add_handler('player_score', self._update_player) self.machine.events.add_handler('ball_ended', self._update_player_no_game) self._pending_bcp_connection = False self._asset_percent = 0 self._bcp_status = (0, 0, 0) # type: Tuple[float, int, int] self._draw_screen() self.screen.refresh() def _init(self, **kwargs): del kwargs self.machine.mode_controller.register_start_method(self._mode_change) self.machine.mode_controller.register_stop_method(self._mode_change) self.machine.switch_controller.add_monitor(self._update_switches) self.machine.bcp.interface.register_command_callback( "status_report", self._bcp_status_report) for bd in [ x for x in self.machine.ball_devices if not x.is_playfield() ]: self.ball_devices.append(bd) self.ball_devices.sort() self._draw_player_header() self._update_switch_layout() def _bcp_status_report(self, client, cpu, rss, vms): del client self._bcp_status = cpu, rss, vms def _draw_screen(self): for i, percent in enumerate(self.column_positions): if not i: self.columns[i] = 1 self.columns[i] = int(self.screen.width * percent) height, width = self.screen.dimensions title = 'Mission Pinball Framework v{}'.format( mpf._version.__version__) # noqa padding = int((self.screen.width - len(title)) / 2) self.screen.print_at((' ' * padding) + title + (' ' * (padding + 1)), 0, 0, colour=7, bg=1) self.screen.print_at('<CTRL+C> TO EXIT', width - 16, 0, colour=0, bg=1) self.screen.print_at('ACTIVE MODES', self.columns[0], 2) self.screen.print_at('SWITCHES', int((width * .5) - 8), 2) self.screen.print_at('BALL COUNTS', self.columns[3], 2) self.screen.print_at('-' * width, 0, 3) self.screen.print_at(self.machine.machine_path, 0, height - 2, colour=3) if 0 < self._asset_percent < 100: self.screen.print_at(' ' * width, 0, int(height / 2) + 1, bg=3) self.screen.print_at('LOADING ASSETS: {}%'.format( self._asset_percent), int(width / 2) - 10, int(height / 2) + 1, colour=0, bg=3) if self._pending_bcp_connection: bcp_string = 'WAITING FOR MEDIA CONTROLLER {}...'.format( self._pending_bcp_connection) self.screen.print_at(' ' * width, 0, int(height / 2) - 1, bg=3) self.screen.print_at(bcp_string, int((width - len(bcp_string)) / 2), int(height / 2) - 1, colour=0, bg=3) self._update_stats() def _draw_player_header(self): self.player_start_row = (len(self.ball_devices) + len(self.machine.playfields)) + 7 self.screen.print_at('CURRENT PLAYER', self.columns[3], self.player_start_row - 2) self.screen.print_at('-' * (int(self.screen.width * .75) + 1), self.columns[3], self.player_start_row - 1) self._update_player() def _update_stats(self): height, width = self.screen.dimensions # Runtime rt = (datetime.now() - self.start_time) mins, sec = divmod(rt.seconds + rt.days * 86400, 60) hours, mins = divmod(mins, 60) time_string = 'RUNNING {:d}:{:02d}:{:02d}'.format(hours, mins, sec) self.screen.print_at(time_string, width - len(time_string), height - 2, colour=2) # System Stats system_str = 'Free Memory (MB): {} CPU:{:3d}%'.format( round(virtual_memory().available / 1048576), round(cpu_percent(interval=None, percpu=False))) self.screen.print_at(system_str, width - len(system_str), height - 1, colour=2) # MPF process stats stats_str = 'MPF (CPU RSS/VMS): {}% {}/{} MB '.format( round(self.mpf_process.cpu_percent()), round(self.mpf_process.memory_info().rss / 1048576), round(self.mpf_process.memory_info().vms / 1048576)) self.screen.print_at(stats_str, 0, height - 1, colour=6) # MC process stats if self._bcp_status != (0, 0, 0): bcp_string = 'MC (CPU RSS/VMS) {}% {}/{} MB '.format( round(self._bcp_status[0]), round(self._bcp_status[1] / 1048576), round(self._bcp_status[2] / 1048576)) self.screen.print_at(bcp_string, len(stats_str) - 2, height - 1, colour=5) def _update_switch_layout(self): start_row = 4 cutoff = int(len(self.machine.switches) / 2) + start_row - 1 row = start_row col = 1 for sw in sorted(self.machine.switches): if sw.invert: name = sw.name + '*' else: name = sw.name self.switches[sw] = (name, self.columns[col], row) if row == cutoff: row = start_row col += 1 else: row += 1 self._update_switches() def _update_switches(self, *args, **kwargs): del args, kwargs for sw, info in self.switches.items(): if sw.state: self.screen.print_at(*info, colour=0, bg=2) else: self.screen.print_at(*info) self.screen.refresh() def _mode_change(self, *args, **kwargs): # Have to call this on the next frame since the mode controller's # active list isn't updated yet del args del kwargs self.machine.clock.schedule_once(self._update_modes) def _update_modes(self, *args, **kwargs): del args del kwargs modes = self.machine.mode_controller.active_modes for i, mode in enumerate(modes): self.screen.print_at(' ' * (self.columns[0] - 1), self.columns[0], i + 4) self.screen.print_at('{} ({})'.format(mode.name, mode.priority), self.columns[0], i + 4) self.screen.print_at(' ' * (int(self.screen.width * .25) - 1), self.columns[0], len(modes) + 4) def _update_ball_devices(self, **kwargs): del kwargs row = 4 try: for pf in self.machine.playfields: self.screen.print_at('{}: {} '.format(pf.name, pf.balls), self.columns[3], row, colour=2 if pf.balls else 7) row += 1 except AttributeError: pass for bd in self.ball_devices: # extra spaces to overwrite previous chars if the str shrinks self.screen.print_at('{}: {} ({}) '.format( bd.name, bd.balls, bd.state), self.columns[3], row, colour=2 if bd.balls else 7) row += 1 def _update_player(self, **kwargs): del kwargs for i in range(3): self.screen.print_at( ' ' * (int(self.screen.width * (1 / len(self.columns))) + 1), self.columns[3], self.player_start_row + i) try: self.screen.print_at( 'PLAYER: {}'.format(self.machine.game.player.number), self.columns[3], self.player_start_row) self.screen.print_at( 'BALL: {}'.format(self.machine.game.player.ball), self.columns[3], self.player_start_row + 1) self.screen.print_at( 'SCORE: {:,}'.format(self.machine.game.player.score), self.columns[3], self.player_start_row + 2) except AttributeError: self._update_player_no_game() def _update_player_no_game(self, **kwargs): del kwargs for i in range(3): self.screen.print_at( ' ' * (int(self.screen.width * (1 / len(self.columns))) + 1), self.columns[3], self.player_start_row + i) self.screen.print_at('NO GAME IN PROGRESS', self.columns[3], self.player_start_row) def _tick(self): if self.screen.has_resized(): self.screen = Screen.open() self._update_switch_layout() self._update_modes() self._draw_screen() self._draw_player_header() self.machine.bcp.transport.send_to_all_clients("status_request") self._update_stats() self._update_ball_devices() self.screen.refresh() def _bcp_connection_attempt(self, name, host, port, **kwargs): del name del kwargs self._pending_bcp_connection = '{}:{}'.format(host, port) self._draw_screen() def _bcp_connected(self, **kwargs): del kwargs self._pending_bcp_connection = None self.screen.print_at(' ' * self.screen.width, 0, int(self.screen.height / 2) - 1) self._update_modes() self._update_switches() self._update_ball_devices() def _asset_load_change(self, percent, **kwargs): del kwargs self._asset_percent = percent self._draw_screen() def _asset_load_complete(self, **kwargs): del kwargs self._asset_percent = 100 self.screen.print_at(' ' * self.screen.width, 0, int(self.screen.height / 2) + 1) self._update_modes() self._update_switches() self._update_ball_devices() def stop(self, **kwargs): """Stop the Text UI and restore the original console screen.""" del kwargs if self.screen: self.machine.clock.unschedule(self._tick_task) logger = logging.getLogger() logger.addHandler(logging.StreamHandler()) self.screen.close(True)
def track_process(self, p: psutil.Process): with p.oneshot(): key = None if p.pid in self.pids: key = self.pids[p.pid] if self.processes[key].name != p.name(): key = None if key is None: key = len(self.processes) self.processes.append(ProcessInfo(key, p.pid, p.name())) self.pids[p.pid] = key self.data.update({ f'process.{key}.name': p.name(), f'process.{key}.pid': p.pid, f'process.{key}.ppid': p.ppid(), f'process.{key}.create_time': p.create_time(), }) try: self.data.update({ f'process.{key}.exe': p.exe(), }) except (psutil.AccessDenied, psutil.ZombieProcess): pass try: self.data.update({ f'process.{key}.cmdline': '\n'.join(p.cmdline()), }) except (psutil.AccessDenied, psutil.ZombieProcess): pass self.processes[key].active = True try: res = p.memory_info() self.data.update({ f'process.{key}.rss': res.rss, f'process.{key}.vms': res.vms, }) except (psutil.AccessDenied, psutil.ZombieProcess): pass try: res = p.cpu_times() self.data.update({ f'process.{key}.user': res.user, f'process.{key}.system': res.system, }) if hasattr(res, 'iowait'): self.data.update({ f'process.{key}.iowait': res.iowait, }) except (psutil.AccessDenied, psutil.ZombieProcess): pass try: res = p.cpu_percent() self.data.update({ f'process.{key}.cpu': res, }) except (psutil.AccessDenied, psutil.ZombieProcess): pass try: res = p.num_threads() self.data.update({ f'process.{key}.threads': res, }) except (psutil.AccessDenied, psutil.ZombieProcess): pass
if not exists(log_path): makedirs(log_path) basicConfig( filename='%s/log_%s.log' % ('Log', strftime("%Y-%m-%d-%H-%M-%S", gmtime())), level=TRACE, format= "%(levelname)s:%(filename)s,%(lineno)d:%(name)s.%(funcName)s:%(message)s" ) #, stream=sys.stderr srv = Server() server_thread = Thread(target=srv.run) server_thread.start() print("VERSION: ", __version__) self_usage = Process(getpid()) while True: sleep(1) u_cpu = int(self_usage.cpu_percent()) u_memory = int(self_usage.memory_info()[0] / 2.**20) t_memory = int(virtual_memory()[0] / 2.**20) u_disk = int(self_usage.io_counters()[0] / self_usage.io_counters()[1]) o_slots = len(srv.connections.keys()) t_slots = srv.setting.maximum_users sys.stdout.write( "\rSERVER MONITOR [CPU: %s%% | RAM: %s/%s Mb | DISK: %s%% | SLOTS: %s/%s]" % (u_cpu, u_memory, t_memory, u_disk, o_slots, t_slots)) sys.stdout.flush()
class ProcessWrapper: def check_alive(self): if self._alive and self.process.returncode is not None: self._alive = False def check_pipe(self, read_pipe, write_pipe, write_stdout=False, write_stderr=False): with self.lock: self._pipes_watching += 1 OutputHandler.print( f'Start watching {"stdout" if write_stdout else "stderr"} ' f'Pipes watching = {self._pipes_watching}') while not self.is_finished(): try: new_output = read_pipe.read(1) except ValueError: self.check_alive() continue if len(new_output) == 0: with self.lock: self._pipes_watching -= 1 OutputHandler.print( f'Out of {"stdout" if write_stdout else "stderr"}... ' f'Maybe program terminated. Pipes watching = {self._pipes_watching}' ) if self._pipes_watching == 0: self._alive = False self.terminate() break try: if self.register_output: write_pipe.write(new_output) except ExitException: self._alive = False self.terminate() break if write_stdout: self.stdout += new_output if write_stderr: self.stderr += new_output self.check_alive() def check_stdout(self): self.check_pipe(self.process.stdout, sys.stdout, write_stdout=True) def check_stderr(self): self.check_pipe(self.process.stderr, sys.stderr, write_stderr=True) def check_cpuload(self): while self._alive: try: cpu_load = self.ps.cpu_percent() OutputHandler.print(f'Check cpuload - {cpu_load}') self.cpu_load_history.append(cpu_load) if len(self.cpu_load_history) > self.cpu_load_length: self.cpu_load_history.pop(0) except NoSuchProcess: OutputHandler.print('Check cpuload finished, waiting output') self.wait_output() OutputHandler.print( 'Check cpuload finished, set alive = false') self._alive = False break sleep(0.01) self.check_alive() def is_waiting_input(self) -> bool: return len(self.cpu_load_history) == self.cpu_load_length and sum( self.cpu_load_history) < 1 def register_input_request(self): if not self.is_waiting_input(): raise RuntimeError('Program is not waiting for the input') self.cpu_load_history = [] def is_finished(self): if not self.check_early_finish: return not self._alive else: if not self._alive: return True try: is_running = self.ps.status() == 'running' if not is_running: self._alive = False return not is_running except NoSuchProcess: self._alive = False return True def __init__(self, *args, check_early_finish=False, register_output=True): self.lock = Lock() try: self.process = subprocess.Popen( [str(a) for a in args], bufsize=0, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, encoding='utf-8', ) except Exception: raise self.ps = Process(self.process.pid) self.cpu_load = self.ps.cpu_percent() self.cpu_load_history = [] self.cpu_load_length = 10 self.stdout = '' self.stderr = '' self._alive = True self._pipes_watching = 0 self.terminated = False self.check_early_finish = check_early_finish self.register_output = register_output Thread(target=lambda: self.check_cpuload(), daemon=True).start() Thread(target=lambda: self.check_stdout(), daemon=True).start() Thread(target=lambda: self.check_stderr(), daemon=True).start() def terminate(self): OutputHandler.print('Terminate called') with self.lock: OutputHandler.print('Terminate - LOCK ACQUIRED') if not self.terminated: OutputHandler.print('Terminate - BEFORE WAIT STDERR') self.wait_output() OutputHandler.print('Terminate - AFTER WAIT STDERR') self._alive = False OutputHandler.print('Terminate - SELF ALIVE == FALSE') is_exit_replaced = ExitHandler.is_replaced() if is_exit_replaced: ExitHandler.revert_exit() OutputHandler.print('Terminate - EXIT REVERTED') try: parent = Process(self.process.pid) OutputHandler.print(f'Terminate - parent == {parent}') for child in parent.children(recursive=True): OutputHandler.print(f'Terminate - child kill {child}') child.kill() OutputHandler.print(f'Terminate - parent kill {parent}') parent.kill() except NoSuchProcess: OutputHandler.print(f'Terminate - NO SUCH PROCESS') pass finally: OutputHandler.print(f'Terminate - finally before kill') self.process.kill() OutputHandler.print(f'Terminate - finally before wait') self.process.wait() self.process.stdout.close() self.process.stderr.close() self.process.stdin.close() if is_exit_replaced: ExitHandler.replace_exit() OutputHandler.print(f'Terminate - EXIT REPLACED AGAIN') self.terminated = True OutputHandler.print(f'Terminate - TERMINATED') OutputHandler.print(f'Terminate - finished') def wait_output(self): iterations = 50 sleep_time = 50 / 1000 curr_stdout = self.stdout curr_stderr = self.stderr while iterations != 0: sleep(sleep_time) if self.stderr == curr_stderr and self.stdout == curr_stdout: break curr_stderr = self.stderr curr_stdout = self.stdout iterations -= 1 def wait(self): while not self.is_finished(): sleep(0.01) self.wait_output() def is_error_happened(self) -> bool: return (not self._alive and len(self.stderr) > 0 and self.process.returncode != 0 or 'Traceback' in self.stderr)
class Misc(Cog): def __init__(self, bot: CustomBot): super().__init__(self.__class__.__name__) self.bot = bot pid = getpid() self.process = Process(pid) self.process.cpu_percent() async def cog_command_error(self, ctx: Context, error): '''Local error handler for the cog''' # Throw errors properly for me if ctx.author.id in self.bot.config['owners'] and not isinstance( error, CommandOnCooldown): text = f'```py\n{error}```' await ctx.send(text) raise error # Cooldown if isinstance(error, CommandOnCooldown): if ctx.author.id in self.bot.config['owners']: await ctx.reinvoke() else: await ctx.send( f"You can only use this command once every `{error.cooldown.per:.0f} seconds` per server. You may use this again in `{error.retry_after:.2f} seconds`." ) return # Disabled command elif isinstance(error, DisabledCommand): if ctx.author.id in self.bot.config['owners']: await ctx.reinvoke() else: await ctx.send("This command has been disabled.") return @command(enabled=False, aliases=['upvote']) @cooldown(1, 5, BucketType.user) async def vote(self, ctx: Context): '''Gives you a link to upvote the bot''' if self.bot.config['dbl_vainity']: await ctx.send( f"<https://discordbots.org/bot/{self.bot.config['dbl_vainity']}/vote>\nSee {ctx.prefix}perks for more information." ) else: await ctx.send( f"<https://discordbots.org/bot/{self.bot.user.id}/vote>\nSee {ctx.prefix}perks for more information." ) @command(aliases=['git', 'code']) @cooldown(1, 5, BucketType.user) async def github(self, ctx: Context): '''Gives you a link to the bot's code repository''' await ctx.send(f"<{self.bot.config['github']}>") @command(aliases=['patreon', 'paypal']) @cooldown(1, 5, BucketType.user) async def donate(self, ctx: Context): '''Gives you the creator's donation links''' links = [] if self.bot.config['patreon']: links.append( f"Patreon: <{self.bot.config['patreon']}> (see {ctx.prefix}perks to see what you get)" ) if self.bot.config['paypal']: links.append( f"PayPal: <{self.bot.config['paypal']}> (doesn't get you the perks, but is very appreciated)" ) if not links: ctx.command.enabled = False ctx.command.hidden = True await ctx.send('\n'.join(links)) @command() @cooldown(1, 5, BucketType.user) async def invite(self, ctx: Context): '''Gives you an invite link for the bot''' await ctx.send( f"<https://discordapp.com/oauth2/authorize?client_id={self.bot.user.id}&scope=bot&permissions=314432>" ) @command(aliases=['guild', 'support']) @cooldown(1, 5, BucketType.user) async def server(self, ctx: Context): '''Gives you a server invite link''' await ctx.send(self.bot.config['guild_invite']) @command(hidden=True) @cooldown(1, 5, BucketType.user) async def echo(self, ctx: Context, *, content: str): '''Echos a saying''' await ctx.send(content) @command(enabled=False) @cooldown(1, 5, BucketType.user) async def perks(self, ctx: Context): '''Shows you the perks associated with different support tiers''' # DISABLED UNTIL I KNOW WHAT TO DO WITH IT # Normies normal_users = [ "60s tree cooldown", "5 children", ] # Perks for voting voting_perks = [ "30s tree cooldown", ] # Perks for $1 Patrons t1_donate_perks = [ "15s tree cooldown", "Up to 10 children", "`disownall` command (disowns all of your children at once)", ] # $3 Patrons t2_donate_perks = [ "Up to 15 children", "`stupidtree` command (shows all relations, not just blood relatives)", ] # Perks for $5 Patrons t3_donate_perks = [ "5s tree cooldown", "Up to 20 children", ] e = Embed() e.add_field(name=f'Normal Users', value=f"Gives you access to:\n* " + '\n* '.join(normal_users)) e.add_field(name=f'Voting ({ctx.clean_prefix}vote)', value=f"Gives you access to:\n* " + '\n* '.join(voting_perks)) e.add_field(name=f'T1 Patreon Donation ({ctx.clean_prefix}donate)', value=f"Gives you access to:\n* " + '\n* '.join(t1_donate_perks)) e.add_field(name=f'T2 Patreon Donation ({ctx.clean_prefix}donate)', value=f"Gives you access to:\n* " + '\n* '.join(t2_donate_perks)) e.add_field(name=f'T3 Patreon Donation ({ctx.clean_prefix}donate)', value=f"Gives you access to:\n* " + '\n* '.join(t3_donate_perks)) await ctx.send(embed=e) @command(aliases=['status']) @cooldown(1, 5, BucketType.user) async def stats(self, ctx: Context): '''Gives you the stats for the bot''' # await ctx.channel.trigger_typing() embed = Embed(colour=0x1e90ff) embed.set_footer(text=str(self.bot.user), icon_url=self.bot.user.avatar_url) embed.add_field( name="ProfileBot", value="A bot to make the process of filling out forms fun.") creator_id = self.bot.config["owners"][0] creator = await self.bot.fetch_user(creator_id) embed.add_field(name="Creator", value=f"{creator!s}\n{creator_id}") embed.add_field(name="Library", value=f"Discord.py {dpy_version}") try: embed.add_field( name="Average Guild Count", value=int((len(self.bot.guilds) / len(self.bot.shard_ids)) * self.bot.shard_count)) except TypeError: embed.add_field(name="Guild Count", value=len(self.bot.guilds)) embed.add_field(name="Shard Count", value=self.bot.shard_count) embed.add_field(name="Average WS Latency", value=f"{(self.bot.latency * 1000):.2f}ms") embed.add_field( name="Coroutines", value= f"{len([i for i in Task.all_tasks() if not i.done()])} running, {len(Task.all_tasks())} total." ) embed.add_field(name="Process ID", value=self.process.pid) embed.add_field(name="CPU Usage", value=f"{self.process.cpu_percent():.2f}") embed.add_field( name="Memory Usage", value= f"{self.process.memory_info()[0]/2**20:.2f}MB/{virtual_memory()[0]/2**20:.2f}MB" ) # ut = self.bot.get_uptime() # Uptime # uptime = [ # int(ut // (60*60*24)), # int((ut % (60*60*24)) // (60*60)), # int(((ut % (60*60*24)) % (60*60)) // 60), # ((ut % (60*60*24)) % (60*60)) % 60, # ] # embed.add_field(name="Uptime", value=f"{uptime[0]} days, {uptime[1]} hours, {uptime[2]} minutes, and {uptime[3]:.2f} seconds.") try: await ctx.send(embed=embed) except Exception: await ctx.send("I tried to send an embed, but I couldn't.") @command(aliases=['clean']) async def clear(self, ctx: Context): '''Clears the bot's commands from chat''' if ctx.channel.permissions_for(ctx.guild.me).manage_messages: _ = await ctx.channel.purge( limit=100, check=lambda m: m.author.id == self.bot.user.id) else: _ = await ctx.channel.purge( limit=100, check=lambda m: m.author.id == self.bot.user.id, bulk=False) await ctx.send(f"Cleared `{len(_)}` messages from chat.", delete_after=3.0) @command() async def shard(self, ctx: Context): '''Gives you the shard that your server is running on''' await ctx.send( f"The shard that your server is on is shard `{ctx.guild.shard_id}`." )
def get_cpu(self, process: Process) -> Percentage: """Get the process CPU usage.""" try: return int(process.cpu_percent()) except NoSuchProcess: return 0
class Misc(Cog): def __init__(self, bot:CustomBot): super().__init__(self.__class__.__name__) self.bot = bot pid = getpid() self.process = Process(pid) self.process.cpu_percent() async def cog_command_error(self, ctx:Context, error): '''Local error handler for the cog''' # Throw errors properly for me if ctx.original_author_id in self.bot.config['owners'] and not isinstance(error, CommandOnCooldown): text = f'```py\n{error}```' await ctx.send(text) raise error # Cooldown if isinstance(error, CommandOnCooldown): if ctx.original_author_id in self.bot.config['owners']: await ctx.reinvoke() else: await ctx.send(f"You can only use this command once every `{error.cooldown.per:.0f} seconds` per server. You may use this again in `{error.retry_after:.2f} seconds`.") return # Disabled command elif isinstance(error, DisabledCommand): if ctx.original_author_id in self.bot.config['owners']: await ctx.reinvoke() else: await ctx.send("This command has been disabled.") return @command(aliases=['upvote']) @cooldown(1, 5, BucketType.user) async def vote(self, ctx:Context): '''Gives you a link to upvote the bot''' if self.bot.config['dbl_vainity']: await ctx.send(f"[Add a DBL vote](https://discordbots.org/bot/{self.bot.config['dbl_vainity']}/vote)!\nSee {ctx.prefix}perks for more information.") else: await ctx.send(f"[Add a DBL vote](https://discordbots.org/bot/{self.bot.user.id}/vote)!\nSee {ctx.prefix}perks for more information.") @command(aliases=['git', 'code']) @cooldown(1, 5, BucketType.user) async def github(self, ctx:Context): '''Gives you a link to the bot's code repository''' await ctx.send(f"<{self.bot.config['github']}>", embeddify=False) @command(aliases=['patreon', 'paypal']) @cooldown(1, 5, BucketType.user) async def donate(self, ctx:Context): '''Gives you the creator's donation links''' links = [] if self.bot.config['patreon']: links.append(f"Patreon: <{self.bot.config['patreon']}> (see {ctx.prefix}perks to see what you get)") if self.bot.config['paypal']: links.append(f"PayPal: <{self.bot.config['paypal']}> (doesn't get you the perks, but is very appreciated)") if not links: ctx.command.enabled = False ctx.command.hidden = True await ctx.send('\n'.join(links), embeddify=False) @command() @cooldown(1, 5, BucketType.user) async def invite(self, ctx:Context): '''Gives you an invite link for the bot''' await ctx.send(f"<{self.bot.invite_link}>", embeddify=False) @command(aliases=['guild', 'support']) @cooldown(1, 5, BucketType.user) async def server(self, ctx:Context): '''Gives you a server invite link''' await ctx.send(self.bot.config['guild_invite'], embeddify=False) @command(hidden=True) @cooldown(1, 5, BucketType.user) async def echo(self, ctx:Context, *, content:str): '''Echos a saying''' await ctx.send(content, embeddify=False) @command() @cooldown(1, 5, BucketType.user) async def perks(self, ctx:Context): '''Shows you the perks associated with different support tiers''' # Normies normal_users = [ "60s tree cooldown", "5 children", ] # Perks for voting voting_perks = [ "30s tree cooldown", ] # Perks for $1 Patrons t1_donate_perks = [ "15s tree cooldown", "Up to 10 children", "`disownall` command (disowns all of your children at once)", ] # $3 Patrons t2_donate_perks = [ "Up to 15 children", "`stupidtree` command (shows all relations, not just blood relatives)", ] # Perks for $5 Patrons t3_donate_perks = [ "5s tree cooldown", "Up to 20 children", ] # Perks for MarriageBot Gold gold_perks = [ "Togglable incest", "Faster bot responses", "Server specific families", "Access to the `forcemarry`, `forcedivorce`, and `forceemancipate` commands" ] e = Embed() e.add_field(name=f'Normal Users', value=f"Gives you access to:\n* " + '\n* '.join(normal_users), inline=False) e.add_field(name=f'Voting ({ctx.clean_prefix}vote)', value=f"Gives you access to:\n* " + '\n* '.join(voting_perks), inline=False) e.add_field(name=f'T1 Patreon Donation ({ctx.clean_prefix}donate)', value=f"Gives you access to:\n* " + '\n* '.join(t1_donate_perks), inline=False) e.add_field(name=f'T2 Patreon Donation ({ctx.clean_prefix}donate)', value=f"Gives you access to:\n* " + '\n* '.join(t2_donate_perks), inline=False) e.add_field(name=f'T3 Patreon Donation ({ctx.clean_prefix}donate)', value=f"Gives you access to:\n* " + '\n* '.join(t3_donate_perks), inline=False) e.add_field(name=f'MarriageBot Gold ({ctx.clean_prefix}ssf)', value=f"Gvies you access to:\n* " + '\n* '.join(gold_perks), inline=False) await ctx.send(embed=e) @command(aliases=['status']) @cooldown(1, 5, BucketType.user) async def stats(self, ctx:Context): '''Gives you the stats for the bot''' # await ctx.channel.trigger_typing() embed = Embed( colour=0x1e90ff ) embed.set_footer(text=str(self.bot.user), icon_url=self.bot.user.avatar_url) embed.add_field(name="MarriageBot", value="A robot for marrying your friends and adopting your enemies.") creator_id = self.bot.config["owners"][0] creator = await self.bot.get_name(creator_id) embed.add_field(name="Creator", value=f"{creator}\n{creator_id}") embed.add_field(name="Library", value=f"Discord.py {dpy_version}") embed.add_field(name="Average Guild Count", value=int((len(self.bot.guilds) / len(self.bot.shard_ids)) * self.bot.shard_count)) embed.add_field(name="Shard Count", value=self.bot.shard_count) embed.add_field(name="Average WS Latency", value=f"{(self.bot.latency * 1000):.2f}ms") embed.add_field(name="Coroutines", value=f"{len([i for i in Task.all_tasks() if not i.done()])} running, {len(Task.all_tasks())} total.") embed.add_field(name="Process ID", value=self.process.pid) embed.add_field(name="CPU Usage", value=f"{self.process.cpu_percent():.2f}") embed.add_field(name="Memory Usage", value=f"{self.process.memory_info()[0]/2**20:.2f}MB/{virtual_memory()[0]/2**20:.2f}MB") ut = self.bot.get_uptime() # Uptime uptime = [ int(ut // (60*60*24)), int((ut % (60*60*24)) // (60*60)), int(((ut % (60*60*24)) % (60*60)) // 60), ((ut % (60*60*24)) % (60*60)) % 60, ] embed.add_field(name="Uptime", value=f"{uptime[0]} days, {uptime[1]} hours, {uptime[2]} minutes, and {uptime[3]:.2f} seconds.") # embed.add_field(name="Family Members", value=len(FamilyTreeMember.all_users) - 1) try: await ctx.send(embed=embed) except Exception: await ctx.send("I tried to send an embed, but I couldn't.") @command(aliases=['clean']) async def clear(self, ctx:Context): '''Clears the bot's commands from chat''' if ctx.channel.permissions_for(ctx.guild.me).manage_messages: _ = await ctx.channel.purge(limit=100, check=lambda m: m.author.id == self.bot.user.id) else: _ = await ctx.channel.purge(limit=100, check=lambda m: m.author.id == self.bot.user.id, bulk=False) await ctx.send(f"Cleared `{len(_)}` messages from chat.", delete_after=3.0) @command() async def block(self, ctx:Context, user:Member): '''Blocks a user from being able to adopt/makeparent/whatever you''' current_blocks = self.bot.blocked_users.get(ctx.author.id, list()) if user.id in current_blocks: await ctx.send("That user is already blocked.") return current_blocks.append(user.id) self.bot.blocked_users[ctx.author.id] = current_blocks async with self.bot.database() as db: await db( 'INSERT INTO blocked_user (user_id, blocked_user_id) VALUES ($1, $2)', ctx.author.id, user.id ) await ctx.send("That user is now blocked.") @command() async def unblock(self, ctx:Context, user:UserID): '''Unblocks a user and allows them to adopt/makeparent/whatever you''' # Get the current blocks for a user current_blocks = self.bot.blocked_users[ctx.author.id] if user not in current_blocks: await ctx.send("You don't have that user blocked.") return current_blocks.remove(user) self.bot.blocked_users[ctx.author.id] = current_blocks async with self.bot.database() as db: await db( 'DELETE FROM blocked_user WHERE user_id=$1 AND blocked_user_id=$2', ctx.author.id, user ) await ctx.send("That user is now unblocked.") @command() async def shard(self, ctx:Context): '''Gives you the shard that your server is running on''' await ctx.send(f"The shard that your server is on is shard `{ctx.guild.shard_id}`.") @command(enabled=False) @cooldown(1, 5, BucketType.guild) async def toptree(self, ctx:Context): '''Gives you the amount of users in the top tree''' async with ctx.channel.typing(): top_users = max([len(i.span(add_parent=True, expand_upwards=True)) for i in FamilyTreeMember.all_users.copy().values() if i]) await ctx.send(f"The top tree I handle has `{top_users}` users in it.")
class Misc(Cog): def __init__(self, bot: CustomBot): super().__init__(self.__class__.__name__) self.bot = bot pid = getpid() self.process = Process(pid) self.process.cpu_percent() async def cog_command_error(self, ctx: Context, error): ''' Local error handler for the cog ''' # Throw errors properly for me if ctx.author.id in self.bot.config['owners'] and not isinstance( error, CommandOnCooldown): text = f'```py\n{error}```' await ctx.send(text) raise error # Cooldown if isinstance(error, CommandOnCooldown): if ctx.author.id in self.bot.config['owners']: await ctx.reinvoke() else: await ctx.send( f"You can only use this command once every `{error.cooldown.per:.0f} seconds` per server. You may use this again in `{error.retry_after:.2f} seconds`." ) return @command(aliases=['upvote']) @cooldown(1, 5, BucketType.user) async def vote(self, ctx: Context): ''' Gives you a link to upvote the bot ''' if self.bot.config['dbl_vainity']: await ctx.send( f"<https://discordbots.org/bot/{self.bot.config['dbl_vainity']}/vote>", embeddify=False) else: await ctx.send( f"<https://discordbots.org/bot/{self.bot.user.id}/vote>", embeddify=False) @command(aliases=['git', 'code']) @cooldown(1, 5, BucketType.user) async def github(self, ctx: Context): ''' Gives you a link to the bot's code repository ''' await ctx.send(f"<{self.bot.config['github']}>", embeddify=False) @command(aliases=['patreon', 'paypal']) @cooldown(1, 5, BucketType.user) async def donate(self, ctx: Context): ''' Gives you the creator's donation links ''' links = [] if self.bot.config['patreon']: links.append(f"Patreon: <{self.bot.config['patreon']}>") if self.bot.config['paypal']: links.append( f"PayPal: <{self.bot.config['paypal']}> (doesn't get you the perks, but is very appreciated)" ) if not links: return await ctx.send('\n'.join(links), embeddify=False) @command() @cooldown(1, 5, BucketType.user) async def invite(self, ctx: Context): ''' Gives you an invite link for the bot ''' await ctx.send( f"<https://discordapp.com/oauth2/authorize?client_id={self.bot.user.id}&scope=bot&permissions=35840>", embeddify=False) @command(aliases=['guild', 'support']) @cooldown(1, 5, BucketType.user) async def server(self, ctx: Context): ''' Gives you a server invite link ''' await ctx.send(self.bot.config['guild'], embeddify=False) @command(hidden=True) @cooldown(1, 5, BucketType.user) async def echo(self, ctx: Context, *, content: str): ''' Echos a saying ''' await ctx.send(content, embeddify=False) @command(aliases=['status']) @cooldown(1, 5, BucketType.user) async def stats(self, ctx: Context): ''' Gives you the stats for the bot ''' # await ctx.channel.trigger_typing() embed = Embed(colour=0x1e90ff) embed.set_footer(text=str(self.bot.user), icon_url=self.bot.user.avatar_url) embed.add_field( name="MarriageBot", value="A robot for marrying your friends and adopting your enemies." ) creator = self.bot.get_user(self.bot.config["owners"][0]) embed.add_field(name="Creator", value=f"{creator!s}\n{creator.id}") embed.add_field(name="Library", value=f"Discord.py {dpy_version}") embed.add_field(name="Guild Count", value=len(self.bot.guilds)) embed.add_field(name="Shard Count", value=self.bot.shard_count) embed.add_field(name="Average Latency", value=f"{(self.bot.latency * 1000):.2f}ms") embed.add_field(name="Member Count", value=sum((len(i.members) for i in self.bot.guilds))) embed.add_field( name="Coroutines", value= f"{len([i for i in Task.all_tasks() if not i.done()])} running, {len(Task.all_tasks())} total." ) embed.add_field(name="Process ID", value=self.process.pid) embed.add_field(name="CPU Usage", value=f"{self.process.cpu_percent():.2f}") embed.add_field( name="Memory Usage", value= f"{self.process.memory_info()[0]/2**20:.2f}MB/{virtual_memory()[0]/2**20:.2f}MB" ) ut = self.bot.get_uptime() # Uptime uptime = [ int(ut // (60 * 60 * 24)), int((ut % (60 * 60 * 24)) // (60 * 60)), int(((ut % (60 * 60 * 24)) % (60 * 60)) // 60), ((ut % (60 * 60 * 24)) % (60 * 60)) % 60, ] embed.add_field( name="Uptime", value= f"{uptime[0]} days, {uptime[1]} hours, {uptime[2]} minutes, and {uptime[3]:.2f} seconds." ) # embed.add_field(name="Family Members", value=len(FamilyTreeMember.all_users) - 1) try: await ctx.send(embed=embed) except Exception: await ctx.send("I tried to send an embed, but I couldn't.") @command(aliases=['clean']) async def clear(self, ctx: Context): ''' Clears the bot's commands from chat ''' if ctx.channel.permissions_for(ctx.guild.me).manage_messages: _ = await ctx.channel.purge( limit=100, check=lambda m: m.author.id == self.bot.user.id) else: _ = await ctx.channel.purge( limit=100, check=lambda m: m.author.id == self.bot.user.id, bulk=False) await ctx.send(f"Cleared `{len(_)}` messages from chat.", delete_after=3.0) @command() async def block(self, ctx: Context, user: Member): ''' Blocks a user from being able to adopt/makeparent/whatever you ''' current_blocks = self.bot.blocked_users.get(ctx.author.id, list()) if user.id in current_blocks: await ctx.send("That user is already blocked.") return current_blocks.append(user.id) self.bot.blocked_users[ctx.author.id] = current_blocks async with self.bot.database() as db: await db( 'INSERT INTO blocked_user (user_id, blocked_user_id) VALUES ($1, $2)', ctx.author.id, user.id) await ctx.send("That user is now blocked.") @command() async def unblock(self, ctx: Context, user: Member): ''' Unblocks a user and allows them to adopt/makeparent/whatever you ''' current_blocks = self.bot.blocked_users.get(ctx.author.id, list()) if user.id not in current_blocks: await ctx.send("You don't have that user blocked.") return current_blocks.remove(user.id) self.bot.blocked_users[ctx.author.id] = current_blocks async with self.bot.database() as db: await db( 'DELETE FROM blocked_user WHERE user_id=$1 AND blocked_user_id=$2', ctx.author.id, user.id) await ctx.send("That user is now unblocked.")
async def botinfo(self, ctx): """Viser info om meg""" dev = await self.bot.fetch_user(170506717140877312) now = time() diff = int(now - self.bot.uptime) days, remainder = divmod(diff, 24 * 60 * 60) hours, remainder = divmod(remainder, 60 * 60) minutes, seconds = divmod(remainder, 60) process = Process(getpid()) memory_usage = round(process.memory_info().rss / 1000000, 1) cpu_percent = process.cpu_percent() total_members = [] online_members = [] idle_members = [] dnd_members = [] offline_members = [] for guild in self.bot.guilds: for member in guild.members: if member.id in total_members: continue total_members.append(member.id) if str(member.status) == 'online': online_members.append(member.id) elif str(member.status) == 'idle': idle_members.append(member.id) elif str(member.status) == 'dnd': dnd_members.append(member.id) elif str(member.status) == 'offline': offline_members.append(member.id) embed = discord.Embed(color=ctx.me.color, url=website) embed.set_author(name=dev.name, icon_url=dev.avatar_url) embed.set_thumbnail(url=self.bot.user.avatar_url) embed.add_field(name='Dev', value=f'{dev.mention}\n{dev.name}#{dev.discriminator}') embed.add_field(name='Oppetid', value=f'{days}d {hours}t {minutes}m {seconds}s') embed.add_field(name='Ping', value=f'{int(self.bot.latency * 1000)} ms') embed.add_field(name='Servere', value=len(self.bot.guilds)) embed.add_field(name='Discord.py Versjon', value=discord.__version__) embed.add_field(name='Python Versjon', value=platform.python_version()) embed.add_field(name='Ressursbruk', value=f'RAM: {memory_usage} MB\nCPU: {cpu_percent}%') embed.add_field(name='Maskin', value=f'{platform.system()} {platform.release()}') embed.add_field( name=f'Brukere ({len(total_members)})', value=f'<:online:516328785910431754>{len(online_members)} ' + f'<:idle:516328783347843082>{len(idle_members)} ' + f'<:dnd:516328782844395579>{len(dnd_members)} ' + f'<:offline:516328785407246356>{len(offline_members)}') embed.add_field( name='Lenker', value='[Inviter](https://discordapp.com/oauth2/authorize?client_' + f'id={self.bot.user.id}&permissions=388174&scope=bot) ' + f'| [Nettside]({website}) | [Kildekode]({github})') await Defaults.set_footer(ctx, embed) await ctx.send(embed=embed)
class _ProcessMonitor: WARNING_THRESHOLD = 100 * 1024 * 1024 busy = False def __init__(self): self.process = Process() self.peak_mem_res = 0 self.low_mem_warning = False def monitor_task(self): if sys.stdout.isatty(): while self.busy: try: # only print the data out every 10 seconds if datetime.now().second / 10 == 0: info = self._get_info() output.debug(info) else: # call get_mem so that we record peak more accurately self._get_mem() time.sleep(1) except Exception: output.debug_exception() self.busy = False pass else: # if this isn't a TTY, no point in doing any of this self.busy = False def _get_info(self) -> str: from yawast.external.memory_size import Size # prime the call to cpu_percent, as the first call doesn't return useful data self.process.cpu_percent(interval=1) # use oneshot() to cache the data, so we minimize hits with self.process.oneshot(): pct = self.process.cpu_percent() times = self.process.cpu_times() mem = self._get_mem() mem_res = "{0:cM}".format(Size(mem.rss)) mem_virt = "{0:cM}".format(Size(mem.vms)) thr = self.process.num_threads() vm = psutil.virtual_memory() mem_total = "{0:cM}".format(Size(vm.total)) mem_avail_bytes = vm.available mem_avail = "{0:cM}".format(Size(vm.available)) if mem_avail_bytes < self.WARNING_THRESHOLD and not self.low_mem_warning: self.low_mem_warning = True output.error(f"Low RAM Available: {mem_avail}") cons = -1 try: cons = len(self.process.connections(kind="inet")) except Exception: # we don't care if this fails output.debug_exception() cpu_freq = psutil.cpu_freq() info = (f"Process Stats: CPU: {pct}% - Sys: {times.system} - " f"User: {times.user} - Res: {mem_res} - Virt: {mem_virt} - " f"Available: {mem_avail}/{mem_total} - Threads: {thr} - " f"Connections: {cons} - CPU Freq: " f"{int(cpu_freq.current)}MHz/{int(cpu_freq.max)}MHz") return info def _get_mem(self): mem = self.process.memory_info() if mem.rss > self.peak_mem_res: self.peak_mem_res = mem.rss output.debug(f"New high-memory threshold: {self.peak_mem_res}") return mem def __enter__(self): self.busy = True threading.Thread(target=self.monitor_task).start() return self def __exit__(self, exception, value, tb): self.busy = False if exception is not None: return False
class CPUUsage(Metric[float]): """ The standalone CPU usage metric. Instances of this metric compute the average CPU usage as a float value. The metric starts tracking the CPU usage when the `update` method is called for the first time. That is, the tracking does not start at the time the constructor is invoked. Calling the `update` method more than twice will update the metric to the average usage between the first and the last call to `update`. The result, obtained using the `result` method, is the usage computed as stated above. The reset method will bring the metric to its initial state. By default this metric in its initial state will return an usage value of 0. """ def __init__(self): """ Creates an instance of the standalone CPU usage metric. By default this metric in its initial state will return a CPU usage value of 0. The metric can be updated by using the `update` method while the average CPU usage can be retrieved using the `result` method. """ self._mean_usage = Mean() """ The mean utility that will be used to store the average usage. """ self._process_handle: Optional[Process] = None """ The process handle, lazily initialized. """ self._first_update = True """ An internal flag to keep track of the first call to the `update` method. """ def update(self) -> None: """ Update the running CPU usage. For more info on how to set the starting moment see the class description. :return: None. """ if self._first_update: self._process_handle = Process(os.getpid()) last_time = getattr(self._process_handle, '_last_sys_cpu_times', None) utilization = self._process_handle.cpu_percent() current_time = getattr(self._process_handle, '_last_sys_cpu_times', None) if self._first_update: self._first_update = False else: if current_time is None or last_time is None: warnings.warn('CPUUsage can\'t detect the elapsed time. It is ' 'recommended to update avalanche to the latest ' 'version.') # Fallback, shouldn't happen current_time = 1.0 last_time = 0.0 self._mean_usage.update(utilization, current_time - last_time) def result(self) -> float: """ Retrieves the average CPU usage. Calling this method will not change the internal state of the metric. :return: The average CPU usage, as a float value. """ return self._mean_usage.result() def reset(self) -> None: """ Resets the metric. :return: None. """ self._mean_usage.reset() self._process_handle = None self._first_update = True
def _get_cpu_percent(process: psutil.Process): return process.cpu_percent()
async def botinfo(self, ctx): """Viser info om meg""" dev = await self.bot.fetch_user(170506717140877312) start = perf_counter() status_msg = await ctx.send('Beregner ping...') end = perf_counter() ping = int((end - start) * 1000) now = time() diff = int(now - self.bot.uptime) days, remainder = divmod(diff, 24 * 60 * 60) hours, remainder = divmod(remainder, 60 * 60) minutes, seconds = divmod(remainder, 60) process = Process(getpid()) memory_usage = round(process.memory_info().rss / 1000000, 1) cpu_percent = process.cpu_percent() total_members = [] online_members = [] idle_members = [] dnd_members = [] offline_members = [] for guild in self.bot.guilds: for member in guild.members: if member.id in total_members: continue total_members.append(member.id) if str(member.status) == 'online': online_members.append(member.id) elif str(member.status) == 'idle': idle_members.append(member.id) elif str(member.status) == 'dnd': dnd_members.append(member.id) elif str(member.status) == 'offline': offline_members.append(member.id) embed = discord.Embed(color=ctx.me.color, url=self.bot.misc['website']) embed.set_author(name=dev.name, icon_url=dev.avatar_url) embed.set_thumbnail(url=self.bot.user.avatar_url) embed.add_field(name='Dev', value=f'{dev.mention}\n{dev.name}#{dev.discriminator}') embed.add_field(name='Oppetid', value=f'{days}d {hours}t {minutes}m {seconds}s') embed.add_field( name='Ping', value= f'Ekte ping: {ping} ms\nWebsocket ping: {int(self.bot.latency * 1000)} ms' ) embed.add_field(name='Servere', value=len(self.bot.guilds)) embed.add_field(name='Discord.py Versjon', value=discord.__version__) embed.add_field(name='Python Versjon', value=platform.python_version()) embed.add_field(name='Ressursbruk', value=f'RAM: {memory_usage} MB\nCPU: {cpu_percent}%') embed.add_field(name='Maskin', value=f'{platform.system()} {platform.release()}') embed.add_field( name=f'Brukere ({len(total_members)})', value=f'{self.bot.emoji["online"]}{len(online_members)} ' + f'{self.bot.emoji["idle"]}{len(idle_members)} ' + f'{self.bot.emoji["dnd"]}{len(dnd_members)} ' + f'{self.bot.emoji["offline"]}{len(offline_members)}') embed.add_field( name='Lenker', value='[Inviter](https://discordapp.com/oauth2/authorize?client_' + f'id={self.bot.user.id}&permissions=388174&scope=bot) ' + f'| [Nettside]({self.bot.misc["website"]}) ' + f'| [Kildekode]({self.bot.misc["source_code"]})') await Defaults.set_footer(ctx, embed) await status_msg.edit(embed=embed, content=None)
class TextUi(MpfController): """Handles the text-based UI.""" config_name = "text_ui" __slots__ = [ "start_time", "machine", "_tick_task", "screen", "mpf_process", "ball_devices", "switches", "config", "_pending_bcp_connection", "_asset_percent", "_player_widgets", "_machine_widgets", "_bcp_status", "frame", "layout", "scene", "footer_memory", "switch_widgets", "mode_widgets", "ball_device_widgets", "footer_cpu", "footer_mc_cpu", "footer_uptime", "delay", "_layout_change" ] def __init__(self, machine: "MachineController") -> None: """Initialize TextUi.""" super().__init__(machine) self.delay = DelayManager(machine) self.config = machine.config.get('text_ui', {}) self.screen = None if not machine.options['text_ui'] or not Scene: return # hack to add themes until https://github.com/peterbrittain/asciimatics/issues/207 is implemented THEMES["mpf_theme"] = defaultdict( lambda: (Screen.COLOUR_WHITE, Screen.A_NORMAL, Screen.COLOUR_BLACK), { "active_switch": (Screen.COLOUR_BLACK, Screen.A_NORMAL, Screen.COLOUR_GREEN), "pf_active": (Screen.COLOUR_GREEN, Screen.A_NORMAL, Screen.COLOUR_BLACK), "pf_inactive": (Screen.COLOUR_WHITE, Screen.A_NORMAL, Screen.COLOUR_BLACK), "label": (Screen.COLOUR_WHITE, Screen.A_NORMAL, Screen.COLOUR_BLACK), "title": (Screen.COLOUR_WHITE, Screen.A_NORMAL, Screen.COLOUR_RED), "title_exit": (Screen.COLOUR_BLACK, Screen.A_NORMAL, Screen.COLOUR_RED), "footer_cpu": (Screen.COLOUR_CYAN, Screen.A_NORMAL, Screen.COLOUR_BLACK), "footer_path": (Screen.COLOUR_YELLOW, Screen.A_NORMAL, Screen.COLOUR_BLACK), "footer_memory": (Screen.COLOUR_GREEN, Screen.A_NORMAL, Screen.COLOUR_BLACK), "footer_mc_cpu": (Screen.COLOUR_MAGENTA, Screen.A_NORMAL, Screen.COLOUR_BLACK), }) self.start_time = datetime.now() self.machine = machine self.mpf_process = Process() self.ball_devices = list() # type: List[BallDevice] self.switches = {} # type: Dict[str, Switch] self.machine.events.add_handler('init_phase_2', self._init) # self.machine.events.add_handler('init_phase_3', self._init2) self.machine.events.add_handler('loading_assets', self._asset_load_change) self.machine.events.add_handler('bcp_connection_attempt', self._bcp_connection_attempt) self.machine.events.add_handler('asset_loading_complete', self._asset_load_complete) self.machine.events.add_handler('bcp_clients_connected', self._bcp_connected) self.machine.events.add_handler('shutdown', self.stop) self.machine.add_crash_handler(self.stop) self.machine.events.add_handler('player_number', self._update_player) self.machine.events.add_handler('player_ball', self._update_player) self.machine.events.add_handler('player_score', self._update_player) self.machine.events.add_handler('ball_ended', self._update_player) self._pending_bcp_connection = False self._asset_percent = 0 self._bcp_status = (0, 0, 0) # type: Tuple[float, int, int] self.switch_widgets = [] # type: List[Widget] self.mode_widgets = [] # type: List[Widget] self.ball_device_widgets = [] # type: List[Widget] self._machine_widgets = [] # type: List[Widget] self._player_widgets = [] # type: List[Widget] self.footer_memory = None self.footer_cpu = None self.footer_mc_cpu = None self.footer_uptime = None self._layout_change = True self._tick_task = self.machine.clock.schedule_interval(self._tick, 1) self._create_window() self._draw_screen() def _init(self, **kwargs): del kwargs for mode in self.machine.modes.values(): self.machine.events.add_handler( "mode_{}_started".format(mode.name), self._mode_change) self.machine.events.add_handler( "mode_{}_stopped".format(mode.name), self._mode_change) self.machine.switch_controller.add_monitor(self._update_switches) self.machine.register_monitor("machine_vars", self._update_machine_vars) self.machine.variables.machine_var_monitor = True self.machine.bcp.interface.register_command_callback( "status_report", self._bcp_status_report) for bd in [ x for x in self.machine.ball_devices.values() if not x.is_playfield() ]: self.ball_devices.append(bd) self.ball_devices.sort() self._update_switch_layout() self._schedule_draw_screen() async def _bcp_status_report(self, client, cpu, rss, vms): del client self._bcp_status = cpu, rss, vms def _update_stats(self): # Runtime rt = (datetime.now() - self.start_time) mins, sec = divmod(rt.seconds + rt.days * 86400, 60) hours, mins = divmod(mins, 60) self.footer_uptime.text = 'RUNNING {:d}:{:02d}:{:02d}'.format( hours, mins, sec) # System Stats self.footer_memory.text = 'Free Memory (MB): {} CPU:{:3d}%'.format( round(virtual_memory().available / 1048576), round(cpu_percent(interval=None, percpu=False))) # MPF process stats self.footer_cpu.text = 'MPF (CPU RSS/VMS): {}% {}/{} MB '.format( round(self.mpf_process.cpu_percent()), round(self.mpf_process.memory_info().rss / 1048576), round(self.mpf_process.memory_info().vms / 1048576)) # MC process stats if self._bcp_status != (0, 0, 0): self.footer_mc_cpu.text = 'MC (CPU RSS/VMS) {}% {}/{} MB '.format( round(self._bcp_status[0]), round(self._bcp_status[1] / 1048576), round(self._bcp_status[2] / 1048576)) else: self.footer_mc_cpu.text = "" def _update_switch_layout(self): num = 0 self.switch_widgets = [] self.switches = {} self.switch_widgets.append((Label("SWITCHES"), 1)) self.switch_widgets.append((Divider(), 1)) self.switch_widgets.append((Label(""), 2)) self.switch_widgets.append((Divider(), 2)) for sw in sorted(self.machine.switches.values()): if sw.invert: name = sw.name + '*' else: name = sw.name col = 1 if num <= int(len(self.machine.switches) / 2) else 2 switch_widget = Label(name) if sw.state: switch_widget.custom_colour = "active_switch" self.switch_widgets.append((switch_widget, col)) self.switches[sw.name] = (sw, switch_widget) num += 1 self._schedule_draw_screen() def _update_switches(self, change, *args, **kwargs): del args del kwargs try: sw, switch_widget = self.switches[change.name] except KeyError: return if sw.state: switch_widget.custom_colour = "active_switch" else: switch_widget.custom_colour = "label" self._schedule_draw_screen() def _draw_switches(self): """Draw all switches.""" for widget, column in self.switch_widgets: self.layout.add_widget(widget, column) def _mode_change(self, *args, **kwargs): # Have to call this on the next frame since the mode controller's # active list isn't updated yet del args del kwargs self.mode_widgets = [] self.mode_widgets.append(Label("ACTIVE MODES")) self.mode_widgets.append(Divider()) try: modes = self.machine.mode_controller.active_modes except AttributeError: modes = None if modes: for mode in modes: self.mode_widgets.append( Label('{} ({})'.format(mode.name, mode.priority))) else: self.mode_widgets.append(Label("No active modes")) # empty line at the end self.mode_widgets.append(Label("")) self._layout_change = True self._schedule_draw_screen() def _draw_modes(self): for widget in self.mode_widgets: self.layout.add_widget(widget, 0) def _draw_ball_devices(self): for widget in self.ball_device_widgets: self.layout.add_widget(widget, 3) def _update_ball_devices(self, **kwargs): del kwargs # TODO: do not create widgets. just update contents self.ball_device_widgets = [] self.ball_device_widgets.append(Label("BALL COUNTS")) self.ball_device_widgets.append(Divider()) try: for pf in self.machine.playfields.values(): widget = Label('{}: {} '.format(pf.name, pf.balls)) if pf.balls: widget.custom_colour = "pf_active" else: widget.custom_colour = "pf_inactive" self.ball_device_widgets.append(widget) except AttributeError: pass for bd in self.ball_devices: widget = Label('{}: {} ({})'.format(bd.name, bd.balls, bd.state)) if bd.balls: widget.custom_colour = "pf_active" else: widget.custom_colour = "pf_inactive" self.ball_device_widgets.append(widget) self.ball_device_widgets.append(Label("")) self._layout_change = True self._schedule_draw_screen() def _update_player(self, **kwargs): del kwargs self._player_widgets = [] self._player_widgets.append(Label("CURRENT PLAYER")) self._player_widgets.append(Divider()) try: player = self.machine.game.player self._player_widgets.append( Label('PLAYER: {}'.format(player.number))) self._player_widgets.append(Label('BALL: {}'.format(player.ball))) self._player_widgets.append( Label('SCORE: {:,}'.format(player.score))) except AttributeError: self._player_widgets.append(Label("NO GAME IN PROGRESS")) return player_vars = player.vars.copy() player_vars.pop('score', None) player_vars.pop('number', None) player_vars.pop('ball', None) names = self.config.get('player_vars', player_vars.keys()) for name in names: self._player_widgets.append( Label("{}: {}".format(name, player_vars[name]))) self._layout_change = True self._schedule_draw_screen() def _draw_player(self, **kwargs): del kwargs for widget in self._player_widgets: self.layout.add_widget(widget, 3) def _update_machine_vars(self, **kwargs): """Update machine vars.""" del kwargs self._machine_widgets = [] self._machine_widgets.append(Label("MACHINE VARIABLES")) self._machine_widgets.append(Divider()) machine_vars = self.machine.variables.machine_vars # If config defines explict vars to show, only show those. Otherwise, all names = self.config.get('machine_vars', machine_vars.keys()) for name in names: self._machine_widgets.append( Label("{}: {}".format(name, machine_vars[name]['value']))) self._layout_change = True self._schedule_draw_screen() def _draw_machine_variables(self): """Draw machine vars.""" for widget in self._machine_widgets: self.layout.add_widget(widget, 0) def _create_window(self): self.screen = Screen.open() self.frame = Frame(self.screen, self.screen.height, self.screen.width, has_border=False, title="Test") self.frame.set_theme("mpf_theme") title_layout = Layout([1, 5, 1]) self.frame.add_layout(title_layout) title_left = Label("") title_left.custom_colour = "title" title_layout.add_widget(title_left, 0) title = 'Mission Pinball Framework v{}'.format( mpf._version.__version__) # noqa title_text = Label(title, align="^") title_text.custom_colour = "title" title_layout.add_widget(title_text, 1) exit_label = Label("< CTRL + C > TO EXIT", align=">") exit_label.custom_colour = "title_exit" title_layout.add_widget(exit_label, 2) self.layout = MpfLayout([1, 1, 1, 1], fill_frame=True) self.frame.add_layout(self.layout) footer_layout = Layout([1, 1, 1]) self.frame.add_layout(footer_layout) self.footer_memory = Label("", align=">") self.footer_memory.custom_colour = "footer_memory" self.footer_uptime = Label("", align=">") self.footer_uptime.custom_colour = "footer_memory" self.footer_mc_cpu = Label("") self.footer_mc_cpu.custom_colour = "footer_mc_cpu" self.footer_cpu = Label("") self.footer_cpu.custom_colour = "footer_cpu" footer_path = Label(self.machine.machine_path) footer_path.custom_colour = "footer_path" footer_empty = Label("") footer_empty.custom_colour = "footer_memory" footer_layout.add_widget(footer_path, 0) footer_layout.add_widget(self.footer_cpu, 0) footer_layout.add_widget(footer_empty, 1) footer_layout.add_widget(self.footer_mc_cpu, 1) footer_layout.add_widget(self.footer_uptime, 2) footer_layout.add_widget(self.footer_memory, 2) self.scene = Scene([self.frame], -1) self.screen.set_scenes([self.scene], start_scene=self.scene) # prevent main from scrolling out the footer self.layout.set_max_height(self.screen.height - 2) def _schedule_draw_screen(self): # schedule the draw in 10ms if it is not scheduled self.delay.add_if_doesnt_exist(10, self._draw_screen, "draw_screen") def _draw_screen(self): if not self.screen: # probably drawing during game end return if self._layout_change: self.layout.clear_columns() self._draw_modes() self._draw_machine_variables() self._draw_switches() self._draw_ball_devices() self._draw_player() self.frame.fix() self._layout_change = False self.screen.force_update() self.screen.draw_next_frame() def _tick(self): if self.screen.has_resized(): self._create_window() self._update_ball_devices() self._update_stats() self._schedule_draw_screen() self.machine.bcp.transport.send_to_clients_with_handler( handler="_status_request", bcp_command="status_request") def _bcp_connection_attempt(self, name, host, port, **kwargs): del name del kwargs self._pending_bcp_connection = PopUpDialog( self.screen, 'WAITING FOR MEDIA CONTROLLER {}:{}'.format(host, port), []) self.scene.add_effect(self._pending_bcp_connection) self._schedule_draw_screen() def _bcp_connected(self, **kwargs): del kwargs self.scene.remove_effect(self._pending_bcp_connection) self._schedule_draw_screen() def _asset_load_change(self, percent, **kwargs): del kwargs if self._asset_percent: self.scene.remove_effect(self._asset_percent) self._asset_percent = PopUpDialog( self.screen, 'LOADING ASSETS: {}%'.format(percent), []) self.scene.add_effect(self._asset_percent) self._schedule_draw_screen() def _asset_load_complete(self, **kwargs): del kwargs self.scene.remove_effect(self._asset_percent) self._schedule_draw_screen() def stop(self, **kwargs): """Stop the Text UI and restore the original console screen.""" del kwargs if self.screen: self.machine.clock.unschedule(self._tick_task) self.screen.close(True) self.screen = None
class FactorioManager(object): def __init__(self, name: str, port: int, root_path, is_steam=False): self.name = name self.port = port self.process = None self.root_path = Path(root_path) self.log = make_log(f'{name}_factorio') self.update_available = False self._ps_proc = None self._virtual_mem = VIRTUAL_MEMORY self._is_steam = is_steam self._player_data = None self._config = None self._server_config = None self._version_info = None self._available_versions = None self._temp_update = None self._log_queue = deque(maxlen=20) self._status_history = deque(maxlen=50) @property def version(self): if self._version_info is None: self.set_version_info() return self._version_info['version'] @property def build(self): if self._version_info is None: self.set_version_info() return self._version_info['build'] @property def build_num(self): if self._version_info is None: self.set_version_info() return self._version_info['build']['number'] @property def build_platform(self): if self._version_info is None: self.set_version_info() return self._version_info['build']['platform'] @property def build_mode(self): if self._version_info is None: self.set_version_info() return self._version_info['build']['mode'] @property def bin_version(self): if self._version_info is None: self.set_version_info() return self._version_info['binary version'] @property def map_in_version(self): if self._version_info is None: self.set_version_info() return self._version_info['map input version'] @property def map_out_version(self): if self._version_info is None: self.set_version_info() return self._version_info['map output version'] @property def is_experimental(self): if not self._available_versions: t = self.fetch_factorio_versions() t.join() if self.version_list.index(self.stable) < self.version_list.index( self.version): return True return False @property def stable(self): if not self._available_versions: t = self.fetch_factorio_versions() t.join() return self._available_versions['stable'] @property def version_list(self): if not self._available_versions: t = self.fetch_factorio_versions() t.join() return self._available_versions['version_list'] @property def experimental_version_list(self): if not self._available_versions: t = self.fetch_factorio_versions() t.join() return self.version_list[self.version_list.index(self.stable) + 1:] @property def stable_version_list(self): if not self._available_versions: t = self.fetch_factorio_versions() t.join() return self.version_list[:self.version_list.index(self.stable) + 1] @property def executable(self): if OS_WIN: return (self.root_path / 'bin' / 'x64' / 'factorio.exe').resolve() else: return (self.root_path / 'bin' / 'x64' / 'factorio').resolve() @property def save_file(self): return (self.root_path / 'saves' / self.name / f'{self.name}.zip').resolve() @property def player_data(self): if not self._player_data: self._player_data = json.load( (self.root_path / 'player-data.json').resolve().open()) return self._player_data @property def service_username(self): if not self._player_data: self._player_data = json.load( (self.root_path / 'player-data.json').resolve().open()) return self._player_data['service-username'] @property def service_token(self): if not self._player_data: self._player_data = json.load( (self.root_path / 'player-data.json').resolve().open()) return self._player_data['service-token'] @property def config(self): if not self._config: conf_parser = ConfigParser() self._config = conf_parser.read( (self.root_path / 'config' / 'config.ini').resolve().as_posix()) return self._config @property def server_config(self): if not self._server_config: self._server_config = json.load( (self.root_path / 'config' / 'server-settings.json').resolve().open()) return self._server_config @server_config.setter def server_config(self, config): self._server_config = merge_two_dicts(config, self._server_config) save_settings(self._server_config, (self.root_path / 'config' / 'server-settings.json').resolve()) @property def bits(self): if self.build_platform[-2:] == '64': return '64' else: return '32' # I don't have any 32 bit systems so I wasn't sure what factorio would respond with @property def core_str(self): core = f'core-{self.build_platform[:-2]}' if self.build_mode == 'headless': core = core + '_headless' core = core + self.bits return core def set_version_info(self): log.info(f'Getting the version info for {self.name}') commands = [self.executable.as_posix(), '--version'] p = Popen(commands, stdout=PIPE, stderr=PIPE) std_out, std_err = p.communicate() self._version_info = std_out.decode().splitlines() self._version_info = { l.split(':')[0].lower(): l.split(':')[1] for l in self._version_info } self._version_info['build'] = self._version_info['version'].split( '(')[1] self._version_info['build'] = self._version_info['build'].replace( ')', '').split(', ') self._version_info['build'] = { 'number': self._version_info['build'][0].replace('build', ''), 'platform': self._version_info['build'][1], 'mode': self._version_info['build'][2] } self._version_info['version'] = self._version_info['version'].split( '(')[0].strip() def status(self): if self.process: if not self._ps_proc: self._ps_proc = Process(self.process.pid) try: data = { 'status': self._ps_proc.status(), 'cpu': self._ps_proc.cpu_percent(interval=2), 'mem': naturalsize(self._ps_proc.memory_info().rss), 'mem_raw': self._ps_proc.memory_info().rss, 'available_mem': naturalsize(self._virtual_mem.available), 'available_mem_raw': self._virtual_mem.available, 'total_mem': naturalsize(TOTAL_MEMORY), 'total_mem_raw': TOTAL_MEMORY, } except (NoSuchProcess, AttributeError): log.warn( f'Factorio Process {self.name} does not exist anymore') return self._status_history.appendleft(data) return list(self._status_history) @run_in_thread def start(self): log.info(f'Starting Factorio instance {self.name}') if self.name in app_settings.factorio_instances: if isinstance(self.process, Popen): # TODO: need to do more here to actually check if it is running log.warn(f'{self.name} factorio instance is already running') return if self.name not in app_settings.factorio_instances: log.warn(f'{self.name} factorio instance does not exist') return commands = [ self.executable.as_posix(), '--start-server', self.save_file, '--port', str(self.port) ] log.debug(f'Starting {self.name}') self.process = Popen(commands, stdin=PIPE, stdout=PIPE, stderr=PIPE) self.output_log() @run_in_thread def output_log(self): while True: std_out = self.process.stdout.readline() if std_out: std_out = std_out.decode() self._log_queue.append('{} {}: {}'.format( datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3], self.name.upper(), std_out.replace('\n', ''))) self.log.info(std_out) else: sleep(.05) if self.process is None: break if self.process.poll() is not None: break def get_log_line(self): if len(self._log_queue): return self._log_queue.pop() else: return @run_in_thread def stop(self): log.debug(f'Stopping {self.name}') if self.process: self.process.terminate() self.process = None self._ps_proc = None @run_in_thread def kill(self): log.debug(f'Killing {self.name}') if self.process: self.process.kill() self.process = None self._ps_proc = None @run_in_thread def send_command(self, command): # TODO: This does not work. No idea how it should work if self.process: self.process.communicate(f'{command}\n'.encode()) def create_save_file(self, map_gen_file_path=None, map_settings_path=None, preset=None, map_preview_path=None): if not self.save_file.is_file(): if not (self.save_file / '..').resolve().is_dir(): (self.save_file / '..').resolve().mkdir() commands = [ self.executable.as_posix(), '--create', self.save_file.as_posix() ] # TODO: Add the optional arguments to commands p = Popen(commands, stdout=PIPE, stderr=PIPE) log.info(p.communicate()) def get_version_info(self): if self._version_info is None: self.set_version_info() return self._version_info def check_for_update(self): self.get_version_info() t = self.fetch_factorio_versions() t.join() if self.is_experimental: version_list = self.experimental_version_list else: version_list = self.stable_version_list if self.version != version_list[-1]: self.update_available = version_list[-1] return version_list[-1] else: self.update_available = False def get_download_link(self, version): get_link_url = 'https://www.factorio.com/get-download-link' update_version_info = list( filter(lambda x: x['to'] == version, self._available_versions['available_versions']))[0] data = { 'username': self.service_username, 'token': self.service_token, 'package': self.core_str, 'from': update_version_info['from'], 'to': update_version_info['to'], 'apiVersion': 2 } req = request.Request(get_link_url + '?' + parse.urlencode(data)) resp = request.urlopen(req) download_link = json.loads(resp.read()) return download_link[0] def download_update(self, version): link = self.get_download_link(version) log.info(link) with TqdmUpTo(unit='B', unit_scale=True, miniters=1, desc=link.split('/')[-1]) as t: self._temp_update = request.urlretrieve( link, reporthook=t.download_progress)[0] @run_in_thread def apply_update(self): if self._temp_update: commands = [ self.executable.as_posix(), '--apply-update', self._temp_update ] p = Popen(commands, stdout=PIPE, stderr=PIPE) log.info(p.communicate()) p.terminate() self.set_version_info() self.update_available = False @run_in_thread def fetch_factorio_versions(self): available_versions_url = 'https://www.factorio.com/get-available-versions' data = parse.urlencode({ 'username': self.service_username, 'token': self.service_token, 'apiVersion': 2 }) req = request.Request(available_versions_url + '?' + data) resp = request.urlopen(req) json_resp = json.loads(resp.read()) available_versions = json_resp[self.core_str] stable_version = list( filter(lambda x: True if 'stable' in x else False, available_versions))[0]['stable'] available_versions = list( filter(lambda x: False if 'stable' in x else True, available_versions)) version_list = sorted( available_versions, key=lambda s: [int(u) for u in s['to'].split('.')]) version_list = [u['to'] for u in version_list] self._available_versions = { 'stable': stable_version, 'version_list': version_list, 'available_versions': available_versions } return json_resp