import curses from html.parser import HTMLParser from mastodon import Mastodon def initColours(): # 0: White on black curses.init_pair(1, 4, 0) # 1: Blue on black curses.init_pair(2, 1, 0) # 2: Red on black curses.init_pair(3, 6, 0) # 2: Cyan on black def newLine(ncwin, lines=1): cy, cx = ncwin.getyx() ncwin.move(cy + lines, 0) def bump(win): my, mx = win.getmaxyx() cy, cx = win.getyx() if cx != mx: win.move(cy, cx + 1) def typeset(text, win, attr, colour): my, mx = win.getmaxyx() line = 0 position = 0 strlen = len(text) while position < (strlen - 1): cy, cx = win.getyx() if cx == mx: if cy == my: raise curses.error("Out of space") rem = (mx - cx) + 1 nsp = text.find(' ', position) if nsp == -1 or nsp - position > rem: if cx == 0: win.addnstr(text[position:], rem, attr | curses.color_pair(colour)) position = position + rem newLine(win) else: win.addnstr(text[position:], nsp - position, attr | curses.color_pair(colour)) position = nsp + 1 bump(win) def printPost(win, post, parser): win.addstr(post["account"]["acct"], curses.A_BOLD) if (post["reblog"] is not None): win.addstr(" boosted ") win.addstr(post["reblog"]["account"]["acct"], curses.A_BOLD) win.addstr(":") newLine(win) if (post["spoiler_text"] != ""): win.addstr(post["spoiler_text"], curses.A_UNDERLINE) newLine(win) parser.feed(post["content"]) if parser.openp: newLine(win) botstring = "{} @ {} UTC".format(post["visibility"], post["created_at" ].strftime( "%Y-%m-%d %H:%M:%S")) cy, cx = win.getyx() my, mx = win.getmaxyx() win.addstr(cy, mx - len(botstring), botstring, curses.A_UNDERLINE) newLine(win, 1) def printAtLevel(stdscr, col1, col2, posts, parser, level): col1.clear() col2.clear() col1.move(0, 0) col2.move(0, 0) printPost(col1, posts[level], parser) newLine(col1) printPost(col1, posts[(level + 1) % len(posts)], parser) try: col2.addstr(str(posts[level])) except curses.error: pass stdscr.noutrefresh() col1.noutrefresh() col2.noutrefresh() curses.doupdate() class PostParser(HTMLParser): def __init__(self, ncwin, defncatt): HTMLParser.__init__(self) self.win = ncwin self.defncatt = defncatt self.curatt = defncatt self.openp = False self.colour = 0 self.colstack = [] def handle_starttag(self, tag, attrs): if tag == 'p': self.curatt = self.defncatt self.colstack = [] self.colour = 0 # newLine(self.win) elif tag == 'strong' or tag == 'b': self.curatt = self.curatt ^ curses.A_REVERSE elif tag == 'em' or tag == 'i': self.curatt = self.curatt ^ curses.A_BOLD elif tag == 'br': newLine(self.win) elif tag == 'a': self.colstack.append(self.colour) self.colour = 1 elif tag == 'code': self.colstack.append(self.colour) self.colour = 3 def handle_endtag(self, tag): if tag == 'p': newLine(self.win) self.openp = False elif tag == 'strong' or tag == 'b': self.curatt = self.curatt ^ curses.A_REVERSE elif tag == 'em' or tag == 'i': self.curatt = self.curatt ^ curses.A_BOLD elif tag == 'a': self.colour = self.colstack.pop() elif tag == 'code': self.colour = self.colstack.pop() def handle_data(self, data): try: # self.win.addstr(data, self.curatt | curses.color_pair(self.colour)) typeset(data, self.win, self.curatt, self.colour) except curses.error: pass self.openp = True # print(mastodon.timeline()[0]["content"]) def main(stdscr): initColours() mastodon = Mastodon( access_token = 'd100.club_usercred.secret', api_base_url = 'd100.club' ) posts = mastodon.timeline() stdscr.clear() # stdscr.addstr("test") curses.halfdelay(10) my, mx = stdscr.getmaxyx() c1w = c2w = (mx - 3) // 2 if (mx % 2) != 0: c2w = c2w - 1 colw1 = curses.newwin(my - 2, c1w, 1, 1) colw2 = curses.newwin(my - 2, c2w, 1, 1 + c1w + 2) stdscr.border() # colw1.border() # colw2.border() parser = PostParser(colw1, 0) # colw1.move(0,0) # stdscr.addstr(0, 0, 'test') # colw2.addstr(0, 0, 'test2') # colw1.addstr(0, 0, 'test3') i = 'c' q = 0 printAtLevel(stdscr, colw1, colw2, posts, parser, q) con = True while con: i = stdscr.getch() if (i == ord('q')): con = False elif (i == ord('j')): q = (q + 1) % len(posts) printAtLevel(stdscr, colw1, colw2, posts, parser, q) elif (i == ord('k')): q = (q - 1) % len(posts) printAtLevel(stdscr, colw1, colw2, posts, parser, q) curses.wrapper(main)