ChaTerminal 2.0.0__tar.gz → 2.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.
- {chaterminal-2.0.0 → chaterminal-2.0.1}/ChaTerminal.egg-info/PKG-INFO +1 -1
- chaterminal-2.0.1/ChaTerminal.egg-info/SOURCES.txt +33 -0
- chaterminal-2.0.1/ChaTerminal.egg-info/top_level.txt +1 -0
- {chaterminal-2.0.0 → chaterminal-2.0.1}/PKG-INFO +1 -1
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/core/events.py +8 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/core/state.py +2 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/main.py +1 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/services/auth_service.py +48 -4
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/services/presence_service.py +56 -1
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/services/websocket_service.py +5 -1
- chaterminal-2.0.1/chaterminal/ui/console.py +26 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/ui/terminal_ui.py +79 -41
- {chaterminal-2.0.0 → chaterminal-2.0.1}/setup.py +1 -1
- chaterminal-2.0.0/ChaTerminal/ui/console.py +0 -28
- chaterminal-2.0.0/ChaTerminal.egg-info/SOURCES.txt +0 -33
- chaterminal-2.0.0/ChaTerminal.egg-info/top_level.txt +0 -1
- {chaterminal-2.0.0 → chaterminal-2.0.1}/ChaTerminal.egg-info/dependency_links.txt +0 -0
- {chaterminal-2.0.0 → chaterminal-2.0.1}/ChaTerminal.egg-info/entry_points.txt +0 -0
- {chaterminal-2.0.0 → chaterminal-2.0.1}/ChaTerminal.egg-info/requires.txt +0 -0
- {chaterminal-2.0.0 → chaterminal-2.0.1}/LICENSE +0 -0
- {chaterminal-2.0.0 → chaterminal-2.0.1}/README.md +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/__init__.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/__main__.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/core/__init__.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/core/logger.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/core/threads.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/crypto/__init__.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/crypto/encryption.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/services/__init__.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/services/firebase_service.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/services/message_service.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/storage/__init__.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/storage/database.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/storage/session_store.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/ui/__init__.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/ui/panels.py +0 -0
- {chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/ui/splash.py +0 -0
- {chaterminal-2.0.0 → chaterminal-2.0.1}/setup.cfg +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
ChaTerminal.egg-info/PKG-INFO
|
|
5
|
+
ChaTerminal.egg-info/SOURCES.txt
|
|
6
|
+
ChaTerminal.egg-info/dependency_links.txt
|
|
7
|
+
ChaTerminal.egg-info/entry_points.txt
|
|
8
|
+
ChaTerminal.egg-info/requires.txt
|
|
9
|
+
ChaTerminal.egg-info/top_level.txt
|
|
10
|
+
chaterminal/__init__.py
|
|
11
|
+
chaterminal/__main__.py
|
|
12
|
+
chaterminal/main.py
|
|
13
|
+
chaterminal/core/__init__.py
|
|
14
|
+
chaterminal/core/events.py
|
|
15
|
+
chaterminal/core/logger.py
|
|
16
|
+
chaterminal/core/state.py
|
|
17
|
+
chaterminal/core/threads.py
|
|
18
|
+
chaterminal/crypto/__init__.py
|
|
19
|
+
chaterminal/crypto/encryption.py
|
|
20
|
+
chaterminal/services/__init__.py
|
|
21
|
+
chaterminal/services/auth_service.py
|
|
22
|
+
chaterminal/services/firebase_service.py
|
|
23
|
+
chaterminal/services/message_service.py
|
|
24
|
+
chaterminal/services/presence_service.py
|
|
25
|
+
chaterminal/services/websocket_service.py
|
|
26
|
+
chaterminal/storage/__init__.py
|
|
27
|
+
chaterminal/storage/database.py
|
|
28
|
+
chaterminal/storage/session_store.py
|
|
29
|
+
chaterminal/ui/__init__.py
|
|
30
|
+
chaterminal/ui/console.py
|
|
31
|
+
chaterminal/ui/panels.py
|
|
32
|
+
chaterminal/ui/splash.py
|
|
33
|
+
chaterminal/ui/terminal_ui.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
chaterminal
|
|
@@ -12,3 +12,11 @@ class EventType(Enum):
|
|
|
12
12
|
def put_event(state, event_type: EventType, **kwargs):
|
|
13
13
|
kwargs["type"] = event_type
|
|
14
14
|
state.event_queue.put(kwargs)
|
|
15
|
+
|
|
16
|
+
if event_type == EventType.LOGOUT:
|
|
17
|
+
wake_prompt = getattr(state, "wake_prompt", None)
|
|
18
|
+
if callable(wake_prompt):
|
|
19
|
+
try:
|
|
20
|
+
wake_prompt()
|
|
21
|
+
except Exception:
|
|
22
|
+
pass
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
|
+
from collections.abc import Callable
|
|
2
3
|
from queue import Queue
|
|
3
4
|
import threading
|
|
4
5
|
|
|
@@ -17,3 +18,4 @@ class AppState:
|
|
|
17
18
|
|
|
18
19
|
event_queue: Queue = field(default_factory=Queue)
|
|
19
20
|
lock: threading.Lock = field(default_factory=threading.Lock)
|
|
21
|
+
wake_prompt: Callable[[], None] | None = None
|
|
@@ -65,6 +65,7 @@ def main():
|
|
|
65
65
|
worker_manager = WorkerManager(state)
|
|
66
66
|
worker_manager.start("WebsocketClient", ws_service.run)
|
|
67
67
|
worker_manager.start("Heartbeat", presence.heartbeat_loop)
|
|
68
|
+
worker_manager.start("SessionGuard", presence.session_guard_loop)
|
|
68
69
|
worker_manager.start("MessageSender", message_service.sender_worker)
|
|
69
70
|
|
|
70
71
|
# 7. Run UI - single-threaded, blocks until /exit or Ctrl+C
|
|
@@ -14,17 +14,21 @@ class AuthService:
|
|
|
14
14
|
session = load_session()
|
|
15
15
|
|
|
16
16
|
if session:
|
|
17
|
-
self.
|
|
18
|
-
self.state.refresh_token = session.get("refreshToken")
|
|
19
|
-
self.state.uid = session.get("localId")
|
|
20
|
-
self.state.token_expires_at = session.get("expiresAt", 0)
|
|
17
|
+
self._apply_session(session)
|
|
21
18
|
|
|
22
19
|
if self.state.token_expires_at <= int(time.time()):
|
|
23
20
|
logger.info("[*] Refreshing session...")
|
|
24
21
|
if not self.firebase.refresh_token():
|
|
25
22
|
delete_session()
|
|
23
|
+
self._clear_session()
|
|
26
24
|
session = None
|
|
27
25
|
|
|
26
|
+
if session and not self._stored_session_is_active():
|
|
27
|
+
logger.info("[*] Stored ChaTerminal session was revoked. Relinking required.")
|
|
28
|
+
delete_session()
|
|
29
|
+
self._clear_session()
|
|
30
|
+
session = None
|
|
31
|
+
|
|
28
32
|
if not session:
|
|
29
33
|
code = self._generate_activation_code()
|
|
30
34
|
if not code:
|
|
@@ -72,6 +76,46 @@ class AuthService:
|
|
|
72
76
|
|
|
73
77
|
return True
|
|
74
78
|
|
|
79
|
+
def _apply_session(self, session):
|
|
80
|
+
self.state.token = session.get("idToken")
|
|
81
|
+
self.state.refresh_token = session.get("refreshToken")
|
|
82
|
+
self.state.uid = session.get("localId")
|
|
83
|
+
self.state.token_expires_at = session.get("expiresAt", 0)
|
|
84
|
+
|
|
85
|
+
def _clear_session(self):
|
|
86
|
+
self.state.token = None
|
|
87
|
+
self.state.refresh_token = None
|
|
88
|
+
self.state.uid = None
|
|
89
|
+
self.state.token_expires_at = 0
|
|
90
|
+
|
|
91
|
+
def _stored_session_is_active(self):
|
|
92
|
+
try:
|
|
93
|
+
res = self.firebase.get(f"chaterminal/sessions/{self.state.uid}")
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.error(f"[!] Failed to validate stored session: {e}")
|
|
96
|
+
return True
|
|
97
|
+
|
|
98
|
+
if res.status_code in (401, 403):
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
if res.status_code != 200:
|
|
102
|
+
logger.error(f"[!] Failed to validate stored session: {res.status_code} {res.text}")
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
if res.text.strip() == "null":
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
data = res.json()
|
|
110
|
+
except Exception:
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
if not isinstance(data, dict):
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
active_device_id = data.get("activeDeviceId")
|
|
117
|
+
return bool(active_device_id and active_device_id == self.state.device_id)
|
|
118
|
+
|
|
75
119
|
def _generate_activation_code(self):
|
|
76
120
|
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
77
121
|
attempts = 0
|
{chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/services/presence_service.py
RENAMED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import time
|
|
2
2
|
from chaterminal.core.logger import logger
|
|
3
3
|
from chaterminal.core.events import EventType, put_event
|
|
4
|
-
from chaterminal.storage.session_store import get_device_name
|
|
4
|
+
from chaterminal.storage.session_store import delete_session, get_device_name
|
|
5
|
+
|
|
6
|
+
SESSION_GUARD_INTERVAL = 5
|
|
5
7
|
|
|
6
8
|
class PresenceService:
|
|
7
9
|
def __init__(self, state, firebase_client, ws_service):
|
|
@@ -54,6 +56,59 @@ class PresenceService:
|
|
|
54
56
|
|
|
55
57
|
time.sleep(60)
|
|
56
58
|
|
|
59
|
+
def session_guard_loop(self):
|
|
60
|
+
while self.state.running:
|
|
61
|
+
reason = self._remote_session_logout_reason()
|
|
62
|
+
if reason:
|
|
63
|
+
self._handle_remote_logout(reason)
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
time.sleep(SESSION_GUARD_INTERVAL)
|
|
67
|
+
|
|
68
|
+
def _remote_session_logout_reason(self):
|
|
69
|
+
if not self.state.uid or not self.state.device_id or not self.state.token:
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
res = self.firebase.get(f"chaterminal/sessions/{self.state.uid}")
|
|
74
|
+
except Exception:
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
if res.status_code in (401, 403):
|
|
78
|
+
return "Your ChaTerminal session is no longer authorised."
|
|
79
|
+
|
|
80
|
+
if res.status_code != 200:
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
if res.text.strip() == "null":
|
|
84
|
+
return "Your ChaTerminal session was revoked from MemerDevs settings."
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
session = res.json()
|
|
88
|
+
except Exception:
|
|
89
|
+
return "Your ChaTerminal session record is invalid."
|
|
90
|
+
|
|
91
|
+
if not isinstance(session, dict):
|
|
92
|
+
return "Your ChaTerminal session record is invalid."
|
|
93
|
+
|
|
94
|
+
active_device_id = session.get("activeDeviceId")
|
|
95
|
+
if active_device_id != self.state.device_id:
|
|
96
|
+
return "You were logged out because ChaTerminal is active on another device."
|
|
97
|
+
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
def _handle_remote_logout(self, reason):
|
|
101
|
+
logger.error(f"[!] {reason}")
|
|
102
|
+
delete_session()
|
|
103
|
+
self.state.token = None
|
|
104
|
+
self.state.refresh_token = None
|
|
105
|
+
put_event(self.state, EventType.LOGOUT, text=reason)
|
|
106
|
+
try:
|
|
107
|
+
if self.ws_service.ws:
|
|
108
|
+
self.ws_service.ws.close()
|
|
109
|
+
except Exception:
|
|
110
|
+
pass
|
|
111
|
+
|
|
57
112
|
def fetch_online_users(self):
|
|
58
113
|
try:
|
|
59
114
|
res = self.firebase.get("users")
|
{chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/services/websocket_service.py
RENAMED
|
@@ -55,7 +55,11 @@ class WebsocketService:
|
|
|
55
55
|
active_id = payload.get("activeDeviceId")
|
|
56
56
|
if active_id and active_id != self.state.device_id:
|
|
57
57
|
logger.error("\n[!] Logged out from another device.")
|
|
58
|
-
put_event(
|
|
58
|
+
put_event(
|
|
59
|
+
self.state,
|
|
60
|
+
EventType.LOGOUT,
|
|
61
|
+
text="You were logged out because ChaTerminal is active on another device.",
|
|
62
|
+
)
|
|
59
63
|
elif msg_type == "message_ack":
|
|
60
64
|
logger.info(f"[+] Message acknowledged: {payload.get('msg_id', 'unknown')}")
|
|
61
65
|
except Exception as e:
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from colorama import just_fix_windows_console
|
|
7
|
+
|
|
8
|
+
just_fix_windows_console()
|
|
9
|
+
except Exception:
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
def _env_flag(name):
|
|
13
|
+
value = os.environ.get(name, "").strip().lower()
|
|
14
|
+
if not value:
|
|
15
|
+
return None
|
|
16
|
+
return value in {"1", "true", "yes", "on"}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_color_forced = _env_flag("CHATTERMINAL_COLOR")
|
|
20
|
+
|
|
21
|
+
console = Console(
|
|
22
|
+
color_system="auto",
|
|
23
|
+
force_terminal=True if _color_forced is True else None,
|
|
24
|
+
no_color=True if _color_forced is False else None,
|
|
25
|
+
highlight=False,
|
|
26
|
+
)
|
|
@@ -8,6 +8,7 @@ from prompt_toolkit.patch_stdout import patch_stdout
|
|
|
8
8
|
|
|
9
9
|
from chaterminal.core.events import EventType
|
|
10
10
|
from chaterminal.core.logger import logger
|
|
11
|
+
from chaterminal.storage.session_store import delete_session
|
|
11
12
|
from chaterminal.ui.panels import get_user_color, activation_panel
|
|
12
13
|
from chaterminal.ui.console import console
|
|
13
14
|
|
|
@@ -17,6 +18,7 @@ class TerminalUI:
|
|
|
17
18
|
self.state = state
|
|
18
19
|
self.message_service = message_service
|
|
19
20
|
self.presence_service = presence_service
|
|
21
|
+
self._prompt_session = None
|
|
20
22
|
|
|
21
23
|
def print_header(self):
|
|
22
24
|
console.rule(f"[bold cyan]ChaTerminal v2[/bold cyan] - [green]{self.state.username}[/green] - E2EE Secure")
|
|
@@ -62,51 +64,83 @@ class TerminalUI:
|
|
|
62
64
|
def run(self):
|
|
63
65
|
"""Main loop: drain event queue, then block on user input."""
|
|
64
66
|
prompt = PromptSession()
|
|
67
|
+
self._prompt_session = prompt
|
|
68
|
+
self.state.wake_prompt = self._wake_prompt
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
70
|
+
try:
|
|
71
|
+
with patch_stdout():
|
|
72
|
+
self.print_header()
|
|
73
|
+
self.print_history()
|
|
74
|
+
console.print("[bold green][+] Connected! Type a command below.[/bold green]\n")
|
|
75
|
+
|
|
76
|
+
while True:
|
|
77
|
+
# Drain any pending events first (non-blocking)
|
|
78
|
+
self._drain_events()
|
|
79
|
+
|
|
80
|
+
if not self.state.running:
|
|
81
|
+
break
|
|
82
|
+
|
|
83
|
+
# Now block on user input
|
|
84
|
+
try:
|
|
85
|
+
line = prompt.prompt("> ")
|
|
86
|
+
except (KeyboardInterrupt, EOFError):
|
|
87
|
+
self.state.running = False
|
|
88
|
+
break
|
|
89
|
+
|
|
90
|
+
if not line or not line.strip():
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
line = line.strip()
|
|
94
|
+
|
|
95
|
+
if line == "/exit":
|
|
96
|
+
self.state.running = False
|
|
97
|
+
break
|
|
98
|
+
|
|
99
|
+
elif line == "/help":
|
|
100
|
+
console.print("[dim]Commands:[/dim] [yellow]/dm <user> <msg>[/yellow] [yellow]/list[/yellow] [yellow]/exit[/yellow]")
|
|
101
|
+
|
|
102
|
+
elif line == "/list":
|
|
103
|
+
online = self.presence_service.fetch_online_users()
|
|
104
|
+
if online:
|
|
105
|
+
console.print(f"[bold cyan][Online][/bold cyan] {', '.join(online)}")
|
|
106
|
+
else:
|
|
107
|
+
console.print("[yellow][!] No users currently online.[/yellow]")
|
|
108
|
+
|
|
109
|
+
elif line.startswith("/dm "):
|
|
110
|
+
parts = line.split(" ", 2)
|
|
111
|
+
if len(parts) < 3:
|
|
112
|
+
console.print("[red][!] Usage: /dm <username> <message>[/red]")
|
|
113
|
+
else:
|
|
114
|
+
self.message_service.queue_message(parts[1], parts[2])
|
|
93
115
|
|
|
94
|
-
elif line == "/list":
|
|
95
|
-
online = self.presence_service.fetch_online_users()
|
|
96
|
-
if online:
|
|
97
|
-
console.print(f"[bold cyan][Online][/bold cyan] {', '.join(online)}")
|
|
98
116
|
else:
|
|
99
|
-
console.print("[
|
|
117
|
+
console.print("[red][!] Unknown command. Try /help, /dm, /list, or /exit.[/red]")
|
|
118
|
+
finally:
|
|
119
|
+
if self.state.wake_prompt == self._wake_prompt:
|
|
120
|
+
self.state.wake_prompt = None
|
|
121
|
+
self._prompt_session = None
|
|
122
|
+
|
|
123
|
+
def _wake_prompt(self):
|
|
124
|
+
prompt = self._prompt_session
|
|
125
|
+
if not prompt:
|
|
126
|
+
return
|
|
100
127
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
128
|
+
app = prompt.app
|
|
129
|
+
|
|
130
|
+
def exit_prompt():
|
|
131
|
+
try:
|
|
132
|
+
if app.is_running:
|
|
133
|
+
app.exit(result="")
|
|
134
|
+
except Exception:
|
|
135
|
+
pass
|
|
107
136
|
|
|
108
|
-
|
|
109
|
-
|
|
137
|
+
try:
|
|
138
|
+
if app.is_running and app.loop and app.loop.is_running():
|
|
139
|
+
app.loop.call_soon_threadsafe(exit_prompt)
|
|
140
|
+
elif app.is_running:
|
|
141
|
+
exit_prompt()
|
|
142
|
+
except Exception:
|
|
143
|
+
exit_prompt()
|
|
110
144
|
|
|
111
145
|
def _drain_events(self):
|
|
112
146
|
"""Consume all pending events from the queue without blocking."""
|
|
@@ -144,5 +178,9 @@ class TerminalUI:
|
|
|
144
178
|
console.print(event.get("text", ""))
|
|
145
179
|
|
|
146
180
|
elif event_type == EventType.LOGOUT:
|
|
147
|
-
|
|
181
|
+
delete_session()
|
|
182
|
+
self.state.token = None
|
|
183
|
+
self.state.refresh_token = None
|
|
184
|
+
message = event.get("text") or "You were logged out from another device."
|
|
185
|
+
console.print(f"\n[bold red][!] {message}[/bold red]")
|
|
148
186
|
self.state.running = False
|
|
@@ -6,7 +6,7 @@ long_description = (this_directory / "README.md").read_text(encoding="utf-8")
|
|
|
6
6
|
|
|
7
7
|
setup(
|
|
8
8
|
name="ChaTerminal",
|
|
9
|
-
version="2.0.
|
|
9
|
+
version="2.0.1",
|
|
10
10
|
author="Gofaone Tlalang",
|
|
11
11
|
author_email="gofaone315@memerdevs.com",
|
|
12
12
|
description="A terminal-based encrypted chat system for MemerDevs",
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from rich.console import Console
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def _env_flag(name):
|
|
7
|
-
value = os.environ.get(name, "").strip().lower()
|
|
8
|
-
if not value:
|
|
9
|
-
return None
|
|
10
|
-
return value in {"1", "true", "yes", "on"}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def supports_ansi():
|
|
14
|
-
forced = _env_flag("CHATTERMINAL_COLOR")
|
|
15
|
-
if forced is not None:
|
|
16
|
-
return forced
|
|
17
|
-
|
|
18
|
-
return False
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
_use_color = supports_ansi()
|
|
22
|
-
|
|
23
|
-
console = Console(
|
|
24
|
-
color_system="auto" if _use_color else None,
|
|
25
|
-
force_terminal=True if _use_color else False,
|
|
26
|
-
no_color=not _use_color,
|
|
27
|
-
highlight=False,
|
|
28
|
-
)
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
setup.py
|
|
4
|
-
ChaTerminal/__init__.py
|
|
5
|
-
ChaTerminal/__main__.py
|
|
6
|
-
ChaTerminal/main.py
|
|
7
|
-
ChaTerminal.egg-info/PKG-INFO
|
|
8
|
-
ChaTerminal.egg-info/SOURCES.txt
|
|
9
|
-
ChaTerminal.egg-info/dependency_links.txt
|
|
10
|
-
ChaTerminal.egg-info/entry_points.txt
|
|
11
|
-
ChaTerminal.egg-info/requires.txt
|
|
12
|
-
ChaTerminal.egg-info/top_level.txt
|
|
13
|
-
ChaTerminal/core/__init__.py
|
|
14
|
-
ChaTerminal/core/events.py
|
|
15
|
-
ChaTerminal/core/logger.py
|
|
16
|
-
ChaTerminal/core/state.py
|
|
17
|
-
ChaTerminal/core/threads.py
|
|
18
|
-
ChaTerminal/crypto/__init__.py
|
|
19
|
-
ChaTerminal/crypto/encryption.py
|
|
20
|
-
ChaTerminal/services/__init__.py
|
|
21
|
-
ChaTerminal/services/auth_service.py
|
|
22
|
-
ChaTerminal/services/firebase_service.py
|
|
23
|
-
ChaTerminal/services/message_service.py
|
|
24
|
-
ChaTerminal/services/presence_service.py
|
|
25
|
-
ChaTerminal/services/websocket_service.py
|
|
26
|
-
ChaTerminal/storage/__init__.py
|
|
27
|
-
ChaTerminal/storage/database.py
|
|
28
|
-
ChaTerminal/storage/session_store.py
|
|
29
|
-
ChaTerminal/ui/__init__.py
|
|
30
|
-
ChaTerminal/ui/console.py
|
|
31
|
-
ChaTerminal/ui/panels.py
|
|
32
|
-
ChaTerminal/ui/splash.py
|
|
33
|
-
ChaTerminal/ui/terminal_ui.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
ChaTerminal
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chaterminal-2.0.0/ChaTerminal → chaterminal-2.0.1/chaterminal}/services/firebase_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|