ChaTerminal 0.0.1__py3-none-any.whl

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 @@
1
+
@@ -0,0 +1,4 @@
1
+ from ChaTerminal.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
ChaTerminal/cli.py ADDED
@@ -0,0 +1,15 @@
1
+ from cha_terminal import server, client
2
+ import sys
3
+
4
+ def main():
5
+ if len(sys.argv) < 3 or sys.argv[1] != "init":
6
+ print("Usage: ChaTerminal init [server|client]")
7
+ sys.exit(1)
8
+
9
+ mode = sys.argv[2]
10
+ if mode == "server":
11
+ server.main()
12
+ elif mode == "client":
13
+ client.main()
14
+ else:
15
+ print("Usage: ChaTerminal init [server|client]")
@@ -0,0 +1 @@
1
+
cha_terminal/client.py ADDED
@@ -0,0 +1,101 @@
1
+ import socket
2
+ import threading
3
+ import hashlib
4
+ import readline
5
+ import platform
6
+ import time
7
+ import sys
8
+ from .crypto_utils import encrypt_msg, decrypt_msg
9
+ from .splash import splash
10
+ from colorama import init, Fore, Style
11
+
12
+ COMMANDS = ["/dm", "/kick", "/list", "/me", "/rename", "/help"]
13
+ client = None
14
+
15
+ def completer(text, state):
16
+ options = [cmd for cmd in COMMANDS if cmd.startswith(text)]
17
+ return options[state] if state < len(options) else None
18
+
19
+ def get_user_color(name):
20
+ hash_val = int(hashlib.sha256(name.encode()).hexdigest(), 16)
21
+ colors = [Fore.CYAN, Fore.GREEN, Fore.MAGENTA, Fore.YELLOW, Fore.BLUE, Fore.WHITE]
22
+ return colors[hash_val % len(colors)]
23
+
24
+ def sound_alert():
25
+ try:
26
+ if platform.system() == "Windows":
27
+ import winsound
28
+ winsound.Beep(800, 150)
29
+ else:
30
+ import os
31
+ os.system('printf "\\a"')
32
+ except:
33
+ pass
34
+
35
+ def receive():
36
+ while True:
37
+ try:
38
+ msg = client.recv(2048)
39
+ if not msg:
40
+ print("\n[!] Server disconnected.")
41
+ break
42
+ text = decrypt_msg(msg)
43
+ if "[DM from" in text or "kicked" in text:
44
+ sound_alert()
45
+
46
+ if "[DM" in text:
47
+ print(f"\r{Fore.MAGENTA}{text}{Style.RESET_ALL}\n> ", end="")
48
+ elif "[+]" in text:
49
+ print(f"\r{Fore.GREEN}{text}{Style.RESET_ALL}\n> ", end="")
50
+ elif "[-]" in text:
51
+ print(f"\r{Fore.YELLOW}{text}{Style.RESET_ALL}\n> ", end="")
52
+ elif "[!]" in text:
53
+ print(f"\r{Fore.RED}{text}{Style.RESET_ALL}\n> ", end="")
54
+ elif "* " in text:
55
+ print(f"\r{Fore.BLUE}{text}{Style.RESET_ALL}\n> ", end="")
56
+ elif ": " in text:
57
+ timestamp, rest = text.split("] ", 1)
58
+ timestamp += "]"
59
+ name, msg = rest.split(": ", 1)
60
+ color = get_user_color(name.strip())
61
+ print(f"\r{timestamp} {color}{name}{Style.RESET_ALL}: {msg}\n> ", end="")
62
+ else:
63
+ print(f"\r{text}\n> ", end="")
64
+ except Exception as e:
65
+ print(f"\n[!] Connection lost: {e}")
66
+ break
67
+
68
+ try:
69
+ client.close()
70
+ except:
71
+ pass
72
+
73
+ def send():
74
+ while True:
75
+ try:
76
+ msg = input("> ")
77
+ client.sendall(encrypt_msg(msg))
78
+ except:
79
+ break
80
+
81
+ def main():
82
+ global client
83
+ splash()
84
+ init(autoreset=True)
85
+ readline.set_completer(completer)
86
+ readline.parse_and_bind("tab: complete")
87
+
88
+ SERVER = input("Server IP: ")
89
+ PORT = int(input("Server Port: "))
90
+ username = input("Username: ")
91
+
92
+ client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
93
+ try:
94
+ client.connect((SERVER, PORT))
95
+ client.sendall(encrypt_msg(username))
96
+ except Exception as e:
97
+ print(f"[!] Could not connect: {e}")
98
+ return
99
+
100
+ threading.Thread(target=receive, daemon=True).start()
101
+ send()
@@ -0,0 +1,7 @@
1
+ KEY = 17 # Basic XOR key
2
+
3
+ def encrypt_msg(msg):
4
+ return bytes([b ^ KEY for b in msg.encode('utf-8')])
5
+
6
+ def decrypt_msg(data):
7
+ return ''.join([chr(b ^ KEY) for b in data])
cha_terminal/server.py ADDED
@@ -0,0 +1,150 @@
1
+ import socket
2
+ import threading
3
+ from datetime import datetime
4
+ from .crypto_utils import encrypt_msg, decrypt_msg
5
+ from .splash import splash
6
+
7
+ HOST = '0.0.0.0'
8
+ PORT = None
9
+ ADMIN_USERNAME = None
10
+ clients = {}
11
+ usernames = {}
12
+
13
+ def broadcast(message, exclude_socket=None):
14
+ for client in list(clients):
15
+ if client != exclude_socket:
16
+ try:
17
+ client.sendall(encrypt_msg(message))
18
+ except:
19
+ remove_client(client)
20
+
21
+ def remove_client(sock):
22
+ try:
23
+ if sock in clients:
24
+ username = clients[sock]
25
+ del usernames[username]
26
+ del clients[sock]
27
+ sock.close()
28
+ broadcast(f"[-] {username} has left the chat.")
29
+ except Exception as e:
30
+ print(f"[ERROR: remove_client] {e}")
31
+
32
+ def handle_client(client):
33
+ try:
34
+ username = decrypt_msg(client.recv(1024))
35
+ if username in usernames:
36
+ client.sendall(encrypt_msg("[!] Username already taken."))
37
+ client.close()
38
+ return
39
+
40
+ clients[client] = username
41
+ usernames[username] = client
42
+ broadcast(f"[+] {username} joined the chat.")
43
+
44
+ while True:
45
+ raw = client.recv(2048)
46
+ if not raw:
47
+ break
48
+ msg = decrypt_msg(raw)
49
+ timestamp = datetime.now().strftime("[%H:%M]")
50
+
51
+ if msg.startswith("/dm "):
52
+ _, to_user, *message = msg.split()
53
+ message = " ".join(message)
54
+ if to_user in usernames:
55
+ target_socket = usernames[to_user]
56
+ target_socket.sendall(encrypt_msg(f"{timestamp} [DM from {username}] {message}"))
57
+ client.sendall(encrypt_msg(f"{timestamp} [DM to {to_user}] {message}"))
58
+ else:
59
+ client.sendall(encrypt_msg("[!] User not found."))
60
+
61
+ elif msg.startswith("/kick ") and username == ADMIN_USERNAME:
62
+ _, to_kick = msg.split()
63
+ if to_kick in usernames:
64
+ kick_socket = usernames[to_kick]
65
+ kick_socket.sendall(encrypt_msg("[!] You have been kicked by admin."))
66
+ remove_client(kick_socket)
67
+ broadcast(f"[!] {to_kick} was kicked by admin.")
68
+ else:
69
+ client.sendall(encrypt_msg("[!] User not found."))
70
+
71
+ elif msg.startswith("/list"):
72
+ active_users = ", ".join(usernames.keys())
73
+ client.sendall(encrypt_msg(f"[Users Online] {active_users}"))
74
+
75
+ elif msg.startswith("/rename "):
76
+ newname = msg.split()[1]
77
+ if newname in usernames:
78
+ client.sendall(encrypt_msg("[!] Username already taken."))
79
+ else:
80
+ broadcast(f"[!] {username} changed name to {newname}")
81
+ del usernames[username]
82
+ usernames[newname] = client
83
+ clients[client] = newname
84
+ username = newname
85
+
86
+ elif msg.startswith("/help"):
87
+ help_text = (
88
+ "\033[1;36m[ChaTerminal Help Menu]\033[0m\n"
89
+ "\033[1;33m/dm <user> <msg>\033[0m - Send a private message\n"
90
+ "\033[1;33m/kick <user>\033[0m - Kick a user (admin only)\n"
91
+ "\033[1;33m/list\033[0m - List online users\n"
92
+ "\033[1;33m/rename <newname>\033[0m - Change your username\n"
93
+ "\033[1;33m/me <action>\033[0m - Perform an action\n"
94
+ "\033[1;33m/help\033[0m - Show this help menu\n"
95
+ )
96
+ client.sendall(encrypt_msg(help_text))
97
+
98
+ else:
99
+ broadcast(f"{timestamp} {username}: {msg}", exclude_socket=client)
100
+
101
+ except Exception as e:
102
+ print(f"[ERROR: handle_client] {e}")
103
+ finally:
104
+ remove_client(client)
105
+
106
+ def get_local_ip():
107
+ try:
108
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
109
+ s.connect(("8.8.8.8", 80))
110
+ ip = s.getsockname()[0]
111
+ s.close()
112
+ return ip
113
+ except:
114
+ return "Unavailable"
115
+
116
+ def print_colored_box(lines, border_color="\033[96m", text_color="\033[97m"):
117
+ reset = "\033[0m"
118
+ width = max(len(line) for line in lines) + 4
119
+ print(f"{border_color}┌{'─' * width}┐{reset}")
120
+ for line in lines:
121
+ print(f"{border_color}│{reset} {text_color}{line.ljust(width - 2)}{reset} {border_color}│{reset}")
122
+ print(f"{border_color}└{'─' * width}┘{reset}")
123
+
124
+ def main():
125
+ global PORT, ADMIN_USERNAME
126
+ splash()
127
+ PORT = int(input("Port: "))
128
+ ADMIN_USERNAME = input("Admin Username: ")
129
+
130
+ server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
131
+ server.bind((HOST, PORT))
132
+ server.listen()
133
+
134
+ local_ip = get_local_ip()
135
+ info_lines = [
136
+ f"ChaTerminal running on local IP: {local_ip}:{PORT}",
137
+ "Other users on the same Wi-Fi/LAN can connect using this IP.",
138
+ "",
139
+ "To allow others to connect over the internet:",
140
+ f" - Use Ngrok: ngrok tcp {PORT}",
141
+ f" - Or LocalXpose: ./lx tcp {PORT}",
142
+ f" - Or port forward your router to {local_ip}",
143
+ "",
144
+ "Then share the public IP/URL and port with clients."
145
+ ]
146
+ print_colored_box(info_lines)
147
+
148
+ while True:
149
+ client_socket, addr = server.accept()
150
+ threading.Thread(target=handle_client, args=(client_socket,), daemon=True).start()
cha_terminal/splash.py ADDED
@@ -0,0 +1,23 @@
1
+ import pyfiglet
2
+ import time
3
+ import sys
4
+
5
+ def typewriter(text, delay=0.005, beep=False):
6
+ for char in text:
7
+ sys.stdout.write(char)
8
+ sys.stdout.flush()
9
+ if beep and char not in [' ', '\n']:
10
+ sys.stdout.write('\a') # ASCII bell
11
+ sys.stdout.flush()
12
+ time.sleep(delay)
13
+ print()
14
+
15
+ def splash():
16
+ banner = pyfiglet.figlet_format("ChaTerminal", font="slant")
17
+ print("\033[1;32m", end="") # Green
18
+ typewriter(banner, delay=0.002, beep=True)
19
+
20
+ print("\033[1;34m", end="") # Blue
21
+ typewriter("[ secure terminal chat • \033[1;35mChaTerminal\033[1;34m ]", delay=0.01, beep=True)
22
+
23
+ print("\033[0m") # Reset color
@@ -0,0 +1,137 @@
1
+ Metadata-Version: 2.4
2
+ Name: ChaTerminal
3
+ Version: 0.0.1
4
+ Summary: A terminal-based encrypted chat system for LAN and remote connections
5
+ Home-page: https://github.com/Gofaone315/ChaTerminal
6
+ Author: Gofaone Tlalang
7
+ Author-email: gofaonetlalang@gmail.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Environment :: Console
12
+ Classifier: Topic :: Communications :: Chat
13
+ Classifier: Topic :: Security :: Cryptography
14
+ Requires-Python: >=3.6
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: colorama
18
+ Requires-Dist: pyfiglet
19
+ Dynamic: author
20
+ Dynamic: author-email
21
+ Dynamic: classifier
22
+ Dynamic: description
23
+ Dynamic: description-content-type
24
+ Dynamic: home-page
25
+ Dynamic: license-file
26
+ Dynamic: requires-dist
27
+ Dynamic: requires-python
28
+ Dynamic: summary
29
+
30
+ # ChaTerminal
31
+
32
+ **ChaTerminal** is a cross-platform terminal-based encrypted chat system built with Python. It supports multiple users on a local network or via tunneling tools like Ngrok or LocalXpose, providing a lightweight and interactive chat experience — right from the terminal.
33
+
34
+ ![Python](https://img.shields.io/badge/Python-3.9+-blue.svg)
35
+ ![License](https://img.shields.io/github/license/Gofaone315/ChaTerminal)
36
+ ![Platform](https://img.shields.io/badge/Platform-Terminal-informational)
37
+
38
+ ## Features
39
+
40
+ - **Encrypted communication** using custom crypto utilities
41
+ - **Multi-user chat** over LAN or tunneled internet
42
+ - **Color-coded messages** with timestamp formatting
43
+ - **Auto-complete** for commands via `readline` and TAB
44
+ - **Direct messaging** using `/dm <user> <message>`
45
+ - **Username renaming** with `/rename <new_name>`
46
+ - **List online users** using `/list`
47
+ - **Admin controls**: `/kick <user>` (admin-only)
48
+ - **Emote support**: `/me <action>`
49
+ - **Help menu**: `/help` displays command reference
50
+ - **User join/leave notifications**
51
+ - **Sound alert** for DMs and admin actions
52
+ - **Graceful client disconnect and server feedback**
53
+ - **Color personalization** based on username hash
54
+
55
+ ## Getting Started
56
+
57
+ ### Requirements
58
+
59
+ - Python 3.9+
60
+ - pip packages: `colorama`, `pyfiglet`, `readline` (Linux/macOS), `pyreadline3` (Windows)
61
+
62
+ ### Installation
63
+
64
+ ```bash
65
+ pip install ChaTerminal
66
+ ```
67
+
68
+ ### Running the Server
69
+
70
+ ```bash
71
+ ChaTerminal init server
72
+ ```
73
+
74
+ - Enter a port (e.g., `5555`) and the admin username
75
+ - Displays local IP with instructions for LAN or tunneled access
76
+
77
+ ### Running the Client
78
+
79
+ In a **new terminal window**:
80
+
81
+ ```bash
82
+ ChaTerminal init client
83
+ ```
84
+
85
+ - Enter the server IP, port, and a unique username
86
+
87
+ ## Commands
88
+
89
+ | Command | Description |
90
+ |---------------------|------------------------------------------|
91
+ | `/dm <user> <msg>` | Send a private message |
92
+ | `/kick <user>` | Kick a user (admin only) |
93
+ | `/list` | View online users |
94
+ | `/rename <name>` | Change your username |
95
+ | `/me <action>` | Send an action/emote (e.g., waves) |
96
+ | `/help` | Show all available commands |
97
+
98
+ ## Networking Tips
99
+
100
+ To let others join:
101
+
102
+ - **Local network**: Share the IP and port printed on server start
103
+ - **Ngrok**: `ngrok tcp <port>`
104
+ - **LocalXpose**: `./lx tcp <port>`
105
+ - **Port Forwarding**: Map the server's port on your router
106
+
107
+ ## Example
108
+
109
+ ```bash
110
+ # Start server
111
+ $ ChaTerminal init server
112
+ Port: 5555
113
+ Admin Username: admin
114
+
115
+ # Start client
116
+ $ ChaTerminal init client
117
+ Server IP: 192.168.1.100
118
+ Server Port: 5555
119
+ Username: myname
120
+ ```
121
+
122
+ ## Security Note
123
+
124
+ All messages are encrypted using the `crypto_utils` module before transmission. Ensure that this module uses secure encryption practices for production deployments.
125
+
126
+ ## License
127
+
128
+ This project is licensed under the MIT License. See `LICENSE` for more info.
129
+
130
+ ## Author
131
+
132
+ **Gofaone Tlalang**
133
+ GitHub: [@Gofaone315](https://github.com/Gofaone315)
134
+
135
+ ---
136
+
137
+ **ChaTerminal** – Real-time encrypted chat, right from your terminal.
@@ -0,0 +1,14 @@
1
+ ChaTerminal/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
+ ChaTerminal/__main__.py,sha256=XZQEiEg1VXxCIEX_EL-FGLLl-k4qXjYxhD06UoRRsP0,72
3
+ ChaTerminal/cli.py,sha256=-VloPrDGkSjNYfV6aEB9FI3hTkmtQScUUwyjTbSJnL0,379
4
+ cha_terminal/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
5
+ cha_terminal/client.py,sha256=ZMvT2GMRqIC23pHGFpVzwOCG3ZWeY8EYdU_iRwL1Ioc,3002
6
+ cha_terminal/crypto_utils.py,sha256=bO4g6aJx-yY3RiwIJgEY59x2_GjadwRPvMk62gzkkho,179
7
+ cha_terminal/server.py,sha256=pEKDQs9-nAx_Q2o_qau9ojX_nobWXwGerpr_WavUIG8,5521
8
+ cha_terminal/splash.py,sha256=mU1_EiOnX68blw9CF4U9RXThQZzNgOs6JFRx_8_Z324,678
9
+ chaterminal-0.0.1.dist-info/licenses/LICENSE,sha256=GvmOpkMWXCP9-bywb4DwoRxMT_zOWu5cuYilKfdf2vU,1072
10
+ chaterminal-0.0.1.dist-info/METADATA,sha256=yj_4sWdyIA4Rqth9oce9hx6VuP0KFO_3afmvdyQ44z8,4016
11
+ chaterminal-0.0.1.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
12
+ chaterminal-0.0.1.dist-info/entry_points.txt,sha256=IpdZdr_Me6Gp4uKTnAwdQgvLrfSiK4Fbmlz2UYSU9as,46
13
+ chaterminal-0.0.1.dist-info/top_level.txt,sha256=NHGCl4mRpbXM55jGfq8zvR6xKi1EDjB57lgmitVOrqw,25
14
+ chaterminal-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.8.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ chat = ChaTerminal.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Gofaone Tlalang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ ChaTerminal
2
+ cha_terminal