22# Created by @Gitago for TouchPortal
33# Jan, 2023
44
5- ## If users ever want multiple PIDS for the .exe we can make an action take it as an argument and then when user selects .exe it will show all the PIDs for that .exe and user can select which one they want to monitor
6-
7-
5+ ## If users ever want multiple PIDS for the .exe we can make an action take it as an argument and then when user selects .exe it will show all the PIDs for that .exe and user can select which one they want to monito
86## BUGS
97## when process is checked with a 0 timer it is still counting as an active monitor although its only checking it once and stopping..
10-
118## when checking for a process if its NOT loaded, then no states are made or mention that its not running.. but if its runing then you close it updates as expected
129# when stopping a monitor it doesnt seem to clear the length of total?
1310
2421import requests
2522import base64
2623import time
27-
24+ from datetime import datetime
2825PLUGIN_NAME = "Process Monitor"
2926PLUGIN_ID = "tp.plugin.process_monitor"
3027GITHUB_URL = "process-monitor-touchportal-plugin"
31- # DEFAULT_CONFIG_SAVE_PATH = path.join(path.dirname(path.realpath(__file__)), "color_config.json")
28+
3229
3330
3431class ProcessMonitorData :
@@ -69,7 +66,7 @@ def the_task(self, process_name, the_process):
6966 TPC .TPClient .createState (stateId = PLUGIN_ID + f".state.{ self .process_name } .process_info.{ x } " , description = f"PM | { self .process_name } - { x } " , value = "" , parentGroup = str (self .process_name ))
7067
7168 # Updating Status to "Closed" since the process appears to not be running
72- TPC .TPClient .createState (stateId = PLUGIN_ID + f".state.{ self .process_name } .process_info.status" , description = f"PM | { self .process_name } - status" , value = "Closed " , parentGroup = str (self .process_name ))
69+ TPC .TPClient .createState (stateId = PLUGIN_ID + f".state.{ self .process_name } .process_info.status" , description = f"PM | { self .process_name } - status" , value = "closed " , parentGroup = str (self .process_name ))
7370
7471 if process_checked :
7572 PM .add_to_dict (self .process_name , the_process )
@@ -83,7 +80,7 @@ def the_task(self, process_name, the_process):
8380 TPC .TPClient .choiceUpdate (choiceId = PLUGIN_ID + ".act.process_name.stop" , values = the_list )
8481
8582 PM .add_to_choiceList (the_list )
86- # process_monitor_choiceList = the_list
83+ # process_monitor_choiceList = the_list
8784
8885 ## update a state showing how many values are in the list minus the "ALL" value
8986 TPC .TPClient .stateUpdate (stateId = PLUGIN_ID + ".state.process_monitor.count" , stateValue = str (len (the_list ) - 1 ))
@@ -96,60 +93,36 @@ def the_task(self, process_name, the_process):
9693 process_checked [x ] = round (process_checked [x ], 2 )
9794
9895 if x == 'create_time' :
99- from datetime import datetime
10096 create_time = process_checked .get ('create_time' , "None" )
10197 if create_time is not None :
10298 create_time_datetime = datetime .fromtimestamp (create_time )
10399 process_checked [x ] = create_time_datetime .strftime ("%m/%d/%Y [%I:%M:%S %p]" )
104-
105- ## use a thread to create sttes as fast as possible
100+
106101 TPC .TPClient .createState (stateId = PLUGIN_ID + f".state.{ the_process .process_name } .process_info.{ x } " , description = f"PM | { the_process .process_name } - { x } " , value = str (process_checked .get (x , "None" )), parentGroup = str (the_process .process_name ))
107102
108103
109- def is_running (self ):
110-
104+ def is_running (self ):
111105 for process in psutil .process_iter ():
112106 if process .name ().lower () == self .process_name .lower ():
113107 process_Info = process .as_dict (attrs = ['pid' , 'name' , 'username' , 'cpu_percent' , 'memory_percent' , 'cmdline' , 'create_time' , 'status' ])
114108 # print("Before joiing the cmdline: ", process_checked)
115109 process_Info ["cmdline" ] = ' ' .join (process_Info ["cmdline" ])
116-
117- # print("After joiing the cmdline: ", process_checked)
118- # memory_info = process.memory_info().rss
119- # memory_mb = memory_info / 1048576
120- # process_Info['memory_MB'] = memory_mb
121110 return process_Info
122111
123112 return False
124113
125114
126115 def check_continuously (self , interval , process_name , the_process ):
127116 while self .should_continue :
128- TPC .g_log .debug ("Checking if " + self .process_name + " is running" )
129-
117+ TPC .g_log .debug ("Checking if " + self .process_name + " is running" )
130118 self .the_task (process_name = process_name , the_process = the_process )
131- time .sleep (interval )
132-
119+ time .sleep (interval )
133120 return False
134121
135122
136123 def stop (self ):
137124 self .should_continue = False
138125
139-
140-
141- ## ### The TP Client
142- ## try:
143- ## TPClient = TP.Client(
144- ## pluginId = PLUGIN_ID,
145- ## sleepPeriod = 0.05,
146- ## autoClose = True,
147- ## checkPluginId = True,
148- ## maxWorkers = 4,
149- ## updateStatesOnBroadcast = False,
150- ## )
151- ## except Exception as e:
152- ## sys.exit(f"Could not create TP Client, exiting. Error was:\n{repr(e)}")
153126class TPClientClass :
154127 def __init__ (self , pluginId , sleepPeriod = 0.05 , autoClose = True , checkPluginId = True , maxWorkers = 4 , updateStatesOnBroadcast = False ):
155128 self .pluginId = pluginId
@@ -175,11 +148,6 @@ def __init__(self, pluginId, sleepPeriod=0.05, autoClose=True, checkPluginId=Tru
175148
176149TPC = TPClientClass (PLUGIN_ID )
177150
178- # Crate the global logger
179- #g_log = getLogger()
180-
181-
182-
183151@TPC .TPClient .on (TP .TYPES .onNotificationOptionClicked )
184152def check_noti (data ):
185153 if data ['optionId' ] == PLUGIN_ID + '.update.download' :
@@ -188,15 +156,32 @@ def check_noti(data):
188156 webbrowser .open (url , new = 0 , autoraise = True )
189157
190158
191-
192159@TPC .TPClient .on (TP .TYPES .onConnect )
193160def onConnect (data ):
194161 TPC .g_log .info (f"Connected to TP v{ data .get ('tpVersionString' , '?' )} , plugin v{ data .get ('pluginVersion' , '?' )} ." )
195162 TPC .g_log .debug (f"Connection: { data } " )
196163 if settings := data .get ('settings' ):
197164 handleSettings (settings , True )
198-
165+
166+ if settings :#('Auto Monitor Programs (comma seperated)') != "":
167+ auto_monitor_programs = settings [0 ]['The Programs to Monitor (comma separated)' ]
168+
169+ if auto_monitor_programs != "" :
170+ auto_monitor_programs = auto_monitor_programs .split ("," )
171+ every_X_seconds = 5 ## default to 5 seconds unless specified in settings
172+ if settings [1 ]['Check every x seconds' ] != "" :
173+ every_X_seconds = int (settings [1 ]['Check every x seconds' ])
174+
175+ for x in auto_monitor_programs :
176+ TPC .g_log .info (f"Checking every 5 seconds for { x } " )
177+ the_process = ProcessChecker (x )
178+ if x not in PM .process_monitor_dict .keys ():
179+ th = threading .Thread (target = the_process .check_continuously , args = (every_X_seconds , x , the_process ))
180+ th .start ()
181+
182+
199183 plugin_update_check (data )
184+
200185 TPC .TPClient .stateUpdate (stateId = PLUGIN_ID + ".state.process_monitor.count" , stateValue = "0" )
201186
202187
@@ -207,14 +192,31 @@ def onSettingUpdate(data):
207192 if (settings := data .get ('values' )):
208193 handleSettings (settings , False )
209194
195+
196+ if settings :#('Auto Monitor Programs (comma seperated)') != "":
197+ auto_monitor_programs = settings [0 ]['The Programs to Monitor (comma separated)' ]
198+
199+ if auto_monitor_programs != "" :
200+ auto_monitor_programs = auto_monitor_programs .split ("," )
201+ every_X_seconds = 5 ## default to 5 seconds unless specified in settings
202+ if settings [1 ]['Check every x seconds' ] != "" :
203+ every_X_seconds = int (settings [1 ]['Check every x seconds' ])
204+
205+ for x in auto_monitor_programs :
206+ TPC .g_log .info (f"Checking every 5 seconds for { x } " )
207+ the_process = ProcessChecker (x )
208+ if x not in PM .process_monitor_dict .keys ():
209+ th = threading .Thread (target = the_process .check_continuously , args = (every_X_seconds , x , the_process ))
210+ th .start ()
211+
212+
213+
210214
211215
212216
213217@TPC .TPClient .on (TP .TYPES .onAction )
214218def onAction (data ):
215219 TPC .g_log .debug (f"Action: { data } " )
216-
217-
218220 if not (action_data := data .get ('data' )) or not (aid := data .get ('actionId' )):
219221 return
220222
@@ -232,11 +234,8 @@ def onAction(data):
232234 if data ['data' ][0 ]['value' ] not in PM .process_monitor_dict .keys ():
233235
234236 th = threading .Thread (target = the_process .check_continuously , args = (int (data ['data' ][1 ]['value' ]), data ['data' ][0 ]['value' ], the_process ))
235- th .start ()
236-
237-
238- # process_checked = the_process.check_continuously(int(data['data'][1]['value']), data=data['data'][0]['value'], the_process=the_process)
239-
237+ th .start ()
238+
240239
241240 if data ['actionId' ] == PLUGIN_ID + ".act.stop_process.Monitor" :
242241 the_process = data ['data' ][0 ]['value' ]
@@ -260,8 +259,6 @@ def onAction(data):
260259 TPC .g_log .error (f"Error stopping the process: { e } " )
261260
262261
263-
264-
265262def handleSettings (settings , on_connect = False ):
266263 pass
267264
@@ -296,20 +293,12 @@ def plugin_update_check(data):
296293 TPC .g_log .error ("[UPDATE CHECK] Something went wrong checking update" , e )
297294
298295
299-
300-
301296# Shutdown handler
302297@TPC .TPClient .on (TP .TYPES .onShutdown )
303298def onShutdown (data ):
304299 TPC .g_log .info ('Received shutdown event from TP Client.' )
305300
306301
307-
308-
309-
310-
311-
312- ## The Main + Logging System
313302def main ():
314303 ret = 0 # sys.exit() value
315304
@@ -370,13 +359,45 @@ def main():
370359 ret = - 1
371360 finally :
372361 TPC .TPClient .disconnect ()
373-
374362 del TPC .TPClient
375-
376- # TPC.g_log.info(f"{TP_PLUGIN_INFO['name']} stopped.")
377363 return ret
378364
379365
380366if __name__ == "__main__" :
381367 PM = ProcessMonitorData ()
382368 sys .exit (main ())
369+
370+
371+
372+ # import win32gui
373+ # import win32process
374+ # import psutil
375+ #
376+ # process_info = None
377+ # ### have to make a special 'check' in the plugin action to see if process is 'visible' rather than running..
378+ # ## this will help people determine if a browser source is actually open or not.. this needs to be optional as if we take out iswindowvisible then it says msedge is open even though its not..
379+ # def callback(hwnd, app_processes):
380+ # global process_info
381+ # if win32gui.IsWindowVisible(hwnd):
382+ # try:
383+ # pid = win32process.GetWindowThreadProcessId(hwnd)[1]
384+ # process = psutil.Process(pid)
385+ # if process.name().lower() in app_processes:
386+ # process_info = process.as_dict(attrs=['num_threads','pid', 'name', 'username', 'cpu_percent', 'memory_percent', 'cmdline', 'create_time', 'status', 'cwd', 'exe'])
387+ # app_processes[process.name().lower()] = True
388+ #
389+ # except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
390+ # pass
391+ #
392+ # # define the process names for different browsers
393+ # app_process = {'msedge.exe': False}
394+ #
395+ # # loop through all the windows and check if any belong to the browser process
396+ # win32gui.EnumWindows(callback, app_process)
397+ #
398+ # # check if any of the browser processes have an open window
399+ # for process_name, has_window in app_process.items():
400+ # if has_window:
401+ # print(f"Yes, a {process_name} window is open")
402+ # else:
403+ # print(f"No, a {process_name} window is not open")
0 commit comments