fchan.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. #!/usr/bin/env python
  2. import requests
  3. import argparse
  4. import html
  5. import os
  6. import tempfile
  7. from datetime import datetime, timezone
  8. import time
  9. import re
  10. import sys
  11. import urllib.parse
  12. import random
  13. VIEWER = "imcat"
  14. INSTANCE = "https://chan.getgle.org/"
  15. def parse_arguments():
  16. parser = argparse.ArgumentParser(description='Terminal fchannel client')
  17. parser.add_argument('-b', '--board', type=str, metavar='BOARD', help='Select a board to view e.g. "g"')
  18. parser.add_argument('-t', '--thread', type=str, metavar='BOARD/THREAD', help='Select a thread to view e.g. "g"')
  19. parser.add_argument('-p', '--page', type=int, metavar='NUMBER', help='Go to a certain page number in board.')
  20. parser.add_argument('-l', '--list', action="store_true", default=False, help='List boards')
  21. parser.add_argument('-li', '--list-instances', action="store_true", default=False, help='List boards')
  22. return parser.parse_args()
  23. def list_instances():
  24. data = requests.get("https://fchannel.org/instance-index.html").text
  25. links = re.findall("<a href=\".+?\">", data)
  26. working = []
  27. for link in links:
  28. link = link.replace("<a href=","").replace('"',"").replace(">","")
  29. if not link.endswith(".onion"):
  30. working.append(link)
  31. return working
  32. def content(stuff):
  33. com = html.unescape(stuff).replace("<br>","\n")
  34. cleaner = re.compile('<.*?>')
  35. com = re.sub(cleaner, '', com)
  36. return com
  37. def list_boards():
  38. data = requests.get("https://chan.getgle.org/following").json()
  39. items = []
  40. for item in data["items"]:
  41. items.append(item["id"].split("/")[-1])
  42. return '\n'.join(items)
  43. def board_error(board):
  44. args = parse_arguments()
  45. if board not in list_boards():
  46. print("ERROR: Can't find this board do -l/--list for a list of boards")
  47. sys.exit(1)
  48. def title(stuff):
  49. date = datetime.strptime(stuff['published'], "%Y-%m-%dT%H:%M:%S.%fZ")
  50. date = date.replace(tzinfo=timezone.utc).astimezone(tz=None)
  51. date = date.strftime("%m/%d/%y(%a)%H:%M:%S")
  52. title = f"{date} No. {stuff['id'].split('/')[-1]}"
  53. if "attributedTo" in stuff:
  54. title = f"{stuff['attributedTo']} {title}"
  55. else:
  56. title = f"Anonymous {title}"
  57. if "name" in stuff:
  58. title = f"{stuff['name']} {title}"
  59. return title
  60. def image(stuff):
  61. if stuff["name"] != "":
  62. file = os.path.join(tempfile.gettempdir(),stuff["name"])
  63. with open(file,"wb") as f:
  64. f.write(requests.get(stuff["href"]).content)
  65. os.system(f'{VIEWER} "{file}"')
  66. print(stuff["href"])
  67. def main():
  68. args = parse_arguments()
  69. if args.list:
  70. print(list_boards())
  71. elif args.list_instances:
  72. print(list_instances())
  73. elif args.board:
  74. board_error(args.board)
  75. url = urllib.parse.urljoin(INSTANCE, args.board+"/outbox")
  76. data = requests.get(url).json()
  77. for i, thread in enumerate(data["orderedItems"]):
  78. print(title(thread))
  79. print(thread["content"])
  80. image(thread["attachment"][0])
  81. if 'orderedItems' in thread['replies']:
  82. for reply in thread['replies']['orderedItems']:
  83. print("\n "+title(reply))
  84. for thing in reply['content'].splitlines():
  85. print(" "+thing)
  86. if i == 0:
  87. input("Press ENTER for next post:")
  88. else:
  89. input(":")
  90. if args.thread:
  91. instances = '\n'.join(list_instances())
  92. result = urllib.parse.urlparse(args.thread)
  93. isit = all([result.scheme, result.netloc])
  94. if isit == False:
  95. print("This is not a valid url.")
  96. sys.exit(1)
  97. data = requests.get(args.thread, headers={"Accept": "application/ld+json"})
  98. status = data.status_code
  99. url = urllib.parse.urlparse(args.thread)
  100. url = url.scheme+"://"+url.netloc
  101. url = url[:-1] if url[-1] == '/' else url
  102. if url not in list_instances():
  103. print(f"""Couldn't get this thread try to get a thread from one of these instances
  104. {instances}""")
  105. sys.exit(1)
  106. data = data.json()["orderedItems"][0]
  107. print(title(data))
  108. print(data['content'])
  109. print(image(data["attachment"][0]))
  110. for i, reply in enumerate(data["replies"]["orderedItems"]):
  111. print(f"\n {title(reply)}")
  112. for thing in content(reply['content']).splitlines():
  113. print(" "+thing)
  114. if __name__ == '__main__':
  115. main()