WIP repository for a ncurses fediverse/mastodon client, using python mastodon.py

fedicurses.py 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import curses
  2. from html.parser import HTMLParser
  3. from mastodon import Mastodon
  4. def newLine(ncwin, lines=1):
  5. cy, cx = ncwin.getyx()
  6. ncwin.move(cy + lines, 0)
  7. def printPost(win, post, parser):
  8. if (post["reblog"] is not None):
  9. win.addstr("{} boosted {}:".format(post["account"]["acct"],
  10. post["reblog"]["account"]["acct"]),
  11. curses.A_BOLD)
  12. else:
  13. win.addstr("{}:".format(post["account"]["acct"]), curses.A_BOLD)
  14. newLine(win)
  15. if (post["spoiler_text"] != ""):
  16. win.addstr(post["spoiler_text"], curses.A_UNDERLINE)
  17. newLine(win)
  18. parser.feed(post["content"])
  19. if parser.openp:
  20. newLine(win)
  21. botstring = "{} @ {} UTC".format(post["visibility"],
  22. post["created_at"
  23. ].strftime(
  24. "%Y-%m-%d %H:%M:%S"))
  25. cy, cx = win.getyx()
  26. my, mx = win.getmaxyx()
  27. win.addstr(cy, mx - len(botstring), botstring, curses.A_UNDERLINE)
  28. newLine(win, 1)
  29. def printAtLevel(stdscr, col1, col2, posts, parser, level):
  30. col1.clear()
  31. col2.clear()
  32. col1.move(0, 0)
  33. col2.move(0, 0)
  34. printPost(col1, posts[level], parser)
  35. newLine(col1)
  36. printPost(col1, posts[(level + 1) % len(posts)], parser)
  37. try:
  38. col2.addstr(str(posts[level]))
  39. except curses.error:
  40. pass
  41. stdscr.noutrefresh()
  42. col1.noutrefresh()
  43. col2.noutrefresh()
  44. curses.doupdate()
  45. class PostParser(HTMLParser):
  46. def __init__(self, ncwin, defncatt):
  47. HTMLParser.__init__(self)
  48. self.win = ncwin
  49. self.defncatt = defncatt
  50. self.curatt = defncatt
  51. self.openp = False
  52. def handle_starttag(self, tag, attrs):
  53. if tag == 'p':
  54. self.curatt = self.defncatt
  55. # newLine(self.win)
  56. elif tag == 'strong' or tag == 'b':
  57. self.curatt = self.curatt ^ curses.A_REVERSE
  58. elif tag == 'em' or tag == 'i':
  59. self.curatt = self.curatt ^ curses.A_BOLD
  60. elif tag == 'br':
  61. newLine(self.win)
  62. def handle_endtag(self, tag):
  63. if tag == 'p':
  64. newLine(self.win)
  65. self.openp = False
  66. elif tag == 'strong' or tag == 'b':
  67. self.curatt = self.curatt ^ curses.A_REVERSE
  68. elif tag == 'em' or tag == 'i':
  69. self.curatt = self.curatt ^ curses.A_BOLD
  70. def handle_data(self, data):
  71. try:
  72. self.win.addstr(data, self.curatt)
  73. except curses.error:
  74. pass
  75. self.openp = True
  76. # print(mastodon.timeline()[0]["content"])
  77. def main(stdscr):
  78. mastodon = Mastodon(
  79. access_token = 'd100.club_usercred.secret',
  80. api_base_url = 'd100.club'
  81. )
  82. posts = mastodon.timeline()
  83. stdscr.clear()
  84. # stdscr.addstr("test")
  85. curses.halfdelay(10)
  86. my, mx = stdscr.getmaxyx()
  87. c1w = c2w = (mx - 3) // 2
  88. if (mx % 2) != 0:
  89. c2w = c2w - 1
  90. colw1 = curses.newwin(my - 2, c1w, 1, 1)
  91. colw2 = curses.newwin(my - 2, c2w, 1, 1 + c1w + 2)
  92. stdscr.border()
  93. # colw1.border()
  94. # colw2.border()
  95. parser = PostParser(colw1, 0)
  96. # colw1.move(0,0)
  97. # stdscr.addstr(0, 0, 'test')
  98. # colw2.addstr(0, 0, 'test2')
  99. # colw1.addstr(0, 0, 'test3')
  100. i = 'c'
  101. q = 0
  102. printAtLevel(stdscr, colw1, colw2, posts, parser, q)
  103. con = True
  104. while con:
  105. i = stdscr.getch()
  106. if (i == ord('q')):
  107. con = False
  108. elif (i == ord('j')):
  109. q = (q + 1) % len(posts)
  110. printAtLevel(stdscr, colw1, colw2, posts, parser, q)
  111. elif (i == ord('k')):
  112. q = (q - 1) % len(posts)
  113. printAtLevel(stdscr, colw1, colw2, posts, parser, q)
  114. curses.wrapper(main)