├── client ├── .hostinfo ├── constants.py ├── setup.py ├── client.py └── requirements.txt ├── .gitignore ├── docs ├── structure.jpeg └── todo.md ├── server ├── constants.py ├── utils.py ├── requirements.txt └── server.py ├── README.md └── LICENSE /client/.hostinfo: -------------------------------------------------------------------------------- 1 | localhost:1234 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | server/__pycache__ 3 | client/__pycache__ 4 | -------------------------------------------------------------------------------- /client/constants.py: -------------------------------------------------------------------------------- 1 | # constants 2 | 3 | ENCODING = 'utf-8' 4 | BUFFERSIZE = 1024 5 | -------------------------------------------------------------------------------- /docs/structure.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madhav-MKNC/X-Hall/HEAD/docs/structure.jpeg -------------------------------------------------------------------------------- /server/constants.py: -------------------------------------------------------------------------------- 1 | # constants 2 | BUFFERSIZE = 1024 3 | ENCODING = 'utf-8' 4 | MAX_CONNECTIONS = 5 5 | 6 | -------------------------------------------------------------------------------- /docs/todo.md: -------------------------------------------------------------------------------- 1 | >> **Tasks** 2 | 3 | - better security, inside class 4 | - better error handling 5 | - broadcasting all at once 6 | - broadcasting between all peers not only server-client 7 | - handling I/O, better interface 8 | 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **[UNDER DEVELOPMENT CURRENTLY!!]** 2 | 3 | # X-Hall 4 | X-Hall is a CLI-based Chatroom program hosted on Internal IIT Delhi Network 5 | 6 | # How to Setup your own server 7 | - Clone the repo on the device you want to host the server on 8 | - Use the following commands 9 | 10 | ``` 11 | git clone https://github.com/Madhav-MKNC/X-Hall.git 12 | cd X-Hall 13 | pip install -r requirements.txt 14 | ``` 15 | 16 | - Now run the Server Program and Host it on the network 17 | ``` 18 | python server.py 19 | ``` 20 | or 21 | ``` 22 | python3 server.py 23 | ``` 24 | 25 | - Do not forget to update Host IP and Port in the Client program 26 | - Now users can run the Client program and join the Server 27 | -------------------------------------------------------------------------------- /server/utils.py: -------------------------------------------------------------------------------- 1 | # helper functions 2 | 3 | # for unique usernames 4 | unique_username = 1 5 | def get_username(): 6 | global unique_username 7 | unique_username += 1 8 | return "user{unique_username}" 9 | 10 | 11 | def filterinfo(ip,port): 12 | try: 13 | port = int(port) 14 | # other host names also valid so will update this using sockets 15 | if ip=='localhost': 16 | if 0 'localhost' 27 | # default port => 1234 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Madhav Kumar 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. 22 | -------------------------------------------------------------------------------- /client/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | """ 5 | This is setup script which will set the environment for client.py program 6 | """ 7 | 8 | import os 9 | import sys 10 | from os import system as cmd 11 | 12 | def install_dependencies(): 13 | try: 14 | if os.path.exists('requirements.txt'): 15 | print("[+] Wait for a moment, installing dependencies (requirements.txt)") 16 | cmd("pip install -r requirements.txt") 17 | else: 18 | print("[-] requirements.txt not found, install all the dependencies [https://github.com/madhav-mknc/x-hall]") 19 | sys.exit() 20 | except Exception as e: 21 | print("[!]",str(e)) 22 | 23 | def filterinfo(ip,port): 24 | try: 25 | port = int(port) 26 | 27 | # other host names also valid so will update this using sockets 28 | if ip=='localhost': 29 | if 0 'localhost' 40 | # default port => 1234 41 | 42 | class Host: 43 | def __init__(self) -> None: 44 | try: 45 | with open('.hostinfo','r') as file: 46 | info = file.read().split(":") 47 | self.ip, self.port = filterinfo(*info) 48 | except Exception as e: 49 | print("[error]",str(e)) 50 | self.ip, self.port = filterinfo(0,0) 51 | 52 | 53 | if __name__ == "__main__": 54 | ip = input("[=] Enter Host IP: ") 55 | port = input("[=] Enter port to connect: ") 56 | 57 | ip, port = filterinfo(ip,port) 58 | info = f"{ip}:{port}" 59 | 60 | file = open('.hostinfo','w') 61 | file.write(info) 62 | file.close() 63 | print("[+] Host info saved") 64 | 65 | install_dependencies() -------------------------------------------------------------------------------- /client/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | # title: X-HALL client 5 | # author: Madhav Kumar (https://github.com/Madhav-MKNC) 6 | # created: 20th Feb 2023 7 | 8 | import socket 9 | import threading 10 | import sys 11 | from setup import Host 12 | from constants import * 13 | 14 | # client class 15 | class Client: 16 | def __init__(self, name): 17 | self.NAME = name 18 | self.HOSTIP = Host().ip 19 | self.PORT = Host().port 20 | self.sys_exit = False 21 | 22 | def send(self, data): 23 | try: 24 | self.sock.send(data.encode(ENCODING)) 25 | except ConnectionError: 26 | self.sock.close() 27 | print("[Connection lost!]") 28 | self.sys_exit = True 29 | 30 | def recv(self): 31 | try: 32 | data = self.sock.recv(BUFFERSIZE).decode(ENCODING) 33 | return data 34 | except ConnectionError: 35 | self.sock.close() 36 | print("[Connection lost!]") 37 | self.sys_exit = True 38 | 39 | def enter_chatroom(self): 40 | # This method first sends the user info of the client to the server and then receives a banner or a welcome 41 | self.send(self.NAME) 42 | banner = self.recv() 43 | print(banner) 44 | 45 | def connect(self): 46 | try: 47 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 48 | except socket.error as e: 49 | print("[!] Failed to create a socket") 50 | print("[error]",str(e)) 51 | sys.exit() 52 | 53 | try: 54 | print(f"[*] Connecting to {self.HOSTIP}:{self.PORT}") 55 | self.sock.connect((self.HOSTIP,self.PORT)) 56 | print(f"[+] Connected") 57 | self.enter_chatroom() 58 | self.chat() 59 | except Exception as e: 60 | print("[error]", str(e)) 61 | self.sock.close() 62 | 63 | def send_messages(self): 64 | try: 65 | while True: 66 | print(f"<{self.NAME} *> ",end="") 67 | data = input().strip() 68 | if len(data)==0: 69 | continue 70 | if data.lower() in ["exit", "quit"]: 71 | print("[You Exited!]") 72 | self.sock.close() 73 | self.sys_exit = True 74 | data = f"<{self.NAME}>: {data}" 75 | self.send(data) 76 | except KeyboardInterrupt: 77 | print("[You Exited!]") 78 | self.sock.close() 79 | self.sys_exit = True 80 | 81 | def recv_messages(self): 82 | try: 83 | while True: 84 | data = self.recv() 85 | if data: print(data) 86 | except Exception as e: 87 | print("[error]",str(e)) 88 | self.sock.close() 89 | self.sys_exit = True 90 | 91 | def chat(self): 92 | try: 93 | if self.sys_exit: sys.exit() 94 | threading.Thread(target=self.send_messages, daemon=True).start() 95 | threading.Thread(target=self.recv_messages, daemon=True).start() 96 | threading.Event().wait() # wait forever 97 | except Exception as e: 98 | print("[ERROR]",str(e)) 99 | self.sock.close() 100 | 101 | if __name__ == "__main__": 102 | name = input("[ ] Enter your name: ").strip().replace(' ','_') 103 | Client(name).connect() -------------------------------------------------------------------------------- /client/requirements.txt: -------------------------------------------------------------------------------- 1 | alabaster==0.7.12 2 | altgraph==0.17 3 | appdirs==1.4.4 4 | APScheduler==3.6.3 5 | astroid==2.5.1 6 | atomicwrites==1.4.0 7 | attrs==20.3.0 8 | Automat==20.2.0 9 | Babel==2.9.0 10 | beautifulsoup4==4.9.3 11 | black==20.8b1 12 | bs4==0.0.1 13 | certifi==2020.12.5 14 | cffi==1.14.6 15 | chardet==4.0.0 16 | cheap-repr==0.4.5 17 | click==7.1.2 18 | colorama==0.4.4 19 | comtypes==1.1.9 20 | constantly==15.1.0 21 | crayons==0.3.1 22 | cryptography==3.4.7 23 | cssselect==1.1.0 24 | cyberbrain==0.1.5 25 | DateTime==4.3 26 | decorator==4.4.2 27 | dnspython==2.3.0 28 | docopt==0.6.2 29 | docutils==0.16 30 | et-xmlfile==1.1.0 31 | flake8==3.9.1 32 | Flask==2.0.2 33 | future==0.18.2 34 | get-port==0.0.5 35 | google==3.0.0 36 | greenlet==2.0.1 37 | gTTS==2.1.1 38 | gTTS-token==1.1.4 39 | h2==3.2.0 40 | hpack==3.0.0 41 | huepy==1.2.1 42 | hyperframe==5.2.0 43 | hyperlink==21.0.0 44 | idna==2.10 45 | imageio==2.9.0 46 | imageio-ffmpeg==0.4.3 47 | imagesize==1.2.0 48 | incremental==21.3.0 49 | iniconfig==1.1.1 50 | insta-scrape==2.1.2 51 | instabot==0.117.0 52 | instagram-scraper==1.10.2 53 | instagramy==4.4 54 | isort==5.7.0 55 | itemadapter==0.3.0 56 | itemloaders==1.0.4 57 | itsdangerous==2.0.1 58 | Jinja2==3.0.2 59 | jmespath==0.10.0 60 | Js2Py==0.70 61 | jsonpickle==1.5.2 62 | lazy-object-proxy==1.5.2 63 | ldap3==2.9.1 64 | ldapdomaindump==0.9.4 65 | linecache2==1.0.0 66 | lxml==4.6.3 67 | MarkupSafe==2.0.1 68 | mccabe==0.6.1 69 | mock==4.0.3 70 | more-itertools==8.7.0 71 | MouseInfo==0.1.3 72 | moviepy==1.0.3 73 | msgpack==1.0.2 74 | mypy-extensions==0.4.3 75 | mysql==0.0.2 76 | mysql-connector-python==8.0.24 77 | mysqlclient==2.0.3 78 | newsapi-python==0.2.6 79 | numpy==1.20.1 80 | opencv-contrib-python==4.5.3.56 81 | opencv-python==4.5.3.56 82 | openpyxl==3.0.10 83 | packaging==20.9 84 | pandas==1.2.4 85 | parsel==1.6.0 86 | pathspec==0.8.1 87 | pefile==2021.5.24 88 | phonenumbers==8.10.2 89 | Pillow==8.1.2 90 | pipwin==0.5.1 91 | playsound==1.2.2 92 | playwright==1.28.0 93 | pluggy==0.13.1 94 | plyer==2.0.0 95 | portpicker==1.3.1 96 | priority==1.3.0 97 | proglog==0.1.9 98 | prompt-toolkit==1.0.14 99 | Protego==0.1.16 100 | protobuf==3.15.8 101 | psutil==5.8.0 102 | py==1.10.0 103 | pyasn1==0.4.8 104 | pyasn1-modules==0.2.8 105 | PyAudio @ file:///C:/Users/Madhav/PyAudio-0.2.11-cp39-cp39-win_amd64.whl 106 | PyAutoGUI==0.9.52 107 | pycodestyle==2.7.0 108 | pycparser==2.20 109 | pycryptodomex==3.17 110 | PyDispatcher==2.0.5 111 | pyee==9.0.4 112 | pyfiglet==0.8.post1 113 | pyflakes==2.3.1 114 | pygame==2.0.1 115 | PyGetWindow==0.0.9 116 | Pygments==2.8.1 117 | pyinstaller==4.4 118 | pyinstaller-hooks-contrib==2021.2 119 | pyjsparser==2.7.1 120 | pylint==2.7.2 121 | PyMsgBox==1.0.9 122 | PyMySQL==1.0.2 123 | pynput==1.7.5 124 | pyOpenSSL==20.0.1 125 | pyparsing==2.4.7 126 | pyperclip==1.8.2 127 | pypiwin32==223 128 | pyppeteer==0.2.5 129 | PyPrind==2.11.2 130 | PyRect==0.1.4 131 | PyScreeze==0.1.27 132 | pySmartDL==1.3.4 133 | PySocks==1.7.1 134 | pyTelegramBotAPI==4.7.1 135 | pytest==6.2.2 136 | pytest-base-url==2.0.0 137 | pytest-playwright==0.3.0 138 | python-dateutil==2.8.1 139 | python-dotenv==0.12.0 140 | python-nmap==0.7.1 141 | python-slugify==7.0.0 142 | python-telegram-bot==13.4.1 143 | pyttsx3==2.90 144 | pytube==10.6.0 145 | PyTweening==1.0.3 146 | pytz==2021.1 147 | pywhatkit==4.1 148 | pywin32==300 149 | pywin32-ctypes==0.2.0 150 | queuelib==1.6.1 151 | redis==3.5.3 152 | regex==2020.11.13 153 | requests==2.25.1 154 | requests-toolbelt==0.9.1 155 | responses==0.16.0 156 | schedule==1.1.0 157 | Scrapy==2.5.0 158 | screen-brightness-control==0.11.3 159 | selenium==3.141.0 160 | service-identity==21.1.0 161 | shortuuid==1.0.1 162 | six==1.15.0 163 | snowballstemmer==2.1.0 164 | soupsieve==2.2.1 165 | SpeechRecognition==3.8.1 166 | sphinxcontrib-applehelp==1.0.2 167 | sphinxcontrib-devhelp==1.0.2 168 | sphinxcontrib-htmlhelp==1.0.3 169 | sphinxcontrib-jsmath==1.0.1 170 | sphinxcontrib-qthelp==1.0.3 171 | sphinxcontrib-serializinghtml==1.1.4 172 | termcolor==1.1.0 173 | text-unidecode==1.3 174 | toml==0.10.2 175 | tornado==6.1 176 | tqdm==4.59.0 177 | traceback2==1.4.0 178 | transitions==0.8.8 179 | Twisted==21.2.0 180 | twisted-iocpsupport==1.0.1 181 | typed-ast==1.4.2 182 | typing-extensions==3.7.4.3 183 | tzlocal==2.1 184 | ujson==4.0.2 185 | unittest2==1.1.0 186 | urllib3==1.26.4 187 | w3lib==1.22.0 188 | wcwidth==0.2.5 189 | websockets==8.1 190 | Werkzeug==2.0.2 191 | whaaaaat==0.5.2 192 | wikipedia==1.4.0 193 | WMI==1.5.1 194 | wplay==8.0.5 195 | wrapt==1.12.1 196 | xlrd==2.0.1 197 | zope.interface==5.4.0 -------------------------------------------------------------------------------- /server/requirements.txt: -------------------------------------------------------------------------------- 1 | alabaster==0.7.12 2 | altgraph==0.17 3 | appdirs==1.4.4 4 | APScheduler==3.6.3 5 | astroid==2.5.1 6 | atomicwrites==1.4.0 7 | attrs==20.3.0 8 | Automat==20.2.0 9 | Babel==2.9.0 10 | beautifulsoup4==4.9.3 11 | black==20.8b1 12 | bs4==0.0.1 13 | certifi==2020.12.5 14 | cffi==1.14.6 15 | chardet==4.0.0 16 | cheap-repr==0.4.5 17 | click==7.1.2 18 | colorama==0.4.4 19 | comtypes==1.1.9 20 | constantly==15.1.0 21 | crayons==0.3.1 22 | cryptography==3.4.7 23 | cssselect==1.1.0 24 | cyberbrain==0.1.5 25 | DateTime==4.3 26 | decorator==4.4.2 27 | dnspython==2.3.0 28 | docopt==0.6.2 29 | docutils==0.16 30 | et-xmlfile==1.1.0 31 | flake8==3.9.1 32 | Flask==2.0.2 33 | future==0.18.2 34 | get-port==0.0.5 35 | google==3.0.0 36 | greenlet==2.0.1 37 | gTTS==2.1.1 38 | gTTS-token==1.1.4 39 | h2==3.2.0 40 | hpack==3.0.0 41 | huepy==1.2.1 42 | hyperframe==5.2.0 43 | hyperlink==21.0.0 44 | idna==2.10 45 | imageio==2.9.0 46 | imageio-ffmpeg==0.4.3 47 | imagesize==1.2.0 48 | incremental==21.3.0 49 | iniconfig==1.1.1 50 | insta-scrape==2.1.2 51 | instabot==0.117.0 52 | instagram-scraper==1.10.2 53 | instagramy==4.4 54 | isort==5.7.0 55 | itemadapter==0.3.0 56 | itemloaders==1.0.4 57 | itsdangerous==2.0.1 58 | Jinja2==3.0.2 59 | jmespath==0.10.0 60 | Js2Py==0.70 61 | jsonpickle==1.5.2 62 | lazy-object-proxy==1.5.2 63 | ldap3==2.9.1 64 | ldapdomaindump==0.9.4 65 | linecache2==1.0.0 66 | lxml==4.6.3 67 | MarkupSafe==2.0.1 68 | mccabe==0.6.1 69 | mock==4.0.3 70 | more-itertools==8.7.0 71 | MouseInfo==0.1.3 72 | moviepy==1.0.3 73 | msgpack==1.0.2 74 | mypy-extensions==0.4.3 75 | mysql==0.0.2 76 | mysql-connector-python==8.0.24 77 | mysqlclient==2.0.3 78 | newsapi-python==0.2.6 79 | numpy==1.20.1 80 | opencv-contrib-python==4.5.3.56 81 | opencv-python==4.5.3.56 82 | openpyxl==3.0.10 83 | packaging==20.9 84 | pandas==1.2.4 85 | parsel==1.6.0 86 | pathspec==0.8.1 87 | pefile==2021.5.24 88 | phonenumbers==8.10.2 89 | Pillow==8.1.2 90 | pipwin==0.5.1 91 | playsound==1.2.2 92 | playwright==1.28.0 93 | pluggy==0.13.1 94 | plyer==2.0.0 95 | portpicker==1.3.1 96 | priority==1.3.0 97 | proglog==0.1.9 98 | prompt-toolkit==1.0.14 99 | Protego==0.1.16 100 | protobuf==3.15.8 101 | psutil==5.8.0 102 | py==1.10.0 103 | pyasn1==0.4.8 104 | pyasn1-modules==0.2.8 105 | PyAudio @ file:///C:/Users/Madhav/PyAudio-0.2.11-cp39-cp39-win_amd64.whl 106 | PyAutoGUI==0.9.52 107 | pycodestyle==2.7.0 108 | pycparser==2.20 109 | pycryptodomex==3.17 110 | PyDispatcher==2.0.5 111 | pyee==9.0.4 112 | pyfiglet==0.8.post1 113 | pyflakes==2.3.1 114 | pygame==2.0.1 115 | PyGetWindow==0.0.9 116 | Pygments==2.8.1 117 | pyinstaller==4.4 118 | pyinstaller-hooks-contrib==2021.2 119 | pyjsparser==2.7.1 120 | pylint==2.7.2 121 | PyMsgBox==1.0.9 122 | PyMySQL==1.0.2 123 | pynput==1.7.5 124 | pyOpenSSL==20.0.1 125 | pyparsing==2.4.7 126 | pyperclip==1.8.2 127 | pypiwin32==223 128 | pyppeteer==0.2.5 129 | PyPrind==2.11.2 130 | PyRect==0.1.4 131 | PyScreeze==0.1.27 132 | pySmartDL==1.3.4 133 | PySocks==1.7.1 134 | pyTelegramBotAPI==4.7.1 135 | pytest==6.2.2 136 | pytest-base-url==2.0.0 137 | pytest-playwright==0.3.0 138 | python-dateutil==2.8.1 139 | python-dotenv==0.12.0 140 | python-nmap==0.7.1 141 | python-slugify==7.0.0 142 | python-telegram-bot==13.4.1 143 | pyttsx3==2.90 144 | pytube==10.6.0 145 | PyTweening==1.0.3 146 | pytz==2021.1 147 | pywhatkit==4.1 148 | pywin32==300 149 | pywin32-ctypes==0.2.0 150 | queuelib==1.6.1 151 | redis==3.5.3 152 | regex==2020.11.13 153 | requests==2.25.1 154 | requests-toolbelt==0.9.1 155 | responses==0.16.0 156 | schedule==1.1.0 157 | Scrapy==2.5.0 158 | screen-brightness-control==0.11.3 159 | selenium==3.141.0 160 | service-identity==21.1.0 161 | shortuuid==1.0.1 162 | six==1.15.0 163 | snowballstemmer==2.1.0 164 | soupsieve==2.2.1 165 | SpeechRecognition==3.8.1 166 | sphinxcontrib-applehelp==1.0.2 167 | sphinxcontrib-devhelp==1.0.2 168 | sphinxcontrib-htmlhelp==1.0.3 169 | sphinxcontrib-jsmath==1.0.1 170 | sphinxcontrib-qthelp==1.0.3 171 | sphinxcontrib-serializinghtml==1.1.4 172 | termcolor==1.1.0 173 | text-unidecode==1.3 174 | toml==0.10.2 175 | tornado==6.1 176 | tqdm==4.59.0 177 | traceback2==1.4.0 178 | transitions==0.8.8 179 | Twisted==21.2.0 180 | twisted-iocpsupport==1.0.1 181 | typed-ast==1.4.2 182 | typing-extensions==3.7.4.3 183 | tzlocal==2.1 184 | ujson==4.0.2 185 | unittest2==1.1.0 186 | urllib3==1.26.4 187 | w3lib==1.22.0 188 | wcwidth==0.2.5 189 | websockets==8.1 190 | Werkzeug==2.0.2 191 | whaaaaat==0.5.2 192 | wikipedia==1.4.0 193 | WMI==1.5.1 194 | wplay==8.0.5 195 | wrapt==1.12.1 196 | xlrd==2.0.1 197 | zope.interface==5.4.0 -------------------------------------------------------------------------------- /server/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | # title: X-HALL server 5 | # author: Madhav Kumar (https://github.com/Madhav-MKNC) 6 | # created: 20th Feb 2023 7 | 8 | import socket 9 | import threading 10 | import sys 11 | from constants import BUFFERSIZE, ENCODING, MAX_CONNECTIONS 12 | from utils import get_username, filterinfo 13 | 14 | # INNER Class STUFF TO BE DONE 15 | class Client: 16 | def __init__(self, client_sock, hostname): 17 | self.sock = client_sock 18 | self.name = "" 19 | self.HOSTNAME = hostname 20 | 21 | def exists(self, name): 22 | # check the username if already active on the server (INNER CLASS implementation will fix this) 23 | return False 24 | 25 | def set_username(self): 26 | response = self.recv() 27 | if len(response)>0 and self.exists(response)==False: 28 | self.name = response 29 | else: 30 | self.name = get_username() 31 | self.send(f"[{self.HOSTNAME}] Username Invalid! You are {self.name}") 32 | 33 | def send_banner(self): 34 | banner = f"[ ---------- WELCOME TO THE '{self.HOSTNAME}' CHATROOM ---------- ]" 35 | self.send(banner) 36 | 37 | def send(self, message): 38 | try: 39 | self.sock.send(message.encode(ENCODING)) 40 | except ConnectionError: 41 | print(f"[<{self.name}> DISCONNECTED!]") 42 | self.sock.close() 43 | 44 | def recv(self): 45 | try: 46 | data = self.sock.recv(BUFFERSIZE).decode(ENCODING) 47 | return data 48 | except ConnectionError: 49 | print(f"[<{self.name}> DISCONNECTED!]") 50 | self.sock.close() 51 | 52 | def send_messages(self): 53 | try: 54 | while True: 55 | print(f"<{self.HOSTNAME} *> ",end="") 56 | message = input().strip() 57 | if len(message)==0: continue 58 | if message=="shutdown": 59 | print("[shutdown!]") 60 | self.sock.close() 61 | return 62 | message = f"<{self.HOSTNAME} *> "+message 63 | self.send(message) 64 | except Exception as e: 65 | print("[error]",str(e)) 66 | self.sock.close() 67 | return 68 | 69 | def recv_messages(self): 70 | try: 71 | while True: 72 | data = self.recv() 73 | if data: print(data) 74 | except Exception as e: 75 | print("[error]",str(e)) 76 | self.sock.close() 77 | return 78 | 79 | class Server: 80 | def __init__(self, ip, port, hostname): 81 | self.HOSTIP = ip 82 | self.PORT = port 83 | self.HOSTNAME = hostname 84 | self.clients = [] 85 | 86 | def chat(self, client): 87 | try: 88 | client.send_banner() 89 | client.set_username() 90 | self.clients.append(client) 91 | self.broadcast(f"[+] <{client.name}> joined the chat room.") 92 | threading.Thread(target=client.send_messages, daemon=True).start() 93 | threading.Thread(target=client.recv_messages, daemon=True).start() 94 | threading.Event().wait() 95 | except KeyboardInterrupt: 96 | self.sock.shutdown(2) 97 | print("[shutdown!]") 98 | return 99 | except ConnectionError: 100 | self.clients.remove(client) 101 | self.broadcast(f"[<{client.name}> left!]") 102 | client.sock.close() 103 | except Exception as e: 104 | print("[error]",str(e)) 105 | self.sock.shutdown() 106 | self.exit() 107 | 108 | def broadcast(self, message): 109 | for client in self.clients: 110 | try: 111 | client.send(message) 112 | except: 113 | self.clients.remove(client) 114 | self.broadcast(f"[<{client.name}> left!]") 115 | 116 | def start(self): 117 | try: 118 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 119 | self.sock.bind((self.HOSTIP, self.PORT)) 120 | # maximum connections 5 121 | self.sock.listen(MAX_CONNECTIONS) 122 | print(f"[+] Waiting for Connections at {self.HOSTIP}:{self.PORT}") 123 | 124 | while True: 125 | client_sock, addr = self.sock.accept() 126 | client = Client(client_sock, self.HOSTNAME) 127 | print(f"[+] New client connected from {addr[0]}:{addr[1]}") 128 | threading.Thread(target=self.chat, daemon=True, args=(client,)).start() 129 | # threading.Event().wait() 130 | except socket.error as e: 131 | print("[!] Failed to start server") 132 | print("[error]", str(e)) 133 | 134 | 135 | if __name__ == '__main__': 136 | host = input("Enter host IP: ") 137 | # port = int(input("Enter Port: ")) 138 | # host = "10.7.10.71" 139 | port = input("Enter port: ") 140 | host, port = filterinfo(host, port) 141 | 142 | Server(host, port, 'X-Hall').start() 143 | --------------------------------------------------------------------------------