comments.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  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, url, 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: "+url+" PAGE : "+str(page))
  49. if not comment_id:
  50. out = check_output(["flbry/lbrynet",
  51. "comment", "list", claim_id,
  52. '--page='+str(page),
  53. '--page_size='+str(page_size)])
  54. else:
  55. out = check_output(["flbry/lbrynet",
  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. print(" Connect to LBRY first.")
  66. return
  67. d = {"categories":["Tip LBC", "Comments", "Channel", "Preview"],
  68. "size":[1,1,2,5],
  69. "data":[]}
  70. try:
  71. # List what we found
  72. for n, i in enumerate(out["items"]):
  73. preview = "---!Failed Loading comment---"
  74. support = 0
  75. replies = 0
  76. bywho = "[anonymous]"
  77. try:
  78. comment = i["comment"]
  79. preview = comment.replace("\n", " ")
  80. support = i["support_amount"]
  81. bywho = i["channel_name"]
  82. replies = i["replies"]
  83. except:
  84. pass
  85. d["data"].append([support, replies, bywho, preview])
  86. table(d)
  87. # Tell the user that he might want to load more
  88. center("---type 'more' to load more---")
  89. page = page +1
  90. # Error messages
  91. except Exception as e:
  92. if "code" in out:
  93. print(" Error code: ", out["code"] )
  94. if out["code"] == -32500:
  95. print(" SDK is still starting. Patience!")
  96. else:
  97. print( e)
  98. return
  99. # Making sure that we stop every time a new page is reached
  100. c = input(typing_dots())
  101. if c != "more":
  102. break
  103. try:
  104. c = int(c)
  105. except:
  106. return
  107. while True:
  108. view(out["items"][c])
  109. c = input(typing_dots())
  110. if not c:
  111. break
  112. try:
  113. c = int(c)
  114. except:
  115. return
  116. def view(i):
  117. # This function will give a user an ability to interact / read a
  118. # given comment.
  119. preview = "---!Failed Loading comment---"
  120. comment = ""
  121. support = 0
  122. bywho = "[anonymous]"
  123. replies = 0
  124. try:
  125. comment = i["comment"]
  126. preview = comment.replace("\n", " ")
  127. support = i["support_amount"]
  128. bywho = i["channel_name"]
  129. replies = i["replies"]
  130. except:
  131. pass
  132. # TIP LBC # COMMENTS ( REPLIES ) # CHANNEL
  133. d = {"categories":["Tip LBC", "Comments", "Channel"],
  134. "size":[1,1,3],
  135. "data":[[support, replies, bywho]]}
  136. table(d, False)
  137. # Preview
  138. d = {"categories":["Preview"],
  139. "size":[1],
  140. "data":[[preview]]}
  141. table(d, False)
  142. # The help thing
  143. center("--- for comment commands list type 'help' --- ")
  144. # List of commands for autocomplete feature.
  145. complete([
  146. "help",
  147. "read",
  148. "channel",
  149. "comments",
  150. "reply",
  151. "delete",
  152. "edit"
  153. ])
  154. # let's implement commands
  155. while True:
  156. c = input(typing_dots())
  157. if not c:
  158. break
  159. elif c == "help":
  160. markdown.draw("help/comments.md", "Comments Help")
  161. elif c == "read":
  162. savedes = open("/tmp/fastlbrylastcomment.md", "w")
  163. savedes.write(comment)
  164. savedes.close()
  165. markdown.draw("/tmp/fastlbrylastcomment.md", "Full Text Of a Comment")
  166. elif c == "channel":
  167. channel.simple(i["channel_url"])
  168. elif c == "comments":
  169. list(i["claim_id"], " ", i["comment_id"],)
  170. elif c.startswith("reply"):
  171. c = c + ' '
  172. post(i["claim_id"], c[c.find(" "):], i["comment_id"])
  173. elif c == "delete":
  174. out = check_output(["flbry/lbrynet", "comment", "abandon", i["comment_id"]])
  175. out = json.loads(out)
  176. try:
  177. if out["abandoned"] == True:
  178. center("Comment deleted!", "bdgr")
  179. break
  180. except:
  181. if out["message"].startswith("Couldn't find channel with channel_id"):
  182. center("You can't delete a comment you didn't post", "bdrd")
  183. elif c.startswith("edit"):
  184. c = c + ' '
  185. update(i, c[c.find(" "):])
  186. def post(claim_id, args, parent_id=""):
  187. # This will post a comment under either a publication or a
  188. # comment as a reply.
  189. if len(args) > 1:
  190. text = file_or_editor(args, "Type your reply here. Don't forget to save. Then return to FastLBRY.")
  191. else:
  192. text = input(" Text: ")
  193. post_as = channel.select("Reply as who? Select Channel.")
  194. if not post_as.startswith("@"):
  195. post_as = "@"+post_as
  196. if not parent_id:
  197. out = check_output(["flbry/lbrynet",
  198. "comment", "create",
  199. text,
  200. '--channel_name='+post_as,
  201. '--claim_id='+claim_id])
  202. else:
  203. out = check_output(["flbry/lbrynet",
  204. "comment", "create",
  205. text,
  206. '--channel_name='+post_as,
  207. '--parent_id='+parent_id,
  208. '--claim_id='+claim_id])
  209. out = json.loads(out)
  210. if "message" in out:
  211. print("ERROR! "+out["message"], "bdrf")
  212. else:
  213. center("Comment is sent.", "bdgr")
  214. def inbox(opt=10):
  215. # This function will return the latest comments from the latest
  216. # publications. Similar to an email inbox. But with a limitation.
  217. # There is no system in the SDK to implement a history of comments
  218. # seamlessly. So then I need to cash a large file of comments. Or
  219. # do something clever. I think there will be a few options.
  220. # You noticed the opt=10 preset on the top. It's the default value.
  221. # Basically the user might type one of 4 things.
  222. # inbox
  223. # inbox 40 (or any number what so ever)
  224. # inbox all
  225. # inbox cashed
  226. # Each will run a slightly different algorithm to get the inbox
  227. # comments.
  228. # inbox
  229. # This will use the predefined 10 and read last 10 publications
  230. # comments to add. It will combine them with the pre-cashed ones
  231. # for the user to view. As you may imagine, giving it a number as
  232. # in:
  233. # inbox 40
  234. # inbox 2
  235. # inbox 50
  236. # Will load this number of publications. To update with them the
  237. # cash and then present it to the user.
  238. # inbox all
  239. # This one will take longest. But might be useful for some users.
  240. # This will go through all publications and cash comments from all
  241. # of them.
  242. # inbox cashed
  243. # This one is the fastest of them. It will only read the cash file
  244. # and present it to the user. So for instance you want to quickly
  245. # go back to the inbox without loading anything at all.
  246. try:
  247. opt = int(opt)
  248. reached = opt
  249. goal = opt
  250. except:
  251. goal = 0
  252. if opt == "all":
  253. reached = True
  254. else:
  255. reached = False
  256. # Updating the cash file ( inbox.json )
  257. page = 0
  258. items_total = 0
  259. current_item = 0
  260. try:
  261. with open(settings.get_settings_folder()+'inbox.json') as json_file:
  262. comments_cache = json.load(json_file)
  263. except:
  264. comments_cache = []
  265. checked_publications = []
  266. while reached > 0:
  267. if type(reached) == int:
  268. reached = reached - 50
  269. page = page + 1
  270. page_size = 50
  271. # Getting data about publications.
  272. if page != 1:
  273. out = check_output(["flbry/lbrynet",
  274. "stream", "list",
  275. '--page='+str(page),
  276. '--page_size='+str(page_size),
  277. "--no_totals"])
  278. else:
  279. out = check_output(["flbry/lbrynet",
  280. "stream", "list",
  281. '--page='+str(page),
  282. '--page_size='+str(page_size)])
  283. # Now we want to parse the json
  284. items = []
  285. try:
  286. out = json.loads(out)
  287. items = out["items"]
  288. except:
  289. break
  290. if not items:
  291. break
  292. if page == 1:
  293. # Getting Totals to calculate the progress bar
  294. if reached == True:
  295. items_total = out["total_items"]
  296. else:
  297. try:
  298. items_total = int(opt)
  299. except:
  300. items_total = 0
  301. # Reading items from the items
  302. for publication in items:
  303. # skip dublicate publications. ( like when you edited
  304. # a publication )
  305. if publication["name"] in checked_publications:
  306. continue
  307. checked_publications.append(publication["name"])
  308. current_item = current_item + 1
  309. # If above the requested amount.
  310. if current_item > items_total:
  311. break
  312. # Draw progress bar
  313. progress_bar(current_item, items_total, publication["name"])
  314. # let's now get all the comments
  315. claim_id = publication["claim_id"]
  316. comment_page = 0
  317. while True:
  318. comment_page = comment_page + 1
  319. cout = check_output(["flbry/lbrynet",
  320. "comment", "list", '--claim_id='+claim_id,
  321. '--page='+str(comment_page),
  322. '--page_size='+str(50),
  323. '--include_replies'])
  324. try:
  325. cout = json.loads(cout)
  326. except:
  327. break
  328. if "items" not in cout:
  329. break
  330. for i in cout["items"]:
  331. # I want to add a few things into the comment data
  332. i["publication_url"] = publication["permanent_url"]
  333. i["publication_name"] = publication["name"]
  334. try:
  335. i["publication_title"] = publication["value"]["title"]
  336. except:
  337. i["publication_title"] = publication["name"]
  338. if i not in comments_cache:
  339. comments_cache.append(i)
  340. print()
  341. # Let's sort the comments based on the time they were sent
  342. comments_cache = sorted(comments_cache, key=lambda k: k['timestamp'], reverse=True)
  343. with open(settings.get_settings_folder()+'inbox.json', 'w') as fp:
  344. json.dump(comments_cache, fp , indent=4)
  345. # Now that we have comments cached and ready. I can start actually showing
  346. # them.
  347. w, h = tsize()
  348. page_size = (h-5)
  349. page = 0
  350. while True:
  351. d = {"categories":["Tip LBC", "Comments", "Publication", "Channel", "Preview"],
  352. "size":[1,1,4,2,4],
  353. "data":[]}
  354. items = []
  355. for n, i in enumerate(comments_cache):
  356. startfrom = int( page * page_size )
  357. endat = int( startfrom + page_size )
  358. if n in range(startfrom, endat):
  359. items.append(i)
  360. preview = "---!Failed Loading comment---"
  361. support = 0
  362. replies = 0
  363. where = "[some publication]"
  364. bywho = "[anonymous]"
  365. try:
  366. comment = i["comment"]
  367. preview = comment.replace("\n", " ")
  368. where = i["publication_title"]
  369. support = i["support_amount"]
  370. bywho = i["channel_name"]
  371. replies = i["replies"]
  372. except:
  373. pass
  374. d["data"].append([support, replies, where, bywho, preview])
  375. table(d)
  376. # Tell the user that he might want to load more
  377. center("---type 'more' to load more---")
  378. # Making sure that we stop every time a new page is reached
  379. c = input(typing_dots())
  380. if c == "more":
  381. page = page +1
  382. continue
  383. try:
  384. c = int(c)
  385. except:
  386. return
  387. view(items[c])
  388. c = input(typing_dots())
  389. def update(i, args):
  390. comment = i["comment"]
  391. if len(args) > 1:
  392. text = file_or_editor(args, comment)
  393. else:
  394. print("Comment: "+comment)
  395. text = input("Edited comment: ")
  396. out = check_output(["flbry/lbrynet",
  397. "comment", "update", "--comment_id="+i["comment_id"],
  398. "--comment="+text])
  399. out = json.loads(out)
  400. try:
  401. if out["message"].startswith("Couldn't find channel with channel_id"):
  402. center("You cant' edit a comment that isn't yours", "bdrd")
  403. except:
  404. center("Comment edited!", "bdgr")