cyberdash 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: cyberdash
3
+ Version: 1.0.1
4
+ Summary: A modern hacker-style TUI dashboard for Linux and Termux
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: textual
8
+ Requires-Dist: psutil
9
+ Requires-Dist: requests
10
+
11
+ # cyberdash
@@ -0,0 +1 @@
1
+ # cyberdash
@@ -0,0 +1,18 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cyberdash"
7
+ version = "1.0.1"
8
+ description = "A modern hacker-style TUI dashboard for Linux and Termux"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ dependencies = [
12
+ "textual",
13
+ "psutil",
14
+ "requests"
15
+ ]
16
+
17
+ [project.scripts]
18
+ cyberdash = "cyberdash.app:run"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,206 @@
1
+ import random, os, socket, asyncio, subprocess
2
+ from collections import deque
3
+ from textual.app import App, ComposeResult
4
+ from textual.containers import Horizontal, Vertical
5
+ from textual.widgets import Header, Footer, Static, DataTable, Label, Input, RichLog, Sparkline
6
+
7
+ # Import Modular Plugins
8
+ from .plugins.base import ResourceBar
9
+ from .plugins.hardware import HardwareMonitor
10
+ from .plugins.network import NetworkTracker
11
+ from .plugins.processes import ProcessEngine
12
+ from .plugins.shell import ShellCore
13
+
14
+ class OverlordApp(App):
15
+ ENABLE_COMMAND_PALETTE = False
16
+
17
+ BINDINGS = [
18
+ ("q", "quit", "Shutdown"),
19
+ ("r", "refresh_net", "Re-trace IP"),
20
+ ("l", "clear_logs", "Wipe Logs"),
21
+ ("h", "help_menu", "Help"),
22
+ ("s", "status_check", "Status"),
23
+ ("k", "kill_process", "Kill Selected"),
24
+ ]
25
+
26
+ CSS = """
27
+ Screen { background: #000; opacity: 0%; }
28
+ .panel { border: double #004400; background: #050505; padding: 1; transition: border 500ms; }
29
+ .glow { border: double #00FF00; }
30
+ #top-stats { height: 7; border: round #333; margin: 1; padding: 1; }
31
+ ResourceBar { width: 1fr; margin: 0 2; }
32
+ ProgressBar > .bar--bar { color: #00FF00 !important; background: #111; }
33
+ ProgressBar > .bar--complete { color: #00FF00 !important; }
34
+ #label { color: #00FF00; margin-bottom: 0; }
35
+ #pct-label { width: 6; margin-left: 1; color: #00FF00; }
36
+ #middle-container { height: 1fr; }
37
+ #left-col { width: 40%; }
38
+ #right-col { width: 60%; }
39
+ DataTable { height: 1fr; background: #050505; color: #00FF00; border: none; }
40
+ DataTable > .datatable--cursor { background: #00FF00; color: #000; }
41
+ RichLog { background: #000; border: tall #111; }
42
+ Input { border: tall #004400; color: #00FF00; height: 3; transition: border 200ms; }
43
+ Input:focus { border: tall #00FF00; }
44
+ #osc-header { height: 1; margin-bottom: 0; }
45
+ #osc-title { width: 1fr; color: #00FF00; }
46
+ #cpu-temp { width: 12; text-align: right; color: #00FF00; }
47
+ #cpu-sparkline { color: #00FF00; height: 3; min-height: 3; margin-top: 0; }
48
+ #net-id { height: 7; margin-bottom: 1; color: #00FF00; }
49
+ Footer { dock: bottom; height: 1; background: #000; color: #00FF00; margin-top: 1; }
50
+ Footer > .footer--key { color: #FFF; background: #000; text-style: bold; }
51
+ Footer > .footer--description { color: #00FF00; background: #000; margin-right: 2; }
52
+ """
53
+
54
+ def __init__(self):
55
+ super().__init__()
56
+ self.hw = HardwareMonitor()
57
+ self.net = NetworkTracker()
58
+ self.proc = ProcessEngine()
59
+ self.shell = ShellCore()
60
+ self.cpu_history = deque([0]*45, maxlen=45)
61
+ self.app_data_cache, self.dns_name = None, "Detecting..."
62
+ self.last_net_key = self.net.get_network_state_key()
63
+ self.HACKER_EVENTS = [
64
+ "[cyan][*][/] Memory optimized.", "[green][+][/] Sync: [b]COMPLETE[/]",
65
+ "[red][!][/] Ping blocked.", "[cyan][*][/] Buffer flushed.",
66
+ "[yellow][!][/] Partition 04: [u]ReadOnly[/]", "[green][+][/] Security: [b]ACTIVE[/]"
67
+ ]
68
+
69
+ def compose(self) -> ComposeResult:
70
+ yield Header()
71
+ with Vertical():
72
+ with Horizontal(id="top-stats"):
73
+ yield ResourceBar("CPU LOAD", id="cpu-bar")
74
+ yield ResourceBar("RAM USAGE", id="ram-bar")
75
+ yield ResourceBar("STORAGE", id="disk-bar")
76
+ with Horizontal(id="middle-container"):
77
+ with Vertical(id="left-col", classes="panel"):
78
+ yield Label("[b][cyan]>_[/] PROCESS ENGINE")
79
+ yield DataTable(id="proc-table")
80
+ yield Input(placeholder="Root Shell Access...", id="cmd-input")
81
+ with Vertical(id="right-col", classes="panel"):
82
+ with Horizontal(id="osc-header"):
83
+ yield Label("[b]CPU OSCILLOSCOPE[/]", id="osc-title")
84
+ yield Label("--°C", id="cpu-temp")
85
+ yield Sparkline(id="cpu-sparkline")
86
+ yield Label("\n[b]NETWORK IDENTITY[/]")
87
+ yield Static("[dim]Requesting Satellite Data...[/]", id="net-id")
88
+ yield Label("\n[b]KERNEL LOGS[/]")
89
+ yield RichLog(id="sys-log", markup=True)
90
+ yield Footer()
91
+
92
+ # --- PROTECTED KILL ACTION ---
93
+ def action_kill_process(self):
94
+ table = self.query_one("#proc-table", DataTable)
95
+ log = self.query_one("#sys-log")
96
+ try:
97
+ row_index = table.cursor_row
98
+ row_data = table.get_row_at(row_index)
99
+ target_pid = int(row_data[0])
100
+ target_name = row_data[1]
101
+
102
+ # --- SUICIDE PROTECTION ---
103
+ my_pid = os.getpid()
104
+ if target_pid == my_pid:
105
+ log.write("[bold red][!][/] ACCESS DENIED: Suicide protocol blocked.\n")
106
+ return
107
+
108
+ # Execute Kill
109
+ self.shell.execute(f"kill -9 {target_pid}")
110
+ log.write(f"[bold red][!][/] SIGKILL SENT TO: {target_name} ({target_pid})\n")
111
+ except:
112
+ log.write("[red][!][/] Selection Error: No target PID found.\n")
113
+
114
+ def update_dashboard(self):
115
+ log = self.query_one("#sys-log")
116
+ table = self.query_one("#proc-table")
117
+ current_cursor = table.cursor_row
118
+
119
+ cpu, ram, disk, is_locked, temp = self.hw.get_stats()
120
+ temp_wid = self.query_one("#cpu-temp")
121
+ if temp:
122
+ t_color = "red" if temp > 70 else "yellow" if temp > 45 else "green"
123
+ temp_wid.update(f"[{t_color}]{temp:.1f}°C[/]")
124
+ else:
125
+ temp_wid.update("[red]N/A[/]")
126
+
127
+ if is_locked:
128
+ self.query_one("#cpu-bar").update_bar(0)
129
+ self.query_one("#cpu-bar").query_one("#pct-label").update("[red]LOCK[/]")
130
+ cpu_val = cpu
131
+ else:
132
+ self.query_one("#cpu-bar").update_bar(cpu)
133
+ cpu_val = cpu
134
+
135
+ self.query_one("#ram-bar").update_bar(ram)
136
+ self.query_one("#disk-bar").update_bar(disk)
137
+ self.cpu_history.append(cpu_val)
138
+ self.query_one("#cpu-sparkline").data = list(self.cpu_history)
139
+
140
+ ms = self.net.get_latency()
141
+ ms_text = f"[bold green]{ms}ms[/]" if ms > 0 else "[red]OFFLINE[/]"
142
+ if self.app_data_cache:
143
+ d = self.app_data_cache
144
+ self.query_one("#net-id").update(f"IP: [y]{d['ip']}[/]\nISP: [y]{d['isp']}[/]\nLOC: [y]{d['loc']}[/]\nDNS: [cyan]{self.dns_name}[/]\nLATENCY: {ms_text}")
145
+
146
+ table.clear()
147
+ for p in self.proc.get_top_processes():
148
+ table.add_row(str(p['pid']), p['name'][:12], f"{p['memory_percent']:.1f}%")
149
+
150
+ if current_cursor is not None and current_cursor < len(table.rows):
151
+ table.move_cursor(row=current_cursor)
152
+
153
+ if random.random() > 0.8:
154
+ log.write(random.choice(self.HACKER_EVENTS) + "\n")
155
+
156
+ async def on_mount(self):
157
+ self.screen.styles.animate("opacity", value=1.0, duration=1.0)
158
+ self.set_interval(1.5, self.pulse_borders)
159
+ table = self.query_one("#proc-table")
160
+ table.add_columns("PID", "NAME", "MEM%")
161
+ table.cursor_type = "row"
162
+ self.set_interval(2.0, self.update_dashboard)
163
+ self.run_worker(self.fetch_network_info, thread=True)
164
+
165
+ async def fetch_network_info(self):
166
+ data, dns = self.net.trace_identity(), self.net.get_dns_provider()
167
+ if data:
168
+ self.app_data_cache, self.dns_name = data, dns
169
+ self.query_one("#net-id").update(f"IP: [y]{data['ip']}[/]\nISP: [y]{data['isp']}[/]\nDNS: [cyan]{dns}[/]")
170
+
171
+ async def on_input_submitted(self, event: Input.Submitted):
172
+ cmd = event.value.strip()
173
+ if not cmd: return
174
+ log = self.query_one("#sys-log")
175
+ event.input.value = ""
176
+ if cmd.lower() in ["clear", "cls"]:
177
+ log.clear()
178
+ return
179
+ log.write(f"[bold cyan]#[/] {cmd}\n")
180
+ self.run_worker(lambda: self.shell_worker(cmd), thread=True)
181
+
182
+ def shell_worker(self, cmd):
183
+ output = self.shell.execute(cmd)
184
+ self.call_from_thread(self.display_output, output)
185
+
186
+ def display_output(self, output):
187
+ log = self.query_one("#sys-log")
188
+ if output:
189
+ for line in output.splitlines()[-8:]: log.write(f" [dim]{line}[/]\n")
190
+ log.scroll_end()
191
+
192
+ def pulse_borders(self):
193
+ for panel in self.query(".panel"): panel.toggle_class("glow")
194
+ def action_refresh_net(self): self.run_worker(self.fetch_network_info, thread=True)
195
+ def action_clear_logs(self): self.query_one("#sys-log").clear()
196
+ def action_help_menu(self): self.query_one("#sys-log").write("[bold yellow]=== COMMAND GUIDE ===[/]\n")
197
+ def action_status_check(self):
198
+ uptime = self.shell.execute("uptime -p")
199
+ self.query_one("#sys-log").write(f"[cyan][*][/] [white]Uptime:[/] {uptime.strip()}\n")
200
+
201
+ def run():
202
+ app = OverlordApp()
203
+ app.run()
204
+
205
+ if __name__ == "__main__":
206
+ run()
@@ -0,0 +1,252 @@
1
+ import random
2
+ import os
3
+ import socket
4
+ import asyncio
5
+ from collections import deque
6
+ from textual.app import App, ComposeResult
7
+ from textual.containers import Horizontal, Vertical
8
+ from textual.widgets import Header, Footer, Static, DataTable, Label, Input, RichLog, Sparkline
9
+
10
+ # Import Modular Plugins
11
+ from plugins.base import ResourceBar
12
+ from plugins.hardware import HardwareMonitor
13
+ from plugins.network import NetworkTracker
14
+ from plugins.processes import ProcessEngine
15
+ from plugins.shell import ShellCore
16
+
17
+ class OverlordApp(App):
18
+ ENABLE_COMMAND_PALETTE = False
19
+
20
+ # User Bindings
21
+ BINDINGS = [
22
+ ("q", "quit", "Shutdown"),
23
+ ("r", "refresh_net", "Re-trace IP"),
24
+ ("l", "clear_logs", "Wipe Logs"),
25
+ ("h", "help_menu", "Help"),
26
+ ("s", "status_check", "Status"),
27
+ ]
28
+
29
+ # Optimized Stealth CSS
30
+ CSS = """
31
+ Screen { background: #000; }
32
+ .panel { border: double #00FF00; background: #050505; padding: 1; }
33
+
34
+ #top-stats { height: 7; border: round #333; margin: 1; padding: 1; }
35
+ ResourceBar { width: 1fr; margin: 0 2; }
36
+
37
+ ProgressBar > .bar--bar { color: #00FF00 !important; background: #111; }
38
+ ProgressBar > .bar--complete { color: #00FF00 !important; }
39
+
40
+ #label { color: #00FF00; margin-bottom: 0; }
41
+ #pct-label { width: 6; margin-left: 1; color: #00FF00; }
42
+
43
+ #middle-container { height: 1fr; }
44
+ #left-col { width: 40%; }
45
+ #right-col { width: 60%; }
46
+
47
+ DataTable { height: 1fr; background: #050505; color: #00FF00; border: none; }
48
+ RichLog { background: #000; border: tall #111; }
49
+
50
+ Input { border: tall #00FF00; color: #00FF00; height: 3; }
51
+ Sparkline { color: #00FF00; height: 3; }
52
+
53
+ #net-id { height: 7; margin-bottom: 1; color: #00FF00; }
54
+
55
+ Footer { dock: bottom; height: 1; background: #000; color: #00FF00; margin-top: 1; }
56
+ Footer > .footer--key { color: #FFF; background: #000; }
57
+ Footer > .footer--description { color: #00FF00; background: #000; margin-right: 2; }
58
+ """
59
+
60
+ def __init__(self):
61
+ super().__init__()
62
+ # Initialize Logic Plugins
63
+ self.hw = HardwareMonitor()
64
+ self.net = NetworkTracker()
65
+ self.proc = ProcessEngine()
66
+ self.shell = ShellCore()
67
+
68
+ # UI & Data State
69
+ self.cpu_history = deque([0]*40, maxlen=40)
70
+ self.app_data_cache = None
71
+ self.dns_name = "Detecting..."
72
+ self.last_net_key = self.net.get_network_state_key()
73
+
74
+ # System Event Templates
75
+ self.HACKER_EVENTS = [
76
+ "[cyan][*][/] Memory clusters optimized.",
77
+ "[green][+][/] Background sync: [bold white]COMPLETE[/]",
78
+ "[red][!][/] Unauthorized ping blocked.",
79
+ "[cyan][*][/] Kernel buffer flushed.",
80
+ "[yellow][!][/] Partition 04: [u]Read-only[/]",
81
+ "[green][+][/] Security protocol: [bold green]ACTIVE[/]",
82
+ "[dim][*] Signal telemetry recalibrated.[/]"
83
+ ]
84
+
85
+ def compose(self) -> ComposeResult:
86
+ yield Header()
87
+ with Vertical():
88
+ # Hardware Status Bar
89
+ with Horizontal(id="top-stats"):
90
+ yield ResourceBar("CPU LOAD", id="cpu-bar")
91
+ yield ResourceBar("RAM USAGE", id="ram-bar")
92
+ yield ResourceBar("STORAGE", id="disk-bar")
93
+
94
+ # Interactive Core
95
+ with Horizontal(id="middle-container"):
96
+ # Shell & Process Side
97
+ with Vertical(id="left-col", classes="panel"):
98
+ yield Label("[b][cyan]>_[/] PROCESS ENGINE")
99
+ yield DataTable(id="proc-table")
100
+ yield Input(placeholder="Root Shell Access...", id="cmd-input")
101
+
102
+ # Diagnostics & Log Side
103
+ with Vertical(id="right-col", classes="panel"):
104
+ yield Label("[b]CPU OSCILLOSCOPE[/]")
105
+ yield Sparkline(id="cpu-sparkline")
106
+
107
+ yield Label("\n[b]NETWORK IDENTITY[/]")
108
+ yield Static("[dim]Requesting Satellite Data...[/]", id="net-id")
109
+
110
+ yield Label("\n[b]KERNEL LOGS[/]")
111
+ yield RichLog(id="sys-log", markup=True)
112
+ yield Footer()
113
+
114
+ # --- ACTIONS (Keyboard Shortcuts) ---
115
+
116
+ def action_help_menu(self):
117
+ log = self.query_one("#sys-log")
118
+ log.write("\n[bold yellow]=== COMMAND GUIDE ===[/]\n")
119
+ log.write("[white]Q[/]: Shutdown | [white]R[/]: IP Trace | [white]L[/]: Wipe Logs\n")
120
+ log.write("[white]H[/]: Help | [white]S[/]: Diagnostics\n")
121
+ log.write("[dim]Terminal: 'clear' to wipe, 'help' for guide.[/]\n")
122
+
123
+ def action_status_check(self):
124
+ log = self.query_one("#sys-log")
125
+ log.write("\n[bold green]>>> INITIATING DIAGNOSTICS...[/]\n")
126
+ uptime = self.shell.execute("uptime -p")
127
+ log.write(f"[cyan][*][/] [white]Uptime:[/] {uptime.strip()}\n")
128
+ log.write(f"[cyan][*][/] [white]DNS Service:[/] {self.dns_name}\n")
129
+ log.write("[bold green]>>> DIAGNOSTICS COMPLETE.[/]\n")
130
+
131
+ def action_refresh_net(self):
132
+ self.query_one("#net-id").update("[dim]Re-tracing...[/]")
133
+ self.run_worker(self.fetch_network_info, thread=True)
134
+
135
+ def action_clear_logs(self):
136
+ self.query_one("#sys-log").clear()
137
+
138
+ # --- CORE WORKERS & UPDATERS ---
139
+
140
+ def on_mount(self):
141
+ log = self.query_one("#sys-log")
142
+ self.query_one("#proc-table").add_columns("PID", "NAME", "MEM%")
143
+
144
+ # Initial Boot UI
145
+ log.write("[yellow][!][/] KERNEL BOOT SEQUENCE INITIATED...\n")
146
+ log.write("[cyan][*][/] MODULE: HardwareMonitor... [green]LOADED[/]\n")
147
+ log.write("[cyan][*][/] MODULE: NetworkTracker... [green]LOADED[/]\n")
148
+ log.write("[white][!][/] SYSTEM OVERLORD [bold green]ONLINE[/].\n")
149
+
150
+ # Start intervals
151
+ self.set_interval(2.0, self.update_dashboard)
152
+ self.run_worker(self.fetch_network_info, thread=True)
153
+
154
+ async def fetch_network_info(self):
155
+ """Fetch heavy network data (IP/ISP/DNS) in background."""
156
+ data = self.net.trace_identity()
157
+ dns = self.net.get_dns_provider()
158
+ if data:
159
+ self.app_data_cache = data
160
+ self.dns_name = dns
161
+ val = f"IP: [y]{data['ip']}[/]\nISP: [y]{data['isp']}[/]\nDNS: [cyan]{dns}[/]"
162
+ self.query_one("#net-id").update(val)
163
+
164
+ def update_dashboard(self):
165
+ """Main refresh loop for hardware and live latency."""
166
+ log = self.query_one("#sys-log")
167
+
168
+ # 1. AUTO-REFRESH NETWORK ON CONNECTIVITY CHANGE
169
+ current_net_key = self.net.get_network_state_key()
170
+ if current_net_key != self.last_net_key:
171
+ self.last_net_key = current_net_key
172
+ log.write("[yellow][!][/] Network change detected. Re-tracing...\n")
173
+ self.run_worker(self.fetch_network_info, thread=True)
174
+
175
+ # 2. Hardware Resource Update
176
+ cpu, ram, disk = self.hw.get_stats()
177
+ if cpu == -1.0:
178
+ self.query_one("#cpu-bar").update_bar(0)
179
+ self.query_one("#cpu-bar").query_one("#pct-label").update("[red]LOCK[/]")
180
+ cpu_val = 0
181
+ else:
182
+ self.query_one("#cpu-bar").update_bar(cpu)
183
+ cpu_val = cpu
184
+
185
+ self.query_one("#ram-bar").update_bar(ram)
186
+ self.query_one("#disk-bar").update_bar(disk)
187
+
188
+ # Sparkline update
189
+ self.cpu_history.append(cpu_val)
190
+ self.query_one("#cpu-sparkline").data = list(self.cpu_history)
191
+
192
+ # 3. Network Latency Update (LIVE)
193
+ ms = self.net.get_latency()
194
+ latency_color = "green" if ms < 100 else "yellow" if ms < 300 else "red"
195
+ ms_text = f"[bold {latency_color}]{ms}ms[/]" if ms > 0 else "[red]OFFLINE[/]"
196
+
197
+ if self.app_data_cache:
198
+ d = self.app_data_cache
199
+ self.query_one("#net-id").update(
200
+ f"IP: [y]{d['ip']}[/]\nISP: [y]{d['isp']}[/]\n"
201
+ f"LOC: [y]{d['loc']}[/]\nDNS: [cyan]{self.dns_name}[/]\n"
202
+ f"LATENCY: {ms_text}"
203
+ )
204
+ elif ms == -1:
205
+ self.query_one("#net-id").update("[red]NO INTERNET CONNECTION[/]")
206
+
207
+ # 4. Process Table Update
208
+ table = self.query_one("#proc-table")
209
+ table.clear()
210
+ for p in self.proc.get_top_processes():
211
+ table.add_row(str(p['pid']), p['name'][:12], f"{p['memory_percent']:.1f}%")
212
+
213
+ # 5. Background Activity Logs
214
+ if random.random() > 0.8:
215
+ log.write(random.choice(self.HACKER_EVENTS) + "\n")
216
+
217
+ async def on_input_submitted(self, event: Input.Submitted):
218
+ cmd = event.value.strip()
219
+ log = self.query_one("#sys-log")
220
+ event.input.value = ""
221
+
222
+ if not cmd: return
223
+
224
+ if cmd.lower() in ["clear", "cls"]:
225
+ log.clear()
226
+ return
227
+
228
+ if cmd.lower() == "help":
229
+ self.action_help_menu()
230
+ return
231
+
232
+ # NEW: Run the shell command in a background worker
233
+ log.write(f"[bold cyan]#[/] {cmd}\n")
234
+
235
+ # We define a small internal function to run the command
236
+ def run_shell():
237
+ output = self.shell.execute(cmd)
238
+ # Use call_from_thread to safely update the UI from the worker
239
+ self.call_from_thread(self.post_shell_output, output)
240
+
241
+ self.run_worker(run_shell, thread=True)
242
+
243
+ def post_shell_output(self, output: str):
244
+ """Callback to print the result once the background task finishes."""
245
+ log = self.query_one("#sys-log")
246
+ if output:
247
+ for line in output.splitlines()[-15:]: # Increased to 15 lines
248
+ log.write(f" [dim]{line}[/]\n")
249
+ log.scroll_end()
250
+
251
+ if __name__ == "__main__":
252
+ OverlordApp().run()
File without changes
@@ -0,0 +1,23 @@
1
+ from textual.widgets import Static, Label, ProgressBar
2
+ from textual.containers import Horizontal
3
+
4
+ class ResourceBar(Static):
5
+ def __init__(self, name: str, **kwargs):
6
+ super().__init__(**kwargs)
7
+ self.resource_name = name
8
+
9
+ def compose(self):
10
+ yield Label(f"[b]{self.resource_name}[/]", id="label")
11
+ with Horizontal():
12
+ yield ProgressBar(total=100, show_eta=False, show_percentage=False, id="bar")
13
+ yield Label("0%", id="pct-label")
14
+
15
+ def update_bar(self, value: float):
16
+ try:
17
+ bar = self.query_one("#bar", ProgressBar)
18
+ # FIX: Animate the 'progress' attribute of the bar widget directly
19
+ bar.animate("progress", value=float(value), duration=0.5)
20
+ # Update the text label
21
+ self.query_one("#pct-label").update(f"{int(value)}%")
22
+ except:
23
+ pass
@@ -0,0 +1,65 @@
1
+ import psutil, shutil, os, random, subprocess, json
2
+ # At the top:
3
+ from .base import ResourceBar
4
+
5
+ class HardwareMonitor:
6
+ def __init__(self):
7
+ try:
8
+ psutil.cpu_percent(interval=None)
9
+ except:
10
+ pass
11
+
12
+ def get_cpu_temp(self):
13
+ """Tries 3 methods to get temperature: Real, Battery, or Estimated."""
14
+ # --- Method 1: Real Hardware Sensors (SysFS) ---
15
+ for i in range(15):
16
+ path = f"/sys/class/thermal/thermal_zone{i}/temp"
17
+ try:
18
+ if os.path.exists(path):
19
+ with open(path, "r") as f:
20
+ t = int(f.read().strip())
21
+ if t > 1000: t /= 1000
22
+ if 25 < t < 95: return t
23
+ except: continue
24
+
25
+ # --- Method 2: Termux-API (Battery Temp Proxy) ---
26
+ try:
27
+ # This calls the termux-api command
28
+ res = subprocess.check_output(["termux-battery-status"], timeout=1)
29
+ data = json.loads(res)
30
+ return data.get("temperature") # Returns float like 38.5
31
+ except:
32
+ pass
33
+
34
+ # --- Method 3: Thermal Proxy (Simulated based on Load) ---
35
+ # If we can't read anything, we simulate 'System Warmth'
36
+ # based on current CPU usage to keep the dashboard 'alive'.
37
+ try:
38
+ load = psutil.cpu_percent()
39
+ # Base temp 32C + (Load * 0.2) + random fluctuation
40
+ estimate = 32 + (load * 0.15) + random.uniform(-1, 1)
41
+ return estimate
42
+ except:
43
+ return random.uniform(35, 42)
44
+
45
+ def get_stats(self):
46
+ try:
47
+ cpu = psutil.cpu_percent(interval=0.1)
48
+ is_locked = False
49
+ except PermissionError:
50
+ cpu = random.uniform(1.0, 5.0)
51
+ is_locked = True
52
+
53
+ try:
54
+ ram = psutil.virtual_memory().percent
55
+ except:
56
+ ram = 0.0
57
+
58
+ try:
59
+ usage = shutil.disk_usage(os.path.expanduser("~"))
60
+ disk = (usage.used / usage.total) * 100
61
+ except:
62
+ disk = 0.0
63
+
64
+ temp = self.get_cpu_temp()
65
+ return cpu, ram, disk, is_locked, temp
@@ -0,0 +1,59 @@
1
+ import requests
2
+
3
+ class NetworkTracker:
4
+ def trace_identity(self):
5
+ headers = {"User-Agent": "TermuxCyberDash/1.2"}
6
+ # List of URLs to try
7
+ urls = ["https://ipapi.co/json/", "http://ip-api.com/json/"]
8
+
9
+ for url in urls:
10
+ try:
11
+ # Short 3-second timeout to prevent UI hanging
12
+ res = requests.get(url, headers=headers, timeout=3).json()
13
+ if res:
14
+ return {
15
+ "ip": res.get('ip') or res.get('query'),
16
+ "isp": res.get('org') or res.get('isp'),
17
+ "loc": f"{res.get('city')}, {res.get('country_name') or res.get('country')}"
18
+ }
19
+ except:
20
+ continue
21
+ return None
22
+
23
+ def get_latency(self):
24
+ """Measure latency (ms) to Cloudflare DNS."""
25
+ target = "1.1.1.1"
26
+ try:
27
+ start = time.time()
28
+ # Try a TCP connection to port 53 (DNS)
29
+ socket.create_connection((target, 53), timeout=2)
30
+ end = time.time()
31
+ return int((end - start) * 1000)
32
+ except:
33
+ return -1
34
+
35
+ def get_dns_provider(self):
36
+ """Identify current DNS provider."""
37
+ try:
38
+ # On Termux/Android, we check getprop
39
+ dns_ip = subprocess.getoutput("getprop net.dns1").strip()
40
+ if not dns_ip:
41
+ # Fallback for standard Linux
42
+ with open("/etc/resolv.conf", "r") as f:
43
+ for line in f:
44
+ if line.startswith("nameserver"):
45
+ dns_ip = line.split()[1]
46
+ break
47
+
48
+ # Map common IPs to names
49
+ mapping = {
50
+ "1.1.1.1": "Cloudflare",
51
+ "1.0.0.1": "Cloudflare",
52
+ "8.8.8.8": "Google Public DNS",
53
+ "8.8.4.4": "Google Public DNS",
54
+ "9.9.9.9": "Quad9",
55
+ "208.67.222.222": "OpenDNS"
56
+ }
57
+ return mapping.get(dns_ip, dns_ip if dns_ip else "System Default")
58
+ except:
59
+ return "Internal/Protected"
@@ -0,0 +1,80 @@
1
+ import requests
2
+ import socket
3
+ import time
4
+ import subprocess
5
+ import psutil
6
+
7
+ class NetworkTracker:
8
+ def trace_identity(self):
9
+ """Fetch ISP/IP info."""
10
+ headers = {"User-Agent": "TermuxCyberDash/1.2"}
11
+ try:
12
+ res = requests.get("https://ipapi.co/json/", headers=headers, timeout=5).json()
13
+ if res and not res.get("error"):
14
+ return {
15
+ "ip": res.get('ip') or res.get('query'),
16
+ "isp": res.get('org') or res.get('isp'),
17
+ "loc": f"{res.get('city')}, {res.get('country_name') or res.get('country')}"
18
+ }
19
+ except:
20
+ pass
21
+ return None
22
+
23
+ def get_network_state_key(self):
24
+ """
25
+ Creates a fingerprint of the network.
26
+ Now watches local IPs AND System DNS properties.
27
+ """
28
+ key = ""
29
+ try:
30
+ # 1. Watch Local IP changes (Wi-Fi vs Data)
31
+ interfaces = psutil.net_if_addrs()
32
+ for snic in interfaces:
33
+ for addr in interfaces[snic]:
34
+ key += str(addr.address)
35
+
36
+ # 2. Watch Android DNS properties
37
+ # If you switch Private DNS, these properties often change
38
+ key += subprocess.getoutput("getprop net.dns1")
39
+ key += subprocess.getoutput("getprop net.dns2")
40
+
41
+ return key
42
+ except:
43
+ return key
44
+
45
+ def get_latency(self):
46
+ """Measure latency (ms)."""
47
+ targets = [("1.1.1.1", 53), ("8.8.8.8", 53), ("google.com", 80)]
48
+ for host, port in targets:
49
+ try:
50
+ start = time.time()
51
+ socket.create_connection((host, port), timeout=2)
52
+ end = time.time()
53
+ return int((end - start) * 1000)
54
+ except:
55
+ continue
56
+ return -1
57
+
58
+ def get_dns_provider(self):
59
+ """Identifies the DNS provider handling your traffic."""
60
+ try:
61
+ # Check Cloudflare's diagnostic trace (Very fast)
62
+ trace = requests.get("https://1.1.1.1/cdn-cgi/trace", timeout=2).text
63
+ if "dns=on" in trace: return "Cloudflare (1.1.1.1)"
64
+ if "warp=on" in trace: return "Cloudflare WARP"
65
+ except:
66
+ pass
67
+
68
+ try:
69
+ # Server-side DNS leak probe
70
+ res = requests.get("http://edns.ip-api.com/json", timeout=2).json()
71
+ dns_data = res.get("dns", {})
72
+ geo = dns_data.get("geo", "")
73
+ if "Cloudflare" in geo: return "Cloudflare DNS"
74
+ if "Google" in geo: return "Google DNS"
75
+ if "OpenDNS" in geo: return "OpenDNS"
76
+
77
+ ip = dns_data.get("ip", "")
78
+ return f"Resolver ({ip[:15]})" if ip else "ISP Default"
79
+ except:
80
+ return "ISP / Private DNS"
@@ -0,0 +1,12 @@
1
+ import psutil
2
+
3
+ class ProcessEngine:
4
+ def get_top_processes(self, limit=8):
5
+ procs = []
6
+ for p in psutil.process_iter(['pid', 'name', 'memory_percent']):
7
+ try:
8
+ if p.info['name'] and p.info['memory_percent'] is not None:
9
+ procs.append(p.info)
10
+ except (psutil.AccessDenied, psutil.NoSuchProcess):
11
+ continue
12
+ return sorted(procs, key=lambda x: x['memory_percent'], reverse=True)[:limit]
@@ -0,0 +1,18 @@
1
+ import subprocess
2
+
3
+ class ShellCore:
4
+ def execute(self, cmd: str):
5
+ try:
6
+ # Increased timeout to 300 seconds (5 minutes) for big tasks like 'pkg up'
7
+ result = subprocess.run(
8
+ cmd,
9
+ shell=True,
10
+ capture_output=True,
11
+ text=True,
12
+ timeout=300
13
+ )
14
+ return result.stdout if result.stdout else result.stderr
15
+ except subprocess.TimeoutExpired:
16
+ return "Error: Command timed out. Task is too large for dashboard shell."
17
+ except Exception as e:
18
+ return f"Error: {str(e)}"
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: cyberdash
3
+ Version: 1.0.1
4
+ Summary: A modern hacker-style TUI dashboard for Linux and Termux
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: textual
8
+ Requires-Dist: psutil
9
+ Requires-Dist: requests
10
+
11
+ # cyberdash
@@ -0,0 +1,17 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/cyberdash/app.py
4
+ src/cyberdash/m.py
5
+ src/cyberdash.egg-info/PKG-INFO
6
+ src/cyberdash.egg-info/SOURCES.txt
7
+ src/cyberdash.egg-info/dependency_links.txt
8
+ src/cyberdash.egg-info/entry_points.txt
9
+ src/cyberdash.egg-info/requires.txt
10
+ src/cyberdash.egg-info/top_level.txt
11
+ src/cyberdash/plugins/__init__.py
12
+ src/cyberdash/plugins/base.py
13
+ src/cyberdash/plugins/hardware.py
14
+ src/cyberdash/plugins/n.py
15
+ src/cyberdash/plugins/network.py
16
+ src/cyberdash/plugins/processes.py
17
+ src/cyberdash/plugins/shell.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cyberdash = cyberdash.app:run
@@ -0,0 +1,3 @@
1
+ textual
2
+ psutil
3
+ requests
@@ -0,0 +1 @@
1
+ cyberdash