tamagonion 0.0.0__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.
- tamagonion-0.0.0/PKG-INFO +125 -0
- tamagonion-0.0.0/README.md +114 -0
- tamagonion-0.0.0/pyproject.toml +21 -0
- tamagonion-0.0.0/pyproject.toml.orig +20 -0
- tamagonion-0.0.0/src/tamagonion/__init__.py +48 -0
- tamagonion-0.0.0/src/tamagonion/app_data.py +152 -0
- tamagonion-0.0.0/src/tamagonion/art.py +276 -0
- tamagonion-0.0.0/src/tamagonion/draw.py +140 -0
- tamagonion-0.0.0/src/tamagonion/relay_manager.py +33 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: tamagonion
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: This is Stinky, he's running your Tor relay. He mostly loafs around and browses the Web anonymously, but he's more entertaining to look at than a graph!
|
|
5
|
+
Author: Samuel De Grace
|
|
6
|
+
Author-email: Samuel De Grace <48831884+daxAKAhackerman@users.noreply.github.com>
|
|
7
|
+
Requires-Dist: psutil==7.2.2
|
|
8
|
+
Requires-Dist: stem==1.8.2
|
|
9
|
+
Requires-Python: ==3.14.*
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# Tamagonion
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
This is Stinky, it's a Tamagonion and it's running your Tor relay. Tamagonions are cyber creatures that have bravely stepped out of the Tor network to learn what life is like in the clearnet and to help you monitor your relay. It mostly loafs around and browses the Web anonymously, but it's more entertaining to look at than a graph!
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
▌▌
|
|
20
|
+
▓▓▀
|
|
21
|
+
▄▄▄▀▀▀▀▄▄▄
|
|
22
|
+
█ ─ ─ █
|
|
23
|
+
█ ▄ ▄ █
|
|
24
|
+
█ ▀ ▀ █
|
|
25
|
+
█ ── █
|
|
26
|
+
█ █
|
|
27
|
+
█▄▄▄▄▄▄▄▄▄▄▄▄█
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Activation
|
|
31
|
+
|
|
32
|
+
To activate the viewport, use the `tamagonion` command as such:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# By default, we will try to connect to the control port at 127.0.0.1:9051
|
|
36
|
+
$ uv run tamagonion
|
|
37
|
+
|
|
38
|
+
# If you use password authentication, you can supply it with -P or with the TAMAGONION_PASSWORD environment variable
|
|
39
|
+
$ uv run tamagonion -P my_password
|
|
40
|
+
$ TAMAGONION_PASSWORD=my_password uv run tamagonion
|
|
41
|
+
|
|
42
|
+
# If the port is listening somewhere else, you can specify it with -i and -p
|
|
43
|
+
$ uv run tamagonion -i 192.168.0.10 -p 9999
|
|
44
|
+
|
|
45
|
+
# If you are connecting using a Unix socket, you can point to it with -s
|
|
46
|
+
$ uv run tamagonion -s /var/run/tor/control
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Status check
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
┌──────────────────────────────────────────────┐
|
|
53
|
+
│Relay nickname: exitafpwrwmm │
|
|
54
|
+
│Uptime: 0d 05h 53m 06s │
|
|
55
|
+
│Connections (N/L/Co/F/Cl): 0,0,7,0,0 │
|
|
56
|
+
│Download (Cur/Avg/Tot): 338B / 116B / 2MB │
|
|
57
|
+
│Upload (Cur/Avg/Tot): 1KB / 152B / 3MB │
|
|
58
|
+
│Version: 0.4.9.11 (none recommended) │
|
|
59
|
+
│Process info (CPU/Mem): 0.0%/867MB │
|
|
60
|
+
│ ░░░░░░░░ │
|
|
61
|
+
│ ░╔══════╗░ █▄ │
|
|
62
|
+
│ ▌▌ z ░║ EXIT ║░ █████ │
|
|
63
|
+
│ ▓▓▀ Z ░╚══════╝░ █▀ │
|
|
64
|
+
│ ▄▄▄▀▀▀▀▄▄▄ Z ░░░░░░░░ │
|
|
65
|
+
│ █ ─ ─ ▄▄▄▄▄ │
|
|
66
|
+
│ █ █ █ █ │
|
|
67
|
+
│ █ ── ── █ ▀█▀ █ ╓───┬───╖╤═══╕ │
|
|
68
|
+
│ ____ █ ∙ █ █ █ ║---│---║╧╤══╧╤ │
|
|
69
|
+
││oV2o│█ ▀▀▀▀▀ ║---│---║╒╧══╤╧ │
|
|
70
|
+
│╘════╛ █▄▄▄▄▄▄▄▄▄▄▄▄█ ╙───┴───╜╘═══╧ │
|
|
71
|
+
└───────────────────────────Hit ctrl+c to exit─┘
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The top of the viewport contains various information about your relay. More specifically:
|
|
75
|
+
|
|
76
|
+
- The relay nickname as defined in your Tor configuration
|
|
77
|
+
- The uptime since the Tor daemon has started
|
|
78
|
+
- The number of connections
|
|
79
|
+
- NEW: We have received a new incoming OR connection, and are starting the server-side handshake.
|
|
80
|
+
- LAUNCHED: We have launched a new outgoing OR connection, and are starting the client-side handshake.
|
|
81
|
+
- CONNECTED: The OR connection has been connected and the handshake is done.
|
|
82
|
+
- FAILED: Our attempt to open the OR connection failed.
|
|
83
|
+
- CLOSED: The OR connection closed in an unremarkable way.
|
|
84
|
+
- The current, average and total amount of data that was downloaded and uploaded
|
|
85
|
+
- The version of the Tor binary, as well as the recommendation from the consensus (if any)
|
|
86
|
+
- If you are connected locally (localhost or Unix socket), the CPU and memory usage of the Tor daemon
|
|
87
|
+
|
|
88
|
+
## Biology
|
|
89
|
+
|
|
90
|
+
Tamagonions are very atuned to a relay's operation. You can know how your relay is going by looking at how Stinky is feeling or behaving.
|
|
91
|
+
|
|
92
|
+
| Behavior | Meaning |
|
|
93
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| Asleep | The relay is "dormant", meaning it has gone idle due to lack of use or some similar reason. |
|
|
95
|
+
| Sad | The relay doesn't have the both the "Valid" and the "Running" flags, or it has the "NoEdConsensus" flag. |
|
|
96
|
+
| Confused | The relay discarded expired statuses and server descriptors to fall below the desired threshold of directory information. |
|
|
97
|
+
| Embarassed | A directory authority rejected our descriptor. |
|
|
98
|
+
| Looking away | The relay's ORPort is unreachable. |
|
|
99
|
+
| Bored | The relay has the "MiddleOnly" flag. |
|
|
100
|
+
| Glitchy | The relay doesn't have the "Stable" flag. |
|
|
101
|
+
| Old | The relay has the "StaleDesc" flag. |
|
|
102
|
+
| Egg | The relay is bootstrapping. |
|
|
103
|
+
| Slow | The relay doesn't have the "Fast" flag. |
|
|
104
|
+
|
|
105
|
+
## Room status check
|
|
106
|
+
|
|
107
|
+
Stinky's room can get a bit crowded with things it collected along the way. But every thing it collects means something special to it.
|
|
108
|
+
|
|
109
|
+
| Object or room state | Meaning |
|
|
110
|
+
| -------------------- | ---------------------------------------------- |
|
|
111
|
+
| Light's out | The relay believes the network is unreachable. |
|
|
112
|
+
| Exit sign | The relay has the "Exit" flag. |
|
|
113
|
+
| Broken exit sign | The relay has the "BadExit" flag. |
|
|
114
|
+
| Books | The relay has the "HSDir" flag. |
|
|
115
|
+
| Cassette | The relay has the "V2Dir" flag. |
|
|
116
|
+
| Shield | The relay has the "Guard" flag. |
|
|
117
|
+
|
|
118
|
+
## You may also like...
|
|
119
|
+
|
|
120
|
+
- [Testing Tor Network](https://github.com/daxAKAhackerman/testing-tor-network) - CLI tool to setup a testing TOR network with Docker
|
|
121
|
+
- [Tor HashedControlPassword Brute](https://github.com/daxAKAhackerman/tor-hashed-control-password-brute) - C program to execute a dictionary attack against a Tor HashedControlPassword value
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
> This repository contains no AI-generated code and was made with :heart: by [@daxAKAhackerman](https://github.com/daxAKAhackerman/)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Tamagonion
|
|
2
|
+
|
|
3
|
+
## Instructions
|
|
4
|
+
|
|
5
|
+
This is Stinky, it's a Tamagonion and it's running your Tor relay. Tamagonions are cyber creatures that have bravely stepped out of the Tor network to learn what life is like in the clearnet and to help you monitor your relay. It mostly loafs around and browses the Web anonymously, but it's more entertaining to look at than a graph!
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
▌▌
|
|
9
|
+
▓▓▀
|
|
10
|
+
▄▄▄▀▀▀▀▄▄▄
|
|
11
|
+
█ ─ ─ █
|
|
12
|
+
█ ▄ ▄ █
|
|
13
|
+
█ ▀ ▀ █
|
|
14
|
+
█ ── █
|
|
15
|
+
█ █
|
|
16
|
+
█▄▄▄▄▄▄▄▄▄▄▄▄█
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Activation
|
|
20
|
+
|
|
21
|
+
To activate the viewport, use the `tamagonion` command as such:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# By default, we will try to connect to the control port at 127.0.0.1:9051
|
|
25
|
+
$ uv run tamagonion
|
|
26
|
+
|
|
27
|
+
# If you use password authentication, you can supply it with -P or with the TAMAGONION_PASSWORD environment variable
|
|
28
|
+
$ uv run tamagonion -P my_password
|
|
29
|
+
$ TAMAGONION_PASSWORD=my_password uv run tamagonion
|
|
30
|
+
|
|
31
|
+
# If the port is listening somewhere else, you can specify it with -i and -p
|
|
32
|
+
$ uv run tamagonion -i 192.168.0.10 -p 9999
|
|
33
|
+
|
|
34
|
+
# If you are connecting using a Unix socket, you can point to it with -s
|
|
35
|
+
$ uv run tamagonion -s /var/run/tor/control
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Status check
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
┌──────────────────────────────────────────────┐
|
|
42
|
+
│Relay nickname: exitafpwrwmm │
|
|
43
|
+
│Uptime: 0d 05h 53m 06s │
|
|
44
|
+
│Connections (N/L/Co/F/Cl): 0,0,7,0,0 │
|
|
45
|
+
│Download (Cur/Avg/Tot): 338B / 116B / 2MB │
|
|
46
|
+
│Upload (Cur/Avg/Tot): 1KB / 152B / 3MB │
|
|
47
|
+
│Version: 0.4.9.11 (none recommended) │
|
|
48
|
+
│Process info (CPU/Mem): 0.0%/867MB │
|
|
49
|
+
│ ░░░░░░░░ │
|
|
50
|
+
│ ░╔══════╗░ █▄ │
|
|
51
|
+
│ ▌▌ z ░║ EXIT ║░ █████ │
|
|
52
|
+
│ ▓▓▀ Z ░╚══════╝░ █▀ │
|
|
53
|
+
│ ▄▄▄▀▀▀▀▄▄▄ Z ░░░░░░░░ │
|
|
54
|
+
│ █ ─ ─ ▄▄▄▄▄ │
|
|
55
|
+
│ █ █ █ █ │
|
|
56
|
+
│ █ ── ── █ ▀█▀ █ ╓───┬───╖╤═══╕ │
|
|
57
|
+
│ ____ █ ∙ █ █ █ ║---│---║╧╤══╧╤ │
|
|
58
|
+
││oV2o│█ ▀▀▀▀▀ ║---│---║╒╧══╤╧ │
|
|
59
|
+
│╘════╛ █▄▄▄▄▄▄▄▄▄▄▄▄█ ╙───┴───╜╘═══╧ │
|
|
60
|
+
└───────────────────────────Hit ctrl+c to exit─┘
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The top of the viewport contains various information about your relay. More specifically:
|
|
64
|
+
|
|
65
|
+
- The relay nickname as defined in your Tor configuration
|
|
66
|
+
- The uptime since the Tor daemon has started
|
|
67
|
+
- The number of connections
|
|
68
|
+
- NEW: We have received a new incoming OR connection, and are starting the server-side handshake.
|
|
69
|
+
- LAUNCHED: We have launched a new outgoing OR connection, and are starting the client-side handshake.
|
|
70
|
+
- CONNECTED: The OR connection has been connected and the handshake is done.
|
|
71
|
+
- FAILED: Our attempt to open the OR connection failed.
|
|
72
|
+
- CLOSED: The OR connection closed in an unremarkable way.
|
|
73
|
+
- The current, average and total amount of data that was downloaded and uploaded
|
|
74
|
+
- The version of the Tor binary, as well as the recommendation from the consensus (if any)
|
|
75
|
+
- If you are connected locally (localhost or Unix socket), the CPU and memory usage of the Tor daemon
|
|
76
|
+
|
|
77
|
+
## Biology
|
|
78
|
+
|
|
79
|
+
Tamagonions are very atuned to a relay's operation. You can know how your relay is going by looking at how Stinky is feeling or behaving.
|
|
80
|
+
|
|
81
|
+
| Behavior | Meaning |
|
|
82
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------- |
|
|
83
|
+
| Asleep | The relay is "dormant", meaning it has gone idle due to lack of use or some similar reason. |
|
|
84
|
+
| Sad | The relay doesn't have the both the "Valid" and the "Running" flags, or it has the "NoEdConsensus" flag. |
|
|
85
|
+
| Confused | The relay discarded expired statuses and server descriptors to fall below the desired threshold of directory information. |
|
|
86
|
+
| Embarassed | A directory authority rejected our descriptor. |
|
|
87
|
+
| Looking away | The relay's ORPort is unreachable. |
|
|
88
|
+
| Bored | The relay has the "MiddleOnly" flag. |
|
|
89
|
+
| Glitchy | The relay doesn't have the "Stable" flag. |
|
|
90
|
+
| Old | The relay has the "StaleDesc" flag. |
|
|
91
|
+
| Egg | The relay is bootstrapping. |
|
|
92
|
+
| Slow | The relay doesn't have the "Fast" flag. |
|
|
93
|
+
|
|
94
|
+
## Room status check
|
|
95
|
+
|
|
96
|
+
Stinky's room can get a bit crowded with things it collected along the way. But every thing it collects means something special to it.
|
|
97
|
+
|
|
98
|
+
| Object or room state | Meaning |
|
|
99
|
+
| -------------------- | ---------------------------------------------- |
|
|
100
|
+
| Light's out | The relay believes the network is unreachable. |
|
|
101
|
+
| Exit sign | The relay has the "Exit" flag. |
|
|
102
|
+
| Broken exit sign | The relay has the "BadExit" flag. |
|
|
103
|
+
| Books | The relay has the "HSDir" flag. |
|
|
104
|
+
| Cassette | The relay has the "V2Dir" flag. |
|
|
105
|
+
| Shield | The relay has the "Guard" flag. |
|
|
106
|
+
|
|
107
|
+
## You may also like...
|
|
108
|
+
|
|
109
|
+
- [Testing Tor Network](https://github.com/daxAKAhackerman/testing-tor-network) - CLI tool to setup a testing TOR network with Docker
|
|
110
|
+
- [Tor HashedControlPassword Brute](https://github.com/daxAKAhackerman/tor-hashed-control-password-brute) - C program to execute a dictionary attack against a Tor HashedControlPassword value
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
> This repository contains no AI-generated code and was made with :heart: by [@daxAKAhackerman](https://github.com/daxAKAhackerman/)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tamagonion"
|
|
3
|
+
version = "0.0.0"
|
|
4
|
+
description = "This is Stinky, he's running your Tor relay. He mostly loafs around and browses the Web anonymously, but he's more entertaining to look at than a graph!"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = "==3.14.*"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"psutil==7.2.2",
|
|
9
|
+
"stem==1.8.2",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[[project.authors]]
|
|
13
|
+
name = "Samuel De Grace"
|
|
14
|
+
email = "48831884+daxAKAhackerman@users.noreply.github.com"
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
tamagonion = "tamagonion:main"
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["uv_build==0.12.3"]
|
|
21
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tamagonion"
|
|
3
|
+
version = "0.0.0"
|
|
4
|
+
description = "This is Stinky, he's running your Tor relay. He mostly loafs around and browses the Web anonymously, but he's more entertaining to look at than a graph!"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Samuel De Grace", email = "48831884+daxAKAhackerman@users.noreply.github.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = "==3.14.*"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"psutil==7.2.2",
|
|
12
|
+
"stem==1.8.2",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
tamagonion = "tamagonion:main"
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["uv_build==0.12.3"]
|
|
20
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
from time import sleep
|
|
4
|
+
|
|
5
|
+
from tamagonion.app_data import AppData
|
|
6
|
+
from tamagonion.draw import Paper
|
|
7
|
+
from tamagonion.relay_manager import RelayManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main() -> None:
|
|
11
|
+
args = get_args()
|
|
12
|
+
|
|
13
|
+
print("\033[2J\033[H", end="") # Clear the screen and reset cursor
|
|
14
|
+
relay_manager = RelayManager(ip_addr=args.ip, port=args.port, password=args.password, socket_file=args.socket_file)
|
|
15
|
+
app_data = AppData(relay_manager)
|
|
16
|
+
paper = Paper(app_data)
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
while True:
|
|
20
|
+
app_data.update()
|
|
21
|
+
paper.draw()
|
|
22
|
+
sleep(1)
|
|
23
|
+
except KeyboardInterrupt:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
paper.erase(include_frame=True)
|
|
27
|
+
sys.exit(0)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_args() -> argparse.Namespace:
|
|
31
|
+
parser = argparse.ArgumentParser(
|
|
32
|
+
prog="Tamagonion",
|
|
33
|
+
description="This is Stinky, he's running your Tor relay. He mostly loafs around and browses the Web anonymously, but he's more entertaining to look at than a graph!",
|
|
34
|
+
epilog="Hack the planet!",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
parser.add_argument(
|
|
38
|
+
"-i", "--ip", help="IP of the Tor relay where the control protocol listens. Can also be a hostname. Defaults to 127.0.0.1.", default="127.0.0.1"
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument("-p", "--port", help="Control port. Defaults to 9051.", default="9051")
|
|
41
|
+
parser.add_argument("-P", "--password", help="Control port password, if any", required=False)
|
|
42
|
+
parser.add_argument("-s", "--socket-file", help="Path to the socket file to use instead of an IP", required=False)
|
|
43
|
+
|
|
44
|
+
return parser.parse_args()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
main()
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from typing import Any, Self
|
|
4
|
+
|
|
5
|
+
import psutil
|
|
6
|
+
from stem import DescriptorUnavailable
|
|
7
|
+
from stem.version import Version
|
|
8
|
+
|
|
9
|
+
from tamagonion.relay_manager import RelayManager
|
|
10
|
+
|
|
11
|
+
SECONDS_IN_MINUTE = 60
|
|
12
|
+
SECONDS_IN_HOUR = SECONDS_IN_MINUTE * 60
|
|
13
|
+
SECONDS_IN_DAY = SECONDS_IN_HOUR * 24
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Flags(StrEnum):
|
|
17
|
+
BAD_EXIT = "BadExit"
|
|
18
|
+
EXIT = "Exit"
|
|
19
|
+
FAST = "Fast"
|
|
20
|
+
GUARD = "Guard"
|
|
21
|
+
HS_DIR = "HSDir"
|
|
22
|
+
MIDDLE_ONLY = "MiddleOnly"
|
|
23
|
+
STABLE = "Stable"
|
|
24
|
+
STALE_DESC = "StaleDesc"
|
|
25
|
+
V2_DIR = "V2Dir"
|
|
26
|
+
NO_ED_CONSENSUS = "NoEdConsensus"
|
|
27
|
+
RUNNING = "Running"
|
|
28
|
+
VALID = "Valid"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AppData:
|
|
32
|
+
relay_manager: RelayManager
|
|
33
|
+
flags: list[str]
|
|
34
|
+
version: Version
|
|
35
|
+
info: dict[str, Any]
|
|
36
|
+
process_info: dict[str, Any]
|
|
37
|
+
uptime: float = 0.0
|
|
38
|
+
connection_status_map: defaultdict[str, int]
|
|
39
|
+
pid: int = 0
|
|
40
|
+
version_status: str = ""
|
|
41
|
+
relay_name: str = ""
|
|
42
|
+
frame: int = 0
|
|
43
|
+
frame_skip: bool = False
|
|
44
|
+
instance: Self | None = None
|
|
45
|
+
|
|
46
|
+
def __init(self, relay_manager: RelayManager) -> None:
|
|
47
|
+
self.process_info = {}
|
|
48
|
+
self.info = {}
|
|
49
|
+
self.version = Version("0.0.0.0")
|
|
50
|
+
self.flags = []
|
|
51
|
+
self.connection_status_map = defaultdict(lambda: 0)
|
|
52
|
+
|
|
53
|
+
self.relay_manager = relay_manager
|
|
54
|
+
self._get_version()
|
|
55
|
+
self._get_version_status()
|
|
56
|
+
self._get_pid()
|
|
57
|
+
self._get_relay_name()
|
|
58
|
+
|
|
59
|
+
def __new__(cls, *args, **kwargs) -> Self:
|
|
60
|
+
if cls.instance is None:
|
|
61
|
+
cls.instance = super().__new__(cls)
|
|
62
|
+
cls.instance.__init(*args, **kwargs)
|
|
63
|
+
return cls.instance
|
|
64
|
+
|
|
65
|
+
def update(self) -> None:
|
|
66
|
+
self._get_flags()
|
|
67
|
+
self._get_uptime()
|
|
68
|
+
self._get_info()
|
|
69
|
+
self._get_orconn_status()
|
|
70
|
+
|
|
71
|
+
if self.relay_manager.is_local:
|
|
72
|
+
self._get_process_info()
|
|
73
|
+
|
|
74
|
+
def _get_flags(self) -> None:
|
|
75
|
+
try:
|
|
76
|
+
self.flags = self.relay_manager.controller.get_network_status().flags
|
|
77
|
+
except DescriptorUnavailable:
|
|
78
|
+
self.flags = []
|
|
79
|
+
|
|
80
|
+
def _get_uptime(self) -> None:
|
|
81
|
+
self.uptime = self.relay_manager.controller.get_uptime()
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def formated_uptime(self) -> str:
|
|
85
|
+
uptime_as_int = int(self.uptime)
|
|
86
|
+
days = uptime_as_int // SECONDS_IN_DAY
|
|
87
|
+
hours = (uptime_as_int % SECONDS_IN_DAY) // SECONDS_IN_HOUR
|
|
88
|
+
minutes = (uptime_as_int % SECONDS_IN_HOUR) // SECONDS_IN_MINUTE
|
|
89
|
+
seconds = uptime_as_int % SECONDS_IN_MINUTE
|
|
90
|
+
|
|
91
|
+
return f"{days}d {hours:02}h {minutes:02}m {seconds:02}s"
|
|
92
|
+
|
|
93
|
+
def _get_pid(self) -> None:
|
|
94
|
+
self.pid = self.relay_manager.controller.get_pid()
|
|
95
|
+
|
|
96
|
+
def _get_version(self) -> None:
|
|
97
|
+
self.version = self.relay_manager.controller.get_version()
|
|
98
|
+
|
|
99
|
+
def _get_version_status(self) -> None:
|
|
100
|
+
self.version_status = self.relay_manager.controller.get_info("status/version/current")
|
|
101
|
+
|
|
102
|
+
def _get_relay_name(self) -> None:
|
|
103
|
+
nickname = self.relay_manager.controller.get_conf("Nickname")
|
|
104
|
+
self.relay_name = nickname or "Unknown"
|
|
105
|
+
|
|
106
|
+
def _get_info(self) -> None:
|
|
107
|
+
bw_event_cache = self.relay_manager.controller.get_info("bw-event-cache")
|
|
108
|
+
bw_event_latest = bw_event_cache.split(" ")[-1].split(",")
|
|
109
|
+
traffic_read = self.relay_manager.controller.get_info("traffic/read")
|
|
110
|
+
traffic_written = self.relay_manager.controller.get_info("traffic/written")
|
|
111
|
+
|
|
112
|
+
self.info = {
|
|
113
|
+
"dormant": self.relay_manager.controller.get_info("dormant") != "0",
|
|
114
|
+
"traffic_read": int(traffic_read),
|
|
115
|
+
"traffic_written": int(traffic_written),
|
|
116
|
+
"bw_event_cache_down": int(bw_event_latest[0]),
|
|
117
|
+
"bw_event_cache_up": int(bw_event_latest[1]),
|
|
118
|
+
"bw_avg_down": int(traffic_read) / self.uptime,
|
|
119
|
+
"bw_avg_up": int(traffic_written) / self.uptime,
|
|
120
|
+
"network_liveness": self.relay_manager.controller.get_info("network-liveness") == "up",
|
|
121
|
+
"bootstrap_percent": int(self.relay_manager.controller.get_info("status/bootstrap-phase").split(" ")[2].split("=")[-1]),
|
|
122
|
+
"has_enough_dir_info": self.relay_manager.controller.get_info("status/enough-dir-info") == "1",
|
|
123
|
+
"good_server_descriptor": self.relay_manager.controller.get_info("status/good-server-descriptor") == "1",
|
|
124
|
+
"reachability": self.relay_manager.controller.get_info("status/reachability-succeeded/or") == "1",
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
def _get_orconn_status(self) -> None:
|
|
128
|
+
self.connection_status_map = defaultdict(lambda: 0)
|
|
129
|
+
orconn_status = self.relay_manager.controller.get_info("orconn-status")
|
|
130
|
+
|
|
131
|
+
for status in orconn_status.split("\n"):
|
|
132
|
+
_node, state = status.split(" ")
|
|
133
|
+
self.connection_status_map[state] += 1
|
|
134
|
+
|
|
135
|
+
def _get_process_info(self) -> None:
|
|
136
|
+
process = psutil.Process(self.pid)
|
|
137
|
+
|
|
138
|
+
self.process_info = {
|
|
139
|
+
"cpu": process.cpu_percent(),
|
|
140
|
+
"memory": process.memory_info().rss,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@staticmethod
|
|
144
|
+
def format_bytes(b: int) -> str:
|
|
145
|
+
if b < 10**3:
|
|
146
|
+
return f"{int(b)}B"
|
|
147
|
+
elif b < 10**6:
|
|
148
|
+
return f"{int(b / (10**3))}KB"
|
|
149
|
+
elif b < 10**9:
|
|
150
|
+
return f"{int(b / (10**6))}MB"
|
|
151
|
+
else:
|
|
152
|
+
return f"{int(b / (10**9))}GB"
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
frame = """┌──────────────────────────────────────────────┐
|
|
2
|
+
│ │
|
|
3
|
+
│ │
|
|
4
|
+
│ │
|
|
5
|
+
│ │
|
|
6
|
+
│ │
|
|
7
|
+
│ │
|
|
8
|
+
│ │
|
|
9
|
+
│ │
|
|
10
|
+
│ │
|
|
11
|
+
│ │
|
|
12
|
+
│ │
|
|
13
|
+
│ │
|
|
14
|
+
│ │
|
|
15
|
+
│ │
|
|
16
|
+
│ │
|
|
17
|
+
│ │
|
|
18
|
+
│ │
|
|
19
|
+
│ │
|
|
20
|
+
└───────────────────────────Hit ctrl+c to exit─┘"""
|
|
21
|
+
|
|
22
|
+
stinky_base = [
|
|
23
|
+
"""│ ▌▌
|
|
24
|
+
│ ▓▓▀
|
|
25
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
26
|
+
│ █ ─ ─ █
|
|
27
|
+
│ █ ▄ ▄ █
|
|
28
|
+
│ █ ▀ ▀ █
|
|
29
|
+
│ █ ── █
|
|
30
|
+
│ █ █
|
|
31
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄█ """,
|
|
32
|
+
"""│
|
|
33
|
+
│ ▌▌
|
|
34
|
+
│ ▓▓▀
|
|
35
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
36
|
+
│ █ ─ ─ █
|
|
37
|
+
│ █ ▄ ▄ █
|
|
38
|
+
│ █ ▀ ▀ █
|
|
39
|
+
│ █ ── █
|
|
40
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄▄▄█""",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
stinky_asleep = [
|
|
44
|
+
"""│ ▌▌ z
|
|
45
|
+
│ ▓▓▀ Z
|
|
46
|
+
│ ▄▄▄▀▀▀▀▄▄▄ Z
|
|
47
|
+
│ █ ─ ─ █
|
|
48
|
+
│ █ █
|
|
49
|
+
│ █ ── ── █
|
|
50
|
+
│ █ ∙ █
|
|
51
|
+
│ █ █
|
|
52
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄█""",
|
|
53
|
+
"""│
|
|
54
|
+
│ ▌▌ z
|
|
55
|
+
│ ▓▓▀ Z
|
|
56
|
+
│ ▄▄▄▀▀▀▀▄▄▄ Z
|
|
57
|
+
│ █ ─ ─ █
|
|
58
|
+
│ █ █
|
|
59
|
+
│ █ ── ── █
|
|
60
|
+
│ █ ∙ █
|
|
61
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄▄▄█""",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
stinky_blackout = [
|
|
65
|
+
"""│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
66
|
+
│░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
|
|
67
|
+
│░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░
|
|
68
|
+
│░▒▓████████████████████████████████████████▓▒░
|
|
69
|
+
│░▒▓████████████████████████████████████████▓▒░
|
|
70
|
+
│░▒▓█████████▀██▀███████████████████████████▓▒░
|
|
71
|
+
│░▒▓█████████▄██▄███████████████████████████▓▒░
|
|
72
|
+
│░▒▓████████████████████████████████████████▓▒░
|
|
73
|
+
│░▒▓████████████████████████████████████████▓▒░
|
|
74
|
+
│░▒▓████████████████████████████████████████▓▒░""",
|
|
75
|
+
"""│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
76
|
+
│░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
|
|
77
|
+
│░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░
|
|
78
|
+
│░▒▓████████████████████████████████████████▓▒░
|
|
79
|
+
│░▒▓████████████████████████████████████████▓▒░
|
|
80
|
+
│░▒▓████████████████████████████████████████▓▒░
|
|
81
|
+
│░▒▓█████████▀██▀███████████████████████████▓▒░
|
|
82
|
+
│░▒▓█████████▄██▄███████████████████████████▓▒░
|
|
83
|
+
│░▒▓████████████████████████████████████████▓▒░
|
|
84
|
+
│░▒▓████████████████████████████████████████▓▒░""",
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
stinky_sad = [
|
|
88
|
+
"""│ ▌▌
|
|
89
|
+
│ ▓▓▀
|
|
90
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
91
|
+
│ █ / \\ █
|
|
92
|
+
│ █ ▄ ▄ █
|
|
93
|
+
│ █ °▀ ▀° █
|
|
94
|
+
│ █ ° ── █
|
|
95
|
+
│ █ █
|
|
96
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄█ """,
|
|
97
|
+
"""│
|
|
98
|
+
│ ▌▌
|
|
99
|
+
│ ▓▓▀
|
|
100
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
101
|
+
│ █ / \\ █
|
|
102
|
+
│ █ ▄ ▄ █
|
|
103
|
+
│ █ °▀ ▀° █
|
|
104
|
+
│ █ ° ── █
|
|
105
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄▄▄█""",
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
stinky_confused = [
|
|
109
|
+
"""│ ▌▌ ?
|
|
110
|
+
│ ▓▓▀ ?
|
|
111
|
+
│ ▄▄▄▀▀▀▀▄▄▄ ?
|
|
112
|
+
│ █ ─ ^ █
|
|
113
|
+
│ █ ▄ ▄ █
|
|
114
|
+
│ █ ▀ ▀ █
|
|
115
|
+
│ █ ─── █
|
|
116
|
+
│ █ █
|
|
117
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄█ """,
|
|
118
|
+
"""│
|
|
119
|
+
│ ▌▌ ?
|
|
120
|
+
│ ▓▓▀ ?
|
|
121
|
+
│ ▄▄▄▀▀▀▀▄▄▄ ?
|
|
122
|
+
│ █ ─ ^ █
|
|
123
|
+
│ █ ▄ ▄ █
|
|
124
|
+
│ █ ▀ ▀ █
|
|
125
|
+
│ █ ─── █
|
|
126
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄▄▄█ """,
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
stinky_embarassed = [
|
|
130
|
+
"""│ ▌▌
|
|
131
|
+
│ ▓▓▀
|
|
132
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
133
|
+
│ █ / \\ █
|
|
134
|
+
│ █ ° █
|
|
135
|
+
│ █ ^ ^ █
|
|
136
|
+
│ █ O ── O █
|
|
137
|
+
│ █ █
|
|
138
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄█ """,
|
|
139
|
+
"""│
|
|
140
|
+
│ ▌▌
|
|
141
|
+
│ ▓▓▀
|
|
142
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
143
|
+
│ █ / \\ █
|
|
144
|
+
│ █ ° █
|
|
145
|
+
│ █ ^ ^ █
|
|
146
|
+
│ █ O ── O █
|
|
147
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄▄▄█""",
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
stinky_turned_around = [
|
|
151
|
+
"""│ ▐▐
|
|
152
|
+
│ ▀▓▓
|
|
153
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
154
|
+
│ █ █
|
|
155
|
+
│ █ █
|
|
156
|
+
│ █ █
|
|
157
|
+
│ █ █
|
|
158
|
+
│ █ (_)(_) █
|
|
159
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄█ """,
|
|
160
|
+
"""│
|
|
161
|
+
│ ▐▐
|
|
162
|
+
│ ▀▓▓
|
|
163
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
164
|
+
│ █ █
|
|
165
|
+
│ █ █
|
|
166
|
+
│ █ █
|
|
167
|
+
│ █ █
|
|
168
|
+
│ █▄▄▄▄(_)(_)▄▄▄▄█""",
|
|
169
|
+
]
|
|
170
|
+
stinky_bored = [
|
|
171
|
+
"""│ ▌▌
|
|
172
|
+
│ ▓▓▀
|
|
173
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
174
|
+
│ █ ^ ^ █
|
|
175
|
+
│ █ ── ── █
|
|
176
|
+
│ █ __ █
|
|
177
|
+
│ █ (__) █
|
|
178
|
+
│ █ █
|
|
179
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄█ """,
|
|
180
|
+
"""│
|
|
181
|
+
│ ▌▌
|
|
182
|
+
│ ▓▓▀
|
|
183
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
184
|
+
│ █ ^ ^ █
|
|
185
|
+
│ █ ── ── █
|
|
186
|
+
│ █ __ █
|
|
187
|
+
│ █ (__) █
|
|
188
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄▄▄█""",
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
stinky_unstable = [
|
|
192
|
+
"""│ ▌▌
|
|
193
|
+
│ ▓▓▀
|
|
194
|
+
│ ░░░▄▄▄▀▀▀▀▄▄▄
|
|
195
|
+
│ █ ─ ────── ──
|
|
196
|
+
│ ───█─ ─▄ ─▄ █
|
|
197
|
+
│ ▓▓▓▓ ▀ ▀─ ─ █
|
|
198
|
+
│ ── ── ▒▒▒▒
|
|
199
|
+
│ █ ─ ─ ─ ─█ ─ ──
|
|
200
|
+
│ ░░░░░░▄▄▄▄▄▄▄▄█ """,
|
|
201
|
+
"""│
|
|
202
|
+
│ ▌▌
|
|
203
|
+
│ ▓▓▀
|
|
204
|
+
│ ▄░░░▀▀▀▄▄▄
|
|
205
|
+
│ █ ── ── █
|
|
206
|
+
│ █ ░──▄ ▄ ───█
|
|
207
|
+
│ ░░░ ▀ ▀ ▓▓
|
|
208
|
+
│ ── ── ─ ─── █
|
|
209
|
+
│ █▄▄▄▄▄▄▄▄▒▒▒▄▒▄▒▒""",
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
stinky_old = [
|
|
213
|
+
"""│ ││
|
|
214
|
+
│ ▓▓-
|
|
215
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
216
|
+
│ █ ─ ─ █
|
|
217
|
+
│ █ █
|
|
218
|
+
│ █ =▀ ▀= █
|
|
219
|
+
│ █ ( ── ) █
|
|
220
|
+
│ █ █
|
|
221
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄█ """,
|
|
222
|
+
"""│
|
|
223
|
+
│ ││
|
|
224
|
+
│ ▓▓-
|
|
225
|
+
│ ▄▄▄▀▀▀▀▄▄▄
|
|
226
|
+
│ █ ─ ─ █
|
|
227
|
+
│ █ █
|
|
228
|
+
│ █ =▀ ▀= █
|
|
229
|
+
│ █ ( ── ) █
|
|
230
|
+
│ █▄▄▄▄▄▄▄▄▄▄▄▄▄▄█""",
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
stinky_egg = """│ ▄▀▀▄
|
|
234
|
+
│ ▄█ ▀▄
|
|
235
|
+
│ ▄▀ █ ███▄
|
|
236
|
+
│ ▄▀ █ █ ████▄
|
|
237
|
+
│ █ ▀ █ ███▀█
|
|
238
|
+
│ ██ ▀ ▀ ██
|
|
239
|
+
│ █████▄ ▄██▄ ██
|
|
240
|
+
│ ██████ ▀██▀ ██
|
|
241
|
+
│ ▀███▄▄▄▄▄▄▄▄▄▄█▀ """
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
exit_sign = """ ░░░░░░░░
|
|
245
|
+
░╔══════╗░ █▄
|
|
246
|
+
░║ EXIT ║░ █████
|
|
247
|
+
░╚══════╝░ █▀
|
|
248
|
+
░░░░░░░░ """
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
broken_exit_sign = """ ══════╗ ▓▄
|
|
252
|
+
║ EX_T ║ ░▒▓██
|
|
253
|
+
╚════ ═╝ ▓▀ """
|
|
254
|
+
|
|
255
|
+
book = """╓───┬───╖╤═══╕
|
|
256
|
+
║---│---║╧╤══╧╤
|
|
257
|
+
║---│---║╒╧══╤╧
|
|
258
|
+
╙───┴───╜╘═══╧ """
|
|
259
|
+
|
|
260
|
+
cassette = """ ____
|
|
261
|
+
│oV2o│
|
|
262
|
+
╘════╛"""
|
|
263
|
+
|
|
264
|
+
shield = [
|
|
265
|
+
""" ▄▄▄▄▄
|
|
266
|
+
█ █ █
|
|
267
|
+
█ ▀█▀ █
|
|
268
|
+
█ █ █
|
|
269
|
+
▀▀▀▀▀ """,
|
|
270
|
+
"""
|
|
271
|
+
▄▄▄▄▄
|
|
272
|
+
█ █ █
|
|
273
|
+
█ ▀█▀ █
|
|
274
|
+
█ █ █
|
|
275
|
+
▀▀▀▀▀ """,
|
|
276
|
+
]
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from typing import Self
|
|
2
|
+
|
|
3
|
+
from tamagonion import art
|
|
4
|
+
from tamagonion.app_data import AppData, Flags
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Pen:
|
|
8
|
+
@staticmethod
|
|
9
|
+
def draw(image: str, pos_y: int, pos_x: int, transparent: bool = False) -> None:
|
|
10
|
+
if transparent:
|
|
11
|
+
image.replace(" ", "\033C")
|
|
12
|
+
move_x = f"\033[{pos_x}C" if pos_x > 0 else ""
|
|
13
|
+
move_y = f"\033[{pos_y}B" if pos_y > 0 else ""
|
|
14
|
+
|
|
15
|
+
print("\033[s", end="")
|
|
16
|
+
print(f"{move_x}{move_y}", end="")
|
|
17
|
+
for line in image.split("\n"):
|
|
18
|
+
print(line, end="")
|
|
19
|
+
print(f"\r{move_x}\033[B", end="")
|
|
20
|
+
print("\033[u", end="")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Paper:
|
|
24
|
+
app_data: AppData
|
|
25
|
+
instance: Self | None = None
|
|
26
|
+
|
|
27
|
+
def __init(self, app_data: AppData) -> None:
|
|
28
|
+
self.app_data = app_data
|
|
29
|
+
|
|
30
|
+
def __new__(cls, *args, **kwargs) -> Self:
|
|
31
|
+
if cls.instance is None:
|
|
32
|
+
cls.instance = super().__new__(cls)
|
|
33
|
+
cls.instance.__init(*args, **kwargs)
|
|
34
|
+
return cls.instance
|
|
35
|
+
|
|
36
|
+
def draw(self) -> None:
|
|
37
|
+
self.erase()
|
|
38
|
+
|
|
39
|
+
## Draw the frame
|
|
40
|
+
Pen.draw(art.frame, 0, 0)
|
|
41
|
+
|
|
42
|
+
## Draw Stinky
|
|
43
|
+
|
|
44
|
+
# Bootstrap statuses
|
|
45
|
+
if self.app_data.info["bootstrap_percent"] < 100:
|
|
46
|
+
Pen.draw(art.stinky_egg, 10, 0)
|
|
47
|
+
|
|
48
|
+
# Network statuses
|
|
49
|
+
elif not self.app_data.info["network_liveness"]:
|
|
50
|
+
Pen.draw(art.stinky_blackout[self.app_data.frame], 9, 0)
|
|
51
|
+
elif not self.app_data.info["has_enough_dir_info"]:
|
|
52
|
+
Pen.draw(art.stinky_confused[self.app_data.frame], 10, 0)
|
|
53
|
+
|
|
54
|
+
# Relay network statuses
|
|
55
|
+
elif not self.app_data.info["reachability"]:
|
|
56
|
+
Pen.draw(art.stinky_turned_around[self.app_data.frame], 10, 0)
|
|
57
|
+
elif not self.app_data.info["good_server_descriptor"]:
|
|
58
|
+
Pen.draw(art.stinky_embarassed[self.app_data.frame], 10, 0)
|
|
59
|
+
|
|
60
|
+
# Relay statuses
|
|
61
|
+
elif Flags.NO_ED_CONSENSUS in self.app_data.flags or {Flags.VALID, Flags.RUNNING} - set(self.app_data.flags):
|
|
62
|
+
Pen.draw(art.stinky_sad[self.app_data.frame], 10, 0)
|
|
63
|
+
elif Flags.STALE_DESC in self.app_data.flags:
|
|
64
|
+
Pen.draw(art.stinky_old[self.app_data.frame], 10, 0)
|
|
65
|
+
elif Flags.STABLE not in self.app_data.flags:
|
|
66
|
+
Pen.draw(art.stinky_unstable[self.app_data.frame], 10, 0)
|
|
67
|
+
elif Flags.MIDDLE_ONLY in self.app_data.flags:
|
|
68
|
+
Pen.draw(art.stinky_bored[self.app_data.frame], 10, 0)
|
|
69
|
+
elif self.app_data.info["dormant"]:
|
|
70
|
+
Pen.draw(art.stinky_asleep[self.app_data.frame], 10, 0)
|
|
71
|
+
else:
|
|
72
|
+
Pen.draw(art.stinky_base[self.app_data.frame], 10, 0)
|
|
73
|
+
|
|
74
|
+
## Draw the exit sign
|
|
75
|
+
|
|
76
|
+
if Flags.BAD_EXIT in self.app_data.flags:
|
|
77
|
+
Pen.draw(art.broken_exit_sign, 9, 31)
|
|
78
|
+
elif Flags.EXIT in self.app_data.flags:
|
|
79
|
+
Pen.draw(art.exit_sign, 8, 30)
|
|
80
|
+
|
|
81
|
+
## Draw the books
|
|
82
|
+
|
|
83
|
+
if Flags.HS_DIR in self.app_data.flags:
|
|
84
|
+
Pen.draw(art.book, 15, 30)
|
|
85
|
+
|
|
86
|
+
## Draw the cassette
|
|
87
|
+
|
|
88
|
+
if Flags.V2_DIR in self.app_data.flags:
|
|
89
|
+
Pen.draw(art.cassette, 16, 1)
|
|
90
|
+
|
|
91
|
+
## Draw the shield
|
|
92
|
+
|
|
93
|
+
if Flags.GUARD in self.app_data.flags and self.app_data.info["reachability"]:
|
|
94
|
+
Pen.draw(art.shield[self.app_data.frame], 13, 19)
|
|
95
|
+
|
|
96
|
+
## Slow down the animation
|
|
97
|
+
|
|
98
|
+
if Flags.FAST not in self.app_data.flags and self.app_data.frame_skip:
|
|
99
|
+
self.app_data.frame_skip = False
|
|
100
|
+
else:
|
|
101
|
+
self.app_data.frame ^= 1
|
|
102
|
+
self.app_data.frame_skip = True
|
|
103
|
+
|
|
104
|
+
## Draw the stats
|
|
105
|
+
|
|
106
|
+
Pen.draw(f"Relay nickname: {self.app_data.relay_name}", 1, 1)
|
|
107
|
+
Pen.draw(f"Uptime: {self.app_data.formated_uptime}", 2, 1)
|
|
108
|
+
Pen.draw(
|
|
109
|
+
f"Connections (N/L/Co/F/Cl): {self.app_data.connection_status_map['NEW']}/{self.app_data.connection_status_map['LAUNCHED']}/{self.app_data.connection_status_map['CONNECTED']}/{self.app_data.connection_status_map['FAILED']}/{self.app_data.connection_status_map['CLOSED']}",
|
|
110
|
+
3,
|
|
111
|
+
1,
|
|
112
|
+
)
|
|
113
|
+
Pen.draw(
|
|
114
|
+
f"Download (Cur/Avg/Tot): {AppData.format_bytes(self.app_data.info['bw_event_cache_down'])} / {AppData.format_bytes(self.app_data.info['bw_avg_down'])} / {AppData.format_bytes(self.app_data.info['traffic_read'])}",
|
|
115
|
+
4,
|
|
116
|
+
1,
|
|
117
|
+
)
|
|
118
|
+
Pen.draw(
|
|
119
|
+
f"Upload (Cur/Avg/Tot): {AppData.format_bytes(self.app_data.info['bw_event_cache_up'])} / {AppData.format_bytes(self.app_data.info['bw_avg_up'])} / {AppData.format_bytes(self.app_data.info['traffic_written'])}",
|
|
120
|
+
5,
|
|
121
|
+
1,
|
|
122
|
+
)
|
|
123
|
+
Pen.draw(f"Version: {self.app_data.version} ({self.app_data.version_status})", 6, 1)
|
|
124
|
+
|
|
125
|
+
if self.app_data.relay_manager.is_local:
|
|
126
|
+
Pen.draw(
|
|
127
|
+
f"Process info (CPU/Mem): {self.app_data.process_info['cpu']}% / {AppData.format_bytes(self.app_data.process_info['memory'])}",
|
|
128
|
+
7,
|
|
129
|
+
1,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def erase(self, include_frame: bool = False) -> None:
|
|
133
|
+
if include_frame:
|
|
134
|
+
print("\033[0J", end="")
|
|
135
|
+
else:
|
|
136
|
+
split_frame = art.frame.split("\n")
|
|
137
|
+
frame_height = len(split_frame)
|
|
138
|
+
frame_witdh = len(split_frame[0])
|
|
139
|
+
for i in range(1, frame_height - 1):
|
|
140
|
+
Pen.draw(" " * (frame_witdh - 2), i, 1)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Self
|
|
3
|
+
|
|
4
|
+
from stem.control import Controller, EventType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RelayManager:
|
|
8
|
+
controller: Controller
|
|
9
|
+
is_local: bool = False
|
|
10
|
+
instance: Self | None = None
|
|
11
|
+
|
|
12
|
+
def __init(self, ip_addr: str, port: str, password: str | None, socket_file: str | None) -> None:
|
|
13
|
+
if socket_file:
|
|
14
|
+
self.controller = Controller.from_socket_file(socket_file)
|
|
15
|
+
else:
|
|
16
|
+
self.controller = Controller.from_port(address=ip_addr, port=int(port)) # type:ignore
|
|
17
|
+
if password:
|
|
18
|
+
self.controller.authenticate("password")
|
|
19
|
+
elif env_password := os.getenv("TAMAGONION_PASSWORD"):
|
|
20
|
+
self.controller.authenticate(env_password)
|
|
21
|
+
else:
|
|
22
|
+
self.controller.authenticate()
|
|
23
|
+
|
|
24
|
+
if socket_file or self.controller.is_localhost():
|
|
25
|
+
self.is_local = True
|
|
26
|
+
|
|
27
|
+
self.controller.add_event_listener(lambda: True, EventType["BW"])
|
|
28
|
+
|
|
29
|
+
def __new__(cls, *args, **kwargs) -> Self:
|
|
30
|
+
if cls.instance is None:
|
|
31
|
+
cls.instance = super().__new__(cls)
|
|
32
|
+
cls.instance.__init(*args, **kwargs)
|
|
33
|
+
return cls.instance
|