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 prompt_subject(term, msg, colors): """ Prompt for subject of message. """ xpos = max(0, (term.width // 2) - (80 // 2)) echo(u"".join((term.move_x(xpos), term.clear_eos, u"Enter Subject.\r\n", term.move_x(xpos), u":: "))) inp = LineEditor(subject_max_length, msg.subject, colors={"highlight": colors["backlight"]}).read() if inp is None or not inp.strip(): echo(u"".join((term.move_x(xpos), colors["highlight"]("Canceled."), term.clear_eol))) term.inkey(1) return False msg.subject = inp.strip() return True
def prompt_recipient(term, msg, colors, public=True): """ Prompt for recipient of message. """ xpos = max(0, (term.width // 2) - (80 // 2)) echo(term.move_x(xpos) + term.clear_eos) echo(u"Enter handle{0}.\r\n".format(u", empty to address to all" if public else u"")) echo(term.move_x(xpos) + ":: ") inp = LineEditor(username_max_length, msg.recipient, colors={"highlight": colors["backlight"]}).read() if inp is None: echo(u"".join((term.move_x(xpos), colors["highlight"]("Canceled."), term.clear_eol))) term.inkey(1) return False elif not inp.strip(): # empty, recipient is None msg.recipient = None if public: return True return False inp = inp.strip() # validate/find user userlist = list_users() if inp in userlist: # exact match, msg.recipient = inp echo(u"\r\n") return True # nearest match for match in difflib.get_close_matches(inp.strip(), userlist): echo(u"".join((term.move_x(xpos), u"{0} [yn]".format(colors["highlight"](match)), term.clear_eol, u" ?\b\b"))) while True: inp = term.inkey() if inp.code == term.KEY_ESCAPE: # escape/cancel return False elif inp.lower() == u"y": # accept match msg.recipient = match echo(u"\r\n") return True elif inp.lower() == u"n": # next match break echo(u"".join((term.move_x(xpos), colors["highlight"]("No match."), term.clear_eol))) term.inkey(1) return False
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 prompt_subject(term, msg, colors): """ Prompt for subject of message. """ xpos = max(0, (term.width // 2) - (80 // 2)) echo(u''.join((term.move_x(xpos), term.clear_eos, u'Enter Subject.\r\n', term.move_x(xpos), u':: '))) inp = LineEditor(subject_max_length, msg.subject, colors={'highlight': colors['backlight']} ).read() if inp is None or not inp.strip(): echo(u''.join((term.move_x(xpos), colors['highlight']('Canceled.'), term.clear_eol))) term.inkey(1) return False msg.subject = inp.strip() return True
def say_something(term, session): """ Prompt user to post a 'oneliner' entry. """ # use same line as previous prompt, clearing it first, echo(term.move_x(0) + term.clear_eol) colors = {'highlight': term.red_reverse} xpos = max((term.width / 2) - ((MAX_MSGLEN / 2) + 3), 0) echo(term.move_x(xpos)) if MIN_ELAPSED: lastliner = session.user.get('lastliner', 0) if lastliner and time.time() - lastliner > MIN_ELAPSED: echo(term.red_bold("You've said enough already!")) term.inkey(timeout=1) echo(term.move_x(0)) # only re-draw prompt (user canceled) return False echo(term.move_x(xpos)) echo(term.red('say ') + term.bold_red('>') + u' ') inp = LineEditor(MAX_MSGLEN, colors=colors).read() if not inp or not len(inp.strip()): # canceled echo(term.move_x(0) + term.clear_eol) return False echo(term.move_x(xpos) + term.clear_eol) session.user['lastliner'] = time.time() # optionally post to shroo.ms 'global oneliners' if shroo_ms_enabled: echo(term.red_reverse('Burning, please wait ...')) return post_shroo_ms(message=inp, username=session.user.handle) # or, post locally add_oneline(session=session, message=inp.strip()) return True
def do_login(term): sep_ok = getattr(term, color_secondary)(u'::') sep_bad = getattr(term, color_primary)(u'::') colors = {'highlight': getattr(term, color_primary)} for _ in range(login_max_attempts): echo(u'\r\n\r\n{sep} Login: '******'' if handle.strip() == u'': continue # user says goodbye if handle.lower() in bye_usernames: return # user applies for new account if new_allowed and handle.lower() in new_usernames: gosub(new_script) display_banner(term) continue # user wants to reset password if reset_allowed and handle.lower() == 'reset': gosub(reset_script) display_banner(term) continue # user wants to login anonymously if anonymous_allowed and handle.lower() in anonymous_names: user = User('anonymous') else: # authenticate password echo(u'\r\n\r\n{sep} Password: '******'' user = authenticate_user(handle, password) if not user: echo(u'\r\n\r\n{sep} Login failed.'.format(sep=sep_bad)) continue goto(top_script, handle=user.handle) echo(u'\r\n\r\n{sep} Too many authentication attempts.\r\n' .format(sep=sep_bad))
def prompt_input(term, key, **kwargs): """ Prompt for user input. """ sep_ok = getattr(term, color_secondary)(u'::') sep_bad = getattr(term, color_primary)(u'::') colors = {'highlight': getattr(term, color_primary)} echo(fixate_next(term)) echo(u'{sep} {key:>18}: '.format(sep=sep_ok, key=key)) entry = LineEditor(colors=colors, **kwargs).read() or u'' if not entry.strip(): echo(fixate_next(term)) echo(u'{sep} Canceled !\r\n'.format(sep=sep_bad)) log.debug('Password reset canceled at prompt key={0}.'.format(key)) return u'' return entry
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 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 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 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()]: user = get_user((nick for nick in usrlist if nick.lower() == handle.lower()).next()) 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 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 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, showart from x84.bbs import disconnect, getch import time import os session, term = getsession(), getterminal() session.activity = 'logging off' handle = session.user.handle or '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.bold_blue_underline(u'p'), term.bold_blue_underline(u'n'), term.red_underline(u'Escape/g'),)) prompt_say = u''.join((term.bold_blue(handle), term.bold_blue(u' SAYS WhAt'), term.bold(': '),)) boards = (('1984.ws', 'x/84 dEfAUlt bOARd', 'Dingo',), ('htc.zapto.org', 'Haunting the Chapel', 'Mercyful fate',), ('bbs.pharcyde.org', 'Pharcyde BBS', 'Access Denied',), ('bloodisland.ph4.se', 'Blood Island/X', 'Xzippo',), ('blackflag.acid.org:2627', 'Piranha: Black Flag', 'Caphood',), ('oddnetwork.org', '79 columns', 'Haliphax'), ('bbs.beardy.se', 'The Swamp', 'Beardy'), ('maze.io', 'rANDOM nOIZE', 'Maze'),) 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 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.bold_blue(u'%*d' % (len('%d' % (dblen,)), idx,)), term.bold_blue(u':'), term.bold_green(msg.ljust(automsg_len)), term.bold(u'\\'), term.bold_blue(asc_ago),)) echo(u'\r\n'.join(term.wrap(disp))) 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 showart( os.path.join(os.path.dirname(__file__), 'art', 'logoff.ans'), 'cp437'): 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)
def get_username(handle=u''): """ Prompt for a login handle. If unfound, script change to 'nua' when allow_apply is enabled (default=yes). Also allow 'anonymous' when enabled (default=no). A unicode handle of non-zero length is returned when the login handle matches a userbase record. """ # pylint: disable=R0914,R0911 # Too many local variables # Too many return statements from x84.bbs import getterminal, ini, echo, LineEditor, gosub, goto from x84.bbs import find_user, getch term = getterminal() prompt_user = u'user: '******'\r\n\r\n --> Create new account? [ynq] <--' + '\b' * 5 allow_apply = ini.CFG.getboolean('nua', 'allow_apply') enable_anonymous = ini.CFG.getboolean('matrix', 'enable_anonymous') # pylint: disable=E1103 # Instance of '_Chainmap' has no 'split' member # (but some types could not be inferred) newcmds = ini.CFG.get('matrix', 'newcmds').split() byecmds = ini.CFG.get('matrix', 'byecmds').split() denied_msg = u'\r\n\r\nfiRSt, YOU MUSt AbANdON YOUR libERtIES.' badanon_msg = u"\r\n " + term.bright_red + u"'%s' login denied." max_user = ini.CFG.getint('nua', 'max_user') nuascript = ini.CFG.get('nua', 'script') topscript = ini.CFG.get('matrix', 'topscript') echo(prompt_user) handle = LineEditor(max_user, handle).read() if handle is None or 0 == len(handle.strip()): echo(u'\r\n') return u'' elif handle.lower() in newcmds: if allow_apply: gosub('nua', u'') return u'' denied(denied_msg) return u'' elif handle.lower() in byecmds: goto('logoff') elif handle.lower() == u'anonymous': if enable_anonymous: goto(topscript, 'anonymous') denied(badanon_msg % (handle, )) return u'' u_handle = find_user(handle) if u_handle is not None: return u_handle # matched if allow_apply is False: denied(denied_msg) return u'' echo(apply_msg) ynq = getch() if ynq in (u'q', u'Q', term.KEY_EXIT): # goodbye goto('logoff') elif ynq in (u'y', u'Y'): # new user application goto(nuascript, handle) echo(u'\r\n') return u''
def main(quick=False): """ Main procedure. """ session, term = getsession(), getterminal() session.activity = 'checking for new messages' # set syncterm font, if any if term.kind.startswith('ansi'): echo(syncterm_setfont(syncterm_font)) colors = dict(highlight=lambda txt: txt, lowlight=lambda txt: txt, backlight=lambda txt: txt, text=lambda txt: txt) if not colored_menu_items else dict( highlight=getattr(term, color_highlight), lowlight=getattr(term, color_lowlight), backlight=getattr(term, color_backlight), text=getattr(term, color_text)) yloc = top_margin = 0 subscription = session.user.get('msg_subscription', []) dirty = 2 while True: if dirty == 2: # display header art, yloc = display_banner(art_file, encoding=art_encoding, center=True) xloc = max(0, (term.width // 2) - 40) echo(u'\r\n') top_margin = yloc = (yloc + 1) elif dirty: echo(term.move(top_margin, 0) + term.normal + term.clear_eos) echo(term.move(top_margin, xloc)) if dirty: if not subscription: # prompt the user for a tag subscription, and loop # back again when completed to re-draw and show new messages. subscription = session.user['msg_subscription'] = ( prompt_subscription(session=session, term=term, yloc=top_margin, subscription=subscription, colors=colors)) continue messages, messages_bytags = get_messages_by_subscription( session, subscription) # When quick login ('y') selected in top.py, return immediately # when no new messages are matched any longer. if quick and not messages['new']: echo(term.move_x(xloc) + u'\r\nNo new messages.\r\n') return waitprompt(term) txt = describe_message_area(term=term, subscription=subscription, messages_bytags=messages_bytags, colors=colors) yloc = top_margin + show_description( term=term, description=txt, color=None, subsequent_indent=' ' * len('message area: ')) echo( render_menu_entries(term=term, top_margin=yloc, menu_items=get_menu(messages), colors=colors, max_cols=2)) echo(display_prompt(term=term, colors=colors)) echo(colors['backlight'](u' \b')) dirty = False event, data = session.read_events(('refresh', 'newmsg', 'input')) if event == 'refresh': # screen resized, redraw. dirty = 2 continue elif event == 'newmsg': # When a new message is sent, 'newmsg' event is broadcasted. session.flush_event('newmsg') nxt_msgs, nxt_bytags = get_messages_by_subscription( session, subscription) if nxt_msgs['new'] - messages['new']: # beep and re-display when a new message has arrived. echo(u'\b') messages, messages_bytags = nxt_msgs, nxt_bytags dirty = True continue elif event == 'input': # on input, block until carriage return session.buffer_input(data, pushback=True) given_inp = LineEditor(1, colors={ 'highlight': colors['backlight'] }).read() if given_inp is None: # escape/cancel continue inp = given_inp.strip() if inp.lower() in (u'n', 'a', 'v'): # read new/all/private messages message_indices = sorted( list({ 'n': messages['new'], 'a': messages['all'], 'v': messages['private'], }[inp.lower()])) if message_indices: dirty = 2 read_messages(session=session, term=term, message_indices=message_indices, colors=colors) elif inp.lower() == u'm' and messages['new']: # mark all messages as read dirty = 1 do_mark_as_read(session, messages['new']) elif inp.lower() in (u'p', u'w'): # write new public/private message dirty = 2 public = bool(inp.lower() == u'p') msg = Msg() if (not prompt_recipient( term=term, msg=msg, colors=colors, public=public) or not prompt_subject(term=term, msg=msg, colors=colors) or not prompt_body(term=term, msg=msg, colors=colors) or not prompt_tags(session=session, term=term, msg=msg, colors=colors, public=public)): continue do_send_message(session=session, term=term, msg=msg, colors=colors) elif inp.lower() == u'c': # prompt for new tag subscription (at next loop) subscription = [] dirty = 1 elif inp.lower() == u'?': # help echo(term.move(top_margin, 0) + term.clear_eos) do_describe_message_system(term, colors) waitprompt(term) dirty = 2 elif inp.lower() == u'q': return if given_inp: # clear out line editor prompt echo(colors['backlight'](u'\b \b'))
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
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)
def main(quick=False): """ Main procedure. """ session, term = getsession(), getterminal() session.activity = 'checking for new messages' # set syncterm font, if any if term.kind.startswith('ansi'): echo(syncterm_setfont(syncterm_font)) colors = dict( highlight=lambda txt: txt, lowlight=lambda txt: txt, backlight=lambda txt: txt, text=lambda txt: txt ) if not colored_menu_items else dict( highlight=getattr(term, color_highlight), lowlight=getattr(term, color_lowlight), backlight=getattr(term, color_backlight), text=getattr(term, color_text)) yloc = top_margin = 0 subscription = session.user.get('msg_subscription', []) dirty = 2 while True: if dirty == 2: # display header art, yloc = display_banner(art_file, encoding=art_encoding, center=True) xloc = max(0, (term.width // 2) - 40) echo(u'\r\n') top_margin = yloc = (yloc + 1) elif dirty: echo(term.move(top_margin, 0) + term.normal + term.clear_eos) echo(term.move(top_margin, xloc)) if dirty: if not subscription: # prompt the user for a tag subscription, and loop # back again when completed to re-draw and show new messages. subscription = session.user['msg_subscription'] = ( prompt_subscription( session=session, term=term, yloc=top_margin, subscription=subscription, colors=colors)) continue messages, messages_bytags = get_messages_by_subscription( session, subscription) # When quick login ('y') selected in top.py, return immediately # when no new messages are matched any longer. if quick and not messages['new']: echo(term.move_x(xloc) + u'\r\nNo new messages.\r\n') return waitprompt(term) txt = describe_message_area( term=term, subscription=subscription, messages_bytags=messages_bytags, colors=colors) yloc = top_margin + show_description( term=term, description=txt, color=None, subsequent_indent=' ' * len('message area: ')) echo(render_menu_entries( term=term, top_margin=yloc, menu_items=get_menu(messages), colors=colors, max_cols=2)) echo(display_prompt(term=term, colors=colors)) echo(colors['backlight'](u' \b')) dirty = False event, data = session.read_events(('refresh', 'newmsg', 'input')) if event == 'refresh': # screen resized, redraw. dirty = 2 continue elif event == 'newmsg': # When a new message is sent, 'newmsg' event is broadcasted. session.flush_event('newmsg') nxt_msgs, nxt_bytags = get_messages_by_subscription( session, subscription) if nxt_msgs['new'] - messages['new']: # beep and re-display when a new message has arrived. echo(u'\b') messages, messages_bytags = nxt_msgs, nxt_bytags dirty = True continue elif event == 'input': # on input, block until carriage return session.buffer_input(data, pushback=True) given_inp = LineEditor( 1, colors={'highlight': colors['backlight']} ).read() if given_inp is None: # escape/cancel continue inp = given_inp.strip() if inp.lower() in (u'n', 'a', 'v'): # read new/all/private messages message_indices = sorted(list( {'n': messages['new'], 'a': messages['all'], 'v': messages['private'], }[inp.lower()])) if message_indices: dirty = 2 read_messages(session=session, term=term, message_indices=message_indices, colors=colors) elif inp.lower() == u'm' and messages['new']: # mark all messages as read dirty = 1 do_mark_as_read(session, messages['new']) elif inp.lower() in (u'p', u'w'): # write new public/private message dirty = 2 public = bool(inp.lower() == u'p') msg = Msg() if ( not prompt_recipient( term=term, msg=msg, colors=colors, public=public ) or not prompt_subject( term=term, msg=msg, colors=colors ) or not prompt_body( term=term, msg=msg, colors=colors ) or not prompt_tags( session=session, term=term, msg=msg, colors=colors, public=public )): continue do_send_message(session=session, term=term, msg=msg, colors=colors) elif inp.lower() == u'c': # prompt for new tag subscription (at next loop) subscription = [] dirty = 1 elif inp.lower() == u'?': # help echo(term.move(top_margin, 0) + term.clear_eos) do_describe_message_system(term, colors) waitprompt(term) dirty = 1 elif inp.lower() == u'q': return if given_inp: # clear out line editor prompt echo(colors['backlight'](u'\b \b'))
def get_username(handle=u''): """ Prompt for a login handle. If unfound, script change to 'nua' when allow_apply is enabled (default=yes). Also allow 'anonymous' when enabled (default=no). A unicode handle of non-zero length is returned when the login handle matches a userbase record. """ # pylint: disable=R0914,R0911 # Too many local variables # Too many return statements from x84.bbs import getterminal, ini, echo, LineEditor, gosub, goto from x84.bbs import find_user, getch term = getterminal() prompt_user = u'\r\n user: '******'\r\n\r\n --> Create new account? [ynq] <--' + '\b' * 5 allow_apply = ini.CFG.getboolean('nua', 'allow_apply') enable_anonymous = ini.CFG.getboolean('matrix', 'enable_anonymous') # pylint: disable=E1103 # Instance of '_Chainmap' has no 'split' member # (but some types could not be inferred) newcmds = ini.CFG.get('matrix', 'newcmds').split() byecmds = ini.CFG.get('matrix', 'byecmds').split() denied_msg = u'\r\n\r\nfiRSt, YOU MUSt AbANdON YOUR libERtIES.' badanon_msg = u"\r\n " + term.bright_red + u"'%s' login denied." max_user = ini.CFG.getint('nua', 'max_user') nuascript = ini.CFG.get('nua', 'script') topscript = ini.CFG.get('matrix', 'topscript') echo(prompt_user) handle = LineEditor(max_user, handle).read() if handle is None or 0 == len(handle.strip()): echo(u'\r\n') return u'' elif handle.lower() in newcmds: if allow_apply: gosub('nua', u'') return u'' denied(denied_msg) return u'' elif handle.lower() in byecmds: goto('logoff') elif handle.lower() == u'anonymous': if enable_anonymous: goto(topscript, 'anonymous') denied(badanon_msg % (handle,)) return u'' u_handle = find_user(handle) if u_handle is not None: return u_handle # matched if allow_apply is False: denied(denied_msg) return u'' echo(apply_msg) ynq = getch() if ynq in (u'q', u'Q', term.KEY_EXIT): # goodbye goto('logoff') elif ynq in (u'y', u'Y'): # new user application goto(nuascript, handle) echo(u'\r\n') return u''
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_recipient(term, msg, colors, public=True): """ Prompt for recipient of message. """ xpos = max(0, (term.width // 2) - (80 // 2)) echo(term.move_x(xpos) + term.clear_eos) echo(u'Enter handle{0}.\r\n'.format( u', empty to address to all' if public else u'')) echo(term.move_x(xpos) + ':: ') inp = LineEditor(username_max_length, msg.recipient, colors={'highlight': colors['backlight']} ).read() if inp is None: echo(u''.join((term.move_x(xpos), colors['highlight']('Canceled.'), term.clear_eol))) term.inkey(1) return False elif not inp.strip(): # empty, recipient is None msg.recipient = None if public: return True return False inp = inp.strip() # validate/find user userlist = list_users() if inp in userlist: # exact match, msg.recipient = inp echo(u'\r\n') return True # nearest match for match in difflib.get_close_matches(inp.strip(), userlist): echo(u''.join(( term.move_x(xpos), u'{0} [yn]'.format(colors['highlight'](match)), term.clear_eol, u' ?\b\b'))) while True: inp = term.inkey() if inp.code == term.KEY_ESCAPE: # escape/cancel return False elif inp.lower() == u'y': # accept match msg.recipient = match echo(u'\r\n') return True elif inp.lower() == u'n': # next match break echo(u''.join((term.move_x(xpos), colors['highlight']('No match.'), term.clear_eol))) term.inkey(1) return False