# Copyright 2025 - Solely by BrotherBoard # Intended for personal use only # Bug? Feedback? Telegram >> @BroBordd """ PlugTools v1.5 - Live Plugin Action Beta. Feedback is appreciated. Adds a dev console tab for plugin management. Features vary between: - Dynamic Control: Enables immediate loading and reloading of plugins. - Real-time Monitoring: Reports status of plugin files (new, modified, deleted). - Plugin Overview: Displays operational state (enabled/disabled) and integrity (original/modified). - Plugin Data: Provides file path, size, timestamps, and code structure analysis. - Navigation: Offers controls to browse the plugin list. - Logging: Has a built-in log display with proper indentation. """ from os.path import ( splitext, getmtime, getctime, basename, getsize, isfile, exists, join ) from os import ( scandir, access, R_OK, stat ) from babase import ( PluginSpec, Plugin, Call, env, app ) from babase._devconsole import ( DevConsoleTabEntry as ENT, DevConsoleTab as TAB ) from bauiv1 import ( get_string_width as sw, SpecialChar as sc, charstr as cs, apptimer as teck, screenmessage as push, getsound as gs ) from traceback import format_exc as ERR from datetime import datetime from importlib import reload from typing import override from sys import modules from gc import collect from ast import ( FunctionDef, ImportFrom, Attribute, ClassDef, Import, parse, walk, Name ) class PlugTools(TAB): KEY = 'PT_BY' def __init__(s): s.bys = META() s.bad = [] s.logs = 'No errors' s.mem = {_:MT(_) for _ in s.bys} s.eye = look() s.e = False s.spy() def spy(s): b = 0 for _ in s.bys.copy(): if not exists(PAT(_)): s.bys.remove(_) push(f'Plugin {_} suddenly disappeared!\nAnd so, was removed from list.',color=(1,1,0)) gs('block').play() s.eye = look() if s.hl() == _: s.hl(None) b = 1 sp = app.plugins.plugin_specs.get(_,0) if not sp: continue p = app.plugins if getattr(sp,'enabled',False): o = s.sp.plugin if o in p.active_plugins: p.active_plugins.remove(o) if o in p.plugin_specs: p.plugin_specs.pop(o) del s.sp.plugin,o collect() try: reload(modules[NAM(_,0)]) except: pass continue if MT(_) != s.mem[_] and _ not in s.bad: s.bad.append(_) push(f'Plugin {_} was modified!\nSee if you want to take action.',color=(1,1,0)) gs('dingSmall').play() b = 1 if hasattr(s,'sp'): e = getattr(s.sp,'enabled',False) if e != s.e: s.e = e b = 1 eye = look() s1 = set(s.eye) s2 = set(eye) df = list(s2-s1) nu = [] if df: for dd in df: try: _ = kang(dd) except: eye.remove(dd) continue nu.append(_) s.bys.append(_) s.mem[_] = 0 s.bad.append(_) s.eye = eye b = 1 if nu: l = len(nu) push(f"Found {l} new plugin{['s',''][l==1]}:\n{', '.join(nu)}\nSee what to do with {['it','them'][l!=1]}",color=(1,1,0)) gs('dingSmallHigh').play() if b: try: s.request_refresh() except RuntimeError: pass teck(0.1,s.spy) @override def refresh(s): # Preload by = s.hl() if by not in s.bys: by = None s.hl(None) s.by = by s.sp = app.plugins.plugin_specs.get(by,0) if by else 0 s.i = getattr(s,'i',0 if by is None else s.bys.index(by)//10) # UI w = s.width x = -w/2 z = x+w # Bools e = s.e = getattr(s.sp,'enabled',False) m = by in s.bad d = by is None # Buttons sx = w*0.2 mx = sx*0.98 z -= sx s.button( 'Metadata', pos=(z,50), size=(mx,43), call=s.metadata, disabled=d ) s.button( ['Load','Reload'][e], pos=(z,5), size=(mx,43), call=s._load, disabled=d ) # Separator s.button( '', pos=(z-(w*0.006),5), size=(2,90) ) # Plugin info sx = w*0.1 z -= sx az = z+sx/2.23 t = 'Entry' if d else by tw = GSW(t) mx = sx*0.9 s.text( t, pos=(az,80), scale=1 if tw len(s.bys) ) # Plugins sx = w*0.645/5 mx = sx*0.99 zx = mx*0.9 z -= sx*5 for i in range(5): for j in range(2): k = j*5+i+s.i*10 if k >= len(s.bys): break t = s.bys[k] tw = GSW(t) s.button( t, size=(mx,43), pos=(z+sx*i,50-45*j), label_scale=1 if tw export_line_num: for base in node.bases: if (isinstance(base, Name) and base.id == 'Plugin') or \ (isinstance(base, Attribute) and base.attr == 'Plugin' and isinstance(base.value, Name) and base.value.id == 'babase'): return f"{filename_without_ext}.{node.name}" return None deek = lambda: gs('deek').play() # brobord collide grass # ba_meta require api 9 # ba_meta export babase.Plugin class byBordd(Plugin): def __init__(s): C = PlugTools N = C.__name__ E = ENT(N,C) I = app.devconsole I.tabs = [_ for _ in I.tabs if _.name != N]+[E] I._tab_instances[N] = E.factory()