def prompt_tags(msg): """ Prompt for and return tags wished for message. """ # pylint: disable=R0914,W0603 # Too many local variables # Using the global statement from x84.bbs import DBProxy, echo, getterminal, getsession from x84.bbs import Ansi, LineEditor session, term = getsession(), getterminal() tagdb = DBProxy('tags') msg_onlymods = (u"\r\nONlY MEMbERS Of thE '%s' OR '%s' " "GROUP MAY CREAtE NEW tAGS." % ( term.bold_yellow('sysop'), term.bold_blue('moderator'),)) msg_invalidtag = u"\r\n'%s' is not a valid tag." prompt_tags1 = u"ENtER %s, COMMA-dEliMitEd. " % ( term.bold_red('TAG(s)'),) prompt_tags2 = u"OR '/list', %s:quit\r\n : " % ( term.bold_yellow_underline('Escape'),) while True: # Accept user input for multiple 'tag's, or /list command echo(u'\r\n\r\n') echo(prompt_tags1) echo(prompt_tags2) width = term.width - 6 sel_tags = u', '.join(msg.tags) inp_tags = LineEditor(width, sel_tags).read() if inp_tags is not None and 0 == len(inp_tags.strip()): # no tags must be (private ..) msg.tags = set() return True if inp_tags is None or inp_tags.strip().lower() == '/quit': return False elif inp_tags.strip().lower() == '/list': # list all available tags, and number of messages echo(u'\r\n\r\nTags: \r\n') all_tags = sorted(tagdb.items()) if 0 == len(all_tags): echo(u'None !'.center(term.width / 2)) else: echo(Ansi(u', '.join(([u'%s(%d)' % (_key, len(_value),) for (_key, _value) in all_tags])) ).wrap(term.width - 2)) continue echo(u'\r\n') # search input as valid tag(s) tags = set([inp.strip().lower() for inp in inp_tags.split(',')]) err = False for tag in tags.copy(): if not tag in tagdb and not ( 'sysop' in session.user.groups or 'moderator' in session.user.groups): tags.remove(tag) echo(msg_invalidtag % (term.bold_red(tag),)) err = True if err: echo(msg_onlymods) continue msg.tags = tags return True
def chk_thread(thread): """ for asyncronous lightbar, return False if thread is still running, 'dirty' if thread completed and new entries were found, 'None' if thread completed and no no entries were discovered. """ from x84.bbs import getsession, DBProxy if thread is None or thread.is_alive(): return False # check if bbs-scene.org thread finished, silly hack logger = logging.getLogger() session = getsession() udbkeys = DBProxy('bbslist').keys() nlc = 0 for key, value in thread.content: if key not in udbkeys: DBProxy('bbslist')[key] = value DBProxy('bbslist', 'comments')[key] = list() DBProxy('bbslist', 'ratings')[key] = list() nlc += 1 else: # update anyway (fe. port changed), we don't # detect this as an update. DBProxy('bbslist')[key] = value if nlc > 0: logger.info('%d new entries', nlc) session.send_event('global', ('bbslist_update', None)) session.buffer_event('bbslist_update') return 'dirty' else: logger.info('no new bbs-scene.org entries') return None
def get_bbsinfo(key, active=True): """ given a bbs key, fetch detailed information for use in pager """ # pylint: disable=R0914 # Too many local variables. from x84.bbs import getterminal, DBProxy, timeago rstr = u'' term = getterminal() highlight = term.bold_blue if active else term.blue lowlight = term.bold_black bbs = DBProxy('bbslist')[key] epoch = time.mktime(time.strptime(bbs['timestamp'], '%Y-%m-%d %H:%M:%S')) ratings = DBProxy('bbslist', 'ratings')[key] comments = DBProxy('bbslist', 'comments')[key] rstr += (lowlight('bbSNAME') + highlight(u': ') + bbs['bbsname'] + highlight(' +o ') + bbs['sysop'] + u'\r\n') rstr += (lowlight('AddRESS') + highlight(': ') + bbs['address'] + highlight(': ') + bbs['port'] + u'\r\n') rstr += (lowlight('lOCAtiON') + highlight(': ') + bbs['location'] + '\r\n') rstr += (lowlight('SOftWARE') + highlight(': ') + bbs['software'] + '\r\n') rstr += (lowlight('tiMEStAMP') + highlight(':') + lowlight(timeago(time.time() - epoch)) + ' ago\r\n') rstr += ( lowlight('RAtiNG') + highlight(': ') + '%s (%2.2f of %d)\r\n' % (highlight(calc_rating(ratings)), 0 if 0 == len(ratings) else sum( [_rating for (_handle, _rating) in ratings]) / len(ratings), len(ratings))) rstr += u'\r\n' + bbs['notes'] for handle, comment in comments: rstr += '\r\n\r\n' + lowlight(handle) rstr += highlight(': ') rstr += comment return rstr
def add_comment(key): """ Prompt user to add a comment about a bbs. """ # pylint: disable=R0914 # Too many local variables. from x84.bbs import getsession, getterminal, echo, DBProxy, LineEditor from x84.bbs import getch session, term = getsession(), getterminal() prompt_comment = u'\r\n\r\nWhAt YOU GOt tO SAY? ' prompt_chg = u'\r\n\r\nChANGE EXiStiNG ? [yn] ' echo(term.move(term.height, 0)) echo(prompt_comment) comment = LineEditor(max(10, term.width - len(prompt_comment) - 5)).read() if comment is None or 0 == len(comment.strip()): return new_entry = (session.handle, comment) comments = DBProxy('bbslist', 'comments') comments.acquire() existing = comments[key] if session.handle in (_nick for (_nick, _cmt) in comments[key]): # change existing comment, echo(prompt_chg) if getch() not in (u'y', u'Y'): comments.release() return # re-define list without existing entry, + new entry comments[key] = [(_enick, _ecmt) for (_enick, _ecmt) in existing if session.handle != _enick] + [new_entry] comments.release() return # re-define as existing list + new entry comments[key] = existing + [new_entry] comments.release()
def refresh_automsg(idx): """ Refresh automsg database, display automsg of idx, return idx. """ session.flush_event('automsg') autodb = DBProxy('automsg') automsgs = sorted(autodb.values()) if len(autodb) else db_firstrecord dblen = len(automsgs) # bounds check if idx < 0: idx = dblen - 1 elif idx > dblen - 1: idx = 0 tm_ago, handle, msg = automsgs[idx] asc_ago = u'%s ago' % (timeago(time.time() - tm_ago)) disp = (u''.join(('\r\n\r\n', term.bold(handle.rjust(max_user)), term.bold_blue(u'/'), term.blue(u'%*d' % (len('%d' % (dblen,)), idx,)), term.bold_blue(u':'), term.blue_reverse(msg.ljust(automsg_len)), term.bold(u'\\'), term.blue(asc_ago),))) echo(u''.join(( u'\r\n\r\n', Ansi(disp).wrap(term.width), ))) return idx
def refresh_automsg(idx): """ Refresh automsg database, display automsg of idx, return idx. """ session.flush_event('automsg') autodb = DBProxy('automsg') automsgs = sorted(autodb.values()) if len(autodb) else db_firstrecord dblen = len(automsgs) # bounds check if idx < 0: idx = dblen - 1 elif idx > dblen - 1: idx = 0 tm_ago, handle, msg = automsgs[idx] asc_ago = u'%s ago' % (timeago(time.time() - tm_ago)) disp = (u''.join(( '\r\n\r\n', term.bold(handle.rjust(max_user)), term.bold_blue(u'/'), term.blue(u'%*d' % ( len('%d' % (dblen, )), idx, )), term.bold_blue(u':'), term.blue_reverse(msg.ljust(automsg_len)), term.bold(u'\\'), term.blue(asc_ago), ))) echo(u''.join(( u'\r\n\r\n', Ansi(disp).wrap(term.width), ))) return idx
def login(session, user): """ 登记登录用户---没有改操作后边会处理为匿名 Assign ``user`` to ``session`` and return time of last call. performs various saves and lookups of call records """ session.user = user # assign timeout preference timeout = session.user.get('timeout', None) if timeout is not None: session.send_event('set-timeout', timeout) # update call records user.calls += 1 user.lastcall = time.time() # save user record if user.handle != u'anonymous': user.save() # update 'lastcalls' database lc_db = DBProxy('lastcalls') with lc_db: previous_call, _, _ = lc_db.get(user.handle, ( 0, 0, 0, )) lc_db[user.handle] = (user.lastcall, user.calls, user.location) return previous_call
def delete_message(msg): """ Experimental message delete! """ # ! belongs as msg.delete() function ! msg.recipient = u'' msg.subject = u'' msg.body = u'' msg.children = set() msg.parent = None msg.tags = set() msg.save() with DBProxy('tags') as tag_db: for key, values in tag_db.items()[:]: if msg.idx in values: newvalue = values - set([msg.idx]) if newvalue: tag_db[key] = newvalue else: # no more messages by this tag, delete it del tag_db[key] with DBProxy('privmsg') as priv_db: for key, values in priv_db.items()[:]: if msg.idx in values: priv_db[key] = values - set([msg.idx]) with DBProxy('msgbase') as msg_db: del msg_db['%d' % int(msg.idx)]
def view_leaf_msgnet(server_tag=None, board_id=None): if server_tag is None: server_tags = get_ini(section='msg', key='server_tags', split=True) if not server_tags: raise ValueError("no `server_tags' defined in ini file, " "section [msg].") # RECURSION for _st in server_tags: view_leaf_msgnet(server_tag=_st, board_id=None) return if board_id is None: board_ids = DBProxy('{0}keys'.format(server_tag)).keys() for _bid in board_ids: # RECURSION view_leaf_msgnet(server_tag=server_tag, board_id=_bid) return with DBProxy('{0}keys'.format(server_tag)) as key_db: echo(u'\r\n[msgnet_{0}]'.format(server_tag)) echo(u'\r\nurl_base = https://{addr}:{port}/'.format( addr=get_ini('web', 'addr'), port=get_ini('web', 'port'))) echo(u'\r\nboard_id = {0}'.format(board_id)) echo(u'\r\ntoken = {0}'.format(key_db[board_id])) echo(u'\r\npoll_interval = 300') echo(u'\r\n') echo(u'\r\n[msg]') echo(u'\r\nnetwork_tags = {0}'.format(server_tag)) echo(u'\r\n') echo(u'-' * 40)
def process_outbound(): # Scan for New Messages ready for sending out """ :rtype : none """ from x84.bbs import DBProxy print 'process_outbound()' # Status is a list of all Message Keys status = set(int(key) for key in DBProxy(FIDO_DB).keys()) print status # Scan message Status. with DBProxy(FIDO_DB) as fido_db: for key, values in fido_db.items(): # print key, values.check_status # print key, values.check_kludge for k, v in values.check_kludge.items(): # Grabs Key values of all Kludges print k, v # Work out kludge lines now. # Example modern msg id. '''
def process_keystroke(inp, key=None): """ Process general keystroke and call routines. """ from x84.bbs import getsession, DBProxy, gosub, echo session = getsession() if inp is None: return False elif type(inp) is int: return False elif inp in (u't', u'T') and key is not None: bbs = DBProxy('bbslist')[key] gosub( 'telnet', bbs['address'], bbs['port'], ) elif inp in (u'a', u'A'): add_bbs() elif inp in (u'c', u'C') and key is not None: add_comment(key) elif inp in (u'r', u'R') and key is not None: rate_bbs(key) elif inp in (u'i', u'I') and key is not None: echo(get_bbsinfo(key) + '\r\n') elif inp in (u'v', u'V') and key is not None: view_ansi(key) elif inp in (u'd', u'D') and key is not None and ('sysop' in session.user.groups): del DBProxy('bbslist')[key] else: return False # unhandled return True
def login(session, user): """ Assign ``user`` to ``session`` and return time of last call. performs various saves and lookups of call records """ session.user = user # assign timeout preference timeout = session.user.get('timeout', None) if timeout is not None: session.send_event('set-timeout', timeout) # update call records user.calls += 1 user.lastcall = time.time() # save user record if user.handle != u'anonymous': user.save() # update 'lastcalls' database lc_db = DBProxy('lastcalls') with lc_db: previous_call, _, _ = lc_db.get(user.handle, (0, 0, 0,)) lc_db[user.handle] = (user.lastcall, user.calls, user.location) return previous_call
def publish_network_messages(net): " Push messages to network. " from x84.bbs import DBProxy from x84.bbs.msgbase import format_origin_line, MSGDB log = logging.getLogger(__name__) log.debug(u'[{net[name]}] publishing new messages.'.format(net=net)) queuedb = DBProxy('{0}queues'.format(net['name']), use_session=False) transdb = DBProxy('{0}trans'.format(net['name']), use_session=False) msgdb = DBProxy(MSGDB, use_session=False) # publish each message for msg_id in sorted(queuedb.keys(), cmp=lambda x, y: cmp(int(x), int(y))): if msg_id not in msgdb: log.warn('{net[name]} No such message (msg_id={msg_id})' .format(net=net, msg_id=msg_id)) del queuedb[msg_id] continue msg = msgdb[msg_id] trans_parent = None if msg.parent is not None: matches = [key for key, data in transdb.items() if int(data) == msg.parent] if len(matches) > 0: trans_parent = matches[0] else: log.warn('{net[name]} Parent ID {msg.parent} ' 'not in translation-DB (msg_id={msg_id})' .format(net=net, msg=msg, msg_id=msg_id)) trans_id = push_rest(net=net, msg=msg, parent=trans_parent) if trans_id is False: log.error('{net[name]} Message not posted (msg_id={msg_id})' .format(net=net['name'], msg_id=msg_id)) continue if trans_id in transdb.keys(): log.error('{net[name]} trans_id={trans_id} conflicts with ' '(msg_id={msg_id})' .format(net=net, trans_id=trans_id, msg_id=msg_id)) with queuedb: del queuedb[msg_id] continue # transform, and possibly duplicate(?) message .. with transdb, msgdb, queuedb: transdb[trans_id] = msg_id msg.body = u''.join((msg.body, format_origin_line())) msgdb[msg_id] = msg del queuedb[msg_id] log.info('{net[name]} Published (msg_id={msg_id}) => {trans_id}' .format(net=net, msg_id=msg_id, trans_id=trans_id))
def serve_messages_for(board_id, request_data, db_source): " Reply-to api client request to receive new messages. " from x84.bbs import DBProxy, msgbase from x84.bbs.msgbase import to_utctime log = logging.getLogger(__name__) #log.error(msg) db_tags = DBProxy(msgbase.TAGDB, use_session=False) db_messages = DBProxy(msgbase.MSGDB, use_session=False) def message_owned_by(msg_id, board_id): return (msg_id in db_source and db_source[msg_id] == board_id) def msgs_after(idx=None): for msg_id in db_tags.get(request_data['network'], []): if idx is None: yield db_messages[idx] elif (int(msg_id) > int(idx) and not message_owned_by(msg_id, board_id)): yield db_messages[msg_id] last_seen = request_data.get('last', None) pending_messages = msgs_after(last_seen) return_messages = list() num_sent = 0 for num_sent, msg in enumerate(pending_messages, start=1): return_messages.append({ u'id': msg.idx, u'author': msg.author, u'recipient': msg.recipient, u'parent': msg.parent, u'subject': msg.subject, u'tags': list(msg.tags ^ set([request_data['network']])), u'ctime': to_utctime(msg.ctime), u'body': msg.body }) if num_sent >= BATCH_MSGS: log.warn('[{request_data[network]}] Batch limit reached for ' 'board {board_id}; halting'.format( request_data=request_data, board_id=board_id)) break if num_sent > 0: log.info('[{request_data[network]}] {num_sent} messages ' 'served to {board_id}'.format(request_data=request_data, num_sent=num_sent, board_id=board_id)) return {u'response': True, u'messages': return_messages}
def x84net_requeue(): # a message failed to queue for delivery, but hellbeard # really wanted to see em, so re-queue. from x84.bbs import DBProxy, echo from pprint import pformat queuedb = DBProxy('x84netqueues') with queuedb: queuedb['264'] = 1 echo('-') echo(pformat(queuedb.items())) echo('-')
def publish_network_messages(net): """ Push messages to network, ``net``. """ from x84.bbs import DBProxy from x84.bbs.msgbase import format_origin_line, MSGDB log = logging.getLogger(__name__) log.debug(u'[{net[name]}] publishing new messages.'.format(net=net)) queuedb = DBProxy('{0}queues'.format(net['name']), use_session=False) transdb = DBProxy('{0}trans'.format(net['name']), use_session=False) msgdb = DBProxy(MSGDB, use_session=False) # publish each message for msg_id in sorted(queuedb.keys(), cmp=lambda x, y: cmp(int(x), int(y))): if msg_id not in msgdb: log.warn('[{net[name]}] No such message (msg_id={msg_id})' .format(net=net, msg_id=msg_id)) del queuedb[msg_id] continue msg = msgdb[msg_id] trans_parent = None if msg.parent is not None: matches = [key for key, data in transdb.items() if int(data) == msg.parent] if len(matches) > 0: trans_parent = matches[0] else: log.warn('[{net[name]}] Parent ID {msg.parent} ' 'not in translation-DB (msg_id={msg_id})' .format(net=net, msg=msg, msg_id=msg_id)) trans_id = push_rest(net=net, msg=msg, parent=trans_parent) if trans_id is False: log.error('[{net[name]}] Message not posted (msg_id={msg_id})' .format(net=net, msg_id=msg_id)) continue if trans_id in transdb.keys(): log.error('[{net[name]}] trans_id={trans_id} conflicts with ' '(msg_id={msg_id})' .format(net=net, trans_id=trans_id, msg_id=msg_id)) with queuedb: del queuedb[msg_id] continue # transform, and possibly duplicate(?) message .. with transdb, msgdb, queuedb: transdb[trans_id] = msg_id msg.body = u''.join((msg.body, format_origin_line())) msgdb[msg_id] = msg del queuedb[msg_id] log.info('[{net[name]}] Published (msg_id={msg_id}) => {trans_id}' .format(net=net, msg_id=msg_id, trans_id=trans_id))
def add_leaf_msgnet(): import cryptography.fernet server_tags = get_ini(section='msg', key='server_tags', split=True) if not server_tags: raise ValueError(MSG_NO_SERVER_TAGS) if len(server_tags) == 1: server_tag = server_tags[0] else: while True: echo('chose a server tag: ') idx = 0 for idx, tag in server_tags: echo(u'\r\n{0}. {1}'.format(idx, tag)) echo(u'\r\n: ') inp = LineEditor(width=len(str(idx)).read()) if inp is None: return try: server_tag = server_tags[int(inp)] break except ValueError: pass with DBProxy('{0}keys'.format(server_tag)) as key_db: board_id = max(map(int, key_db.keys()) or [-1]) + 1 client_key = cryptography.fernet.Fernet.generate_key() key_db[board_id] = client_key echo(u'\r\n') echo(u'-' * 40) view_leaf_msgnet(server_tag, board_id)
def add_oneline(session, message): """ Add a oneliner to the local database. """ udb = DBProxy('oneliner') with udb: key = max([int(key) for key in udb.keys()] or [0]) + 1 udb[key] = { 'oneliner': message, 'alias': getsession().user.handle, 'bbsname': get_ini('system', 'bbsname'), 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'), } maybe_expunge_records() # tell everybody a new oneliner was posted, including our # -- allows it to work something like a chatroom. session.send_event('global', ('oneliner', True))
def maybe_expunge_records(): """ Check ceiling of database keys; trim-to MAX_HISTORY. """ udb = DBProxy('oneliner') expunged = 0 with udb: if len(udb) > MAX_HISTORY + 10: contents = DBProxy('oneliner').copy() _sorted = sorted( ((value, key) for (key, value) in contents.items()), key=lambda _valkey: keysort_by_datetime(_valkey[0])) for expunged, (_, key) in enumerate(_sorted[len(udb) - MAX_HISTORY:]): del udb[key] if expunged: log = logging.getLogger(__name__) log.info('expunged %d records from database', expunged)
def get_oltxt(): """ Return unicode terminal string of oneliners. """ import time from x84.bbs import getterminal, DBProxy, timeago, decode_pipe term = getterminal() colors = (term.bright_white, term.cyan, term.white) hist = [(int(k), v) for (k, v) in DBProxy('oneliner').items()] hist.sort(_sort_oneliner) output = list() for idx, onel in hist[BUF_HISTORY * -1:]: color = colors[int(idx) % len(colors)] atime = timeago(time.time() - time.mktime( time.strptime(onel['timestamp'], '%Y-%m-%d %H:%M:%S'))).strip() if onel.get('fake', False): alias = term.bold_red(u'x') + color(onel['alias']) else: alias = color(onel['alias']) output.append(u''.join(( term.bold_white('('), color(atime), term.bold_black(u' ago'), term.bold_black(u' '), alias, term.bold_black(u'/'), onel['bbsname'], term.bold_white(u')'), color(u': '), decode_pipe(onel['oneliner']), ))) return output[(BUF_HISTORY * -1):]
def view_ansi(key): """ fetch and view a bbs ansi. They're not often very good ... """ from x84.bbs import getterminal, echo, DBProxy, ini, getch, from_cp437 term = getterminal() ansiurl = DBProxy('bbslist')[key]['ansi'] logger = logging.getLogger() echo(u'\r\n\r\n') if ansiurl is not None and 0 != len(ansiurl) and ansiurl != 'NONE': usernm = ini.CFG.get('bbs-scene', 'user') passwd = ini.CFG.get('bbs-scene', 'pass') req = requests.get(ansiurl, auth=(usernm, passwd)) if req.status_code != 200: echo(u'\r\n\r\nrequest failed,\r\n') echo(u'%r' % (req.content, )) echo(u'\r\n\r\n(code : %s).\r\n' % (req.status_code, )) echo(u'\r\nPress any key ..') logger.warn('ansiurl request failed: %s' % (ansiurl, )) getch() return ansi_txt = from_cp437(sauce.SAUCE(data=req.content).__str__()) echo(ansi_txt) else: echo('no ansi available (%s)' % (ansiurl, )) # move to bottom of screen and getch echo(u''.join((term.move(term.height, 0), term.normal, u'\r\n\r\nPRESS ANY kEY ...'), )) getch()
def generate_database(): # generates a database file with a generic question. db = DBProxy(databasename) index = [] index.append(0) indexcounter = 0 questions = [] questions.append('Which is your prefered BBS software?') alternatives = {} alternatives[(0, 0)] = 'X/84' alternatives[(0, 1)] = 'Daydream' alternatives[(0, 2)] = 'Mystic' alternatives[(0, 3)] = 'Synchronet' results = {} results[(0, 0)] = 0 results[(0, 1)] = 0 results[(0, 2)] = 0 results[(0, 3)] = 0 amount_of_alternatives = [] # this is the only list/dict that is not zerobased.. amount_of_alternatives.append(4) db['indexcounter'] = indexcounter db['index'] = index db['amount_of_alternatives'] = amount_of_alternatives db['alternatives'] = alternatives db['results'] = results db['questions'] = questions
def register_score(handle, score): from x84.bbs import DBProxy db = DBProxy('tetris') if not handle in db: db[handle] = score elif score[0] > db[handle][0]: db[handle] = score
def maybe_expunge_records(): """ Check ceiling of database keys; trim-to MAX_HISTORY. """ udb = DBProxy('oneliner') expunged = 0 with udb: if len(udb) > MAX_HISTORY + 10: contents = DBProxy('oneliner').copy() _sorted = sorted( ((value, key) for (key, value) in contents.items()), key=lambda _valkey: keysort_by_datetime(_valkey[0])) for expunged, (_, key) in enumerate( _sorted[len(udb) - MAX_HISTORY:]): del udb[key] if expunged: log = logging.getLogger(__name__) log.info('expunged %d records from database', expunged)
def __init__(self): # It's not safe to receive data from a database from # within a thread ... something we should address .. # so we do it in __init__, bug #154 self.existing_content = DBProxy('oneliner').copy() self.new_content = None self.log = logging.getLogger(__name__) super(FetchUpdatesShrooMs, self).__init__()
def get_lastcallers(last): timenow = time.time() return sorted([call_record(timeago=timenow - time_called, num_calls=num_calls, location=location, handle=handle.decode('utf8')) for handle, (time_called, num_calls, location) in DBProxy('lastcalls').items()])[:last]
def tygerofdantye_fix(): """ This user was too long! """ from x84.bbs import DBProxy user = DBProxy('userbase')['tygerofdanyte'] user.delete() user.handle = u'tygerdanyte' user.save()
def add_question(): term = getterminal() db = DBProxy(databasename) questions = [] amount_of_alternatives = [] index = {} alternatives = {} results = {} index = db['index'] questions = db['questions'] alternatives = db['alternatives'] results = db['results'] amount_of_alternatives = db['amount_of_alternatives'] amount_of_questions = len(questions) echo(term.clear + term.white + u'\r\nQuestion: ') le = LineEditor(65) new_question = le.read() if new_question == '' or new_question == None: return echo( term.bold_black(u'\r\n\r\nLeave a blank line when you are finished..')) new_amount = 0 while True: echo(term.normal + term.white + u'\r\nchoice ' + term.red + str(new_amount) + term.white + u': ') le = LineEditor(48) alternatives[(amount_of_questions, new_amount)] = le.read() if alternatives[(amount_of_questions, new_amount)] == '' or alternatives[( amount_of_questions, new_amount)] == None: break else: results[(amount_of_questions, new_amount)] = 0 new_amount = new_amount + 1 if new_amount > 0: echo(term.white(u'\r\n\r\nSave this voting question?')) answer = ynprompt() if answer == 1: questions.append(new_question) amount_of_alternatives.append(new_amount) indexcounter = db['indexcounter'] indexcounter = indexcounter + 1 index.append(str(indexcounter)) db['indexcounter'] = indexcounter db['index'] = index db['questions'] = questions db['amount_of_alternatives'] = amount_of_alternatives db['results'] = results db['amount_of_questions'] = amount_of_questions db['alternatives'] = alternatives waitprompt(term)
def migrate_105lc(): from x84.bbs import echo, DBProxy, list_users, get_user # migrating lastcallers database for 1.0.5 upgrade lc = DBProxy('lastcalls') for handle in list_users(): user = get_user(handle) lc[(handle)] = (user.lastcall, user.calls, user.location) echo('\r\n' + user.handle + '.') echo('\r\n\r\nlast callers db rebuilt!')
def list_results(questionnumber): term = getterminal() db = DBProxy(databasename) alternatives = {} questions = [] results = [] amount_of_alternatives = db['amount_of_alternatives'] alternatives = db['alternatives'] questions = db['questions'] results = db['results'] echo(term.clear()) text = (term.white + questions[questionnumber] + u'\r\n' + term.blue + u'-' * len(questions[questionnumber]) + u'\r\n\r\n') # only display full statistics if the screen width is above 79 columns. if term.width > 79: text = text + (term.magenta + u'(alternatives)' + term.move_x(49) + u'(votes)' + term.move_x(57) + u'(percentage)\r\n') totalvotes = 0.00 for i in range(0, amount_of_alternatives[questionnumber]): totalvotes = totalvotes + results[(questionnumber, i)] for i in range(0, amount_of_alternatives[questionnumber]): if results[(questionnumber, i)] > 0: percentage = (results[(questionnumber, i)] / totalvotes) * 100 else: percentage = 0 staple = int(round(percentage / 5)) text = text + u''.join( term.move_x(0) + term.white(alternatives[ (questionnumber, i)]) + term.move_x(49) + term.cyan(str(results[(questionnumber, i)])) + u' ' + term.cyan + str(int(percentage)) + term.cyan(u'%') + term.move_x(57) + term.cyan(u'[') + term.green('#' * staple) + term.move_x(78) + term.cyan(']')) if i != amount_of_alternatives[questionnumber]: text = text + u'\r\n' else: for i in range(0, amount_of_alternatives[questionnumber]): text = text + (term.white(str(alternatives[(questionnumber, i)])) + term.cyan(u' votes: ') + term.magenta( str(results[(questionnumber, i)])) + u'\r\n') text = text.splitlines() prompt_pager(content=text, line_no=0, colors={ 'highlight': term.cyan, 'lowlight': term.green, }, width=term.width, breaker=None, end_prompt=False) echo(term.move_x(0) + term.bold_black(u'* = already voted\r\n')) waitprompt(term)
def generate_recent_oneliners(term, n_liners, offset): """ return string of all oneliners, its vert. hieght and adj. offset. """ # generate a color palette palette = [getattr(term, _color) for _color in color_palette] # for relative 'time ago' now = time.time() # fetch all liners in database, sorted ascending by time oneliners = sorted(DBProxy('oneliner').values(), key=keysort_by_datetime) # decide the start/end by given offset, to allow paging, bounds check to # ensure that it does not scroll out of range offset = min(offset, len(oneliners)) start, end = len(oneliners) - (n_liners + offset), len(oneliners) - offset if start < 0: offset += start start, end = 0, end - start if offset < 0: start, end = start + offset, end + offset offset = 0 # build up one large text field; refresh is smoother when # all text is received as a single packet final_text_field = u'' count = 0 for count, oneliner in enumerate(oneliners[start:end]): _color = palette[count % len(palette)] ago = get_timeago(now, oneliner.get('timestamp')) alias = oneliner.get('alias', 'anonymous') bbsname = ('' if not shroo_ms_enabled else oneliner.get( 'bbsname', 'untergrund')) content = (oneliner.get('oneliner', u'')) max_msglen = MAX_MSGLEN if len(alias) > username_max_length: max_msglen -= (len(alias) - username_max_length) for _ in range(10): left_part = u'{0}: {1} '.format( alias.rjust(username_max_length), term.ljust(decode_pipe(content[:max_msglen]), max_msglen)) right_part = '{0} {1}'.format(_color(bbsname.rjust(4)), ago) txt_field = left_part + right_part if term.length(txt_field) < term.width: break elif term.length(left_part) < term.width: txt_field = left_part break max_msglen -= 2 final_text_field = u''.join( (final_text_field, term.move_x(max(0, (term.width / 2) - 45)), txt_field, term.clear_eol, u'\r\n')) # return text, vertical height, and adjusted offset return final_text_field, count, offset
def save(self): # persist message index record to database from x84.bbs import DBProxy new = self.idx is None with DBProxy(FIDO_DB, use_session=False) as db_index: # Not Used, Messages are saved first, with Fido # Data save with matching index. if new: self.idx = max(map(int, db_index.keys()) or [-1]) + 1 db_index['%d' % (self.idx,)] = self
def GET(self, num=10): """ Return last x oneliners """ num = int(num) oneliners = DBProxy('oneliner', use_session=False).items() oneliners = [(int(k), v) for (k, v) in DBProxy('oneliner', use_session=False).items()] last = oneliners[-num:] # output JSON instead? if 'json' in web.input(_method='get'): return json.dumps(last) board = CFG.get('system', 'bbsname', 'x/84') page_title = 'Last {num} Oneliners on {board}'.format(num=num, board=board) oneliners_html = '' for line in last: val = line[1] oneliners_html += '<li><b>{alias}:</b> {oneliner}</li>'.format( alias=val['alias'], oneliner=val['oneliner']) web.header('Content-Type', 'text/html; charset=utf-8', unique=True) output = """ <!DOCTYPE html> <html lang="en-US"> <head> <meta charset="utf-8" /> <title>{page_title}</title> </head> <body> <h1>{page_title}</h1> <ul> {oneliners_html} </ul> </body> </html> """.format(page_title=page_title, oneliners_html=oneliners_html) return output
def more(cont=False): """ Returns True if user 'q'uit; otherwise False when prompting is complete (moar/next/whatever) """ from x84.bbs import echo, getch, getterminal, LineEditor, DBProxy prompt_key = u'\r\n\r\nENtER BBS iD: ' msg_badkey = u'\r\n\r\nbbS id iNVAliD!' term = getterminal() prompt = u', '.join( fancy_blue(char, blurb) for char, blurb in (( 'i', 'NfO', ), ( 'a', 'dd', ), ( 'c', 'OMMENt', ), ( 'r', 'AtE', ), ( 't', 'ElNEt', ), ('v', 'ANSi'), ( 'q', 'Uit', ))) if cont: prompt += u', ' + fancy_blue(' ', 'more') prompt += u': ' while True: echo(u'\r\n') echo(u'\r\n'.join( term.wrap(text=prompt, width=(term.width - (term.width / 3))))) inp = getch() if inp in (u'q', 'Q'): return True elif inp is not None and type(inp) is not int: if cont and inp == u' ': echo(u'\r\n\r\n') return False if inp.lower() in u'acrtviACRTVI': # these keystrokes require a bbs key argument, # prompt the user for one echo(prompt_key) key = LineEditor(5).read() if (key is None or 0 == len(key.strip()) or not key in DBProxy('bbslist')): echo(msg_badkey) continue process_keystroke(inp, key)
def main(): """ File browser launch point. """ import subprocess import functools session, term = getsession(), getterminal() session.activity = u'Browsing files' db_desc = DBProxy(DIZ_DB) # set syncterm font, if any if SYNCTERM_FONT and term.kind.startswith('ansi'): echo(syncterm_setfont(SYNCTERM_FONT)) # assign extractors to file types browser.diz_extractors['.zip'] = diz_from_zip # detect LHA and DMS support output, _ = subprocess.Popen(('which', 'lha'), stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() if output: browser.diz_extractors['.lha'] = functools.partial( diz_from_lha, output.rstrip()) output, _ = subprocess.Popen(('which', 'xdms'), stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() if output: browser.diz_extractors['.dms'] = functools.partial( diz_from_dms, output.rstrip()) # load flagged files browser.flagged_files = session.user.get('flaggedfiles', set()) # remove missing files/dirs from flagged files, just in case if len(browser.flagged_files): for filepath in list(browser.flagged_files)[:]: if not os.path.exists(filepath): browser.flagged_files.remove(filepath) session.user['flaggedfiles'] = browser.flagged_files # fire it up! lightbar = Lightbar(height=term.height, width=min(10, int(term.width * 0.25)), xloc=0, yloc=0, colors={ 'border': getattr(term, COLOR_BORDER), 'highlight': getattr(term, COLOR_HIGHLIGHT) }) draw_interface(term, lightbar) with term.hidden_cursor(): browse_dir(session, db_desc, term, lightbar, ROOT) echo(term.move(term.height, term.width)) echo(u'\r\n\r\n' + term.normal)
def lc_retrieve(): """ Returns tuple of ([nicknames,] u'text'), where 'text' describes in Ansi color the last callers to the system, and 'nicknames' is simply a list of last callers (for lightbar selection key). """ # pylint: disable=R0914 # Too many local variables from x84.bbs import get_user, ini, timeago, getterminal from x84.bbs import DBProxy import time term = getterminal() udb = DBProxy('lastcalls') # re-order by time called; unfortunate ..; note that sqlite # encodes unicode as utf-8; but doesn't decode it on retrieval, # of dict keys; possible upstream patching opportunity here, sortdb = {} for ((handle), (tm_lc, _nc, origin)) in (udb.items()): while tm_lc in sortdb: tm_lc += 0.1 sortdb[tm_lc] = [handle.decode('utf-8'), _nc, origin] padd_handle = (ini.CFG.getint('nua', 'max_user') + 2) padd_origin = (ini.CFG.getint('nua', 'max_location') + 2) rstr = u'' nicks = [] for tm_lc, (handle, _nc, origin) in (reversed(sorted(sortdb.items()))): try: is_sysop = 'sysop' in get_user(handle).groups except KeyError: # anonymous/deleted accts, is_sysop = False rstr += (term.bold_red(u'@') if is_sysop else u'' ) + (term.ljust(handle, (padd_handle - (2 if is_sysop else 1)))) rstr += term.red(origin.ljust(padd_origin)) rstr += timeago(time.time() - tm_lc) rstr += u'\n' nicks.append(handle) return (nicks, rstr.rstrip())
def chk_thread(thread): """ check if bbs-scene.org thread finished, if so, farm its data and send updates via event 'oneliner_update' if there are any. """ from x84.bbs import getsession, DBProxy import logging log = logging.getLogger(__name__) session = getsession() if thread is not None and not thread.is_alive(): udb = DBProxy('oneliner') udbkeys = udb.keys() nlc = 0 for key, value in thread.content: if key not in udbkeys: udb[key] = value nlc += 1 if nlc: log.debug('%d new entries', nlc) session.buffer_event('oneliner_update', True) else: log.debug('no new %s entries'.format(thread.ident)) return True
def rate_bbs(key): """ Prompt user to rate a bbs. """ # pylint: disable=R0914 # Too many local variables from x84.bbs import getsession, getterminal, echo, LineEditor, DBProxy from x84.bbs import getch session, term = getsession(), getterminal() prompt_rating = u'\r\n\r\nRAtE 0.0 - 4.0: ' prompt_chg = u'\r\n\r\nChANGE EXiStiNG ? [yn] ' msg_invalid = u'\r\n\r\niNVAlid ENtRY.\r\n' echo(term.move(term.height, 0) + '\r\n') echo(prompt_rating) s_rating = LineEditor(3).read() if s_rating is None or 0 == len(s_rating.strip()): return try: f_rating = float(s_rating) except ValueError: echo(msg_invalid) return if f_rating < 0 or f_rating > 4: echo(msg_invalid) return entry = (session.handle, f_rating) ratings = DBProxy('bbslist', 'ratings') ratings.acquire() if session.handle in (_handle for (_handle, _rating) in ratings[key]): echo(prompt_chg) if getch() not in (u'y', u'Y'): ratings.release() return # re-define list without existing entry, + new entry ratings[key] = [(__handle, __rating) for (__handle, __rating) in ratings[key] if session.handle != __handle] + [entry] ratings.release() return # re-define as existing list + new entry ratings[key] = ratings[key] + [entry] ratings.release()
def add_oneline(msg): """ Add a oneliner to the local database. """ import time from x84.bbs import getsession, DBProxy, ini session = getsession() udb = DBProxy('oneliner') udb.acquire() udb[max([int(key) for key in udb.keys()] or [0]) + 1] = { 'oneliner': msg, 'alias': getsession().handle, 'bbsname': ini.CFG.get('system', 'bbsname'), 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'), } udb.release() session.buffer_event('oneliner_update', True) session.send_event('global', ('oneliner_update', True))
def do_merge_shroo_ms(new_content): """ Add oneliners from shroo-ms to local database. """ udb = DBProxy('oneliner') with udb: udb.update(new_content) maybe_expunge_records()
def prompt_tags(msg): """ Prompt for and return tags wished for message. """ # pylint: disable=R0914,W0603 # Too many local variables # Using the global statement from x84.bbs import DBProxy, echo, getterminal, getsession from x84.bbs import LineEditor, ini session, term = getsession(), getterminal() tagdb = DBProxy('tags') # version 1.0.9 introduced new ini option; set defaults for # those missing it from 1.0.8 upgrades. import ConfigParser try: moderated_tags = ini.CFG.getboolean('msg', 'moderated_tags') except ConfigParser.NoOptionError: moderated_tags = False try: moderated_groups = set(ini.CFG.get('msg', 'tag_moderator_groups' ).split()) except ConfigParser.NoOptionError: moderated_groups = ('sysop', 'moderator',) msg_onlymods = (u"\r\nONlY MEMbERS Of GROUPS %s MAY CREAtE NEW tAGS." % ( ", ".join(["'%s'".format(term.bold_yellow(grp) for grp in moderated_groups)]))) msg_invalidtag = u"\r\n'%s' is not a valid tag." prompt_tags1 = u"ENtER %s, COMMA-dEliMitEd. " % (term.bold_red('TAG(s)'),) prompt_tags2 = u"OR '/list', %s:quit\r\n : " % ( term.bold_yellow_underline('Escape'),) while True: # Accept user input for multiple 'tag's, or /list command echo(u'\r\n\r\n') echo(prompt_tags1) echo(prompt_tags2) width = term.width - 6 sel_tags = u', '.join(msg.tags) inp_tags = LineEditor(width, sel_tags).read() if inp_tags is not None and 0 == len(inp_tags.strip()): # no tags must be (private ..) msg.tags = set() return True if inp_tags is None or inp_tags.strip().lower() == '/quit': return False elif inp_tags.strip().lower() == '/list': # list all available tags, and number of messages echo(u'\r\n\r\nTags: \r\n') all_tags = sorted(tagdb.items()) if 0 == len(all_tags): echo(u'None !'.center(term.width / 2)) else: echo(u', '.join((term.wrap([u'%s(%d)' % (_key, len(_value),) for (_key, _value) in all_tags])) ), term.width - 2) continue echo(u'\r\n') # search input as valid tag(s) tags = set([inp.strip().lower() for inp in inp_tags.split(',')]) # if the tag is new, and the user's group is not in # tag_moderator_groups, then dissallow such tag if # 'moderated_tags = yes' in ini cfg if moderated_tags: err = False for tag in tags.copy(): if not tag in tagdb and not ( session.users.groups & moderated_groups): tags.remove(tag) echo(msg_invalidtag % (term.bold_red(tag),)) err = True if err: echo(msg_onlymods) continue msg.tags = tags return True
def poll_network_for_messages(net): " pull for new messages of network, storing locally. " from x84.bbs import Msg, DBProxy from x84.bbs.msgbase import to_localtime log = logging.getLogger(__name__) log.debug(u'[{net[name]}] polling for new messages.'.format(net=net)) try: last_msg_id = get_last_msg_id(net['last_file']) except (OSError, IOError) as err: log.error('[{net[name]}] skipping network: {err}' .format(net=net, err=err)) return msgs = pull_rest(net=net, last_msg_id=last_msg_id) if msgs is not False: log.info('{net[name]} Retrieved {num} messages' .format(net=net, num=len(msgs))) else: log.debug('{net[name]} no messages.'.format(net=net)) return transdb = DBProxy('{0}trans'.format(net['name']), use_session=False) transkeys = transdb.keys() msgs = sorted(msgs, cmp=lambda x, y: cmp(int(x['id']), int(y['id']))) # store messages locally, saving their translated IDs to the transdb for msg in msgs: store_msg = Msg() store_msg.recipient = msg['recipient'] store_msg.author = msg['author'] store_msg.subject = msg['subject'] store_msg.body = msg['body'] store_msg.tags = set(msg['tags']) store_msg.tags.add(u''.join((net['name']))) if msg['recipient'] is None and u'public' not in msg['tags']: log.warn("{net[name]} No recipient (msg_id={msg[id]}), " "adding 'public' tag".format(net=net, msg=msg)) store_msg.tags.add(u'public') if (msg['parent'] is not None and str(msg['parent']) not in transkeys): log.warn('{net[name]} No such parent message ({msg[parent]}, ' 'msg_id={msg[id]}), removing reference.' .format(net=net, msg=msg)) elif msg['parent'] is not None: store_msg.parent = int(transdb[msg['parent']]) if msg['id'] in transkeys: log.warn('{net[name]} dupe (msg_id={msg[id]}) discarded.' .format(net=net, msg=msg)) else: # do not save this message to network, we already received # it from the network, set send_net=False store_msg.save(send_net=False, ctime=to_localtime(msg['ctime'])) with transdb: transdb[msg['id']] = store_msg.idx transkeys.append(msg['id']) log.info('{net[name]} Processed (msg_id={msg[id]}) => {new_id}' .format(net=net, msg=msg, new_id=store_msg.idx)) if 'last' not in net.keys() or int(net['last']) < int(msg['id']): net['last'] = msg['id'] if 'last' in net.keys(): with open(net['last_file'], 'w') as last_fp: last_fp.write(str(net['last'])) return
def main(autoscan_tags=None): """ Main procedure. """ # pylint: disable=W0603,R0912 # Using the global statement # Too many branches from x84.bbs import getsession, getterminal, echo, getch from x84.bbs import list_msgs session, term = getsession(), getterminal() session.activity = 'autoscan msgs' ### 尝试 session.log.info("Hick3") ### 获取所有 tag tagdb = DBProxy('tags') all_tags = sorted(tagdb.items()) session.log.info(all_tags) ### 尝试直接调出显示的 message ,第二个参数应该是标识为未读的 msg = new = [1, 2, 3, 4,5 ,6, 7, 8,9] read_messages(msg, new) return ### 首先是显示提示输入的 tag 的标签 echo(banner()) global ALREADY_READ, SEARCH_TAGS, DELETED if autoscan_tags is not None: SEARCH_TAGS = autoscan_tags echo(u''.join(( term.bold_black('[ '), term.yellow('AUtOSCAN'), term.bold_black(' ]'), u'\r\n'))) ### 默认就往这里了, 提示输入 tag else: # 默认 tag 为 public SEARCH_TAGS = set(['hick3']) # also throw in user groups, maybe the top 3 .. ? SEARCH_TAGS.update(session.user.groups) SEARCH_TAGS = prompt_tags(SEARCH_TAGS) # user escape if SEARCH_TAGS is None: return echo(u'\r\n\r\n%s%s ' % ( term.bold_yellow('SCANNiNG'), term.bold_black(':'),)) echo(u','.join([term.red(tag) for tag in SEARCH_TAGS] if 0 != len(SEARCH_TAGS) else ['<All>', ])) ### 直到有选择 tags , 保存到 session.user 中 if (SEARCH_TAGS != session.user.get('autoscan', None)): echo(u'\r\n\r\nSave tag list as autoscan on login [yn] ?\b\b') while True: inp = getch() if inp in (u'q', 'Q', unichr(27), u'n', u'N'): break elif inp in (u'y', u'Y'): session.user['autoscan'] = SEARCH_TAGS break # retrieve all matching messages,: list_msgs 根据 tags 获得所有记录,看情形这个信息量大了有问题哈 all_msgs = list_msgs(SEARCH_TAGS) echo(u'\r\n\r\n%s messages.' % (term.yellow_reverse(str(len(all_msgs),)))) if 0 == len(all_msgs): getch(0.5) return # filter messages public/private/group-tag/new ### 分 tag 和是否未读,删除等统计 ALREADY_READ = session.user.get('readmsgs', set()) DELETED = session.user.get('trash', set()) msgs, new = msg_filter(all_msgs) if 0 == len(msgs) and 0 == len(new): getch(0.5) return # prompt read 'a'll, 'n'ew, or 'q'uit echo(u'\r\n REAd [%s]ll %d%s message%s [qa%s] ?\b\b' % ( term.yellow_underline(u'a'), len(msgs), ( u' or %d [%s]EW ' % ( len(new), term.yellow_underline(u'n'),) if new else u''), u's' if 1 != len(msgs) else u'', u'n' if new else u'',)) while True: inp = getch() if inp in (u'q', 'Q', unichr(27)): return elif inp in (u'n', u'N') and len(new): # read only new messages msgs = new break elif inp in (u'a', u'A'): break # 根君上面的用户选择,读取消息, 某次记录 log msgs 和 new 都是帖子 id 的 set # read target messages # session.log.info(msgs) # session.log.info(new) read_messages(msgs, new)
def prompt_tags(tags): """ Prompt for and return valid tags from TAGDB. """ # pylint: disable=R0914,W0603 # Too many local variables # Using the global statement from x84.bbs import DBProxy, echo, getterminal, getsession from x84.bbs import Ansi, LineEditor, getch session, term = getsession(), getterminal() tagdb = DBProxy('tags') global FILTER_PRIVATE while True: # Accept user input for a 'search tag', or /list command # echo(u"\r\n\r\nENtER SEARCh %s, COMMA-dEliMitEd. " % ( term.red('TAG(s)'),)) echo(u"OR '/list', %s%s\r\n : " % ( (term.yellow_underline('^x') + u':autoscan ' if session.user.get('autoscan', False) else u''), term.yellow_underline('^a') + u':ll msgs ' + term.yellow_underline('Esc') + u':quit',)) width = term.width - 6 sel_tags = u', '.join(tags) while len(Ansi(sel_tags)) >= (width - 8): tags = tags[:-1] sel_tags = u', '.join(tags) lne = LineEditor(width, sel_tags) echo(lne.refresh()) while not lne.carriage_returned: inp = getch() if inp in (unichr(27), term.KEY_EXIT): return None if inp in (unichr(24),): # ^A:all return set() if inp in (unichr(1),): # ^X:autoscan return session.user.get('autoscan', set()) else: echo(lne.process_keystroke(inp)) if lne.carriage_returned: inp_tags = lne.content if (inp_tags is None or 0 == len(inp_tags) or inp_tags.strip().lower() == '/quit'): return set() elif inp_tags.strip().lower() == '/list': # list all available tags, and number of messages echo(term.normal) echo(u'\r\n\r\nTags: \r\n') all_tags = sorted(tagdb.items()) if 0 == len(all_tags): echo(u'None !'.center(term.width / 2)) else: echo(Ansi(u', '.join(([u'%s(%s)' % ( term.red(tag), term.yellow(str(len(msgs))),) for (tag, msgs) in all_tags]))).wrap(term.width - 2)) continue elif (inp_tags.strip().lower() == '/nofilter' and 'sysop' in session.user.groups): # disable filtering private messages FILTER_PRIVATE = False continue echo(u'\r\n') # search input as valid tag(s) tags = set([_tag.strip().lower() for _tag in inp_tags.split(',')]) for tag in tags.copy(): if not tag in tagdb: tags.remove(tag) echo(u"\r\nNO MESSAGES With tAG '%s' fOUNd." % ( term.red(tag),)) return tags
def main(): """ Main procedure. """ # pylint: disable=R0914,R0912 # Too many local variables # Too many branches from x84.bbs import DBProxy, getsession, getterminal, echo from x84.bbs import ini, LineEditor, timeago, Ansi, showcp437 from x84.bbs import disconnect, getch import time import os session, term = getsession(), getterminal() session.activity = 'logging off' handle = session.handle if ( session.handle is not None ) else 'anonymous' max_user = ini.CFG.getint('nua', 'max_user') prompt_msg = u'[spnG]: ' if session.user.get('expert', False) else ( u'%s:AY SOMEthiNG %s:REViOUS %s:EXt %s:Et thE f**k Off !\b' % ( term.bold_blue_underline(u's'), term.blue_underline(u'p'), term.blue_underline(u'n'), term.red_underline(u'Escape/g'),)) prompt_say = u''.join((term.bold_blue(handle), term.blue(u' SAYS WhAt'), term.bold(': '),)) boards = (('1984.ws', 'x/84 dEfAUlt bOARd', 'dingo',), ('htc.zapto.org', 'Haunting the Chapel', 'Mercyful',), ('pharcyde.ath.cx', 'Pharcyde BBS', 'Access Denied',), ('bloodisland.ph4.se', 'Blood Island', 'xzip',), ('ssl.archaicbinary.net', 'Archaic Binary', 'Wayne Smith',), ('bbs.godta.com', 'godta', 'sk-5',) ,) board_fmt = u'%25s %-30s %-15s\r\n' goodbye_msg = u''.join(( term.move(term.height, 0), u'\r\n' * 10, u'tRY ANOthER fiNE bOARd', term.bold(u':'), u'\r\n\r\n', board_fmt % ( term.underline('host'.rjust(25)), term.underline('board'.ljust(30)), term.underline('sysop'.ljust(15)),), u'\r\n'.join([board_fmt % ( term.bold(host.rjust(25)), term.reverse(board.center(30)), term.bold_underline(sysop),) for (host, board, sysop) in boards]), u'\r\n\r\n', term.bold( u'back to the mundane world...'), u'\r\n',)) commit_msg = term.bold_blue( u'-- ! thANk YOU fOR YOUR CONtRibUtiON, bROthER ! --') write_msg = term.red_reverse( u'bURNiNG tO ROM, PlEASE WAiT ...') db_firstrecord = ((time.time() - 1984, u'B. b.', u'bEhAVE YOURSElVES ...'),) automsg_len = 40 artfile = os.path.join(os.path.dirname(__file__), 'art', '1984.asc') def refresh_prompt(msg): """ Refresh automsg prompt using string msg. """ echo(u''.join((u'\r\n\r\n', term.clear_eol, msg))) def refresh_automsg(idx): """ Refresh automsg database, display automsg of idx, return idx. """ session.flush_event('automsg') autodb = DBProxy('automsg') automsgs = sorted(autodb.values()) if len(autodb) else db_firstrecord dblen = len(automsgs) # bounds check if idx < 0: idx = dblen - 1 elif idx > dblen - 1: idx = 0 tm_ago, handle, msg = automsgs[idx] asc_ago = u'%s ago' % (timeago(time.time() - tm_ago)) disp = (u''.join(('\r\n\r\n', term.bold(handle.rjust(max_user)), term.bold_blue(u'/'), term.blue(u'%*d' % (len('%d' % (dblen,)), idx,)), term.bold_blue(u':'), term.blue_reverse(msg.ljust(automsg_len)), term.bold(u'\\'), term.blue(asc_ago),))) echo(u''.join(( u'\r\n\r\n', Ansi(disp).wrap(term.width), ))) return idx def refresh_all(idx=None): """ refresh screen, database, and return database index """ echo(u''.join((u'\r\n\r\n', term.clear_eol,))) for line in showcp437(artfile): echo(line) idx = refresh_automsg(-1 if idx is None else idx) refresh_prompt(prompt_msg) return idx idx = refresh_all() while True: if session.poll_event('refresh'): idx = refresh_all() elif session.poll_event('automsg'): refresh_automsg(-1) echo(u'\a') # bel refresh_prompt(prompt_msg) inp = getch(1) if inp in (u'g', u'G', term.KEY_EXIT, unichr(27), unichr(3),): # http://www.xfree86.org/4.5.0/ctlseqs.html # Restore xterm icon and window title from stack. echo(unichr(27) + u'[23;0t') echo(goodbye_msg) getch(1.5) disconnect('logoff.') elif inp in (u'n', u'N', term.KEY_DOWN, term.KEY_NPAGE,): idx = refresh_automsg(idx + 1) refresh_prompt(prompt_msg) elif inp in (u'p', u'P', term.KEY_UP, term.KEY_PPAGE,): idx = refresh_automsg(idx - 1) refresh_prompt(prompt_msg) elif inp in (u's', u'S'): # new prompt: say something ! refresh_prompt(prompt_say) msg = LineEditor(width=automsg_len).read() if msg is not None and msg.strip(): echo(u''.join((u'\r\n\r\n', write_msg,))) autodb = DBProxy('automsg') autodb.acquire() idx = max([int(ixx) for ixx in autodb.keys()] or [-1]) + 1 autodb[idx] = (time.time(), handle, msg.strip()) autodb.release() session.send_event('global', ('automsg', True,)) refresh_automsg(idx) echo(u''.join((u'\r\n\r\n', commit_msg,))) getch(0.5) # for effect, LoL # display prompt refresh_prompt(prompt_msg)