Initial Commit
This commit is contained in:
parent
72469e2172
commit
b6b67576c1
2
.kdev4/bbs.kdev4
Normal file
2
.kdev4/bbs.kdev4
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[Project]
|
||||||
|
VersionControlSupport=kdevgit
|
@ -1,3 +1,3 @@
|
|||||||
# Pyscii-BBS
|
# Pyscii-BBS
|
||||||
|
|
||||||
A modern implementation of an ASCII bbs in Python with secure data storage options.
|
An actual ascii-based BBS
|
||||||
|
68
auth.py
Normal file
68
auth.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
'''
|
||||||
|
auth.py - an authentication library for adding users and checking them against a flat-file database
|
||||||
|
|
||||||
|
part of Py-BBS.
|
||||||
|
'''
|
||||||
|
import sys, json, configparser, comms, hashlib, random, string, menus
|
||||||
|
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(sys.argv[1])
|
||||||
|
|
||||||
|
dbFile = config.get("Auth","userDb")
|
||||||
|
|
||||||
|
def login(conn, username, password, node):
|
||||||
|
with open(dbFile) as userdb:
|
||||||
|
users = json.loads(userdb.read())
|
||||||
|
for user in users['users']:
|
||||||
|
if user['name'] == username.replace('\x00',''):
|
||||||
|
if user['id'] == 0:
|
||||||
|
comms.sendString(conn, "\r\nThis account is not usable. This event has been logged.\r\n")
|
||||||
|
conn.close()
|
||||||
|
return
|
||||||
|
password = hashlib.sha256(''.join(password.replace('\x00','')).join(user['salt']).encode('utf-8')).hexdigest()
|
||||||
|
if user['password'] == password:
|
||||||
|
menus.printMenu(conn, 0, node, user['id'])
|
||||||
|
else:
|
||||||
|
comms.sendString(conn, "\r\nInvalid Username or Password!\r\n")
|
||||||
|
|
||||||
|
def setProfile(conn):
|
||||||
|
comms.sendString(conn,"\r\nDo you want to set a Description?(Y/N) ")
|
||||||
|
if comms.getString(conn,2).replace("\x00",'')== "Y":
|
||||||
|
comms.sendString(conn,"\r\nWhat do you want to set as your Description?(max 150 char) ")
|
||||||
|
description = comms.getString(conn, 150)
|
||||||
|
else:
|
||||||
|
description = "You Exist!"
|
||||||
|
return description
|
||||||
|
|
||||||
|
def setPassword(conn, salt):
|
||||||
|
comms.sendString(conn, "\r\nWhat do you want your password to be?(max 32 char) ")
|
||||||
|
comms.sendString(conn, "\r\nThis will be stored salted and hashed")
|
||||||
|
password = comms.getString(conn, 32).replace('\x00','')
|
||||||
|
password = hashlib.sha256(''.join(password).join(salt).encode('utf-8')).hexdigest()
|
||||||
|
return password
|
||||||
|
|
||||||
|
def create(conn):
|
||||||
|
salt = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(16))
|
||||||
|
comms.sendString(conn, "\r\nWelcome to SB17's BBS!\r\nPowered by Pyscii-BBS!")
|
||||||
|
comms.sendString(conn, "\r\nWhat is your name? (max 16 char) ")
|
||||||
|
name = comms.getString(conn, 16)
|
||||||
|
comms.sendString(conn, "\r\nYou want your name to be " + name + "?(Y/N) ")
|
||||||
|
if comms.getChar(conn) == "N":
|
||||||
|
comms.sendString(conn, "\r\nRestarting... Press Enter to continue...")
|
||||||
|
comms.getChar(conn)
|
||||||
|
password = setPassword(conn, salt)
|
||||||
|
description = setProfile(conn)
|
||||||
|
with open(dbFile) as userdb:
|
||||||
|
users = json.load(userdb)
|
||||||
|
user = {}
|
||||||
|
user["id"] = users["users"][-1]["id"] + 1
|
||||||
|
user["name"] = name.replace('\x00','').replace('\u0000','')
|
||||||
|
user["password"] = password
|
||||||
|
user["salt"] = salt
|
||||||
|
user["description"] = description
|
||||||
|
users["users"].append(user)
|
||||||
|
with open(dbFile,'w') as output:
|
||||||
|
json.dump(users, output, indent=4)
|
||||||
|
|
||||||
|
comms.sendString(conn,"\r\nYour profile has been created" + name + "! Reconnect to login!")
|
83
bbs.py
Normal file
83
bbs.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
'''
|
||||||
|
BBS.py
|
||||||
|
|
||||||
|
A BBS that was shamelessly based entirely on the blog post found: https://www.fsxnet.nz/tutorials/bbs/python-bbs/start
|
||||||
|
'''
|
||||||
|
import configparser
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
from comms import comms
|
||||||
|
|
||||||
|
nodes = []
|
||||||
|
|
||||||
|
class bbsServer():
|
||||||
|
def __init__(self, port, host='0.0.0.0', nodemax=1):
|
||||||
|
self.port = port
|
||||||
|
self.host = host
|
||||||
|
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
# try:
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read("config.ini")
|
||||||
|
self.nodemax = config.getint("Main","MaxConnections")
|
||||||
|
# except:
|
||||||
|
# self.nodemax = nodemax
|
||||||
|
|
||||||
|
for index in range(nodemax):
|
||||||
|
nodes.append(False)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.server.bind((self.host, self.port))
|
||||||
|
except socket.error:
|
||||||
|
print("Couldn't bind %s" % (socket.error))
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
self.server.listen(10)
|
||||||
|
|
||||||
|
def run_thread(self, conn, addr, node):
|
||||||
|
global nodes
|
||||||
|
|
||||||
|
try:
|
||||||
|
comms(conn, addr, node + 1)
|
||||||
|
|
||||||
|
|
||||||
|
except RuntimeError:
|
||||||
|
print("Node %s hung up..." % (node + 1))
|
||||||
|
|
||||||
|
print("Node %s offline." % (node + 1))
|
||||||
|
nodes[node] = False
|
||||||
|
conn.close()
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
print("Starting BBS on port %s" % (self.port))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
conn, addr = self.server.accept()
|
||||||
|
|
||||||
|
for index in range(self.nodemax):
|
||||||
|
if nodes[index] == False:
|
||||||
|
|
||||||
|
nodes[index] = True
|
||||||
|
threading.Thread(target=self.run_thread, args=(conn, addr, index)).start()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
conn.sendall("BUSY\r\n")
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
|
||||||
|
print("Usage python bbs.py config.ini")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(sys.argv[1])
|
||||||
|
|
||||||
|
server = bbsServer(config.getint("Main", "Port"))
|
||||||
|
|
||||||
|
server.run()
|
||||||
|
|
88
comms.py
Normal file
88
comms.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import auth, sys
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(sys.argv[1])
|
||||||
|
|
||||||
|
def sendString(conn, text):
|
||||||
|
conn.sendall(text.encode('ascii'))
|
||||||
|
|
||||||
|
def getString(conn, max):
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
index = 0
|
||||||
|
|
||||||
|
while index < max:
|
||||||
|
c = getChar(conn)
|
||||||
|
if (c[0] == '\b' or c[0] == 127) and index > 0:
|
||||||
|
result = result[:-1]
|
||||||
|
index -= 1
|
||||||
|
sendString(conn, "\x1b[D \x1b[D")
|
||||||
|
continue
|
||||||
|
elif c[0] == '\b' or c[0] == 127:
|
||||||
|
continue
|
||||||
|
if c[0] == '\r':
|
||||||
|
return result
|
||||||
|
|
||||||
|
result += c[0]
|
||||||
|
sendString(conn, c)
|
||||||
|
index += 1
|
||||||
|
return result
|
||||||
|
|
||||||
|
def getCharRaw(conn):
|
||||||
|
c = conn.recv(1)
|
||||||
|
if c == b'':
|
||||||
|
print("Connection dropped")
|
||||||
|
raise RuntimeError("Socket connection dropped")
|
||||||
|
|
||||||
|
return c
|
||||||
|
|
||||||
|
def getChar(conn):
|
||||||
|
|
||||||
|
while True:
|
||||||
|
c = getCharRaw(conn)
|
||||||
|
while c[0] == 255:
|
||||||
|
c = getCharRaw(conn)
|
||||||
|
if c[0] == 251 or c[0] == 252 or c[0] == 253 or c[0] == 254:
|
||||||
|
c = getCharRaw(conn)
|
||||||
|
elif c[0] == 250:
|
||||||
|
c = getCharRaw(conn)
|
||||||
|
while c[0] != 240:
|
||||||
|
c = getCharRaw(conn)
|
||||||
|
|
||||||
|
c = getCharRaw(conn)
|
||||||
|
|
||||||
|
if c[0] != '\n':
|
||||||
|
break
|
||||||
|
|
||||||
|
return str(c, 'ascii')
|
||||||
|
|
||||||
|
def comms(conn, addr, node):
|
||||||
|
"""
|
||||||
|
comms - Initializes the communication with the user, authorizes them, and passes them to the menus
|
||||||
|
"""
|
||||||
|
iac_echo = bytes([255, 251, 1])
|
||||||
|
iac_sga = bytes([255, 251, 3])
|
||||||
|
|
||||||
|
conn.sendall(iac_echo)
|
||||||
|
conn.sendall(iac_sga)
|
||||||
|
# Splash Screen
|
||||||
|
try:
|
||||||
|
with open("pages/splash.asc") as splash:
|
||||||
|
for line in splash.readlines():
|
||||||
|
line = line.replace("%node%", str(node))
|
||||||
|
sendString(conn, line + "\r")
|
||||||
|
getString(conn, 1)
|
||||||
|
except:
|
||||||
|
sendString(conn, config.get("Main","DefaultSplash").replace("%node%", str(node)))
|
||||||
|
|
||||||
|
# Login Screen
|
||||||
|
sendString(conn, "\r\nEnter your username, or type NEW if a new user.")
|
||||||
|
sendString(conn, "\r\nLogin: ")
|
||||||
|
username = getString(conn, 16)
|
||||||
|
if username.replace('\x00','') == "NEW":
|
||||||
|
auth.create(conn)
|
||||||
|
else:
|
||||||
|
sendString(conn, "\r\nPassword: ")
|
||||||
|
password = getString(conn, 16)
|
||||||
|
auth.login(conn,username,password, node)
|
27
config.ini
Normal file
27
config.ini
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
[Main]
|
||||||
|
Port = 2043
|
||||||
|
DefaultSplash = "Welcome to the SB17 BBS on %node%!"
|
||||||
|
MaxConnections = 5
|
||||||
|
Menus = 2
|
||||||
|
|
||||||
|
[Auth]
|
||||||
|
userDb = users.json
|
||||||
|
|
||||||
|
[Menu0]
|
||||||
|
Title = "Main Menu"
|
||||||
|
Page = menus/main.asc
|
||||||
|
Groups = 0
|
||||||
|
options = 3
|
||||||
|
Option0 = Profile
|
||||||
|
Option0Key = P
|
||||||
|
Option1 = Edit Profile
|
||||||
|
Option1Key = E
|
||||||
|
Option2 = Logout
|
||||||
|
Option2Key = X
|
||||||
|
|
||||||
|
[Menu1]
|
||||||
|
Title = "Admin Menu"
|
||||||
|
Groups = 99
|
||||||
|
options = 1
|
||||||
|
Option0 = Back
|
||||||
|
Option0Key = B
|
44
docs/Configuration.md
Normal file
44
docs/Configuration.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# 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
|
||||||
|
Except where noted, these can be used in either the asc page files, or the config strings.
|
||||||
|
|
||||||
|
* %node%
|
||||||
|
- Replaced with the node number of the connected party.
|
||||||
|
|
||||||
|
### Messages
|
||||||
|
* %subject%
|
||||||
|
- The message's subject
|
||||||
|
* %content%
|
||||||
|
- The message's content
|
||||||
|
* %date%
|
||||||
|
- The date the message was posted
|
||||||
|
|
||||||
|
## Config.ini Key-Value Pairs
|
||||||
|
|
||||||
|
* [Main] - Main Configuration Information.
|
||||||
|
- Port = The integer value of the port to bind Pyscii BBS to
|
||||||
|
- DefaultSplash = The string that will greet users if no splash.asc page exists in the "pages" location
|
||||||
|
|
||||||
|
* [Auth] - Configuration information related to Authentication and Authorization
|
||||||
|
- jsonFile = The string of the json file that holds the user database. This database is expected to follow the user.schema
|
||||||
|
|
||||||
|
* [Menu#] - Configuration for a menu. Starts with 0
|
||||||
|
- Title = The title of the menu, a string.
|
||||||
|
- Groups = A list of the groups that can see this menu
|
||||||
|
- options = an integer number of the count of options
|
||||||
|
- Option# = The option description to display to the user
|
||||||
|
- Option#Key = The key to press to use the menu option
|
||||||
|
|
||||||
|
## Pages
|
||||||
|
The pages for the BBS are stored in the "pages" directory.
|
||||||
|
|
||||||
|
* splash.asc
|
||||||
|
- Replaces the "DefaultSplash" Config Key/Value pair.
|
||||||
|
- Is shown to the users before login
|
||||||
|
|
||||||
|
* menu#.asc
|
||||||
|
- The default display screen for a given menu.
|
||||||
|
- If this does not exist, the menu will be displayed from the configuration in the config.ini file
|
37
docs/Roadmap.md
Normal file
37
docs/Roadmap.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Pyscii-BBS Roadmap
|
||||||
|
|
||||||
|
This file is the guiding roadmap for the Pyscii-BBS project
|
||||||
|
|
||||||
|
## Project Mission
|
||||||
|
Pyscii-BBS is supposed to be a telnet-capable data-secure BBS solution. It should be extensible, well-documented, and easy to implement.
|
||||||
|
|
||||||
|
## Project Stages
|
||||||
|
Right now the BBS is a barely functional bbs server. It requests Username and Password from the connecting system, and potentially returns an error or the user's description.
|
||||||
|
|
||||||
|
The plan is to write the project in the following stages:
|
||||||
|
|
||||||
|
Remember these rules for working on the following features:
|
||||||
|
* Everything must be extensible. There should be configuration options either in the form of asc page files or configuration key/values
|
||||||
|
* Data must be stored in a secure way. Just because the protocol is insecure, doesn't mean we should be.
|
||||||
|
* Anywhere data is stored, it must be in a file with a specific and valid JSON schema
|
||||||
|
* Document everything. Every configuration key/value, every asc page file purpose, every JSON schema.
|
||||||
|
|
||||||
|
### Pre-Alpha
|
||||||
|
#### Complete
|
||||||
|
- BBS Server accepts multiple connections up to a configurable max ✓
|
||||||
|
- BBS Server sends data to the client and gracefully terminates the connections ✓
|
||||||
|
- BBS Server accepts responses from the client ✓
|
||||||
|
- BBS Server can create users with passwords and profile information ✓
|
||||||
|
- BBS Server can authenticate users ✓
|
||||||
|
- 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 ✓
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
### Alpha
|
35
docs/user.schema.json
Normal file
35
docs/user.schema.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://bbs.sb17.space/user.schema.json",
|
||||||
|
"title": "User",
|
||||||
|
"description": "A Pyscii-BBS user object",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id" : {
|
||||||
|
"description": "The unique ID of the user entity",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "The unique username of the user entity",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"description": "Description of the user Entity. Like a Profile String",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"description": "The user entity's password hash.",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"salt":{
|
||||||
|
"description": "The user entity's salt. This will be generated by the application.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"password",
|
||||||
|
"salt"
|
||||||
|
]
|
||||||
|
}
|
57
functions.py
Normal file
57
functions.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import configparser
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import comms
|
||||||
|
import menus
|
||||||
|
import auth
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(sys.argv[1])
|
||||||
|
|
||||||
|
userDb = config.get("Auth","userDb")
|
||||||
|
|
||||||
|
def printProfile(conn, userId, node, lastMenu):
|
||||||
|
comms.sendString(conn, "\u001B[2JProfile:\r\n")
|
||||||
|
with open(userDb) as userdb:
|
||||||
|
users = json.loads(userdb.read())
|
||||||
|
for user in users['users']:
|
||||||
|
if user['id'] == userId:
|
||||||
|
name = "Name: " + user["name"]
|
||||||
|
description = "Description: " + user['description']
|
||||||
|
profile = name + "\r\n" + description + "\r\n"
|
||||||
|
comms.sendString(conn, profile)
|
||||||
|
comms.sendString(conn, "Press Enter to go back to the Menu... ")
|
||||||
|
comms.getChar(conn)
|
||||||
|
menus.printMenu(conn, lastMenu, node, userId)
|
||||||
|
|
||||||
|
def editProfile(conn, userID, node, lastMenu):
|
||||||
|
comms.sendString(conn, "\u001B[2JProfile:\r\n")
|
||||||
|
with open(userDb) as userdb:
|
||||||
|
users = json.loads(userdb.read())
|
||||||
|
for user in users['users']:
|
||||||
|
if user['id'] == userID:
|
||||||
|
name = "Name: " + user["name"]
|
||||||
|
description = "Description: " + user['description']
|
||||||
|
profile = name + "\r\n" + description + "\r\n"
|
||||||
|
comms.sendString(conn, profile)
|
||||||
|
comms.sendString(conn, "What would you like to edit?\r\n")
|
||||||
|
comms.sendString(conn, "D: Description\tP:Password\r\n? ")
|
||||||
|
response = comms.getString(conn,2)
|
||||||
|
if response.strip('\x00') == "D":
|
||||||
|
description = auth.setProfile(conn)
|
||||||
|
user['description'] = description
|
||||||
|
with open(userDb,'w') as output:
|
||||||
|
json.dump(users, output, indent=4)
|
||||||
|
if response.strip('\x00') == "P":
|
||||||
|
password = auth.setPassword(conn, user['salt'])
|
||||||
|
user['password'] = password
|
||||||
|
with open(userDb,'w') as output:
|
||||||
|
json.dump(users, output, indent=4)
|
||||||
|
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.getChar(conn)
|
||||||
|
menus.printMenu(conn, lastMenu, node, userID)
|
||||||
|
|
||||||
|
|
51
menus.py
Normal file
51
menus.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import configparser, sys, comms, functions
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(sys.argv[1])
|
||||||
|
|
||||||
|
menuCount = config.getint("Main","Menus")
|
||||||
|
|
||||||
|
def printMenu(conn, menu, node, user):
|
||||||
|
comms.sendString(conn, "\u001B[2J\r")
|
||||||
|
try:
|
||||||
|
with open("pages/menu"+str(menu)+".asc") as menuAscii:
|
||||||
|
for line in menuAscii.readlines():
|
||||||
|
line = line.replace("%node%", str(node))
|
||||||
|
comms.sendString(conn, line + "\r")
|
||||||
|
response = comms.getString(conn, 2)
|
||||||
|
except:
|
||||||
|
dispMenu = "Menu"+str(menu)
|
||||||
|
options = []
|
||||||
|
for i in range(0, config.getint(dispMenu, "Options")):
|
||||||
|
option = (config.get(dispMenu, "Option"+str(i)), config.get(dispMenu, "Option"+str(i)+"Key"))
|
||||||
|
options.append(option)
|
||||||
|
comms.sendString(conn,"Menu: \r\n")
|
||||||
|
for option in options:
|
||||||
|
comms.sendString(conn, option[0]+" : "+option[1]+"\r\n")
|
||||||
|
comms.sendString(conn, "What would you like to do? ")
|
||||||
|
response = comms.getString(conn, 2)
|
||||||
|
parseResponse(conn, menu, node, user, response)
|
||||||
|
|
||||||
|
|
||||||
|
def parseResponse(conn, menu, node, user, response):
|
||||||
|
# Add Menu functions and results to this function
|
||||||
|
# Menu 0 Functions
|
||||||
|
if menu == 0:
|
||||||
|
comms.sendString(conn, "\u001B[2J\r")
|
||||||
|
if response.strip('\x00') == "X":
|
||||||
|
comms.sendString(conn, "Goodbye!")
|
||||||
|
|
||||||
|
if response.strip('\x00') == "P":
|
||||||
|
functions.printProfile(conn, user, node, menu)
|
||||||
|
|
||||||
|
if response.strip('\x00') == "E":
|
||||||
|
functions.editProfile(conn, user, node, menu)
|
||||||
|
|
||||||
|
if response.strip('\x00') == "M":
|
||||||
|
functions.pubMessages(conn, user, node, menu)
|
||||||
|
|
||||||
|
# Menu 1 Functions
|
||||||
|
if menu == 1:
|
||||||
|
if response.strip('\x00') == "B":
|
||||||
|
printMenu(conn, 0, node, user)
|
||||||
|
|
7
pages/menu0.asc
Normal file
7
pages/menu0.asc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
======================================*========================================
|
||||||
|
| MAIN MENU |
|
||||||
|
| P: Profile M: Messages |
|
||||||
|
| E: Edit Profile X: Logout |
|
||||||
|
| |
|
||||||
|
===============================================================================
|
||||||
|
What Do you want to do?
|
7
pages/splash.asc
Normal file
7
pages/splash.asc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
____ ____ _ _____ ____ ____ ____
|
||||||
|
/ ___|| __ )/ |___ | | __ )| __ ) ___|
|
||||||
|
\___ \| _ \| | / / | _ \| _ \___ \
|
||||||
|
___) | |_) | | / / | |_) | |_) |_) |
|
||||||
|
|____/|____/|_|/_/ |____/|____/____/
|
||||||
|
You are connected on node: %node%
|
||||||
|
Press Enter to Continue...
|
10
test.py
Normal file
10
test.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
jstring = '{"users":[{"name": "J4YC33","description": "J4YC33\'s Just this person..."}]}'
|
||||||
|
|
||||||
|
with open('users.json') as userdb:
|
||||||
|
users = json.loads(userdb.read())
|
||||||
|
for user in users['users']:
|
||||||
|
if user['name'] == "J4YC33":
|
||||||
|
print(user['description'])
|
||||||
|
|
18
users.json
Normal file
18
users.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "Default",
|
||||||
|
"password": "Unusable",
|
||||||
|
"description": "This account is not to be used",
|
||||||
|
"salt": "This account cannot be accessed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "J4YC33",
|
||||||
|
"password": "d32190c6322e27087c934b7bd9ff642bbe71d5782f2242028c3c15298c7aeac6",
|
||||||
|
"salt": "SI13YMJl8kWLBhNN",
|
||||||
|
"description": "J4YC33 IS SPARTACUS!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user