Пример #1
0
def main(msg=None):
    """ Main procedure. """
    from x84.bbs import Msg, getsession
    session = getsession()
    if msg is None:
        msg = Msg()
        msg.tags = ('public',)
    banner()
    while True:
        session.activity = 'Constructing a %s message' % (
            u'public' if u'public' in msg.tags else u'private',)
        if not prompt_recipient(msg):
            break
        if not prompt_subject(msg):
            break
        if not prompt_public(msg):
            break
        if not prompt_body(msg):
            break
        # XXX
        if not prompt_tags(msg):
            break
        display_msg(msg)
        if not prompt_send():
            break
        msg.save()
        return True
    return False
Пример #2
0
def main(msg=None):
    """ Main procedure. """
    from x84.bbs import Msg, getsession
    session = getsession()
    if msg is None:
        msg = Msg()
        msg.tags = ('public', )
    banner()
    while True:
        session.activity = 'Constructing a %s message' % (
            u'public' if u'public' in msg.tags else u'private', )
        if not prompt_recipient(msg):
            break
        if not prompt_subject(msg):
            break
        if not prompt_tags(msg):
            break
        if not prompt_public(msg):
            break
        if not prompt_body(msg):
            break
        display_msg(msg)
        if not prompt_send():
            break
        msg.save()
        return True
    return False
Пример #3
0
def sendmsg(sessions):
    """
    Prompt for node and gosub 'writemsg' with recipient set to target user.
    """
    from x84.bbs import gosub, Msg
    (node, tgt_session) = get_node(sessions)
    if node is not None:
        msg = Msg()
        msg.recipient = tgt_session['handle']
        msg.tags.add('private')
        gosub('writemsg', msg)
        return True
Пример #4
0
def sendmsg(sessions):
    """
    Prompt for node and gosub 'writemsg' with recipient set to target user.
    """
    from x84.bbs import gosub, Msg
    (node, tgt_session) = get_node(sessions)
    if node is not None:
        msg = Msg()
        msg.recipient = tgt_session['handle']
        msg.tags.add('private')
        gosub('writemsg', msg)
        return True
Пример #5
0
def create_reply_message(session, idx):
    """ Given a message ``idx``, create and return a replying message. """
    parent_msg = get_msg(idx)
    msg = Msg()
    msg.parent = parent_msg.idx

    # flip from/to
    msg.recipient = parent_msg.author
    msg.author = session.user.handle

    # quote message body
    msg.body = quote_body(parent_msg)

    # duplicate subject and tags
    msg.subject = parent_msg.subject
    msg.tags = parent_msg.tags

    return msg
Пример #6
0
def create_reply_message(session, idx):
    """ Given a message ``idx``, create and return a replying message. """
    parent_msg = get_msg(idx)
    msg = Msg()
    msg.parent = parent_msg.idx

    # flip from/to
    msg.recipient = parent_msg.author
    msg.author = session.user.handle

    # quote message body
    msg.body = quote_body(parent_msg)

    # duplicate subject and tags
    msg.subject = parent_msg.subject
    msg.tags = parent_msg.tags

    return msg
Пример #7
0
def main(msg=None):
    """ Main procedure. """
    from x84.bbs import Msg, getsession, echo, getterminal
    from x84.bbs.ini import CFG
    session, term = getsession(), getterminal()
    new_message = True if msg is None else False
    network_tags = None
    is_network_msg = False

    if CFG.has_option('msg', 'network_tags'):
        network_tags = map(
            str.strip, CFG.get('msg', 'network_tags').split(','))

    if new_message:
        msg = Msg()
        msg.tags = ('public',)

    elif network_tags is not None:
        net_tags = [tag for tag in network_tags if tag in msg.tags]
        is_network_msg = True if len(net_tags) > 0 else False

    banner()
    while True:
        session.activity = 'Constructing a %s message' % (
            u'public' if u'public' in msg.tags else u'private',)
        if network_tags and new_message:
            is_network_msg = prompt_network(msg, network_tags)
        if not prompt_recipient(msg, is_network_msg):
            break
        if not prompt_subject(msg):
            break
        if not prompt_public(msg):
            break
        if not prompt_body(msg):
            break
        # XXX
        if not prompt_tags(msg):
            break
        if is_network_msg:
            # XX move to sub-func
            how_many = len([tag for tag in network_tags if tag in msg.tags])
            if how_many == 0:
                echo(u''.join((u'\r\n',
                               term.bold_yellow_on_red(
                                   u' YOU tOld ME thiS WAS A NEtWORk MESSAGE. '
                                   u'WhY did YOU liE?! '),
                               u'\r\n')))
                term.inkey(timeout=7)
                continue
            if how_many > 1:
                echo(u''.join((
                    u'\r\n', term.bold_yellow_on_red(
                        u' ONlY ONE NEtWORk CAN bE POStEd tO At A tiME, '
                        u'SORRY '),
                    u'\r\n')))
                continue
            if u'public' not in msg.tags:
                echo(u''.join((u'\r\n',
                               term.bold_yellow_on_red(
                                   u" YOU ShOUldN't SENd PRiVAtE "
                                   u"MESSAGES OVER tHE NEtWORk... "),
                               u'\r\n')))
                term.inkey(timeout=7)
                continue
        display_msg(msg)
        if not prompt_send():
            break

        msg.save()
        session.user['msgs_sent'] = session.user.get('msgs_sent', 0) + 1
        return True
    return False
