You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
10 KiB
346 lines
10 KiB
#!/usr/bin/env python3 |
|
|
|
import os |
|
import sys |
|
import sqlite3 as sql |
|
import datetime, time |
|
import subprocess |
|
import readline |
|
import signal |
|
|
|
class c: |
|
black = '' |
|
red = '\033[0;31m' |
|
b_red = '\033[1;31m' |
|
yellow = '\033[1;33m' |
|
green = '\033[0;32m' |
|
b_green = '\033[1;32m' |
|
cyan = '\033[0;36m' |
|
b_cyan = '\033[1;36m' |
|
purple = '\033[1;35m' |
|
blue = '\033[0;34m' |
|
b_blue = '\033[1;34m' |
|
white = '\033[1;37m' |
|
end = '\033[0m' |
|
|
|
db_path = '/usr/local/share/cspc/db/cspc' |
|
userdir = os.path.expanduser('~') |
|
user = os.path.split(userdir)[-1] |
|
topic = None |
|
post = None |
|
available_ids = [] |
|
messages = [] |
|
last_log = False |
|
|
|
def go_back(): |
|
global post |
|
global topic |
|
|
|
# If just viewed a post |
|
if post and topic: |
|
post = None |
|
else: |
|
post = None |
|
topic = None |
|
|
|
|
|
def get_prompt(): |
|
if post: |
|
ident = 'Post'.format(post) |
|
elif topic: |
|
ident = 'Posts'.format(topic) |
|
else: |
|
ident = 'Topics' |
|
return '{}CSPC [{}{}{}] {}>{} '.format(c.yellow, c.white, ident, c.yellow, c.b_green, c.end) |
|
|
|
|
|
def get_data(): |
|
if post and topic: |
|
get_single_post() |
|
elif topic: |
|
get_posts() |
|
else: |
|
get_topics() |
|
|
|
|
|
def show_help(): |
|
help_menu = [ |
|
'{}add:{} add a new topic/post/reply (whichever is relavent tot he screen you are on)'.format(c.cyan, c.end), |
|
'{}back:{} move up a level (post->posts->topics)'.format(c.cyan, c.end), |
|
'{}help:{} display this menu'.format(c.cyan, c.end), |
|
'{}item id:{} enter the item number to view it'.format(c.cyan, c.end), |
|
'{}q, quit, exit:{} leave cspc'.format(c.cyan, c.end), |
|
'{}posts created since your last login to cspc have dates in green{}'.format(c.green, c.end) |
|
] |
|
for x in help_menu: |
|
messages.append(x) |
|
|
|
|
|
def make_add(): |
|
action = False |
|
if post and topic: |
|
action = 'reply' |
|
elif topic: |
|
action = 'post' |
|
else: |
|
action = 'topic' |
|
verify = input('{}Are you sure you would like to add a new {}{}{} (y/n)?{} '.format(c.cyan, c.b_blue, action, c.cyan, c.end)) |
|
verify = verify.lower() |
|
if verify in ['y','yes','yeah','yup','ya']: |
|
add_new(action) |
|
|
|
|
|
def add_new(a): |
|
header = " 5 10 15 20 25 30 35 40 45 50\n ....|....|....|....|....|....|....|....|....|....|" |
|
|
|
title = None |
|
body = None |
|
|
|
if not a == 'reply': |
|
title = input('{}Enter the new {}s title:{} '.format(c.white, a, c.end)) |
|
|
|
content = [] |
|
|
|
if not a == 'topic': |
|
print('{}Enter your content. To finish, enter a period as the only\ntext on its row.{}'.format(c.yellow, c.end)) |
|
print(header) |
|
|
|
while True: |
|
line = input('{}>{} '.format(c.green, c.end)) |
|
if line == '.': |
|
break |
|
content.append(line) |
|
|
|
confirm = input('{}Submit the new {} (y/n)?{} '.format(c.white, a, c.end)) |
|
if confirm.lower() in ['y','ya','yes','yeah']: |
|
body = '\n'.join(content) |
|
else: |
|
print('{} not saved'.format(a)) |
|
return False |
|
|
|
payload = {'title': title, 'body': body} |
|
add_to_db(a, payload) |
|
|
|
|
|
def parse_command(com): |
|
global topic |
|
global post |
|
global messages |
|
|
|
comlist = ['add', 'help', 'quit', 'back'] |
|
if com in ['back', 'b']: |
|
go_back() |
|
elif com in ['help', '?', 'h']: |
|
show_help() |
|
elif com in ['quit', 'q', 'exit']: |
|
set_last_log() |
|
sys.exit(0) |
|
elif com == ['add', 'a', 'new']: |
|
make_add() |
|
else: |
|
try: |
|
ident = int(com) |
|
if not ident in available_ids: |
|
messages.append('{}ERROR: {} is not an available option!\n'.format(c.red, com, c.end)) |
|
return False |
|
if not topic: |
|
topic = ident |
|
elif not post: |
|
post = ident |
|
else: |
|
messages.append('{}A number command is not relevant right now...{}'.format(c.purple, c.end)) |
|
except ValueError: |
|
messages.append('{}Input not recognized{}'.format(c.red, c.end)) |
|
|
|
#------- DB Related --------# |
|
|
|
def check_and_build_db(): |
|
if not os.path.isfile(db_path): |
|
conn = sql.connect(db_path) |
|
c = conn.cursor() |
|
|
|
c.execute("CREATE TABLE data (topic_id INTEGER DEFAULT NULL, post_id INTEGER DEFAULT NULL, type text NOT NULL, title text DEFAULT NULL, body text DEFAULT NULL, author text NOT NULL, last_updated INTEGER NOT NULL)") |
|
|
|
conn.commit() |
|
conn.close() |
|
|
|
def db_do(query, var=False, noresval=False): |
|
global messages |
|
if os.path.isfile(db_path): |
|
conn = sql.connect(db_path) |
|
c = conn.cursor() |
|
if var: |
|
c.execute(query, var) |
|
else: |
|
c.execute(query) |
|
if noresval: |
|
out = c.rowcount |
|
else: |
|
out = [] |
|
for row in c: |
|
out.append(row) |
|
conn.commit() |
|
conn.close() |
|
return out |
|
else: |
|
messages.append("{}ERROR:{} Database cannot be found or is corrupt".format(c.red, c.end)) |
|
return False |
|
|
|
|
|
def get_posts(): |
|
global available_ids |
|
global topic |
|
available_ids = [] |
|
q = "SELECT rowid, title, author, last_updated FROM data WHERE type = 'post' and topic_id = ? ORDER BY last_updated DESC" |
|
v = (topic,) |
|
res = db_do(q, v) |
|
print('{}{:^6} {:35} {:12} {:18}{}'.format(c.yellow, 'ID', 'Post Title', 'Author', 'Last Updated', c.end)) |
|
print('{}------------------------------------------------------------------------------{}'.format( c.yellow, c.end)) |
|
if not len(res): |
|
print('\n{}There are no posts for this topic yet. Add one!{}'.format(c.cyan, c.end)) |
|
else: |
|
for row in res: |
|
available_ids.append(row[0]) |
|
dtime = datetime.datetime.utcfromtimestamp(row[3]).strftime('%Y-%m-%d %H:%M') |
|
title = row[1] |
|
if len(title) > 35: |
|
title = '{}...'.format(title[:32]) |
|
if last_log > row[3] or not last_log: |
|
print('({:^4}) {:35} {:12} {:18}'.format(row[0], title, row[2], dtime)) |
|
else: |
|
print('({:^4}) {:35} {:12} {}{:18}{}'.format(row[0], title, row[2], c.green, dtime, c.end)) |
|
|
|
return True |
|
|
|
|
|
def get_topics(): |
|
global available_ids |
|
available_ids = [] |
|
q = "SELECT title, last_updated, rowid FROM data WHERE type = 'topic' ORDER BY last_updated DESC" |
|
res = db_do(q) |
|
print('{}{:^6} {:35} {:18}{}'.format(c.yellow, 'ID', 'Topic Name', 'Last Updated', c.end)) |
|
print('{}-----------------------------------------------------------------{}'.format( c.yellow, c.end)) |
|
for row in res: |
|
available_ids.append(row[2]) |
|
dtime = datetime.datetime.utcfromtimestamp(row[1]).strftime('%Y-%m-%d %H:%M') |
|
title = row[0] |
|
if len(title) > 35: |
|
title = '{}...'.format(title[:32]) |
|
if last_log > row[1] or not last_log: |
|
print('({:^4}) {}{:35}{} {:18}'.format(row[2], c.cyan, title, c.end, dtime)) |
|
else: |
|
print('({:^4}) {}{:35} {}{:18}{}'.format(row[2], c.cyan, title, c.green, dtime, c.end)) |
|
|
|
return True |
|
|
|
|
|
def get_single_post(): |
|
global topic |
|
global post |
|
q1 = "SELECT title, body, author, last_updated FROM data WHERE type = 'post' and topic_id = ? and rowid = ?" |
|
v1 = (topic, post) |
|
res1 = db_do(q1, v1) |
|
q2 = "SELECT body, author, last_updated FROM data WHERE type = 'reply' and post_id = ? and topic_id = ? ORDER BY last_updated ASC" |
|
v2 = (post, topic) |
|
res2 = db_do(q2, v2) |
|
post_title = "{}Title:{} {}{}".format(c.yellow, c.white, res1[0][0], c.end) |
|
post_author = "{}Author:{} {}{}".format(c.yellow, c.white, res1[0][2], c.end) |
|
dtime = datetime.datetime.utcfromtimestamp(res1[0][3]).strftime('%Y-%m-%d %H:%M') |
|
post_time = "{}Post date:{} {}{}\n".format(c.yellow, c.white, dtime, c.end) |
|
post_body = res1[0][1] |
|
print(post_title) |
|
print(post_author) |
|
print(post_time) |
|
print(post_body) |
|
print("\n{}- - - - - - - - - - - - - - - -{}\n".format(c.white, c.end)) |
|
if len(res2): |
|
for row in res2: |
|
dtime = datetime.datetime.utcfromtimestamp(row[2]).strftime('%Y-%m-%d %H:%M') |
|
print("{} | {}\n".format(row[1],dtime)) |
|
print(row[0]) |
|
print("\n- - - - - - -\n") |
|
else: |
|
print('No replies. Add one!') |
|
|
|
|
|
def update_time(t): |
|
if topic and post: |
|
q = "UPDATE data SET last_updated = ? WHERE rowid in (?, ?)" |
|
v = (t, topic, post) |
|
elif topic: |
|
q = "UPDATE data SET last_updated = ? WHERE rowid = ?" |
|
v = (t, topic) |
|
else: |
|
return True |
|
res = db_do(q, v, noresval=True) |
|
return res |
|
|
|
|
|
def add_to_db(action, data): |
|
utime = int(time.time()) |
|
q = "INSERT INTO data VALUES (?, ?, ?, ?, ?, ?, ?)" |
|
v = (topic, post, action, data['title'], data['body'], user, utime) |
|
res = db_do(q, v, noresval=True) |
|
if not res: |
|
messages.append("{}ERROR:{} There was an error adding your topic".format(c.red, c.end)) |
|
return False |
|
tupdate = update_time(utime) |
|
return True |
|
|
|
|
|
def get_last_log(): |
|
global last_log |
|
|
|
filepath = '{}/.cspc'.format(userdir) |
|
if not os.path.isfile(filepath): |
|
with open(filepath, 'w') as f: |
|
current = int(time.time()) |
|
f.write(str(current)) |
|
|
|
with open(filepath, 'r') as f: |
|
data = f.read() |
|
|
|
try: |
|
last_log = int(data) |
|
except: |
|
last_log = False |
|
|
|
def set_last_log(): |
|
filepath = '{}/.cspc'.format(userdir) |
|
with open(filepath, 'w') as f: |
|
current = int(time.time()) |
|
f.write(str(current)) |
|
|
|
|
|
|
|
#------- Main loop --------# |
|
|
|
def mainloop(): |
|
global messages |
|
|
|
while True: |
|
subprocess.run(['clear']) |
|
print('{}Colorfield Space Bulletin Board{}\n\n'.format(c.b_blue, c.end)) |
|
output = get_data() |
|
# print the formatted data |
|
print('') |
|
for x in messages: |
|
print(x) |
|
print('') |
|
command = input(get_prompt()) |
|
print('') |
|
messages = [] |
|
if not command: |
|
continue |
|
parse_command(command) |
|
|
|
|
|
if __name__ == '__main__': |
|
signal.signal(signal.SIGINT, signal.SIG_IGN) |
|
try: |
|
check_and_build_db() |
|
except sql.OperationalError: |
|
print("No cspc instance installed. Is the database path valid?") |
|
sys.exit(2) |
|
get_last_log() |
|
mainloop()
|
|
|