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

fedicurses.py 4.3KB

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