{"id":27522676,"url":"https://github.com/lbxa/ghostr","last_synced_at":"2025-10-11T18:17:52.392Z","repository":{"id":123985301,"uuid":"426896190","full_name":"lbxa/ghostr","owner":"lbxa","description":"CLI socket chat app","archived":false,"fork":false,"pushed_at":"2021-11-19T04:28:00.000Z","size":75,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-18T22:43:09.227Z","etag":null,"topics":["p2p","python","server"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lbxa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-11-11T06:34:25.000Z","updated_at":"2022-02-14T01:00:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"535b1c3b-70b9-4739-8fae-1d17bea7d9ab","html_url":"https://github.com/lbxa/ghostr","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lbxa/ghostr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lbxa%2Fghostr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lbxa%2Fghostr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lbxa%2Fghostr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lbxa%2Fghostr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lbxa","download_url":"https://codeload.github.com/lbxa/ghostr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lbxa%2Fghostr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279008301,"owners_count":26084429,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["p2p","python","server"],"created_at":"2025-04-18T10:26:39.321Z","updated_at":"2025-10-11T18:17:52.386Z","avatar_url":"https://github.com/lbxa.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Assignment Report\n\nLucas Barbosa (z5259433) COMP3331 21T3\n\n# 1. Installation\n\n## 1.1. Pyenv\n\nUsing Python version 3.7.0 (installed locally with pyenv) and managed using venv. \n\n```\n$ pyenv install 3.7.0\n$ pyenv local 3.7.0\n$ python -m venv .venv\n$ . .venv/bin/activate\n(.venv) $ ...\n```\n\nFor VSCode venv configuration add these lines to the `.vscode/settings.json` file\n\n```json\n\"python.terminal.activateEnvironment\": true\n```\n\n## 1.2. Regular Python\n\nThe version of Python used for this project was `3.7.0`.\n\n## 1.3. Usage\n\nOn CSE machines:\n\n- Server\n    \n    ```\n    python3 server.py 3331 10 120\n    ```\n    \n- Client\n    \n    ```\n    python3 client.py 3331\n    ```\n    \n    NOTE*: For private messages both users must use `privatestart \u003cuser\u003e` to initiate a private chat\n    \n\n# 2. Design Choices\n\n## 2.1. Software\n\n- Originally I designed an overcomplicated messaging protocol (XMPP) which made parsing messages between server and client more error prone.\n    - A simple fix was to switch JSON format. The obvious advantages its lightweightness, user-friendlyness and wide adoption.\n- Python was the choice of programming language because of its simplicity, especially for socket programming. Java and C would result in many more lines of code. The **tradeoff** was type and memory safety in exchange for a faster production time. This was a crucial factor considering the numerous deadlines in the final weeks of the term.\n- Multi-threading was used heavily throughout both the client, server and P2P programs. Threads brought more flexibilty and control instead of using the `select` module.\n    - Threads are easily inheritered by custom classes which carry all the functionality to make sending and receiving data asynchronously effortless.\n    - Killing threads was very easy and made the program more stable in the case of an unexpected socket failure.\n- Design patterns such as lookup tables were used to elliminate giant if/else blocks where possible (evident in client.py)\n\n## 2.1. Data Structures\n\n- Python sets were used where a data structure that ensured no duplicate items was needed.\n- Classes were used to encapsulate multiple functions that belonged to the same domain of functionality (e.g. User class in `user.py`)\n- Codebase was split into multiple files (Python modules) for organisation based on functionality\n    - [server.py](http://server.py) handles all the server functionality\n    - [client.py](http://client.py) handles all client functionality including P2P connections\n    - [user.py](http://user.py) contains all management and CRUD operations for individual user operations\n    - [admin.py](http://admin.py) contains all functions responsible for managing all users and their P2P connections\n    - [library.py](http://library.py) contains any helper or auxiliary methods\n    - [constants.py](http://constants.py) holds all constant values common to all modules\n\n# 3. Protocol\n\nFor this assignment, I am adapting my own messaging protocol which is inspired by the [XMPP protocol](https://en.wikipedia.org/wiki/XMPP) used by WhatsApp.\n\nEventually it became way too overkill and I integrated it with JSON to simplify the program logic. Having a more complex protocol layer also made the server more prone to message parsing and encoding errors. \n\n## 3.1. Logging On\n\n```\n# Request\nTYPE: LOGON;;\nWHO: \u003cusername\u003e;;\n\n# Response\nTYPE: LOGON;;\nRET: \u003c1|0\u003e;;\nNEW: \u003c1|0\u003e;;\nERR: \u003c0|string\u003e;;\n```\n\n### 3.1.1. Logged In\n\nThe server will notify the client when a user has successfully logged in\n\n```\n# Response\nTYPE: LOGGEDIN;;\nWHO: \u003cusername\u003e;;\n```\n\n## 3.2. Auth\n\n```\n# Request\nTYPE: AUTH;;\nWHO: \u003cusername\u003e;;\nPASW: \u003cpassword\u003e;;\nNEW: \u003c1|0\u003e;;\n\n# Response\nTYPE: AUTH;;\nPASW: \u003c1|0\u003e;;\nATMP: \u003cint\u003e;;\n```\n\n```json\n{\n  \"action\": \"login\",\n  \"username\": \"username\",\n  \"password\": \"password\",\n\t\"private_port\": \"private_recv_port\"\n}\n```\n\n| action | Description of action the server is taking next. |\n| --- | --- |\n| username | String |\n| password | String |\n| private_port | Each user is assigned a private port upon authentication. This enables the P2P connection for private messaging.  |\n\n## 3.3. Messaging\n\nFor a unicast message (send and received by one person):\n\n```\nTYPE: MSG;;\nFROM: \u003cusername\u003e;;\nTO:   \u003cusername\u003e;;\nBODY: \u003cstring\u003e;;\n```\n\nFor a broadcasted message (sent by one and receivd by many):\n\n```\nTYPE: BROADCAST;;\nFROM: \u003cusername\u003e;;\nBODY: \u003cstring\u003e;;\n```\n\n```json\n{\n  \"action\": \"message\",\n  \"message\": \"message_contents\",\n\t\"user\": \"username\"\n}\n```\n\n## 3.4. Auxilliary Commands\n\n### 3.4.1. Whoelse\n\n```\n# Request\nTYPE: WHOELSE;;\nFROM: \u003cusername\u003e;;\n\n# Response\nTYPE: WHOELSE;;\nFROM: \u003cusername\u003e;;\nBODY: \u003cstring\u003e;;\n```\n\n| TYPE | Command signature |\n| --- | --- |\n| FROM | The user who issued the command |\n| BODY | Comma-separated list of names of users that are currently online |\n\n### 3.4.2. Whoelsesince\n\n```\n# Request\nTYPE: WHOELSESINCE;;\nWHEN: \u003ctime\u003e;;\nFROM: \u003cusername\u003e;;\n\n# Response\nTYPE: WHOELSESINCE;;\nWHEN: \u003ctime\u003e;;\nFROM: \u003cusername\u003e;;\nBODY: \u003cstring\u003e;;\n```\n\n| TYPE | Command signature |\n| --- | --- |\n| WHEN | Time to check since users were last active |\n| FROM | The user who issued the command |\n| BODY | Comma-separated list of names of users that are currently online since TIME x in seconds |\n\n## 3.5. Blocking\n\nBlocks user with a `username` for an indefinite period of time.\n\n```\n# Request\nTYPE: BLOCK|UNBLOCK;;\nWHO: \u003cblockee\u003e;;\nBLOCKER: \u003cusername\u003e;;\n\n# Response\nTYPE: BLOCK|UNBLOCK;;\nWHO: \u003cblockee\u003e;;\nBLOCKER: \u003cusername\u003e;;\nRET: \u003c1|0\u003e;;\nERR: \u003c0|string\u003e;;\n```\n\n### 3.5.1. Server Blocking\n\nIf a user is blocked by the server from multiple incorrect password attempts the field in the blocked data structure would look like this:\n\n```json\n{\n\t\"username\": \"string\",\n\t\"start\": \"string\",\n\t\"end\": \"string\",\n\t\"blocked_by\": \"!SERVER!\"\n}\n```\n\n| START | Time when server blocked user. |\n| --- | --- |\n| END | Timestamp for when server blocking expires. |\n| BLOCKED_BY | Differentiates between regular use and daemon process. |\n\n### 3.5.2. User Blocking\n\nIf a user blocks another user manually it will look slightly differently:\n\n```json\n{\n\t\"username\": \"string\",\n\t\"start\": \"\",\n\t\"end\": \"\",\n\t\"blocked_by\": \"string\"\n}\n```\n\n| START | Not required. |\n| --- | --- |\n| END | Not required. |\n| BLOCKED_BY | Regular user who has blocked username |\n\nWhen a user is blocked/unblocked by another user the JSON message follows this structure:\n\n```json\n{\n  \"action\": \"block/unblock\",\n  \"user\": \"username\",\n}\n```\n\n## 3.6. Private\n\n### 3.6.1. Start Private\n\n```\n# Request\nTYPE: PRIVREQ;;\nTO: \u003cusername\u003e;;\nFROM: \u003cusername\u003e;;\n\n# Response\nTYPE: PRIVREQ;;\nTO: \u003cusername\u003e;;\nFROM: \u003cusername\u003e;;\nRET: \u003c0|1\u003e;;\nDEST: \u003cport\u003e;;\n```\n\n| TO | User on the other side of the private message request. |\n| --- | --- |\n| RET | Flag to verify whether user has accepted the invitation to a private chat. |\n| DEST | Port destination for the current client to communicate with in order to bypass the server. |\n\n### 3.6.2. Private Messages\n\n```\n# Request\nTYPE: PRIVMSG;;\nTO: \u003cusername\u003e;;\nFROM: \u003cusername\u003e;;\nBODY: \u003cstring\u003e;;\n\n# Response\nTYPE: PRIVMSG;;\nTO: \u003cusername\u003e;;\nFROM: \u003cusername\u003e;;\nBODY: \u003cstring\u003e;;\n```\n\n### 3.6.3. Stop Private\n\n```\n# Request\nTYPE: PRIVSTOP;;\nTO: \u003cusername\u003e;;\nFROM: \u003cusername\u003e;;\n\n# Response\nTYPE: PRIVSTOP;;\nTO: \u003cusername\u003e;;\nFROM: \u003cusername\u003e;;\nRET: \u003c0|1\u003e;;\n```\n\n## 3.7 Logout\n\n```\n# Request\nTYPE: LOGOUT;;\nWHO: \u003cusername\u003e;;\n\n# Response\nTYPE: LOGOUT;;\nWHO: \u003cusername\u003e;;\n\n# Response\nTYPE: LOGGEDOUT;;\nWHO: \u003cusername\u003e;;\n```\n\n# 4. Improvements\n\n- Add a type-checking system for Python to ensure maximal type safety (such as mypy)\n- More error checking for incorrect command syntax from the user\n- Adding a `--help` option that lists all available commands and how to use them\n- Support for file upload and download\n- Support for communicating beyond a single machine (hosting on a web server)\n\n# 5. Acknowledgments\n\n- COMP3331 21T3 provided [TCPClient.py](http://TCPClient.py) and [TCPServer.py](http://TCPServer.py) multi-threaded examples\n- Rui Li's explanation of P2P during Week 10 Lab\n- Salil's COMP331 Lecture material on TCP connections and socket programming\n- Various Youtube tutorials on Python socket programming and multi-threading\n- [Stackoverflow.com](http://Stackoverflow.com) for Pythonic design principles and syntactical sugar\n    - Example of how to print to the terminal safely when asynchronously receiving messages from the server that will also be displayed on the terminal\n        \n        ```python\n        import readline\n        def chat_print(*args):\n            sys.stdout.write(\"\\r\" + \" \" * (len(readline.get_line_buffer()) + 2) + \"\\r\")\n            print(*args)\n            sys.stdout.write(\"# \" + readline.get_line_buffer())\n            sys.stdout.flush()\n        ```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flbxa%2Fghostr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flbxa%2Fghostr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flbxa%2Fghostr/lists"}