chan.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. #!/usr/bin/env python
  2. import requests
  3. import argparse
  4. import html
  5. import os
  6. import tempfile
  7. import re
  8. import sys
  9. viewer = "imcat"
  10. def parse_arguments():
  11. parser = argparse.ArgumentParser(description='Terminal 4chan client')
  12. parser.add_argument('-b', '--board', type=str, metavar='BOARD', help='Select a board to view e.g. "g"')
  13. parser.add_argument('-t', '--thread', type=str, metavar='BOARD/THREAD', help='Select a thread to view e.g. "g"')
  14. parser.add_argument('-p', '--page', type=int, metavar='NUMBER', help='Go to a certain page number in board.')
  15. parser.add_argument('-l', '--list', action="store_true", default=False, help='List boards')
  16. return parser.parse_args()
  17. def content(stuff):
  18. com = html.unescape(stuff).replace("<br>","\n")
  19. cleaner = re.compile('<.*?>')
  20. com = re.sub(cleaner, '', com)
  21. return com
  22. def list_boards():
  23. boards = []
  24. for board in requests.get("https://a.4cdn.org/boards.json").json()["boards"]:
  25. boards.append(board["board"])
  26. return boards
  27. def board_error(board):
  28. args = parse_arguments()
  29. if board not in list_boards():
  30. print("ERROR: Can't find this board do -l/--list for a list of boards")
  31. sys.exit(1)
  32. def title(stuff):
  33. title = f"{stuff['name']} {stuff['now']} No.{stuff['no']}"
  34. if "sub" in stuff:
  35. title = f"{html.unescape(stuff['sub'])} | {title}"
  36. return title
  37. def image(stuff, board):
  38. if stuff["filename"] != "":
  39. file = os.path.join(tempfile.gettempdir(),stuff["filename"]+stuff["ext"])
  40. url = f"https://i.4cdn.org/{board}/{stuff['tim']}{stuff['ext']}"
  41. with open(file,"wb") as f:
  42. f.write(requests.get(url).content)
  43. os.system(f'{viewer} "{file}"')
  44. print(url)
  45. def main():
  46. args = parse_arguments()
  47. if args.list:
  48. for board in requests.get("https://a.4cdn.org/boards.json").json()["boards"]:
  49. print(f'{board["board"]} - {board["title"]}')
  50. print(html.unescape(board["meta_description"]))
  51. elif args.board:
  52. board_error(args.board)
  53. data = requests.get(f"https://a.4cdn.org/{args.board}/catalog.json").json()
  54. if args.page:
  55. if args.page >= 0 and args.page <= len(data):
  56. num = args.page
  57. else:
  58. print("ERROR: Too big page number, printing out the first page")
  59. else:
  60. num = 0
  61. for i, thread in enumerate(data[num]["threads"]):
  62. print("---")
  63. print(title(thread))
  64. if "com" in thread:
  65. print(content(thread['com']))
  66. image(thread, args.board)
  67. if "last_replies" in thread:
  68. for reply in thread['last_replies']:
  69. if "name" in reply:
  70. print(f"\n {reply['name']} {reply['now']} No.{reply['no']}")
  71. if "com" in reply:
  72. for thing in content(reply['com']).splitlines():
  73. print(" "+thing)
  74. if i == 0:
  75. input("Press ENTER for next post:")
  76. else:
  77. input(":")
  78. if args.thread:
  79. if "/" in args.thread:
  80. board = args.thread.split("/")[0]
  81. thread = args.thread.split("/")[1]
  82. else:
  83. print("ERROR: Needs to be BOARD/THREAD")
  84. sys.exit(1)
  85. board_error(board)
  86. data = requests.get(f"https://a.4cdn.org/{board}/thread/{thread}.json").json()['posts']
  87. print(title(data[0]))
  88. if "com" in data[0]:
  89. print(content(data[0]['com']))
  90. image(data[0], board)
  91. for i, reply in enumerate(data[1:6]):
  92. if "name" and "com" in reply:
  93. print(f"\n {reply['name']} {reply['now']} No.{reply['no']}")
  94. for thing in content(reply['com']).splitlines():
  95. print(" "+thing)
  96. if __name__ == '__main__':
  97. main()