#!/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()