comments.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. #####################################################################
  2. # #
  3. # THIS IS A SOURCE CODE FILE FROM A PROGRAM TO INTERACT WITH THE #
  4. # LBRY PROTOCOL ( lbry.com ). IT WILL USE THE LBRY SDK ( lbrynet ) #
  5. # FROM THEIR REPOSITORY ( https://github.com/lbryio/lbry-sdk ) #
  6. # WHICH I GONNA PRESENT TO YOU AS A BINARY. SINCE I DID NOT DEVELOP #
  7. # IT AND I'M LAZY TO INTEGRATE IN A MORE SMART WAY. THE SOURCE CODE #
  8. # OF THE SDK IS AVAILABLE IN THE REPOSITORY MENTIONED ABOVE. #
  9. # #
  10. # ALL THE CODE IN THIS REPOSITORY INCLUDING THIS FILE IS #
  11. # (C) J.Y.Amihud and Other Contributors 2021. EXCEPT THE LBRY SDK. #
  12. # YOU CAN USE THIS FILE AND ANY OTHER FILE IN THIS REPOSITORY UNDER #
  13. # THE TERMS OF GNU GENERAL PUBLIC LICENSE VERSION 3 OR ANY LATER #
  14. # VERSION. TO FIND THE FULL TEXT OF THE LICENSE GO TO THE GNU.ORG #
  15. # WEBSITE AT ( https://www.gnu.org/licenses/gpl-3.0.html ). #
  16. # #
  17. # THE LBRY SDK IS UNFORTUNATELY UNDER THE MIT LICENSE. IF YOU ARE #
  18. # NOT INTENDING TO USE MY CODE AND JUST THE SDK. YOU CAN FIND IT ON #
  19. # THEIR OFFICIAL REPOSITORY ABOVE. THEIR LICENSE CHOICE DOES NOT #
  20. # SPREAD ONTO THIS PROJECT. DON'T GET A FALSE ASSUMPTION THAT SINCE #
  21. # THEY USE A PUSH-OVER LICENSE, I GONNA DO THE SAME. I'M NOT. #
  22. # #
  23. # THE LICENSE CHOSEN FOR THIS PROJECT WILL PROTECT THE 4 ESSENTIAL #
  24. # FREEDOMS OF THE USER FURTHER, BY NOT ALLOWING ANY WHO TO CHANGE #
  25. # THE LICENSE AT WILL. SO NO PROPRIETARY SOFTWARE DEVELOPER COULD #
  26. # TAKE THIS CODE AND MAKE THEIR USER-SUBJUGATING SOFTWARE FROM IT. #
  27. # #
  28. #####################################################################
  29. # This file will perform a simple search on the LBRY network.
  30. from subprocess import *
  31. import json
  32. from flbry import url
  33. from flbry.variables import *
  34. from flbry import markdown
  35. from flbry import channel
  36. from flbry import settings
  37. def list(claim_id, link, comment_id=""):
  38. # This function will list a list of comments of a certain claim_id
  39. # It will preview a very basic form of comment. You will have
  40. # to select a comment to interact with it further. Like read the
  41. # whole text, if it not short ( or in a markdown format ). Or
  42. # do other things like replies.
  43. w, h = tsize()
  44. page_size = h - 5
  45. page = 1
  46. while True:
  47. # Printing the search query and page number
  48. center("COMMENTS OF: "+link+" PAGE : "+str(page))
  49. if not comment_id:
  50. out = check_output([lbrynet_binary["b"],
  51. "comment", "list", claim_id,
  52. '--page='+str(page),
  53. '--page_size='+str(page_size)])
  54. else:
  55. out = check_output([lbrynet_binary["b"],
  56. "comment", "list", '--claim_id='+claim_id,
  57. '--parent_id='+comment_id,
  58. '--page='+str(page),
  59. '--page_size='+str(page_size),
  60. '--include_replies'])
  61. # Now we want to parse the JSON
  62. try:
  63. out = json.loads(out)
  64. except:
  65. center("Connect to LBRY first.", "bdrd")
  66. return
  67. if not "items" in out:
  68. center("Publication has no comments", "bdrd")
  69. return
  70. d = {"categories":["Tip LBC", "Comments", "Channel", "Preview"],
  71. "size":[1,1,2,5],
  72. "data":[]}
  73. try:
  74. # List what we found
  75. for n, i in enumerate(out["items"]):
  76. preview = "---!Failed Loading comment---"
  77. support = 0
  78. replies = 0
  79. bywho = "[anonymous]"
  80. try:
  81. comment = i["comment"]
  82. preview = comment.replace("\n", " ")
  83. support = i["support_amount"]
  84. bywho = i["channel_name"]
  85. replies = i["replies"]
  86. except:
  87. pass
  88. d["data"].append([support, replies, bywho, preview])
  89. table(d)
  90. # Tell the user that they might want to load more
  91. center("---type 'more' to load more---")
  92. page = page +1
  93. # Error messages
  94. except Exception as e:
  95. if "code" in out:
  96. center("Error code: "+out["code"], "bdrd")
  97. if "message" in out:
  98. center("Error: "+out["message"], "bdrd")
  99. else:
  100. center("Error: "+e, "bdrd")
  101. return
  102. while True:
  103. # Making sure that we stop every time a new page is reached
  104. c = input(typing_dots())
  105. if c == "more":
  106. break
  107. try:
  108. c = int(c)
  109. except:
  110. return
  111. view(out["items"][c])
  112. # Print the list again
  113. table(d)
  114. center("---type 'more' to load more---")
  115. def show_view_info(i, derived):
  116. comment = derived[0]
  117. preview = derived[1]
  118. support = derived[2]
  119. bywho = derived[3]
  120. replies = derived[4]
  121. # TIP LBC # COMMENTS ( REPLIES ) # CHANNEL
  122. d = {"categories":["Tip LBC", "Comments", "Channel"],
  123. "size":[1,1,3],
  124. "data":[[support, replies, bywho]]}
  125. table(d, False)
  126. # Preview
  127. d = {"categories":["Preview"],
  128. "size":[1],
  129. "data":[[preview]]}
  130. table(d, False)
  131. # The help thing
  132. center("--- for comment commands list type 'help' --- ")
  133. def view(i):
  134. """Allows the user to interact with and/or read a given comment"""
  135. preview = "---!Failed Loading comment---"
  136. comment = ""
  137. support = 0
  138. bywho = "[unknown]"
  139. replies = 0
  140. try:
  141. comment = i["comment"]
  142. preview = comment.replace("\n", " ")
  143. support = i["support_amount"]
  144. bywho = i["channel_name"]
  145. replies = i["replies"]
  146. except:
  147. pass
  148. derived = [comment, preview, support, bywho, replies]
  149. # List of commands for autocomplete feature.
  150. complete([
  151. "help",
  152. "read",
  153. "channel",
  154. "comments",
  155. "reply",
  156. "delete",
  157. "edit"
  158. ])
  159. # let's implement commands
  160. while True:
  161. show_view_info(i, derived)
  162. c = input(typing_dots())
  163. if not c:
  164. break
  165. elif c == "help":
  166. markdown.draw("help/comments.md", "Comments Help")
  167. elif c == "read":
  168. savedes = open("/tmp/fastlbrylastcomment.md", "w")
  169. savedes.write(comment)
  170. savedes.close()
  171. markdown.draw("/tmp/fastlbrylastcomment.md", "Full Text Of a Comment")
  172. elif c == "channel":
  173. channel.simple(i["channel_url"])
  174. elif c == "comments":
  175. list(i["claim_id"], " ", i["comment_id"],)
  176. elif c.startswith("reply"):
  177. c = c + ' '
  178. post(i["claim_id"], c[c.find(" "):], i["comment_id"])
  179. elif c == "delete":
  180. out = check_output([lbrynet_binary["b"], "comment", "abandon", i["comment_id"]])
  181. out = json.loads(out)
  182. try:
  183. if out["abandoned"] == True:
  184. center("Comment deleted!", "bdgr")
  185. break
  186. except:
  187. if out["message"].startswith("Couldn't find channel with channel_id"):
  188. center("You can't delete a comment you didn't post", "bdrd")
  189. elif c.startswith("edit"):
  190. c = c + ' '
  191. update(i, c[c.find(" "):])
  192. def post(claim_id, args, parent_id=""):
  193. # This will post a comment under either a publication or a
  194. # comment as a reply.
  195. editor = settings.get("default_editor")
  196. text = "Type your reply here. Don't forget to save. Then return to FastLBRY."
  197. if len(args) > 1:
  198. text = file_or_editor(args, text)
  199. else:
  200. if editor:
  201. text = file_or_editor(args, text, editor)
  202. else:
  203. text = input(typing_dots("Comment text", give_space=True))
  204. post_as = channel.select("Reply as who? Select Channel.")
  205. if not post_as.startswith("@"):
  206. post_as = "@"+post_as
  207. if not parent_id:
  208. out = check_output([lbrynet_binary["b"],
  209. "comment", "create",
  210. text,
  211. '--channel_name='+post_as,
  212. '--claim_id='+claim_id])
  213. else:
  214. out = check_output([lbrynet_binary["b"],
  215. "comment", "create",
  216. text,
  217. '--channel_name='+post_as,
  218. '--parent_id='+parent_id,
  219. '--claim_id='+claim_id])
  220. out = json.loads(out)
  221. if "message" in out:
  222. center("Error: "+out["message"], "bdrd")
  223. else:
  224. center("Comment is sent.", "bdgr")
  225. def inbox(opt=10):
  226. # This function will return the latest comments from the latest
  227. # publications. Similar to an email inbox. But with a limitation.
  228. # There is no system in the SDK to implement a history of comments
  229. # seamlessly. So then I need to cash a large file of comments. Or
  230. # do something clever. I think there will be a few options.
  231. # You noticed the opt=10 preset on the top. It's the default value.
  232. # Basically the user might type one of 4 things.
  233. # inbox
  234. # inbox 40 (or any number what so ever)
  235. # inbox all
  236. # inbox cashed
  237. # Each will run a slightly different algorithm to get the inbox
  238. # comments.
  239. # inbox
  240. # This will use the predefined 10 and read last 10 publications
  241. # comments to add. It will combine them with the pre-cashed ones
  242. # for the user to view. As you may imagine, giving it a number as
  243. # in:
  244. # inbox 40
  245. # inbox 2
  246. # inbox 50
  247. # Will load this number of publications. To update with them the
  248. # cash and then present it to the user.
  249. # inbox all
  250. # This one will take longest. But might be useful for some users.
  251. # This will go through all publications and cash comments from all
  252. # of them.
  253. # inbox cashed
  254. # This one is the fastest of them. It will only read the cash file
  255. # and present it to the user. So for instance you want to quickly
  256. # go back to the inbox without loading anything at all.
  257. try:
  258. opt = int(opt)
  259. reached = opt
  260. goal = opt
  261. except:
  262. goal = 0
  263. if opt == "all":
  264. reached = True
  265. else:
  266. reached = False
  267. # Updating the cash file ( inbox.json )
  268. page = 0
  269. items_total = 0
  270. current_item = 0
  271. try:
  272. with open(settings.get_settings_folder()+'inbox.json') as json_file:
  273. comments_cache = json.load(json_file)
  274. except:
  275. comments_cache = []
  276. checked_publications = []
  277. while reached > 0:
  278. if type(reached) == int:
  279. reached = reached - 50
  280. page = page + 1
  281. page_size = 50
  282. # Getting data about publications.
  283. if page != 1:
  284. out = check_output([lbrynet_binary["b"],
  285. "stream", "list",
  286. '--page='+str(page),
  287. '--page_size='+str(page_size),
  288. "--no_totals"])
  289. out2 = check_output([lbrynet_binary["b"],
  290. "channel", "list",
  291. '--page='+str(page),
  292. '--page_size='+str(page_size),
  293. "--no_totals"])
  294. else:
  295. out = check_output([lbrynet_binary["b"],
  296. "stream", "list",
  297. '--page='+str(page),
  298. '--page_size='+str(page_size)])
  299. out2 = check_output([lbrynet_binary["b"],
  300. "channel", "list",
  301. '--page='+str(page),
  302. '--page_size='+str(page_size)])
  303. # Now we want to parse the json
  304. items = []
  305. try:
  306. out = json.loads(out)
  307. out2 = json.loads(out2)
  308. items = out["items"]
  309. try:
  310. items = items[:int(opt)]
  311. except:
  312. pass
  313. for i in out2["items"]:
  314. items.append(i)
  315. except:
  316. break
  317. if not items:
  318. break
  319. if page == 1:
  320. # Getting Totals to calculate the progress bar
  321. if reached == True:
  322. items_total = out["total_items"] + out2["total_items"]
  323. else:
  324. try:
  325. items_total = int(opt) + out2["total_items"]
  326. except:
  327. items_total = 0
  328. # Reading items from the items
  329. for publication in items:
  330. # skip dublicate publications. ( like when you edited
  331. # a publication )
  332. if publication["name"] in checked_publications:
  333. continue
  334. checked_publications.append(publication["name"])
  335. current_item = current_item + 1
  336. # If above the requested amount.
  337. #if current_item > items_total:
  338. #break
  339. # Draw progress bar
  340. progress_bar(current_item, items_total, publication["name"])
  341. # let's now get all the comments
  342. claim_id = publication["claim_id"]
  343. comment_page = 0
  344. while True:
  345. comment_page = comment_page + 1
  346. cout = check_output([lbrynet_binary["b"],
  347. "comment", "list", '--claim_id='+claim_id,
  348. '--page='+str(comment_page),
  349. '--page_size='+str(50),
  350. '--include_replies'])
  351. try:
  352. cout = json.loads(cout)
  353. except:
  354. break
  355. # TODO: For now I'm stopping on first page when ever I'm
  356. # loading channel's comments ( community disscussion ).
  357. # This is obviously not going to work with "inbox all",
  358. # so please make the logic a bit smarter, so it will work.
  359. if "items" not in cout or publication["value_type"] == "channel":
  360. break
  361. for i in cout["items"]:
  362. # I want to add a few things into the comment data
  363. i["publication_url"] = publication["permanent_url"]
  364. i["publication_name"] = publication["name"]
  365. try:
  366. i["publication_title"] = publication["value"]["title"]
  367. except:
  368. i["publication_title"] = publication["name"]
  369. if i not in comments_cache:
  370. comments_cache.append(i)
  371. print()
  372. # Let's sort the comments based on the time they were sent
  373. comments_cache = sorted(comments_cache, key=lambda k: k['timestamp'], reverse=True)
  374. # Let's remove duplicate comments
  375. tmp = []
  376. tmp_ids = []
  377. for comment in comments_cache:
  378. if comment["comment_id"] not in tmp_ids:
  379. tmp_ids.append(comment["comment_id"])
  380. tmp.append(comment)
  381. comments_cache = tmp
  382. with open(settings.get_settings_folder()+'inbox.json', 'w') as fp:
  383. json.dump(comments_cache, fp , indent=4)
  384. # Now that we have comments cached and ready. I can start actually showing
  385. # them.
  386. w, h = tsize()
  387. page_size = (h-5)
  388. page = 0
  389. while True:
  390. d = {"categories":["Tip LBC", "Comments", "Publication", "Channel", "Preview"],
  391. "size":[1,1,4,2,4],
  392. "data":[]}
  393. items = []
  394. for n, i in enumerate(comments_cache):
  395. startfrom = int( page * page_size )
  396. endat = int( startfrom + page_size )
  397. if n in range(startfrom, endat):
  398. items.append(i)
  399. preview = "---!Failed Loading comment---"
  400. support = 0
  401. replies = 0
  402. where = "[some publication]"
  403. bywho = "[anonymous]"
  404. try:
  405. comment = i["comment"]
  406. preview = comment.replace("\n", " ")
  407. where = i["publication_title"]
  408. support = i["support_amount"]
  409. bywho = i["channel_name"]
  410. replies = i["replies"]
  411. except:
  412. pass
  413. d["data"].append([support, replies, where, bywho, preview])
  414. table(d)
  415. # Tell the user that they might want to load more
  416. center("---type 'more' to load more---")
  417. # Making sure that we stop every time a new page is reached
  418. c = input(typing_dots())
  419. if c == "more":
  420. page = page +1
  421. continue
  422. try:
  423. c = int(c)
  424. except:
  425. return
  426. view(items[c])
  427. c = input(typing_dots())
  428. def update(i, args):
  429. comment = i["comment"]
  430. editor = settings.get("default_editor")
  431. if len(args) > 1:
  432. text = file_or_editor(args, comment)
  433. else:
  434. if editor:
  435. text = file_or_editor(args, comment, editor)
  436. else:
  437. print("Comment: "+comment)
  438. text = input(typing_dots("Edited comment", give_space=True))
  439. out = check_output([lbrynet_binary["b"],
  440. "comment", "update", "--comment_id="+i["comment_id"],
  441. "--comment="+text])
  442. out = json.loads(out)
  443. try:
  444. if out["message"].startswith("Couldn't find channel with channel_id"):
  445. center("You cant' edit a comment that isn't yours", "bdrd")
  446. except:
  447. center("Comment edited!", "bdgr")