Пример #8
0
def read_messages(msgs, new):
    """
    Provide message reader UI given message list ``msgs``,
    with new messages in list ``new``.
    """
    # pylint: disable=R0914,R0912,R0915
    #         Too many local variables
    #         Too many branches
    #         Too many statements
    from x84.bbs import timeago, get_msg, getterminal, echo, gosub
    from x84.bbs import ini, Pager, getsession, getch, Ansi, Msg
    import x84.default.writemsg
    session, term = getsession(), getterminal()

    session.activity = 'reading msgs'
    # build header
    len_idx = max([len('%d' % (_idx,)) for _idx in msgs])
    len_author = ini.CFG.getint('nua', 'max_user')
    len_ago = 9
    len_subject = ini.CFG.getint('msg', 'max_subject')
    len_preview = min(len_idx + len_author + len_ago + len_subject + -1, term.width - 2)
    reply_depth = ini.CFG.getint('msg', 'max_depth')
    indent_start, indent, indent_end = u'\\', u'-', u'> '

    def get_header(msgs_idx):
        """
        Return list of tuples, (idx, unicodestring), suitable for Lightbar.
        """
        import datetime
        msg_list = list()
        thread_indent = lambda depth: (term.red(
            (indent_start + (indent * depth) + indent_end))
            if depth else u'')

        def head(msg, depth=0, maxdepth=reply_depth):
            """ This recursive routine finds the 'head' message
                of any relationship, up to maxdepth.
            """
            if (depth <= maxdepth
                    and hasattr(msg, 'parent')
                    and msg.parent is not None):
                return head(get_msg(msg.parent), depth + 1, maxdepth)
            return msg.idx, depth

        for idx in msgs_idx:
            msg = get_msg(idx)
            author, subj = msg.author, msg.subject
            tm_ago = (datetime.datetime.now() - msg.stime).total_seconds()
            # pylint: disable=W0631
            #         Using possibly undefined loop variable 'idx'
            attr = lambda arg: (
                term.bold_green(arg) if (
                    not idx in ALREADY_READ
                    and msg.recipient == session.user.handle) else
                term.red(arg) if not idx in ALREADY_READ
                else term.yellow(arg))
            status = [u'U' if not idx in ALREADY_READ else u' ',
                      u'D' if idx in DELETED else u' ', ]
            row_txt = u'%s %s %s %s %s%s ' % (
                u''.join(status),
                attr(str(idx).rjust(len_idx)),
                attr(author.ljust(len_author)),
                (timeago(tm_ago)).rjust(len_ago),
                attr(u'ago'),
                term.bold_black(':'),)
            msg_list.append((head(msg), idx, row_txt, subj))
        msg_list.sort()
        return [(idx, row_txt + thread_indent(depth) + subj)
                for (_threadid, depth), idx, row_txt, subj in msg_list]

    def get_selector(mailbox, prev_sel=None):
        """
        Provide Lightbar UI element given message mailbox returned from
        function get_header, and prev_sel as previously instantiated Lightbar.
        """
        from x84.bbs import Lightbar
        pos = prev_sel.position if prev_sel is not None else (0, 0)
        sel = Lightbar(
            height=(term.height / 3
                    if term.width < 140 else term.height - 3),
            width=len_preview,
            yloc=2, xloc=0)
        sel.glyphs['top-horiz'] = u''
        sel.glyphs['left-vert'] = u''
        sel.colors['highlight'] = term.yellow_reverse
        sel.update(mailbox)
        sel.position = pos
        return sel

    def get_reader():
        """
        Provide Pager UI element for message reading.
        """
        reader_height = (term.height - (term.height / 3) - 2)
        reader_indent = 2
        reader_width = min(term.width - 1, min(term.width - reader_indent, 80))
        reader_ypos = ((term.height - 1 ) - reader_height if
                      (term.width - reader_width) < len_preview else 2)
        reader_height = term.height - reader_ypos - 1
        msg_reader = Pager(
            height=reader_height,
            width=reader_width,
            yloc=reader_ypos,
            xloc=min(len_preview + 2, term.width - reader_width))
        msg_reader.glyphs['top-horiz'] = u''
        msg_reader.glyphs['right-vert'] = u''
        return msg_reader

    def format_msg(reader, idx):
        """ Format message of index ``idx`` into Pager instance ``reader``. """
        msg = get_msg(idx)
        sent = msg.stime.strftime(TIME_FMT)
        to_attr = term.bold_green if (
            msg.recipient == session.user.handle) else term.underline
        ucs = u'\r\n'.join((
            (u''.join((
                term.yellow('fROM: '),
                (u'%s' % term.bold(msg.author,)).rjust(len_author),
                u' ' * (reader.visible_width - (len_author + len(sent))),
                sent,))),
            u''.join((
                term.yellow('tO: '),
                to_attr((u'%s' % to_attr(msg.recipient,)).rjust(len_author)
                        if msg.recipient is not None else u'All'),)),
            (Ansi(
                term.yellow('tAGS: ')
                + (u'%s ' % (term.bold(','),)).join((
                    [term.bold_red(_tag)
                        if _tag in SEARCH_TAGS
                        else term.yellow(_tag)
                        for _tag in msg.tags]))).wrap(
                            reader.visible_width,
                            indent=u'      ')),
            (term.yellow_underline(
                (u'SUbj: %s' % (msg.subject,)).ljust(reader.visible_width)
            )),
            u'', (msg.body),))
        return ucs

    def get_selector_title(mbox, new):
        """
        Returns unicode string suitable for displaying as title of mailbox.
        """
        newmsg = (term.yellow(u' ]-[ ') +
                  term.yellow_reverse(str(len(new))) +
                  term.bold_underline(u' NEW')) if len(new) else u''
        return u''.join((term.yellow(u'[ '),
                         term.bold_yellow(str(len(mbox))),
                         term.bold(
                         u' MSG%s' % (u's' if 1 != len(mbox) else u'',)),
                         newmsg, term.yellow(u' ]'),))

    dispcmd_mark = lambda idx: (
        (term.yellow_underline(u' ') + u':mark' + u' ')
        if idx not in ALREADY_READ else u'')
    dispcmd_delete = lambda idx: (
        (term.yellow_underline(u'D') + u':elete' + u' ')
        if idx not in DELETED else u'')
    dispcmd_tag = lambda idx: (
        (term.yellow_underline(u't') + u':ag' + u' ')
        if allow_tag(idx) else u'')

    def get_selector_footer(idx):
        """
        Returns unicode string suitable for displaying
        as footer of mailbox when window is active.
        """
        return u''.join((
            term.yellow(u'- '),
            u''.join((
                term.yellow_underline(u'>') + u':read ',
                term.yellow_underline(u'r') + u':eply ',
                dispcmd_mark(idx),
                dispcmd_delete(idx),
                dispcmd_tag(idx),
                term.yellow_underline(u'q') + u':uit',)),
            term.yellow(u' -'),))

    def get_reader_footer(idx):
        """
        Returns unicode string suitable for displaying
        as footer of reader when window is active
        """

        return u''.join((
            term.yellow(u'- '),
            u' '.join((
                term.yellow_underline(u'<') + u':back ',
                term.yellow_underline(u'r') + u':eply ',
                dispcmd_delete(idx),
                dispcmd_tag(idx),
                term.yellow_underline(u'q') + u':uit',)),
            term.yellow(u' -'),))

    def refresh(reader, selector, mbox, new):
        """
        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
        title = get_selector_title(mbox, new)
        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(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',
                         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(idx)
                                         ) if not READING else u'',
                         reader.footer(get_reader_footer(idx)
                                       ) if READING else u'',
                         reader.refresh(),
                         ))

    echo((u'\r\n' + term.clear_eol) * (term.height - 1))
    dirty = 2
    msg_selector = None
    msg_reader = None
    idx = None
    # pylint: disable=W0603
    #         Using the global statement
    global READING
    while (msg_selector is None and msg_reader is None
           ) or not (msg_selector.quit or msg_reader.quit):
        if session.poll_event('refresh'):
            dirty = 2
        if dirty:
            if dirty == 2:
                mailbox = get_header(msgs)
            msg_selector = get_selector(mailbox, msg_selector)
            idx = msg_selector.selection[0]
            msg_reader = get_reader()
            msg_reader.update(format_msg(msg_reader, idx))
            echo(refresh(msg_reader, msg_selector, msgs, new))
            dirty = 0
        inp = getch(1)
        if inp in (u'r', u'R'):
            reply_to = get_msg(idx)
            reply_msg = Msg()
            reply_msg.recipient = reply_to.author
            reply_msg.tags = reply_to.tags
            reply_msg.subject = reply_to.subject
            reply_msg.parent = reply_to.idx
            # quote between 30 and 79, 'screen width - 4' as variable dist.
            reply_msg.body = quote_body(reply_to,
                                        max(30, min(79, term.width - 4)))
            echo(term.move(term.height, 0) + u'\r\n')
            if gosub('writemsg', reply_msg):
                reply_msg.save()
                dirty = 2
                READING = False
            else:
                dirty = 1
            mark_read(idx)  # also mark as read

        # 't' uses writemsg.prompt_tags() routine, how confusing ..
        elif inp in (u't',) and allow_tag(idx):
            echo(term.move(term.height, 0))
            msg = get_msg(idx)
            if x84.default.writemsg.prompt_tags(msg):
                msg.save()
            dirty = 2

        # spacebar marks as read, goes to next message
        elif inp in (u' ',):
            dirty = 2 if mark_read(idx) else 1
            msg_selector.move_down()
            idx = msg_selector.selection[0]
            READING = False

        # D marks as deleted, goes to next message
        elif inp in (u'D',):
            dirty = 2 if mark_delete(idx) else 1
            msg_selector.move_down()
            idx = msg_selector.selection[0]
            READING = False

        # U undeletes, does not move.
        elif inp in (u'U',):
            dirty = 2 if mark_undelete(idx) else 1
            msg_selector.move_down()
            idx = msg_selector.selection[0]
            READING = False

        if READING:
            echo(msg_reader.process_keystroke(inp))
            # left, <, or backspace moves UI
            if inp in (term.KEY_LEFT, u'<', u'h',
                       '\b', term.KEY_BACKSPACE):
                READING = False
                dirty = 1
        else:
            echo(msg_selector.process_keystroke(inp))
            idx = msg_selector.selection[0]
            # right, >, or enter marks message read, moves UI
            if inp in (u'\r', term.KEY_ENTER, u'>',
                       u'l', 'L', term.KEY_RIGHT):
                dirty = 2 if mark_read(idx) else 1
                READING = True
            elif msg_selector.moved:
                dirty = 1
    echo(term.move(term.height, 0) + u'\r\n')
    return
Пример #9
0
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'))
Пример #10
0
def poll_network_for_messages(net):
    " pull for new messages of network, storing locally. "
    from x84.bbs import Msg, DBProxy
    from x84.bbs.msgbase import to_localtime

    log = logging.getLogger(__name__)

    log.debug(u'[{net[name]}] polling for new messages.'.format(net=net))

    try:
        last_msg_id = get_last_msg_id(net['last_file'])
    except (OSError, IOError) as err:
        log.error('[{net[name]}] skipping network: {err}'.format(net=net,
                                                                 err=err))
        return

    msgs = pull_rest(net=net, last_msg_id=last_msg_id)

    if msgs is not False:
        log.info('{net[name]} Retrieved {num} messages'.format(net=net,
                                                               num=len(msgs)))
    else:
        log.debug('{net[name]} no messages.'.format(net=net))
        return

    transdb = DBProxy('{0}trans'.format(net['name']), use_session=False)
    transkeys = transdb.keys()
    msgs = sorted(msgs, cmp=lambda x, y: cmp(int(x['id']), int(y['id'])))

    # store messages locally, saving their translated IDs to the transdb
    for msg in msgs:
        store_msg = Msg()
        store_msg.recipient = msg['recipient']
        store_msg.author = msg['author']
        store_msg.subject = msg['subject']
        store_msg.body = msg['body']
        store_msg.tags = set(msg['tags'])
        store_msg.tags.add(u''.join((net['name'])))

        if msg['recipient'] is None and u'public' not in msg['tags']:
            log.warn("{net[name]} No recipient (msg_id={msg[id]}), "
                     "adding 'public' tag".format(net=net, msg=msg))
            store_msg.tags.add(u'public')

        if (msg['parent'] is not None and str(msg['parent']) not in transkeys):
            log.warn('{net[name]} No such parent message ({msg[parent]}, '
                     'msg_id={msg[id]}), removing reference.'.format(net=net,
                                                                     msg=msg))
        elif msg['parent'] is not None:
            store_msg.parent = int(transdb[msg['parent']])

        if msg['id'] in transkeys:
            log.warn('{net[name]} dupe (msg_id={msg[id]}) discarded.'.format(
                net=net, msg=msg))
        else:
            # do not save this message to network, we already received
            # it from the network, set send_net=False
            store_msg.save(send_net=False, ctime=to_localtime(msg['ctime']))
            with transdb:
                transdb[msg['id']] = store_msg.idx
            transkeys.append(msg['id'])
            log.info(
                '{net[name]} Processed (msg_id={msg[id]}) => {new_id}'.format(
                    net=net, msg=msg, new_id=store_msg.idx))

        if 'last' not in net.keys() or int(net['last']) < int(msg['id']):
            net['last'] = msg['id']

    if 'last' in net.keys():
        with open(net['last_file'], 'w') as last_fp:
            last_fp.write(str(net['last']))

    return
Пример #11
0
def poll_network_for_messages(net):
    " pull for new messages of network, storing locally. "
    from x84.bbs import Msg, DBProxy
    from x84.bbs.msgbase import to_localtime

    log = logging.getLogger(__name__)

    log.debug(u'[{net[name]}] polling for new messages.'.format(net=net))

    try:
        last_msg_id = get_last_msg_id(net['last_file'])
    except (OSError, IOError) as err:
        log.error('[{net[name]}] skipping network: {err}'
                  .format(net=net, err=err))
        return

    msgs = pull_rest(net=net, last_msg_id=last_msg_id)

    if msgs is not False:
        log.info('{net[name]} Retrieved {num} messages'
                 .format(net=net, num=len(msgs)))
    else:
        log.debug('{net[name]} no messages.'.format(net=net))
        return

    transdb = DBProxy('{0}trans'.format(net['name']), use_session=False)
    transkeys = transdb.keys()
    msgs = sorted(msgs, cmp=lambda x, y: cmp(int(x['id']), int(y['id'])))

    # store messages locally, saving their translated IDs to the transdb
    for msg in msgs:
        store_msg = Msg()
        store_msg.recipient = msg['recipient']
        store_msg.author = msg['author']
        store_msg.subject = msg['subject']
        store_msg.body = msg['body']
        store_msg.tags = set(msg['tags'])
        store_msg.tags.add(u''.join((net['name'])))

        if msg['recipient'] is None and u'public' not in msg['tags']:
            log.warn("{net[name]} No recipient (msg_id={msg[id]}), "
                     "adding 'public' tag".format(net=net, msg=msg))
            store_msg.tags.add(u'public')

        if (msg['parent'] is not None and
                str(msg['parent']) not in transkeys):
            log.warn('{net[name]} No such parent message ({msg[parent]}, '
                     'msg_id={msg[id]}), removing reference.'
                     .format(net=net, msg=msg))
        elif msg['parent'] is not None:
            store_msg.parent = int(transdb[msg['parent']])

        if msg['id'] in transkeys:
            log.warn('{net[name]} dupe (msg_id={msg[id]}) discarded.'
                     .format(net=net, msg=msg))
        else:
            # do not save this message to network, we already received
            # it from the network, set send_net=False
            store_msg.save(send_net=False, ctime=to_localtime(msg['ctime']))
            with transdb:
                transdb[msg['id']] = store_msg.idx
            transkeys.append(msg['id'])
            log.info('{net[name]} Processed (msg_id={msg[id]}) => {new_id}'
                     .format(net=net, msg=msg, new_id=store_msg.idx))

        if 'last' not in net.keys() or int(net['last']) < int(msg['id']):
            net['last'] = msg['id']

    if 'last' in net.keys():
        with open(net['last_file'], 'w') as last_fp:
            last_fp.write(str(net['last']))

    return
Пример #12
0
def read_messages(msgs, new):
    """
    Provide message reader UI given message list ``msgs``,
    with new messages in list ``new``.
    """
    # pylint: disable=R0914,R0912,R0915
    #         Too many local variables
    #         Too many branches
    #         Too many statements
    from x84.bbs import timeago, get_msg, getterminal, echo, gosub
    from x84.bbs import ini, Pager, getsession, getch, Msg
    import x84.default.writemsg
    session, term = getsession(), getterminal()

    session.activity = 'reading msgs'
    # build header
    len_idx = max([len('%d' % (_idx, )) for _idx in msgs])
    len_author = ini.CFG.getint('nua', 'max_user')
    len_ago = 9
    len_subject = ini.CFG.getint('msg', 'max_subject')
    len_preview = min(len_idx + len_author + len_ago + len_subject + -1,
                      term.width - 2)
    reply_depth = ini.CFG.getint('msg', 'max_depth')
    indent_start, indent, indent_end = u'\\', u'-', u'> '

    def get_header(msgs_idx):
        """
        Return list of tuples, (idx, unicodestring), suitable for Lightbar.
        """
        import datetime
        msg_list = list()
        thread_indent = lambda depth: (term.red(
            (indent_start + (indent * depth) + indent_end)) if depth else u'')

        def head(msg, depth=0, maxdepth=reply_depth):
            """ This recursive routine finds the 'head' message
                of any relationship, up to maxdepth.
            """
            if (depth <= maxdepth and hasattr(msg, 'parent')
                    and msg.parent is not None):
                return head(get_msg(msg.parent), depth + 1, maxdepth)
            return msg.idx, depth

        for idx in msgs_idx:
            msg = get_msg(idx)
            author, subj = msg.author, msg.subject
            tm_ago = (datetime.datetime.now() - msg.stime).total_seconds()
            # pylint: disable=W0631
            #         Using possibly undefined loop variable 'idx'
            attr = lambda arg: (term.bold_green(arg)
                                if (not idx in ALREADY_READ and msg.recipient
                                    == session.user.handle) else term.red(arg)
                                if not idx in ALREADY_READ else term.yellow(arg
                                                                            ))
            status = [
                u'U' if not idx in ALREADY_READ else u' ',
                u'D' if idx in DELETED else u' ',
            ]
            row_txt = u'%s %s %s %s %s%s ' % (
                u''.join(status),
                attr(str(idx).rjust(len_idx)),
                attr(author.ljust(len_author)),
                (timeago(tm_ago)).rjust(len_ago),
                attr(u'ago'),
                term.bold_black(':'),
            )
            msg_list.append((head(msg), idx, row_txt, subj))
        msg_list.sort(reverse=True)
        return [(idx, row_txt + thread_indent(depth) + subj)
                for (_threadid, depth), idx, row_txt, subj in msg_list]

    def get_selector(mailbox, prev_sel=None):
        """
        Provide Lightbar UI element given message mailbox returned from
        function get_header, and prev_sel as previously instantiated Lightbar.
        """
        from x84.bbs import Lightbar
        pos = prev_sel.position if prev_sel is not None else (0, 0)
        sel = Lightbar(height=(term.height /
                               3 if term.width < 140 else term.height - 3),
                       width=len_preview,
                       yloc=2,
                       xloc=0)
        sel.glyphs['top-horiz'] = u''
        sel.glyphs['left-vert'] = u''
        sel.colors['highlight'] = term.yellow_reverse
        sel.update(mailbox)
        sel.position = pos
        return sel

    def get_reader():
        """
        Provide Pager UI element for message reading.
        """
        reader_height = (term.height - (term.height / 3) - 2)
        reader_indent = 2
        reader_width = min(term.width - 1, min(term.width - reader_indent, 80))
        reader_ypos = ((term.height - 1) - reader_height if
                       (term.width - reader_width) < len_preview else 2)
        reader_height = term.height - reader_ypos - 1
        msg_reader = Pager(height=reader_height,
                           width=reader_width,
                           yloc=reader_ypos,
                           xloc=min(len_preview + 2,
                                    term.width - reader_width))
        msg_reader.glyphs['top-horiz'] = u''
        msg_reader.glyphs['right-vert'] = u''
        return msg_reader

    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'),
            )),
            (u'\r\n'.join((term.wrap(
                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
                ])),
                reader.visible_width,
                subsequent_indent=u' ' * 6)))),
            (term.yellow_underline(
                (u'SUbj: %s' % (msg.subject, )).ljust(reader.visible_width))),
            u'',
            (msg.body),
        ))
        return ucs

    def get_selector_title(mbox, new):
        """
        Returns unicode string suitable for displaying as title of mailbox.
        """
        newmsg = (term.yellow(u' ]-[ ') + term.yellow_reverse(str(len(new))) +
                  term.bold_underline(u' NEW')) if len(new) else u''
        return u''.join((
            term.yellow(u'[ '),
            term.bold_yellow(str(len(mbox))),
            term.bold(u' MSG%s' % (u's' if 1 != len(mbox) else u'', )),
            newmsg,
            term.yellow(u' ]'),
        ))

    dispcmd_mark = lambda idx: ((term.yellow_underline(u' ') + u':mark' + u' ')
                                if idx not in ALREADY_READ else u'')
    dispcmd_delete = lambda idx: (
        (term.yellow_underline(u'D') + u':elete' + u' ')
        if idx not in DELETED else u'')
    dispcmd_tag = lambda idx: ((term.yellow_underline(u't') + u':ag' + u' ')
                               if allow_tag(idx) else u'')

    def get_selector_footer(idx):
        """
        Returns unicode string suitable for displaying
        as footer of mailbox when window is active.
        """
        return u''.join((
            term.yellow(u'- '),
            u''.join((
                term.yellow_underline(u'>') + u':read ',
                term.yellow_underline(u'r') + u':eply ',
                dispcmd_mark(idx),
                dispcmd_delete(idx),
                dispcmd_tag(idx),
                term.yellow_underline(u'q') + u':uit',
            )),
            term.yellow(u' -'),
        ))

    def get_reader_footer(idx):
        """
        Returns unicode string suitable for displaying
        as footer of reader when window is active
        """

        return u''.join((
            term.yellow(u'- '),
            u' '.join((
                term.yellow_underline(u'<') + u':back ',
                term.yellow_underline(u'r') + u':eply ',
                dispcmd_delete(idx),
                dispcmd_tag(idx),
                term.yellow_underline(u'q') + u':uit',
            )),
            term.yellow(u' -'),
        ))

    def refresh(reader, selector, mbox, new):
        """
        Returns unicode string suitable for refreshing the screen.
        """
        from x84.bbs import getsession
        session = getsession()

        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
        title = get_selector_title(mbox, new)
        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 - term.length(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',
            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(idx)) if not READING else u'',
            reader.footer(get_reader_footer(idx)) if READING else u'',
            reader.refresh(),
        ))

    echo((u'\r\n' + term.clear_eol) * (term.height - 1))
    dirty = 2
    msg_selector = None
    msg_reader = None
    idx = None
    # pylint: disable=W0603
    #         Using the global statement
    global READING
    while (msg_selector is None and
           msg_reader is None) or not (msg_selector.quit or msg_reader.quit):
        if session.poll_event('refresh'):
            dirty = 2
        if dirty:
            if dirty == 2:
                mailbox = get_header(msgs)
            msg_selector = get_selector(mailbox, msg_selector)
            idx = msg_selector.selection[0]
            msg_reader = get_reader()
            msg_reader.update(format_msg(msg_reader, idx))
            echo(refresh(msg_reader, msg_selector, msgs, new))
            dirty = 0
        inp = getch(1)
        if inp in (u'r', u'R'):
            reply_to = get_msg(idx)
            reply_msg = Msg()
            reply_msg.recipient = reply_to.author
            reply_msg.tags = reply_to.tags
            reply_msg.subject = reply_to.subject
            reply_msg.parent = reply_to.idx
            # quote between 30 and 79, 'screen width - 4' as variable dist.
            reply_msg.body = quote_body(reply_to,
                                        max(30, min(79, term.width - 4)))
            echo(term.move(term.height, 0) + u'\r\n')
            if gosub('writemsg', reply_msg):
                reply_msg.save()
                dirty = 2
                READING = False
            else:
                dirty = 1
            mark_read(idx)  # also mark as read

        # 't' uses writemsg.prompt_tags() routine, how confusing ..
        elif inp in (u't', ) and allow_tag(idx):
            echo(term.move(term.height, 0))
            msg = get_msg(idx)
            if x84.default.writemsg.prompt_tags(msg):
                msg.save()
                session.user['msgs_sent'] = session.user.get('msgs_sent',
                                                             0) + 1
            dirty = 2

        # spacebar marks as read, goes to next message
        elif inp in (u' ', ):
            dirty = 2 if mark_read(idx) else 1
            msg_selector.move_down()
            idx = msg_selector.selection[0]
            READING = False

        # D marks as deleted, goes to next message
        elif inp in (u'D', ):
            dirty = 2 if mark_delete(idx) else 1
            msg_selector.move_down()
            idx = msg_selector.selection[0]
            READING = False

        # U undeletes, does not move.
        elif inp in (u'U', ):
            dirty = 2 if mark_undelete(idx) else 1
            msg_selector.move_down()
            idx = msg_selector.selection[0]
            READING = False

        if READING:
            echo(msg_reader.process_keystroke(inp))
            # left, <, or backspace moves UI
            if inp in (term.KEY_LEFT, u'<', u'h', '\b', term.KEY_BACKSPACE):
                READING = False
                dirty = 1
        else:
            echo(msg_selector.process_keystroke(inp))
            idx = msg_selector.selection[0]
            # right, >, or enter marks message read, moves UI
            if inp in (u'\r', term.KEY_ENTER, u'>', u'l', 'L', term.KEY_RIGHT):
                dirty = 2 if mark_read(idx) else 1
                READING = True
            elif msg_selector.moved:
                dirty = 1
    echo(term.move(term.height, 0) + u'\r\n')
    return
Пример #13
0
def main(msg=None):
    """ Main procedure. """
    from x84.bbs import Msg, getsession, echo, getterminal
    from x84.bbs.ini import CFG
    session, term = getsession(), getterminal()
    new_message = True if msg is None else False
    network_tags = None
    is_network_msg = False

    if CFG.has_option('msg', 'network_tags'):
        network_tags = map(
            str.strip, CFG.get('msg', 'network_tags').split(','))

    if CFG.has_option('msg', 'server_tags'):
        if network_tags is None:
            network_tags = list()
        network_tags += map(
            str.strip, CFG.get('msg', 'server_tags').split(','))

    if new_message:
        msg = Msg()
        msg.tags = ('public',)

    elif network_tags is not None:
        net_tags = [tag for tag in network_tags if tag in msg.tags]
        is_network_msg = True if len(net_tags) > 0 else False

    banner()
    while True:
        session.activity = 'Constructing a %s message' % (
            u'public' if u'public' in msg.tags else u'private',)
        if network_tags and new_message:
            is_network_msg = prompt_network(msg, network_tags)
        if not prompt_recipient(msg, is_network_msg):
            break
        if not prompt_subject(msg):
            break
        if not prompt_public(msg):
            break
        if not prompt_body(msg):
            break
        # XXX
        if not prompt_tags(msg):
            break
        if is_network_msg:
            # XX move to sub-func
            how_many = len([tag for tag in network_tags if tag in msg.tags])
            if how_many == 0:
                echo(u''.join((u'\r\n',
                               term.bold_yellow_on_red(
                                   u' YOU tOld ME thiS WAS A NEtWORk MESSAGE. '
                                   u'WhY did YOU liE?! '),
                               u'\r\n')))
                term.inkey(timeout=7)
                continue
            if how_many > 1:
                echo(u''.join((
                    u'\r\n', term.bold_yellow_on_red(
                        u' ONlY ONE NEtWORk CAN bE POStEd tO At A tiME, '
                        u'SORRY '),
                    u'\r\n')))
                continue
            if u'public' not in msg.tags:
                echo(u''.join((u'\r\n',
                               term.bold_yellow_on_red(
                                   u" YOU ShOUldN't SENd PRiVAtE "
                                   u"MESSAGES OVER tHE NEtWORk... "),
                               u'\r\n')))
                term.inkey(timeout=7)
                continue
        display_msg(msg)
        if not prompt_send():
            break

        msg.save()
        session.user['msgs_sent'] = session.user.get('msgs_sent', 0) + 1
        return True
    return False