diff --git a/bbs.py b/bbs.py index c5a8d63..7cf3dca 100644 --- a/bbs.py +++ b/bbs.py @@ -42,8 +42,7 @@ class bbsServer(): try: comms(conn, addr, node + 1) - - except RuntimeError: + except (RuntimeError, OSError): print("Node %s hung up..." % (node + 1)) print("Node %s offline." % (node + 1)) diff --git a/config.ini b/config.ini index 08b7420..a98651f 100644 --- a/config.ini +++ b/config.ini @@ -1,8 +1,9 @@ [Main] -Port = 2043 +Port = 2062 DefaultSplash = "Welcome to the SB17 BBS on %node%!" MaxConnections = 5 Menus = 2 +msgDb = messages.json [Auth] userDb = users.json diff --git a/docs/Configuration.md b/docs/Configuration.md index 0cd666d..92d7dcc 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -1,5 +1,4 @@ # Configuration - This file contains the information necessary to configure Pyscii-BBS. The default configuration provided will run, but you will likely want to change many of these values. ## String Replacements diff --git a/docs/Roadmap.md b/docs/Roadmap.md index 3091d12..f995525 100644 --- a/docs/Roadmap.md +++ b/docs/Roadmap.md @@ -26,10 +26,10 @@ Remember these rules for working on the following features: - BBS Server can show a menu to the user after logging in ✓ - BBS Server menu allows user to modify their own profile ✓ - BBS Server can display pre-coded messages in a messages space ✓ +- BBS Server can accept messages from users ✓ +- BBS Server can display user submitted messages to message boards ✓ #### ToDo -- BBS Server can accept messages from users -- BBS Server can display user submitted messages to message boards - BBS Server can show only certain users a different menu option (RBAC implementation) At this point we have the bare minimum of a functioning BBS. The user flow at this stage is they connect, log in, and are presented with a menu that will let them edit their profile or visit a message board. The message board will be a single flow of messages and the user will have to select what messages to read. No unread/read list exists. It is a simple text-based telnet message board with an auth system. diff --git a/docs/message.schema.json b/docs/message.schema.json new file mode 100644 index 0000000..a6dbc1e --- /dev/null +++ b/docs/message.schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://bbs.sb17.space/message.schema.json", + "title": "Message", + "description": "A Pyscii-BBS message object", + "type": "object", + "properties": { + "id" : { + "description": "The unique ID of the message entity", + "type": "integer" + }, + "subject": { + "description": "The subject of the message entity", + "type": "string" + }, + "body": { + "description": "The message body.", + "type": "string" + }, + "date": { + "description": "The date the message is posted", + "type": "datetime", + }, + }, + "required": [ + "id", + "subject", + "body", + "date" + ] +} diff --git a/functions.py b/functions.py index 5e89b98..af197b2 100644 --- a/functions.py +++ b/functions.py @@ -4,11 +4,15 @@ import sys import comms import menus import auth +import datetime config = configparser.ConfigParser() config.read(sys.argv[1]) userDb = config.get("Auth","userDb") +msgDB = config.get("Main","msgDb") + +date_format_string = "%Y-%m-%d HH:MM:SS" def printProfile(conn, userId, node, lastMenu): comms.sendString(conn, "\u001B[2JProfile:\r\n") @@ -50,8 +54,66 @@ def editProfile(conn, userID, node, lastMenu): menus.printMenu(conn, lastMenu, node, userID) def pubMessages(conn, userID, node, lastMenu): - comms.sendString(conn, "There are no messages!\r\nPress any key to go back... ") + comms.sendString(conn, "\u001B[2JMessages:\r\n") + with open(msgDB) as msgdb: + messages = json.loads(msgdb.read()) + for message in messages['messages']: + msgListElement = str(message['id']) + "\t|\t" + message['date'] + "\t|\t" + message['author'] + "\t|\t" + message['subject'] + "\t\t\t" + comms.sendString(conn,msgListElement+"\r\n") + + comms.sendString(conn, "\r\nSend the M and the message number (eg. M0) number to read a message.\r\nPress C to Write a Message \r\nSend B to go back to the Menu: ") + response = comms.getString(conn,3) + if response.strip('\x00') == "B": + menus.printMenu(conn, lastMenu, node, userID) + + elif response.strip('\x00')[0] == "M": + printMessage(conn, int(response.strip('\x00').replace('M',''))) + + elif response.strip('\x00') == "C": + writeMessage(conn, userID) + + pubMessages(conn, userID, node, lastMenu) + +def printMessage(conn, messageNum): + comms.sendString(conn, "\u001B[2JMessage:\r\n") + with open(msgDB) as msgdb: + messages = json.loads(msgdb.read()) + for message in messages['messages']: + if message['id'] == int(messageNum): + comms.sendString(conn, "\r\nDate Published: " + message['date']) + comms.sendString(conn, "\r\nSubject: " + message['subject']) + comms.sendString(conn, "\r\nAuthor: " + message['author']) + comms.sendString(conn, "\r\nMessage: " + message['body']) + + comms.sendString(conn, "\r\nPress any key to go back... ") + comms.getString(conn,2) + return + +def writeMessage(conn, userid): + comms.sendString(conn, "\u001B[2JCompose Message:\r\n") + with open(userDb) as userdb: + users = json.load(userdb) + for user in users['users']: + if user['id'] == userid: + username = user['name'] + with open(msgDB) as msgdb: + messages = json.load(msgdb) + + message = {} + message["id"] = messages['messages'][-1]["id"] + 1 + + message['date'] = datetime.datetime.now().strftime(date_format_string) + comms.sendString(conn,"Subject: ") + message['subject'] = comms.getString(conn, 150) + message['author'] = username + comms.sendString(conn,"Message Body: ") + message['body'] = comms.getString(conn, 1500) + messages['messages'].append(message) + + with open(msgDB,'w') as output: + json.dump(messages, output, indent=4) + + comms.sendString(conn, "Message Published!\r\nPress any key to go back... ") comms.getChar(conn) - menus.printMenu(conn, lastMenu, node, userID) diff --git a/menus.py b/menus.py index 4072418..464c963 100644 --- a/menus.py +++ b/menus.py @@ -34,6 +34,7 @@ def parseResponse(conn, menu, node, user, response): comms.sendString(conn, "\u001B[2J\r") if response.strip('\x00') == "X": comms.sendString(conn, "Goodbye!") + conn.close() if response.strip('\x00') == "P": functions.printProfile(conn, user, node, menu) diff --git a/messages.json b/messages.json new file mode 100644 index 0000000..fe171a8 --- /dev/null +++ b/messages.json @@ -0,0 +1,18 @@ +{ + "messages": [ + { + "id": 0, + "author": "Default", + "subject": "Welcome!", + "body": "This is the first message in every PyScii BBS. It sets up the messages json.", + "date": "1970-01-01 00:00:00" + }, + { + "id": 1, + "date": "2025-07-19 HH:MM:SS", + "subject": "\u0000First User Message", + "author": "J4YC33", + "body": "\u0000This is the first messsage on the Pyscii BBS!\\nIt's a fun thing!" + } + ] +} \ No newline at end of file diff --git a/users.json b/users.json index bf8af96..26b0afe 100644 --- a/users.json +++ b/users.json @@ -12,7 +12,7 @@ "name": "J4YC33", "password": "d32190c6322e27087c934b7bd9ff642bbe71d5782f2242028c3c15298c7aeac6", "salt": "SI13YMJl8kWLBhNN", - "description": "J4YC33 IS SPARTACUS!" + "description": "\u0000I *AM* Spartacus!" } ] -} +} \ No newline at end of file