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)