Пример #1
0
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()
Пример #2
0
 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(),
                      ))
Пример #3
0
 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
Пример #4
0
 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(),))
Пример #5
0
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):]
Пример #6
0
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)
Пример #7
0
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
Пример #8
0
 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
Пример #9
0
 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(' )-'),
         ))),
     ))
Пример #10
0
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()))
Пример #11
0
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')
Пример #12
0
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
Пример #13
0
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)
Пример #14
0
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))
Пример #15
0
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))
Пример #16
0
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))
Пример #17
0
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
Пример #18
0
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
Пример #19
0
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
Пример #20
0
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
Пример #21
0
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
Пример #22
0
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)
Пример #23
0
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]))))
Пример #24
0
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()
Пример #25
0
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