ChaTerminal 0.1.0__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,136 @@
1
+ Metadata-Version: 2.4
2
+ Name: ChaTerminal
3
+ Version: 0.1.0
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
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: license-file
25
+ Dynamic: requires-dist
26
+ Dynamic: requires-python
27
+ Dynamic: summary
28
+
29
+ # ChaTerminal
30
+
31
+ **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.
32
+
33
+ ![Python](https://img.shields.io/badge/Python-3.9+-blue.svg)
34
+ ![License](https://img.shields.io/github/license/Gofaone315/ChaTerminal)
35
+ ![Platform](https://img.shields.io/badge/Platform-Terminal-informational)
36
+
37
+ ## Features
38
+
39
+ - **Encrypted communication** using custom crypto utilities
40
+ - **Multi-user chat** over LAN or tunneled internet
41
+ - **Color-coded messages** with timestamp formatting
42
+ - **Auto-complete** for commands via `readline` and TAB
43
+ - **Direct messaging** using `/dm <user> <message>`
44
+ - **Username renaming** with `/rename <new_name>`
45
+ - **List online users** using `/list`
46
+ - **Admin controls**: `/kick <user>` (admin-only)
47
+ - **Emote support**: `/me <action>`
48
+ - **Help menu**: `/help` displays command reference
49
+ - **User join/leave notifications**
50
+ - **Sound alert** for DMs and admin actions
51
+ - **Graceful client disconnect and server feedback**
52
+ - **Color personalization** based on username hash
53
+
54
+ ## Getting Started
55
+
56
+ ### Requirements
57
+
58
+ - Python 3.9+
59
+ - pip packages: `colorama`, `pyfiglet`, `readline` (Linux/macOS), `pyreadline3` (Windows)
60
+
61
+ ### Installation
62
+
63
+ ```bash
64
+ pip install ChaTerminal
65
+ ```
66
+
67
+ ### Running the Server
68
+
69
+ ```bash
70
+ ChaTerminal init server
71
+ ```
72
+
73
+ - Enter a port (e.g., `5555`) and the admin username
74
+ - Displays local IP with instructions for LAN or tunneled access
75
+
76
+ ### Running the Client
77
+
78
+ In a **new terminal window**:
79
+
80
+ ```bash
81
+ ChaTerminal init client
82
+ ```
83
+
84
+ - Enter the server IP, port, and a unique username
85
+
86
+ ## Commands
87
+
88
+ | Command | Description |
89
+ |---------------------|------------------------------------------|
90
+ | `/dm <user> <msg>` | Send a private message |
91
+ | `/kick <user>` | Kick a user (admin only) |
92
+ | `/list` | View online users |
93
+ | `/rename <name>` | Change your username |
94
+ | `/me <action>` | Send an action/emote (e.g., waves) |
95
+ | `/help` | Show all available commands |
96
+
97
+ ## Networking Tips
98
+
99
+ To let others join:
100
+
101
+ - **Local network**: Share the IP and port printed on server start
102
+ - **Ngrok**: `ngrok tcp <port>`
103
+ - **LocalXpose**: `./lx tcp <port>`
104
+ - **Port Forwarding**: Map the server's port on your router
105
+
106
+ ## Example
107
+
108
+ ```bash
109
+ # Start server
110
+ $ ChaTerminal init server
111
+ Port: 5555
112
+ Admin Username: admin
113
+
114
+ # Start client
115
+ $ ChaTerminal init client
116
+ Server IP: 192.168.1.100
117
+ Server Port: 5555
118
+ Username: myname
119
+ ```
120
+
121
+ ## Security Note
122
+
123
+ All messages are encrypted using the `crypto_utils` module before transmission. Ensure that this module uses secure encryption practices for production deployments.
124
+
125
+ ## License
126
+
127
+ This project is licensed under the MIT License. See `LICENSE` for more info.
128
+
129
+ ## Author
130
+
131
+ **Gofaone Tlalang**
132
+ GitHub: [@Gofaone315](https://github.com/Gofaone315)
133
+
134
+ ---
135
+
136
+ **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.1.0.dist-info/licenses/LICENSE,sha256=GvmOpkMWXCP9-bywb4DwoRxMT_zOWu5cuYilKfdf2vU,1072
10
+ chaterminal-0.1.0.dist-info/METADATA,sha256=tOIohPt-oG1ITlGXQgBhqsn6Yb_VPLiDE2kCNLJi3go,3992
11
+ chaterminal-0.1.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
12
+ chaterminal-0.1.0.dist-info/entry_points.txt,sha256=IpdZdr_Me6Gp4uKTnAwdQgvLrfSiK4Fbmlz2UYSU9as,46
13
+ chaterminal-0.1.0.dist-info/top_level.txt,sha256=NHGCl4mRpbXM55jGfq8zvR6xKi1EDjB57lgmitVOrqw,25
14
+ chaterminal-0.1.0.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