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 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 allow_tag(session, idx): """ Whether user is allowed to tag a message. :rtype: bool A user can tag a message if the given session's user is: * the message author or recipient. * a member of sysop or moderator group. * a member of any existing tag-matching user group. """ moderated = get_ini('msg', 'moderated_tags', getter='getboolean') tag_moderators = set(get_ini('msg', 'tag_moderators', split=True)) if not moderated and 'sysop' in session.user.groups: return True elif moderated and (tag_moderators | session.user.groups): # tags are moderated, but user is one of the moderator groups return True msg = get_msg(idx) if session.user.handle in (msg.recipient, msg.author): return True for tag in msg.tags: if tag in session.user.groups: return True return False
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
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 display_message(session, term, msg_index, colors): """ Format message of index ``idx``. """ color_handle = lambda handle: (colors["highlight"](handle) if handle == session.user.handle else handle) msg = get_msg(msg_index) txt_sent = msg.stime.replace(tzinfo=dateutil.tz.tzlocal()).astimezone(dateutil.tz.tzutc()).strftime(TIME_FMT) txt_sentago = colors["highlight"](timeago((datetime.datetime.now() - msg.stime).total_seconds()).strip()) txt_to = color_handle(msg.recipient) txt_private = colors["highlight"](" (private)") if not "public" in msg.tags else u"" txt_from = color_handle(msg.author) txt_tags = u", ".join((quote(tag, colors) for tag in msg.tags)) txt_subject = colors["highlight"](msg.subject) txt_body = decode_pipe(msg.body) txt_breaker = ("-" if session.encoding == "ansi" else u"\u2500") * min(80, term.width) msg_txt = ( u"\r\n{txt_breaker}\r\n" u" from: {txt_from}\r\n" u" to: {txt_to}{txt_private}\r\n" u" sent: {txt_sent} ({txt_sentago} ago)\r\n" u" tags: {txt_tags}\r\n" u"subject: {txt_subject}\r\n" u"\r\n" u"{txt_body}\r\n".format( txt_breaker=txt_breaker, txt_from=txt_from, txt_to=txt_to, txt_sent=txt_sent, txt_sentago=txt_sentago, txt_tags=txt_tags, txt_subject=txt_subject, txt_body=txt_body, txt_private=txt_private, ) ) do_mark_as_read(session, [msg_index]) prompt_pager( content=msg_txt.splitlines(), line_no=0, width=min(80, term.width), colors=colors, breaker=u"- ", end_prompt=False, break_long_words=True, )
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 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
def allow_tag(idx): """ Returns true if user is allowed to 't'ag message at idx: * sysop and moderator * author or recipient * a member of any message tag matching user group """ from x84.bbs import getsession, get_msg session = getsession() if ('sysop' in session.user.groups or 'moderator' in session.user.groups): return True msg = get_msg(idx) if session.user.handle in (msg.recipient, msg.author): return True for tag in msg.tags: if tag in session.user.groups: return True return False
def display_message(session, term, msg_index, colors): """ Format message of index ``idx``. """ color_handle = lambda handle: (colors['highlight'](handle) if handle == session.user.handle else handle) msg = get_msg(msg_index) txt_sent = msg.stime.replace(tzinfo=dateutil.tz.tzlocal()).astimezone( dateutil.tz.tzutc()).strftime(TIME_FMT) txt_sentago = colors['highlight'](timeago( (datetime.datetime.now() - msg.stime).total_seconds()).strip()) txt_to = color_handle(msg.recipient) txt_private = (colors['highlight'](' (private)') if not 'public' in msg.tags else u'') txt_from = color_handle(msg.author) txt_tags = u', '.join((quote(tag, colors) for tag in msg.tags)) txt_subject = colors['highlight'](msg.subject) txt_body = decode_pipe(msg.body) txt_breaker = ('-' if session.encoding == 'ansi' else u'\u2500') * min( 80, term.width) msg_txt = (u'\r\n{txt_breaker}\r\n' u' from: {txt_from}\r\n' u' to: {txt_to}{txt_private}\r\n' u' sent: {txt_sent} ({txt_sentago} ago)\r\n' u' tags: {txt_tags}\r\n' u'subject: {txt_subject}\r\n' u'\r\n' u'{txt_body}\r\n'.format(txt_breaker=txt_breaker, txt_from=txt_from, txt_to=txt_to, txt_sent=txt_sent, txt_sentago=txt_sentago, txt_tags=txt_tags, txt_subject=txt_subject, txt_body=txt_body, txt_private=txt_private)) do_mark_as_read(session, [msg_index]) prompt_pager(content=msg_txt.splitlines(), line_no=0, width=min(80, term.width), colors=colors, breaker=u'- ', end_prompt=False, break_long_words=True)
def read_messages(msgs, title, currentpage, totalpages, threadid, cachetime): """ 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_idx = 40 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() 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, txt in enumerate(msgs_idx): author, subj = txt[0], txt[1] msg_list.append([idx, author, subj]) return 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 = msgs[idx] # author = msg[1][0] # body = msg[1][1] # ucs = u'\r\n'.join(( # (u''.join(( # term.yellow('fROM: '), # (u'%s' % term.bold(author,)).rjust(len(author)), # u' ' * (reader.visible_width - (len(author) )), # ))), # u''.join(( # term.yellow('tO: '), # (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): return cachetime def get_selector_footer(currentpage, totalpages): return 'Page '+currentpage+'/'+totalpages def get_reader_footer(idx): """ Returns unicode string suitable for displaying as footer of reader when window is active """ return u''.join(( idx, term.yellow(u'- '), u' '.join(( term.yellow_underline(u'<') + u':back ', term.yellow_underline(u'r') + u':eply ', term.yellow_underline(u'q') + u':uit',)), term.yellow(u' -'),)) 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(), )) 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(msgs[idx][1]) echo(refresh(msg_reader, msg_selector, msgs, title)) dirty = 0 inp = getch(1) if inp in (u'r', u'R'): reply_msgbody = quote_body(msgs[idx][1], max(30, min(79, term.width - 4)), msgs[idx][0]) echo(term.move(term.height, 0) + u'\r\n') session.user['draft'] = reply_msgbody if gosub('editor', 'draft'): makepost(threadid, session.user['draft']) 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 = 1#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 = 1#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
def msg_filter(msgs): """ filter all matching messages. userland implementation of private/public messaging by using the 'tags' database. 'new', or unread messages are marked by idx matching session.user['readmsgs'] when read. Finally, implement 'group' tagging, so that users of group 'impure' are allowed to read messages tagged by 'impure', regardless of recipient or 'public'. returns (msgs), (new). new contains redundant msgs """ # pylint: disable=R0914,R0912,R0915 # Too many local variables # Too many branches # Too many statements from x84.bbs import list_msgs, echo, getsession, getterminal, get_msg, Ansi session, term = getsession(), getterminal() public_msgs = list_msgs(('public',)) addressed_to = 0 addressed_grp = 0 filtered = 0 deleted = 0 private = 0 public = 0 new = set() echo(u' Processing ' + term.reverse_yellow('..')) for msg_id in msgs.copy(): if msg_id in public_msgs: # can always ready msgs tagged with 'public' public += 1 else: private += 1 msg = get_msg(msg_id) if msg.recipient == session.user.handle: addressed_to += 1 else: # a system of my own, by creating groups # with the same as tagged messages, you may # create private or intra-group messages. tag_matches_group = False for tag in msg.tags: if tag in session.user.groups: tag_matches_group = True break if tag_matches_group: addressed_grp += 1 elif msg_id not in public_msgs: # denied to read this message if FILTER_PRIVATE: msgs.remove(msg_id) filtered += 1 continue elif msg_id in DELETED: msgs.remove(msg_id) deleted += 1 if msg_id not in ALREADY_READ: new.add(msg_id) if 0 == len(msgs): echo(u'\r\n\r\nNo messages (%s filtered).' % (filtered,)) else: txt_out = list() if addressed_to > 0: txt_out.append('%s addressed to you' % ( term.bold_yellow(str(addressed_to)),)) if addressed_grp > 0: txt_out.append('%s addressed by group' % ( term.bold_yellow(str(addressed_grp)),)) if filtered > 0: txt_out.append('%s filtered' % ( term.bold_yellow(str(filtered)),)) if deleted > 0: txt_out.append('%s deleted' % ( term.bold_yellow(str(deleted)),)) if public > 0: txt_out.append('%s public' % ( term.bold_yellow(str(public)),)) if private > 0: txt_out.append('%s private' % ( term.bold_yellow(str(private)),)) if len(new) > 0: txt_out.append('%s new' % ( term.bold_yellow(str(len(new),)),)) if 0 != len(txt_out): echo(u'\r\n\r\n' + Ansi( u', '.join(txt_out) + u'.').wrap(term.width - 2)) return msgs, new
def do_reader_prompt(session, term, index, message_indices, colors): xpos = max(0, (term.width // 2) - (80 // 2)) opts = [] if index: opts += (('p', 'rev'),) if index < len(message_indices) - 1: opts += (('n', 'ext'),) if allow_tag(session, message_indices[index]): opts += (('e', 'dit tags'),) if can_delete(session): opts += (('D', 'elete'),) opts += (('r', 'eply'),) opts += (('q', 'uit'),) opts += (('idx', ''),) while True: echo(term.move_x(xpos)) echo(u''.join(( colors['lowlight'](u'['), colors['highlight'](str(index + 1)), u'/{0}'.format(len(message_indices)), colors['lowlight'](u']'), u' ', u', '.join(( u''.join((colors['lowlight'](u'['), colors['highlight'](key), colors['lowlight'](u']'), value )) for key, value in opts)), u': ', term.clear_eol, ))) width = max(2, len(str(len(message_indices)))) inp = LineEditor(width, colors={'highlight': colors['backlight']}).read() if inp is None or inp.lower() == u'q': return None elif inp in (u'n', u''): # 'n'ext or return key echo(term.move_x(xpos) + term.clear_eol) if index == len(message_indices) - 1: # no more messages, return None return index + 1 elif inp == u'p' and index > 0: # prev echo(term.move_x(xpos) + term.clear_eol) return index - 1 elif inp == u'e' and allow_tag(session, message_indices[index]): msg = get_msg(message_indices[index]) echo(u'\r\n') if prompt_tags(session=session, term=term, msg=msg, colors=colors, public='public' in msg.tags): echo(u'\r\n') msg.save() return index elif inp == u'D' and can_delete(session): delete_message(msg=get_msg(message_indices[index])) return None elif inp == u'r': # write message reply msg = create_reply_message(session=session, idx=message_indices[index]) if ( prompt_subject( term=term, msg=msg, colors=colors ) and prompt_body( term=term, msg=msg, colors=colors ) and prompt_tags( session=session, term=term, msg=msg, colors=colors, public='public' in msg.tags )): do_send_message(session=session, term=term, msg=msg, colors=colors) break else: # not a valid input option, is it a valid integer? (even '-1'!) try: val = int(inp) except ValueError: # some garbage; try again term.inkey(0.15) continue try: # allow a message index, (even pythonic '-1' for 'last') if val > 0: # 1-based indexing val -= 1 nxt_idx = message_indices.index(message_indices[val]) if nxt_idx != index: echo(term.move_x(xpos) + term.clear_eol) return nxt_idx except (IndexError, ValueError): # invalid index; try again term.inkey(0.15) continue
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
def do_reader_prompt(session, term, index, message_indices, colors): xpos = max(0, (term.width // 2) - (80 // 2)) opts = [] if index: opts += (('p', 'rev'), ) if index < len(message_indices) - 1: opts += (('n', 'ext'), ) if allow_tag(session, message_indices[index]): opts += (('e', 'dit tags'), ) if can_delete(session): opts += (('D', 'elete'), ) opts += (('r', 'eply'), ) opts += (('q', 'uit'), ) opts += (('idx', ''), ) while True: echo(term.move_x(xpos)) echo(u''.join(( colors['lowlight'](u'['), colors['highlight'](str(index + 1)), u'/{0}'.format(len(message_indices)), colors['lowlight'](u']'), u' ', u', '.join((u''.join( (colors['lowlight'](u'['), colors['highlight'](key), colors['lowlight'](u']'), value)) for key, value in opts)), u': ', term.clear_eol, ))) width = max(2, len(str(len(message_indices)))) inp = LineEditor(width, colors={ 'highlight': colors['backlight'] }).read() if inp is None or inp.lower() == u'q': return None elif inp in (u'n', u''): # 'n'ext or return key echo(term.move_x(xpos) + term.clear_eol) if index == len(message_indices) - 1: # no more messages, return None return index + 1 elif inp == u'p' and index > 0: # prev echo(term.move_x(xpos) + term.clear_eol) return index - 1 elif inp == u'e' and allow_tag(session, message_indices[index]): msg = get_msg(message_indices[index]) echo(u'\r\n') if prompt_tags(session=session, term=term, msg=msg, colors=colors, public='public' in msg.tags): echo(u'\r\n') msg.save() return index elif inp == u'D' and can_delete(session): delete_message(msg=get_msg(message_indices[index])) return None elif inp == u'r': # write message reply msg = create_reply_message(session=session, idx=message_indices[index]) if (prompt_subject(term=term, msg=msg, colors=colors) and prompt_body(term=term, msg=msg, colors=colors) and prompt_tags(session=session, term=term, msg=msg, colors=colors, public='public' in msg.tags)): do_send_message(session=session, term=term, msg=msg, colors=colors) break else: # not a valid input option, is it a valid integer? (even '-1'!) try: val = int(inp) except ValueError: # some garbage; try again term.inkey(0.15) continue try: # allow a message index, (even pythonic '-1' for 'last') if val > 0: # 1-based indexing val -= 1 nxt_idx = message_indices.index(message_indices[val]) if nxt_idx != index: echo(term.move_x(xpos) + term.clear_eol) return nxt_idx except (IndexError, ValueError): # invalid index; try again term.inkey(0.15) continue
def do_reader_prompt(session, term, index, message_indices, colors): xpos = max(0, (term.width // 2) - (80 // 2)) opts = [] if index: opts += (("p", "rev"),) if index < len(message_indices) - 1: opts += (("n", "ext"),) if allow_tag(session, message_indices[index]): opts += (("e", "dit tags"),) if can_delete(session): opts += (("D", "elete"),) opts += (("r", "eply"),) opts += (("q", "uit"),) opts += (("idx", ""),) while True: echo(term.move_x(xpos)) echo( u"".join( ( colors["lowlight"](u"["), colors["highlight"](str(index + 1)), u"/{0}".format(len(message_indices)), colors["lowlight"](u"]"), u" ", u", ".join( ( u"".join( (colors["lowlight"](u"["), colors["highlight"](key), colors["lowlight"](u"]"), value) ) for key, value in opts ) ), u": ", term.clear_eol, ) ) ) width = max(2, len(str(len(message_indices)))) inp = LineEditor(width, colors={"highlight": colors["backlight"]}).read() if inp is None or inp.lower() == u"q": return None elif inp in (u"n", u""): # 'n'ext or return key echo(term.move_x(xpos) + term.clear_eol) if index == len(message_indices) - 1: # no more messages, return None return index + 1 elif inp == u"p" and index > 0: # prev echo(term.move_x(xpos) + term.clear_eol) return index - 1 elif inp == u"e" and allow_tag(session, message_indices[index]): msg = get_msg(message_indices[index]) echo(u"\r\n") if prompt_tags(session=session, term=term, msg=msg, colors=colors, public="public" in msg.tags): echo(u"\r\n") msg.save() return index elif inp == u"D" and can_delete(session): delete_message(msg=get_msg(message_indices[index])) return None elif inp == u"r": # write message reply msg = create_reply_message(session=session, idx=message_indices[index]) if ( prompt_subject(term=term, msg=msg, colors=colors) and prompt_body(term=term, msg=msg, colors=colors) and prompt_tags(session=session, term=term, msg=msg, colors=colors, public="public" in msg.tags) ): do_send_message(session=session, term=term, msg=msg, colors=colors) break else: # not a valid input option, is it a valid integer? (even '-1'!) try: val = int(inp) except ValueError: # some garbage; try again term.inkey(0.15) continue try: # allow a message index, (even pythonic '-1' for 'last') if val > 0: # 1-based indexing val -= 1 nxt_idx = message_indices.index(message_indices[val]) if nxt_idx != index: echo(term.move_x(xpos) + term.clear_eol) return nxt_idx except (IndexError, ValueError): # invalid index; try again term.inkey(0.15) continue
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 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' + u'\r\n'.join( term.wrap(u', '.join(txt_out) + u'.', (term.width - 2)))) return msgs, new
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