def view_plan(handle): """ Display .plan file for handle. """ from x84.bbs import getterminal, echo, Ansi, get_user, find_user term = getterminal() echo(u'\r\n\r\n') if not find_user(handle): echo(Ansi(u'No Plan.')) else: echo(Ansi(get_user(handle).get('.plan', u'No Plan.')).wrap(term.width)) echo(u'\r\n') pak()
def refresh(reader, selector, mbox, title): """ Returns unicode string suitable for refreshing the screen. """ if READING: reader.colors['border'] = term.bold_yellow selector.colors['border'] = term.bold_black else: reader.colors['border'] = term.bold_black selector.colors['border'] = term.bold_yellow padd_attr = (term.bold_yellow if not READING else term.bold_black) sel_padd_right = padd_attr( u'-' + selector.glyphs['bot-horiz'] * ( selector.visible_width - len(Ansi(str(title))) - 7) + u'-\u25a0-' if READING else u'- -') sel_padd_left = padd_attr( selector.glyphs['bot-horiz'] * 3) idx = selector.selection[0] return u''.join((term.move(0, 0), term.clear, u'\r\n',cachetime, u'// REAdiNG MSGS ..'.center(term.width).rstrip(), selector.refresh(), selector.border() if READING else reader.border(), reader.border() if READING else selector.border(), selector.title( sel_padd_left + title + sel_padd_right), selector.footer(get_selector_footer(currentpage, totalpages) ) if not READING else u'', #reader.footer(get_reader_footer(u'Post '+str(idx)) reader.footer(get_reader_footer(cachetime) ) if READING else u'', reader.refresh(), ))
def format_msg(reader, idx): """ Format message of index ``idx`` into Pager instance ``reader``. """ msg = get_msg(idx) sent = msg.stime.strftime(TIME_FMT) to_attr = term.bold_green if ( msg.recipient == session.user.handle) else term.underline ucs = u'\r\n'.join(( (u''.join(( term.yellow('fROM: '), (u'%s' % term.bold(msg.author, )).rjust(len_author), u' ' * (reader.visible_width - (len_author + len(sent))), sent, ))), u''.join(( term.yellow('tO: '), to_attr(( u'%s' % to_attr(msg.recipient, ) ).rjust(len_author) if msg.recipient is not None else u'All'), )), (Ansi( term.yellow('tAGS: ') + (u'%s ' % (term.bold(','), )).join(([ term.bold_red(_tag) if _tag in SEARCH_TAGS else term.yellow(_tag) for _tag in msg.tags ]))).wrap(reader.visible_width, indent=u' ')), (term.yellow_underline( (u'SUbj: %s' % (msg.subject, )).ljust(reader.visible_width))), u'', (msg.body), )) return ucs
def refresh(sel): """ Refresh art and yes/no prompt, ``sel``. """ session.flush_event('refresh') session.encoding = selector.selection if sel.selection == 'utf8': # ESC %G activates UTF-8 with an unspecified implementation # level from ISO 2022 in a way that allows to go back to # ISO 2022 again. echo(unichr(27) + u'%G') elif sel.selection == 'cp437': # ESC %@ returns to ISO 2022 in case UTF-8 had been entered. # ESC ) U Sets character set G1 to codepage 437 .. usually. echo(unichr(27) + u'%@') echo(unichr(27) + u')U') else: assert False, "Only encodings 'utf8' and 'cp437' supported." # display art, banner, paragraph, refresh selector refresh buf = [line for line in art] return u''.join(( u'\r\n\r\n', u'\r\n'.join(buf), u'\r\n\r\n', Ansi(enc_prompt).wrap(int(term.width * .95)), u'\r\n\r\n', sel.refresh(),))
def get_oltxt(): """ Return unicode terminal string of oneliners. """ import time from x84.bbs import getterminal, DBProxy, timeago, Ansi term = getterminal() colors = (term.bold_white, term.bold_green, term.bold_blue) hist = [(int(k), v) for (k, v) in DBProxy('oneliner').items()] hist.sort() 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() output.append(u''.join(( term.bold_white('('), color(atime), term.bold_black(u' ago'), term.bold_black(u' '), color(onel['alias']), term.bold_black(u'/'), onel['bbsname'], term.bold_white(u')'), color(u': '), Ansi(onel['oneliner']).decode_pipe(), ))) return output[(BUF_HISTORY * -1):]
def prompt(): """ Return string suitable for displaying prompt and available commands. """ from x84.bbs import getsession, getterminal, Ansi session, term = getsession(), getterminal() decorate = lambda key, desc: u''.join(( u'(', term.green_underline(key, ), u')', term.reverse_green(desc.split()[0]), u' ', u' '.join(desc.split()[1:]), u' ', )) return Ansi(u''.join(( u' ' * 2, term.green_reverse(':keys'), u' ', decorate('c', 'hAt USR'), decorate('s', 'ENd MSG'), (u''.join(( decorate('p', 'lAYbACk REC'), decorate('w', 'AtCh liVE'), decorate('d', 'iSCONNECt SiD'), decorate('e', 'diT USR'), decorate('v', 'iEW SiD AttRS'), u' ', )) if 'sysop' in session.user.groups else u''), decorate('Escape/q', 'Uit'), decorate('Spacebar', 'REfRESh'), ))).wrap(int(term.width * .7), indent=u' ' * 8)
def get_lbcontent(lightbar): """ Returns ucs string for content of Lightbar instance, ``lightbar``. """ from x84.bbs import Ansi # a custom 'soft newline' versus 'hard newline' is implemented, # '\n' == 'soft', '\r\n' == 'hard' lines = list() for lno, (_row, ucs) in enumerate(lightbar.content): # first line always appends as-is, otherwise if the previous line # matched a hardwrap, or did not match softwrap, then append as-is. # (a simple .endswith() can't wll work with a scheme of '\n' vs. # '\r\n') if lno == 0 or (is_hardwrapped(lines[-1]) or not is_softwrapped(lines[-1])): lines.append(ucs) else: # otherwise the most recently appended line must end with # SOFTWRAP, strip that softwrap and re-assign value to a # whitespace-joined value by current line value. lines[-1] = WHITESPACE.join(( lines[-1].rstrip(), ucs.lstrip(), )) retval = Ansi(u''.join(lines)).encode_pipe() return retval
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 statusline(lightbar): """ Display status line and command help on ``lightbar`` borders """ lightbar.colors['border'] = term.red if edit else term.yellow keyset_cmd = u'' if not edit: keyset_cmd = u''.join(( term.yellow(u'-( '), term.yellow_underline(u'S'), u':', term.bold(u'ave'), u' ', term.yellow_underline(u'A'), u':', term.bold(u'bort'), u' ', term.yellow_underline(u'?'), u':', term.bold(u'help'), term.yellow(u' )-'), )) keyset_cmd = lightbar.pos( lightbar.height - 1, max(0, lightbar.width - (len(Ansi(keyset_cmd)) + 3))) + keyset_cmd return u''.join(( lightbar.border(), keyset_cmd, lightbar.pos(lightbar.height, lightbar.xpadding), u''.join(( term.red(u'-[ '), u'EditiNG liNE ', term.reverse_red('%d' % (lightbar.index + 1, )), term.red(u' ]-'), )) if edit else u''.join(( term.yellow(u'-( '), u'liNE ', term.yellow('%d/%d ' % ( lightbar.index + 1, len(lightbar.content), )), '%d%% ' % (int((float(lightbar.index + 1) / max(1, len(lightbar.content))) * 100)), term.yellow(u' )-'), )), lightbar.title(u''.join(( term.red('-] '), term.bold(u'Escape'), u':', term.bold_red(u'command mode'), term.red(' [-'), )) if edit else u''.join(( term.yellow('-( '), term.bold(u'Enter'), u':', term.bold_yellow(u'edit mode'), term.yellow(' )-'), ))), ))
def set_lbcontent(lightbar, ucs): """ Sets content of Lightbar instance, ``lightbar`` for given Unicode string, ``ucs``. """ from x84.bbs import Ansi # a custom 'soft newline' versus 'hard newline' is implemented, # '\n' == 'soft', '\r\n' == 'hard' content = dict() lno = 0 lines = ucs.split(HARDWRAP) for idx, ucs_line in enumerate(lines): if idx == len(lines) - 1 and 0 == len(ucs_line): continue ucs_joined = WHITESPACE.join(ucs_line.split(SOFTWRAP)) ucs_wrapped = Ansi(ucs_joined).wrap( lightbar.visible_width).splitlines() for inner_lno, inner_line in enumerate(ucs_wrapped): content[lno] = u''.join( (inner_line, SOFTWRAP if inner_lno != len(ucs_wrapped) - 1 else u'')) lno += 1 if 0 == len(ucs_wrapped): content[lno] = HARDWRAP lno += 1 else: content[lno - 1] += HARDWRAP if 0 == len(content): content[0] = HARDWRAP lightbar.update(sorted(content.items()))
def refresh_opts(pager, handle): """ Refresh pager border with command keys available. """ from x84.bbs import getsession, getterminal, get_user, find_user, Ansi session, term = getsession(), getterminal() if not handle or not find_user(handle): has_plan = 0 else: has_plan = 0 != len(get_user(handle).get('.plan', u'').strip()) decorate = lambda key, desc: u''.join(( term.red_underline(key,), u':', term.yellow(desc.split()[0]), u' ', u' '.join(desc.split()[1:]), u' ' if len(desc.split()) > 1 else u'',)) statusline = u''.join(( term.bold_yellow(u'- '), decorate(u'Escape/q', 'Uit'), decorate(u'v', 'iEW .PLAN') if has_plan else u'', decorate(u'e', 'dit USR') if 'sysop' in session.user.groups else u'', term.bold_yellow(u'-'), )) if len(Ansi(statusline)) < (pager.visible_width - 4): return pager.border() + pager.footer(statusline) else: return pager.border() + pager.footer(term.bold_red('q') + u':uit')
def banner(): """ Return banner """ from x84.bbs import getterminal, Ansi, from_cp437 import os term = getterminal() output = u'' output += u'\r\n\r\n' if term.width >= 78: output += term.home + term.normal + term.clear # xzip's ansi is line-clean, center-align with terminal width, artfile = os.path.join(os.path.dirname(__file__), 'ol.ans') art = open(artfile).readlines() max_ans = max([len(Ansi(from_cp437(line.rstrip()))) for line in art]) for line in art: padded = Ansi(from_cp437(line.rstrip())).center(max_ans) output += term.normal + term.blue # minor fix for this art ;/ output += Ansi(padded).center(term.width).rstrip() + '\r\n' return output + term.normal
def dummy_pager(user): """ A dummy selector for profile attributes """ from x84.bbs import getsession, getterminal, echo, Ansi, getch session, term = getsession(), getterminal() plan = user.get('.plan', False) from x84.bbs.ini import CFG def_timeout = CFG.getint('system', 'timeout') menu = ['(c)%-20s - %s' % (u'hARACtER ENCOdiNG', term.bold(session.encoding),), '(t)%-20s - %s' % (u'ERMiNAl tYPE', term.bold(session.env.get('TERM', 'unknown')),), '(h)%-20s - %s' % (u'ERMiNAl hEiGht', term.bold(str(term.height)),), '(w)%-20s - %s' % (u'ERMiNAl WidtH', term.bold(str(term.width)),), '(l)%-20s - %s' % (u'OCAtiON', term.bold(user.location),), '(p)%-20s - %s' % (u'ASSWORd', term.bold_black(u'******'),), '(!)%-20s - %s' % (u'Set SA User Cookie', term.bold(user['sausercookie']),), '(@)%-20s - %s' % (u'Set SA Pass Cookie', term.bold_black(user['sapasscookie']),), '(e)%-20s - %s' % (u'-MAil AddRESS', term.bold(user.email),), # '(!)%-20s - %s' % (u'SA User Cookie', # term.bold(user['sausercookie']),), # '(@)%-70s - %s' % (u'SA Pass Cookie', # term.bold(user['sapasscookie']),), (term.bold('t') + '(i)%-19s - %s' % (u'MEOUt', term.bold( str(user.get('timeout', def_timeout))),)), '(s)%-20s - %s' % (u'YSOP ACCESS', term.bold(u'ENAblEd' if 'sysop' in user.groups else 'diSAblEd')), '(m)%-20s - %s' % (u'ESG', term.bold(u'[%s]' % ('y' if user.get( 'mesg', True) else 'n',),)), '(.)%-20s - %s' % (u'PlAN filE', '%d bytes' % ( len(plan),) if plan else '(NO PlAN.)'), '(x)%-20s - %s' % (u'PERt MOdE', term.bold(u'ENAblEd' if user.get('expert', False) else 'diSAblEd')), '(q)Uit', ] echo(term.normal + term.clear() ) lines = Ansi('\n'.join(menu)).wrap(term.width).splitlines() xpos = max(1, int(term.width / 2) - (40 / 2)) for row, line in enumerate(lines): if row and (0 == row % (term.height - 2)): echo(term.reverse(u'\r\n-- More --')) getch() echo(u'\r\n' + ' ' * xpos + line) echo(u'\r\n\r\n Enter option [ctle.xq]: ') return process_keystroke(getch(), user)
def quote_body(msg, width=79, author='some asshole', quote_txt=u'> ', hardwrap=u'\r\n'): """ Given a message, return new string suitable for quoting it. """ from x84.bbs import Ansi ucs = u'' for line in msg.splitlines(): ucs += u''.join(( quote_txt, Ansi(line).wrap(width - len(quote_txt), indent=quote_txt), hardwrap,)) return u''.join(( author,' posted:', hardwrap, ucs, hardwrap))
def quote_body(msg, width=79, quote_txt=u'> ', hardwrap=u'\r\n'): """ Given a message, return new string suitable for quoting it. """ from x84.bbs import Ansi ucs = u'' for line in msg.body.splitlines(): ucs += u''.join(( quote_txt, Ansi(line).wrap(width - len(quote_txt), indent=quote_txt), hardwrap, )) return u''.join(('On ', msg.stime.strftime(TIME_FMT), u' ', msg.author, ' wrote:', hardwrap, ucs, hardwrap))
def disp_search_help(): """ Display searchbar usage. """ from x84.bbs import getterminal, echo, Ansi term = getterminal() enter = term.yellow(u'ENtER US') postal = term.bold_yellow(u'POStAl COdE') or_nearest = term.yellow(u', OR NEARESt') int_city = term.bold_yellow(u'iNtERNAtiONAl CitY.') keyhelp = (u'{t.bold_yellow}({t.normal}' u'{t.underline_yellow}ESCAPE{t.normal}' u'{t.bold_white}:{t.normal}' u'{t.yellow}EXit{t.normal}' u'{t.bold_yellow}){t.normal}'.format(t=term)) echo(u'\r\n\r\n' + term.normal) echo(Ansi(u'{enter} {postal}{or_nearest} {int_city} {keyhelp}'.format( enter=enter, postal=postal, or_nearest=or_nearest, int_city=int_city, keyhelp=keyhelp)).wrap(term.width))
def dummy_pager(): """ Display oneliners for dummy terminals and prompt to saysomething """ from x84.bbs import getterminal, Ansi, echo, getch term = getterminal() tail = term.height - 4 indent = 3 prompt_ole = u'\r\n\r\nSAY somethiNG ?! [yn]' buf = list() for record in get_oltxt(): buf.extend( Ansi(record.rstrip()).wrap(term.width - indent).split('\r\n')) echo((u'\r\n' + term.normal).join(buf[tail * -1:])) echo(prompt_ole) while True: inp = getch() if inp in (u'n', u'N', u'q', term.KEY_EXIT): break if inp in (u'y', u'Y'): return saysomething(dumb=True) return
def display_msg(msg): """ Display full message """ from x84.bbs import getterminal, getsession, echo, Ansi session, term = getsession(), getterminal() body = msg.body.splitlines() echo(u' AUthOR: ' + term.bold_yellow(msg.author) + u'\r\n\r\n') echo(u' RECiPiENt: ') echo( term.yellow(msg.recipient if msg. recipient is not None else u'<(None)=All users>')) echo(u'\r\n\r\n') echo(u' SUBjECt: ') echo(term.yellow(msg.subject)) echo(u'\r\n\r\n') echo(u' tAGS: ') echo(term.yellow(u', '.join(msg.tags))) echo(u'\r\n\r\n') echo(term.underline(u' bOdY: '.ljust(term.width - 1)) + u'\r\n') echo(Ansi(u'\r\n'.join(body)).decode_pipe() + term.normal) echo(u'\r\n' + term.underline(u''.ljust(term.width - 1))) echo(u'\r\n\r\n') session.activity = 'Constructing a %s message' % ( u'public' if u'public' in msg.tags else u'private', ) return
def process_keystroke(inp, user): """ Process keystroke, ``inp``, for target ``user``. """ # pylint: disable=R0914,R0912,R0915,R0911,W0603 # Too many local variables # Too many branches # Too many statements # Too many return statements # Using the global statement # ^ lol, this is one of those things that should be # refactored into smaller subroutines =) from x84.bbs import getsession, getterminal, echo, getch, gosub from x84.bbs import LineEditor, Ansi from x84.default.nua import set_email, set_location from x84.bbs.ini import CFG def_timeout = CFG.getint('system', 'timeout') global EXIT session, term = getsession(), getterminal() is_self = bool(user.handle == session.user.handle) invalid = u'\r\niNVAlid.' assert is_self or 'sysop' in session.user.groups if is_self and inp in (u'^',): user['sausercookie'] = u'' user['sapasscookie'] = u'' if is_self and inp in (u'c', u'C'): gosub('charset') elif is_self and inp in (u'@',): echo(u'\r\neNTER SA \'pass\' cookie: ') sapasscookie = LineEditor(50, session.user['sapasscookie']).read() echo(u"\r\n\r\nset SA pass cookie to '%s'? [yn]" % (sapasscookie,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): session.user['sapasscookie'] = sapasscookie break elif inp2 in (u'n', u'N'): break elif is_self and inp in (u'!',): echo(u'\r\neNTER SA \'user\' cookie: ') sausercookie = LineEditor(30, session.user['sausercookie']).read() echo(u"\r\n\r\nset SA user cookie to '%s'? [yn]" % (sausercookie,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): session.user['sausercookie'] = sausercookie break elif inp2 in (u'n', u'N'): break elif is_self and inp in (u't', u'T'): echo(term.move(term.height - 1, 0)) echo(ABOUT_TERM + u'\r\n') echo(u'\r\ntERMiNAl tYPE: ') term = LineEditor(30, session.env.get('TERM')).read() echo(u"\r\n\r\nset TERM to '%s'? [yn]" % (term,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): session.env['TERM'] = term break elif inp2 in (u'n', u'N'): break elif is_self and inp in (u'w', u'W'): echo(u'\r\ntERMiNAl Width: ') width = LineEditor(3, str(term.width)).read() try: width = int(width) except ValueError: echo(invalid) return True if width < 0 or width > 999: echo(invalid) return True echo(u"\r\n\r\nset COLUMNS=%d? [yn]" % (width,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): term.columns = width break elif inp2 in (u'n', u'N'): break elif is_self and inp in (u'h', u'H'): echo(u'\r\ntERMiNAl hEiGht: ') height = LineEditor(3, str(term.height)).read() try: height = int(height) except ValueError: echo(invalid) return True if height < 0 or height > 999: echo(invalid) return True echo(u"\r\n\r\nset LINES=%d? [yn]" % (height,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): term.rows = height break elif inp2 in (u'n', u'N'): break elif 'sysop' in session.user.groups and inp in (u'd', u'D',): echo(u"\r\n\r\ndElEtE %s ? [yn]" % (user.handle,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.delete() break elif inp2 in (u'n', u'N'): break EXIT = True elif 'sysop' in session.user.groups and inp in (u's', u'S',): sysop = not 'sysop' in user.groups echo(u"\r\n\r\n%s SYSOP ACCESS? [yn]" % ( 'ENAblE' if sysop else 'diSAblE',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): if sysop: user.groups.add('sysop') else: user.groups.remove('sysop') user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'p', u'P'): from x84.default.nua import set_password set_password(user) echo(u"\r\n\r\nSEt PASSWORd ? [yn]") while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'.',): echo(term.move(0, 0) + term.normal + term.clear) echo(term.move(int(term.height * .8), 0)) for line in Ansi(ABOUT_DOT_PLAN).wrap( term.width / 3).splitlines(): echo(line.center(term.width).rstrip() + u'\r\n') echo(u'\r\n\r\nPRESS ANY kEY ...') getch() if is_self: gosub('editor', '.plan') else: tmpkey = '%s-%s' % (user.handle, user.plan) draft = user.get('.plan', u'') session.user[tmpkey] = draft gosub('editor', tmpkey) if session.user.get(tmpkey, u'') != draft: echo(u"\r\n\r\nSEt .PlAN ? [yn]") while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['.plan'] = session.user[tmpkey] break elif inp2 in (u'n', u'N'): break elif inp in (u'l', u'L'): echo(term.move(term.height - 1, 0)) set_location(user) echo(u"\r\n\r\nSEt lOCAtiON tO '%s'? [yn]" % (user.location,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'e', u'E'): echo(term.move(term.height - 1, 0)) set_email(user) echo(u"\r\n\r\nSEt EMAil tO '%s'? [yn]" % (user.email,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'i', u'I'): echo(u'\r\ntiMEOUt (0=NONE): ') timeout = LineEditor(6, str(user.get('timeout', def_timeout))).read() try: timeout = int(timeout) except ValueError: echo(invalid) return True if timeout < 0: echo(invalid) return True echo(u"\r\n\r\nSet tiMEOUt=%s? [yn]" % ( timeout if timeout else 'None',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['timeout'] = timeout session.send_event('set-timeout', timeout) break elif inp2 in (u'n', u'N'): break elif inp in (u'm', u'M'): mesg = False if user.get('mesg', True) else True echo(u"\r\n\r\n%s iNStANt MESSAGiNG? [yn]" % ( 'ENAblE' if mesg else 'DiSAblE',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['mesg'] = mesg break elif inp2 in (u'n', u'N'): break elif inp in (u'x', u'X'): expert = not user.get('expert', False) echo(u"\r\n\r\n%s EXPERt MOdE? [yn]" % ( 'ENAblE' if expert else 'DiSAblE',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['expert'] = expert break elif inp2 in (u'n', u'N'): break elif inp in (u'q', u'Q',): EXIT = True else: return False return True
def msg_filter(msgs): """ filter all matching messages. userland implementation of private/public messaging by using the 'tags' database. 'new', or unread messages are marked by idx matching session.user['readmsgs'] when read. Finally, implement 'group' tagging, so that users of group 'impure' are allowed to read messages tagged by 'impure', regardless of recipient or 'public'. returns (msgs), (new). new contains redundant msgs """ # pylint: disable=R0914,R0912,R0915 # Too many local variables # Too many branches # Too many statements from x84.bbs import list_msgs, echo, getsession, getterminal, get_msg, Ansi session, term = getsession(), getterminal() public_msgs = list_msgs(('public', )) addressed_to = 0 addressed_grp = 0 filtered = 0 deleted = 0 private = 0 public = 0 new = set() echo(u' Processing ' + term.reverse_yellow('..')) for msg_id in msgs.copy(): if msg_id in public_msgs: # can always ready msgs tagged with 'public' public += 1 else: private += 1 msg = get_msg(msg_id) if msg.recipient == session.user.handle: addressed_to += 1 else: # a system of my own, by creating groups # with the same as tagged messages, you may # create private or intra-group messages. tag_matches_group = False for tag in msg.tags: if tag in session.user.groups: tag_matches_group = True break if tag_matches_group: addressed_grp += 1 elif msg_id not in public_msgs: # denied to read this message if FILTER_PRIVATE: msgs.remove(msg_id) filtered += 1 continue elif msg_id in DELETED: msgs.remove(msg_id) deleted += 1 if msg_id not in ALREADY_READ: new.add(msg_id) if 0 == len(msgs): echo(u'\r\n\r\nNo messages (%s filtered).' % (filtered, )) else: txt_out = list() if addressed_to > 0: txt_out.append('%s addressed to you' % (term.bold_yellow(str(addressed_to)), )) if addressed_grp > 0: txt_out.append('%s addressed by group' % (term.bold_yellow(str(addressed_grp)), )) if filtered > 0: txt_out.append('%s filtered' % (term.bold_yellow(str(filtered)), )) if deleted > 0: txt_out.append('%s deleted' % (term.bold_yellow(str(deleted)), )) if public > 0: txt_out.append('%s public' % (term.bold_yellow(str(public)), )) if private > 0: txt_out.append('%s private' % (term.bold_yellow(str(private)), )) if len(new) > 0: txt_out.append('%s new' % (term.bold_yellow(str(len(new), )), )) if 0 != len(txt_out): echo(u'\r\n\r\n' + Ansi(u', '.join(txt_out) + u'.').wrap(term.width - 2)) return 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,W0141,R0912 # Too many local variables # Used builtin function 'map' # Too many branches from x84.bbs import getsession, getterminal, Ansi, echo, getch, from_cp437 from x84.engine import __url__ as url import platform import random import sys import os session, term = getsession(), getterminal() session.activity = 'System Info' artfile = os.path.join( os.path.dirname(__file__), 'art', 'plant.ans', ) system, _node, release, _version, machine, _processor = platform.uname() body = [ u'AUthORS:', u'Johannes Lundberg', u'Jeffrey Quast', u'Wijnand Modderman-Lenstra', u'', u'ARtWORk:', u'spidy!food,', u'hellbeard!impure', u'\r\n', u'SYStEM: %s %s %s' % (system, release, machine), u'SOftWARE: X/84', url, u'\r\n', (platform.python_implementation() + u' ' + '-'.join(map(str, sys.version_info[3:]))) + u' ' + (platform.python_version() if hasattr(platform, 'python_implementation') else u'.'.join( map(str, sys.version_info[:3]))), ] melt_colors = ([term.normal] + [term.bold_blue] * 3 + [term.red] * 4 + [term.bold_red] + [term.bold_white] + [term.normal] * 6 + [term.blue] * 2 + [term.bold_blue] + [term.bold_white] + [term.normal]) art = from_cp437(open(artfile).read()) if os.path.exists(artfile) else u'' otxt = list(art.splitlines()) for num, line in enumerate(body): while num > len(otxt): otxt += [ u'', ] otxt[num] = otxt[num][:int(term.width / 2.5)] + u' ' + line width = max([len(Ansi(line)) for line in otxt]) height = len(otxt) num_stars = int((term.width * term.height) * .002) stars = dict([(n, (random.choice('\\|/-'), float(random.choice(range(term.width))), float(random.choice(range(term.height))))) for n in range(num_stars)]) melting = {} show_star = False tm_out, tm_min, tm_max, tm_step = 0.08, 0.01, 2.0, .01 wind = (0.7, 0.1, 0.01, 0.01) def refresh(): """ Refresh screen and return top-left (x, y) location. """ echo(u'\r\n\r\n') if term.width < width: echo(u''.join(( term.move(term.height, 0), u'\r\n\r\n', term.bold_red + 'screen too thin! (%s/%s)' % ( term.width, width, ), u'\r\n\r\n', u'press any key...', ))) getch() return (None, None) if term.height < height: echo(u''.join(( term.move(term.height, 0), u'\r\n\r\n', term.bold_red + 'screen too short! (%s/%s)' % (term.height, height), u'\r\n\r\n', u'press any key...', ))) getch() return (None, None) xloc = (term.width / 2) - (width / 2) yloc = (term.height / 2) - (height / 2) echo(u''.join(( term.normal, (u'\r\n' + term.clear_eol) * term.height, u''.join([ term.move(yloc + abs_y, xloc) + line for abs_y, line in enumerate(otxt) ]), ))) return xloc, yloc txt_x, txt_y = refresh() if (txt_x, txt_y) == (None, None): return def char_at_pos(yloc, xloc, txt_y, txt_x): """ Return art (y, x) for location """ return (u' ' if yloc - txt_y < 0 or yloc - txt_y >= height or xloc - txt_x < 0 or xloc - txt_x >= len(otxt[yloc - txt_y]) else otxt[yloc - txt_y][xloc - txt_x]) def iter_wind(xslope, yslope, xdir, ydir): """ An easterly Wind """ xslope += xdir yslope += ydir if xslope <= 0.5: xdir = random.choice([0.01, 0.015, 0.02]) elif xslope >= 1: xdir = random.choice([-0.01, -0.015, -0.02]) if yslope <= -0.1: ydir = random.choice([0.01, 0.015, 0.02, 0.02]) elif yslope >= 0.1: ydir = random.choice([-0.01, -0.015, -0.02]) return xslope, yslope, xdir, ydir def iter_star(char, xloc, yloc): """ Given char and current position, apply wind and return new char and new position. """ if char == '\\': char = '|' elif char == '|': char = '/' elif char == '/': char = '-' elif char == '-': char = '\\' xloc += wind[0] yloc += wind[1] if xloc < 1 or xloc > term.width: xloc = (1.0 if xloc > term.width else float(term.width)) yloc = (float(random.choice(range(term.height)))) if yloc < 1 or yloc > term.height: yloc = (1.0 if yloc > term.height else float(term.height)) xloc = (float(random.choice(range(term.width)))) return char, xloc, yloc def erase(star_idx): """ erase old star before moving .. """ if show_star: _char, xloc, yloc = stars[star_idx] echo(''.join(( term.move(int(yloc), int(xloc)), term.normal, char_at_pos(int(yloc), int(xloc), txt_y, txt_x), ))) def melt(): """ Iterate through all stars and phase through melt sequence. """ def melted(yloc, xloc): """ shift melt, delete if dissapeared. """ melting[(yloc, xloc)] -= 1 if 0 == melting[(yloc, xloc)]: del melting[(yloc, xloc)] for (yloc, xloc), phase in melting.items(): echo(''.join(( term.move(yloc, xloc), melt_colors[phase - 1], char_at_pos(yloc, xloc, txt_y, txt_x), ))) melted(yloc, xloc) def draw_star(star, xloc, yloc): """ draw star a (x, y) location """ char = char_at_pos(int(yloc), int(xloc), txt_y, txt_x) if char != ' ': melting[(int(yloc), int(xloc))] = len(melt_colors) if show_star: echo(term.move(int(yloc), int(xloc)) + melt_colors[-1] + star) with term.hidden_cursor(): while txt_x is not None and txt_y is not None: if session.poll_event('refresh'): num_stars = int(num_stars) stars = dict([(n, (random.choice('\\|/-'), float(random.choice(range(term.width))), float(random.choice(range(term.height))))) for n in range(num_stars)]) otxt = list(art.splitlines()) for num, line in enumerate(body): while num > len(otxt): otxt += [ u'', ] otxt[num] = (otxt[num][:int(term.width / 2.5)] + u' ' + line) txt_x, txt_y = refresh() continue inp = getch(tm_out) if inp in (term.KEY_UP, 'k'): if tm_out >= tm_min: tm_out -= tm_step elif inp in (term.KEY_DOWN, 'j'): if tm_out <= tm_max: tm_out += tm_step elif inp in (term.KEY_LEFT, 'h'): if num_stars > 2: num_stars = int(num_stars * .5) stars = dict([(n, (random.choice('\\|/-'), float(random.choice(range(term.width))), float(random.choice(range(term.height))))) for n in range(num_stars)]) elif inp in (term.KEY_RIGHT, 'l'): if num_stars < (term.width * term.height) / 4: num_stars = int(num_stars * 1.5) stars = dict([(n, (random.choice('\\|/-'), float(random.choice(range(term.width))), float(random.choice(range(term.height))))) for n in range(num_stars)]) elif inp in (u'*', ) and not show_star: show_star = True elif inp in (u'*', ) and show_star: for star in stars: erase(star) show_star = False elif inp is not None: echo(term.move(term.height, 0)) break melt() for star_key, star_val in stars.items(): erase(star_key) # pylint: disable=W0142 # Used * or ** magic stars[star_key] = iter_star(*star_val) draw_star(*stars[star_key]) # pylint: disable=W0142 # Used * or ** magic wind = iter_wind(*wind)
def refresh(items=[ 'all', ]): """ Refresh main menu. """ # pylint: disable=R0914 # Too many local variables from x84.bbs import getsession, getterminal, echo, Ansi, showcp437, ini, AnsiWindow import os import random, time, glob session, term = getsession(), getterminal() session.activity = u'Main menu' headers = glob.glob( os.path.join(os.path.dirname(__file__), "art", "YOSBBS*.ANS")) bannername = "YOSBBS" + str(random.randrange( 1, len(headers))).zfill(2) + ".ANS" artfile = os.path.join(os.path.dirname(__file__), 'art', bannername) echo(term.clear()) for line in showcp437(artfile): echo(line) echo(u'\r\n\r\n') entries = [ # ('b', 'bS NEXUS'), ('y', 'OsPOsT'), ('l', 'ASt CAllS'), ('o', 'NE liNERS'), ('w', "hO'S ONliNE"), ('n', 'EWS'), # ('c', 'hAt'), ('!', 'ENCOdiNG'), # ('t', 'EtRiS'), # ('s', 'YS. iNfO'), ('f', 'ORECASt'), ('e', 'dit PROfilE'), # ('p', 'OSt A MSG'), # ('r', 'EAd All MSGS'), ('g', 'eT OUt'), ] # add LORD to menu only if enabled, #if ini.CFG.getboolean('dosemu', 'enabled'):# and ( #ini.CFG.get('dosemu', 'lord_path') != 'no'): entries.insert(0, ('#', 'PlAY lORd!')) #if 'sysop' in session.user.groups: # entries += (('v', 'idEO CASSEttE'),) menu_item_width = 20 #allows us 16 char columns after pad/key menu_columns = 4 menulist = list() buf_str = u'' menucol = 1 for key, name in entries: menutext = u''.join(( term.green(name.split()[0]), u' ', u' '.join(name.split()[1:]), )) out_str = Ansi(u''.join( (term.bold(u'('), term.bold_green_underline(key), term.bold(u')'), menutext, u' '))).ljust(menu_item_width) buf_str += out_str menucol += 1 if menucol > menu_columns: menulist.append(buf_str) buf_str = u'' menucol = 1 if len(buf_str) > 0: menulist.append(buf_str) echo(term.move(term.height - len(menulist) - 1, 0)) for i, m in enumerate(menulist): echo(term.move(term.height - i - 2, 0)) echo(Ansi(m).ljust(term.width)) echo(term.move(term.height - 1, 0)) echo(u' [%s]:' % (term.blue_underline(''.join([key for key, name in entries]))))
def dummy_pager(last_callers): """ Dummy pager for displaying last callers """ # pylint: disable=R0914 # Too many local variables from x84.bbs import getterminal, getsession, echo, getch, ini, find_user from x84.bbs import LineEditor, Ansi, list_users, get_user, gosub session, term = getsession(), getterminal() msg_prompt = ( u'\r\n%sONtiNUE, %stOP, %sON-StOP %siEW .PlAN%s ?\b\b' % ( term.bold(u'[c]'), term.bold(u'[s]'), term.bold(u'n'), term.bold(u'[v]'), u' [e]dit USR' if ( 'sysop' in session.user.groups) else u'',)) msg_partial = u'PARtiAl MAtChES' msg_prompt_handle = u'ViEW .PlAN ::- ENtER hANdlE: ' redraw() echo(u'\r\n\r\n') nonstop = False row = 10 # after-art, for txt in last_callers: echo(Ansi(txt).ljust(term.width / 2).center(term.width)) echo(u'\r\n') row += 1 if ((not nonstop and row > 0 and 0 == (row % (term.height - 3))) or (row == len(last_callers) - 1)): echo(msg_prompt) inp = getch() row = 2 if inp in (u's', u'S', u'q', u'Q', term.KEY_EXIT): return if inp in (u'v', u'V') or 'sysop' in session.user.groups and ( inp in (u'e', u'E')): echo(u'\r\n\r\n') echo(msg_prompt_handle) handle = LineEditor(ini.CFG.getint('nua', 'max_user')).read() usrlist = list_users() if handle is None or 0 == len(handle.strip()): continue handle = handle.strip() if handle.lower() in [nick.lower() for nick in list_users()]: nick = ((_nick for _nick in usrlist if _nick.lower() == handle.lower()).next()) if find_user(nick): user = get_user(nick) if 'sysop' in session.user.groups and ( inp in (u'e', u'E')): gosub('profile', user.handle) else: view_plan(user.handle) else: misses = [nick for nick in usrlist.keys() if nick.lower().startswith(handle[:1].lower())] if len(misses) > 0: echo(u'%s:\r\n\r\n%s\r\n' % (msg_partial, Ansi(', '.join(misses)).wrap(term.width))) continue if inp in ('n', u'N'): nonstop = True echo(u'\r\n\r\n') pak()
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, 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( 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(',')]) # 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