Mercurial > code > home > repos > racc
comparison racc.py @ 3:3d7fc94a404a
a few more inputs; daemonize
author | drewp@bigasterisk.com |
---|---|
date | Wed, 08 Mar 2023 10:57:11 -0800 |
parents | 0ecb388a0b90 |
children |
comparison
equal
deleted
inserted
replaced
2:0ecb388a0b90 | 3:3d7fc94a404a |
---|---|
1 from starlette.applications import Starlette | |
2 from starlette.exceptions import HTTPException | |
3 from starlette.requests import Request | |
4 from starlette.responses import JSONResponse, HTMLResponse | |
5 from starlette.staticfiles import StaticFiles | |
6 from starlette.routing import Route, Mount | |
7 from starlette.templating import Jinja2Templates | |
8 import uvicorn | |
9 import psutil | |
10 from starlette_exporter import PrometheusMiddleware, handle_metrics | |
11 from prometheus_client import Summary, Gauge | |
12 import background_loop | |
13 from typing import List, Optional | |
14 import logging | 1 import logging |
2 import os | |
15 import socket | 3 import socket |
16 import sys | 4 import sys |
5 | |
6 import background_loop | |
7 import daemonocle | |
8 import psutil | |
9 import uvicorn | |
10 from prometheus_client import Gauge | |
11 from starlette.applications import Starlette | |
12 from starlette.requests import Request | |
13 from starlette.responses import HTMLResponse | |
14 from starlette.routing import Route | |
15 from starlette.staticfiles import StaticFiles | |
16 from starlette_exporter import PrometheusMiddleware, handle_metrics | |
17 '' | |
18 import progs_all as progs | |
19 | |
17 if psutil.OSX: | 20 if psutil.OSX: |
21 import current_window_title_osx as current_window_title | |
18 import idle_osx as idle | 22 import idle_osx as idle |
23 import power_osx as power | |
19 import volume_osx as volume | 24 import volume_osx as volume |
20 import power_osx as power | |
21 elif psutil.LINUX: | 25 elif psutil.LINUX: |
26 import current_window_title_linux as current_window_title | |
22 import idle_linux as idle | 27 import idle_linux as idle |
28 import power_linux as power | |
23 import volume_linux as volume | 29 import volume_linux as volume |
24 import power_linux as power | |
25 else: | 30 else: |
26 raise NotImplementedError(repr(sys.implementation)) | 31 raise NotImplementedError(repr(sys.implementation)) |
27 | 32 |
28 hostname = socket.gethostname().split('.')[0] | 33 DEBUG = os.environ.get('RACC_DEBUG', False) |
29 logging.basicConfig(level=logging.INFO) | 34 logging.basicConfig(level=logging.INFO) |
30 log = logging.getLogger() | 35 log = logging.getLogger() |
36 hostname = socket.gethostname().split('.')[0] | |
37 RACC_RUNNING = Gauge("racc_running", "program is running", ['host', 'prog']) | |
38 RACC_IDLE = Gauge("racc_idle", "desktop mouse/kb idle seconds", ['host']) | |
39 RACC_SCREEN = Gauge("racc_screen_on", "screen in unlocked/on mode", ['host']) | |
40 RACC_CURRENT_WINDOW = Gauge("racc_current_window", | |
41 "label carries title; site is last part", | |
42 ['host', 'title', 'site']) | |
31 | 43 |
32 | 44 |
33 def progname(cmdline: List[str]) -> Optional[str]: | 45 def update(first_run): |
34 if len(cmdline) < 1: | 46 try: |
35 return None | 47 for p, val in progs.get_running_progs().items(): |
36 if cmdline[-1].endswith('/steam'): | 48 RACC_RUNNING.labels(host=hostname, prog=p).set(val) |
37 return 'steam' | 49 except Exception: |
38 if cmdline[0].endswith('/minecraft-launcher'): | 50 if DEBUG: raise |
39 return 'minecraft-launcher' | |
40 if cmdline[0].endswith('/java') and '--versionType' in cmdline: | |
41 return 'minecraft' | |
42 | 51 |
52 try: | |
53 RACC_IDLE.labels(host=hostname).set(idle.get_idle_seconds()) | |
54 except Exception: | |
55 if DEBUG: raise | |
43 | 56 |
44 RACC_RUNNING = Gauge("racc_running", "program is running", ['host', 'prog']) | 57 try: |
45 RACC_IDLE = Gauge("racc_idle", "desktop mouse/kb idle seconds", ['host']) | 58 RACC_SCREEN.labels(host=hostname).set(power.is_screen_on()) |
46 RACC_SCREEN = Gauge("racc_screen", "screen in unlocked/on mode", ['host']) | 59 except Exception: |
60 if DEBUG: raise | |
47 | 61 |
62 try: | |
63 title = current_window_title.get_current_window_title() | |
64 # chrome, at least on osx, adds icon to window title when it's playing audio | |
65 title = title.rstrip('🔊').strip() | |
66 # some websites choose to title like '<something> - <site name>' | |
67 last_section = title.split(' - ')[-1].strip() | |
68 if last_section in {'YouTube', 'Google Search', 'Google Chrome', 'Visual Studio Code', 'Roblox'}: | |
69 site = last_section.lower().replace(' ', '_') | |
70 else: | |
71 site = '' | |
72 RACC_CURRENT_WINDOW.clear() | |
73 RACC_CURRENT_WINDOW.labels(host=hostname, title=title, | |
74 site=site).set(1) | |
75 except Exception: | |
76 if DEBUG: raise | |
48 | 77 |
49 def update_progs(first_run): | |
50 out = [] | |
51 progs = set() | |
52 for proc in psutil.process_iter(['pid', 'name']): | |
53 try: | |
54 prog = progname(proc.cmdline()) | |
55 if prog: | |
56 progs.add(prog) | |
57 except (psutil.AccessDenied, psutil.NoSuchProcess): | |
58 pass | |
59 | |
60 for p in [ | |
61 'minecraft', | |
62 'minecraft-launcher', | |
63 'steam', | |
64 ]: | |
65 RACC_RUNNING.labels(host=hostname, prog=p).set(p in progs) | |
66 RACC_IDLE.labels(host=hostname).set(idle.get_idle_seconds()) | |
67 RACC_SCREEN.labels(host=hostname).set(power.is_screen_on()) | |
68 | 78 |
69 async def root(req: Request) -> HTMLResponse: | 79 async def root(req: Request) -> HTMLResponse: |
70 vol = await volume.get_volume() | 80 vol = await volume.get_volume() |
71 return HTMLResponse(f'''controls for {hostname} whose volume is {vol}''') | 81 return HTMLResponse(f'''controls for {hostname} whose volume is {vol}''') |
72 | 82 |
75 app = Starlette(debug=True, | 85 app = Starlette(debug=True, |
76 routes=[ | 86 routes=[ |
77 Route('/', root), | 87 Route('/', root), |
78 ], | 88 ], |
79 on_startup=[ | 89 on_startup=[ |
80 lambda: background_loop.loop_forever(update_progs, 3), | 90 lambda: background_loop.loop_forever(update, 5), |
81 ]) | 91 ]) |
82 | 92 |
83 app.add_middleware(PrometheusMiddleware, app_name='racc') | 93 app.add_middleware(PrometheusMiddleware, app_name='racc') |
84 app.add_route("/metrics", handle_metrics) | 94 app.add_route("/metrics", handle_metrics) |
85 return app | 95 uvicorn.run(app, host='0.0.0.0', port=5150) |
86 | 96 |
87 | 97 |
88 if __name__ == "__main__": | 98 if __name__ == "__main__": |
89 uvicorn.run(main(), host='0.0.0.0', port=5150) | 99 d = daemonocle.Daemon( |
100 worker=main, | |
101 work_dir='.', | |
102 pid_file='/tmp/racc.pid', | |
103 detach=True, | |
104 ) | |
105 d.stop(timeout=1, force=True) | |
106 if DEBUG: | |
107 main() | |
108 else: | |
109 d.cli() |