123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- # This is a little script that will generate promotional parts for
- # your articles on LBRY network. Using Markdown.
- # This script is under the terms of the GNU General Public License
- # version 3 or any later version. (c) J.Y.Amihud 2021.
- ############# DEPENDENCIES #############
- # This is not a software project intended for any-who. It's interned
- # for the advanced user. And thus I will not provide a script to
- # install dependecies. Mainly because I'm lazy. You can pull-request
- # me one.
- # This will need:
- # lbrynet executable. That you can either on:
- # https://github.com/lbryio/lbry-sdk/releases
- # or in the FastLBRY project:
- # https://notabug.org/jyamihud/FastLBRY-terminal
- # The creation of images will be done using:
- # cairo
- #
- # And also the following imports should all be present on your system's
- # python installation.
- ################# CODE ##################
- import os
- import sys # 'python3 run.py -t' will activate test mode
- import json
- import math
- import cairo
- import random
- import urllib.request
- import urllib3
- import subprocess
- from PIL import Image
- def comment_request(method: str, params: dict, addition=""):
- # Part of FastLBRY project
-
- """
- Sends a request to the comment API
- """
- data = {
- "method": method,
- "id": 1,
- "jsonrpc":"2.0",
- "params": params
- }
- data = json.dumps(data).encode()
- headers = {
- "Content-Type": "application/json"
- }
-
-
- try:
- req = urllib.request.Request(comments_api, data, headers)
- res = urllib.request.urlopen(req)
- out = res.read().decode()
- return json.loads(out)
- except Exception as e:
- print("COMMENT ERROR:", e)
- return
- # The LBRY SDK reads your account data from ~/.config/lbry folder
- # which is shared with LBRY Desktop and other LBRY based apps. So
- # you will need to login from it.
- # Also, the SDK should be already started. The script is
- # dumb and will not check for it. You will need to use
- # lbrynet start
- # Before running this script.
- # Here put the full file_path of the SDK on your system.
- SDK = "/home/vcs/Software/FastLBRY-GTK/flbry/lbrynet"
- comments_api = "https://comments.odysee.com/api/v2"
- # Let's get the latest publication's claim_id so we could fetch it's
- # comments.
- # HOW MANY PUBLICATIONS TO FETCH?
- DATA_C = [] # This data will be populated with comments Data, so later
- # I could draw an image from it.
-
- visited = [] # In case you've edited a publication.
- amount = ""
- while True:
- amount = input("HOW MANY RECENT PUBLICATIONS TO LOAD? : ")
- try:
- amount = int(amount)
- break
- except:
- print("AMOUNT SHOULD BE A NUMBER!")
- lbc = ""
- while True:
- lbc = input("THE SUPPORT SHOULD BE ABOVE? : ")
- try:
- lbc = int(lbc)
- break
- except:
- print("IT SHOULD BE A NUMBER!")
- fetch = ""
- while True:
- fetch = input("HOW MANY COMMENTS TO FETCH FROM A PUBLICATION? : ")
- try:
- fetch = int(fetch)
- break
- except:
- print("IT SHOULD BE A NUMBER!")
- print("FETCHING THE CLAIM_ID FOR THE LAST "+str(amount)+" PUBLICATIONS ...")
- out = subprocess.check_output([SDK, "stream", "list",
- '--page=1', # FIRST PUBLICATION
- '--page_size='+str(amount), # LOAD SELECTED AMOUNT
- "--no_totals"])
- # Now let's convert what ever we received into a good data
- print("READING THE DATA RECEIVED ...")
- l = json.loads(out)
- for out in l["items"]:
- if out["permanent_url"] in visited:
- continue
- else:
- visited.append(out["permanent_url"])
-
- # Let's give a user a little soothing information about what was
- # actually received.
- try:
- print("PUBLICATION: "+out["value"]["title"])
- except:
- print("PUBLICATION: "+out["permanent_url"])
- # Fetching the transactions of the publication
- print("FETCHING COMMNET-LESS SUPPORT ...")
- txo = subprocess.check_output([SDK,
- "txo", "list", '--claim_id='+out["claim_id"],
- '--page=1',
- '--page_size='+str(fetch) # AMOUNT OF TXO TO LOAD
- ])
- txo = json.loads(txo)
- # This is a bit hacky, but we all love hacking so...
- # basically in each Tip for a publication you can see
- # a channel's claim_id.
- # Often it's 7d0b0f83a195fd1278e1944ddda4cda8d9c01a56
- # but this claim_id belongs to LBRY Fund channel that
- # does tipping based on views.
- # We want to see anything but this channel.
- for t in txo["items"]:
- if "signing_channel" in t and float(t["amount"]) > lbc:
- try:
- if t["signing_channel"]["channel_id"] != "7d0b0f83a195fd1278e1944ddda4cda8d9c01a56":
- # This means we found a support
- print("FOUND SUPPORT OF "+str(t["amount"]))
- # I found that LBRY doesn't give a damn about the name
- # if you have the claim_id.
- print("FETCHING DATA ABOUT SENDER ...")
- sender = subprocess.check_output([SDK,
- "claim","search", "--claim_id="+t["signing_channel"]["channel_id"]])
- sender = json.loads(sender)
- sender = sender["items"][0]
- #print(t)
- #sender = sender["a#"+t["signing_channel"]["channel_id"]]
-
- icon = ""
- banner = ""
- title = ""
- try:
- title = sender["name"]
- except:
- pass
-
-
- try:
- icon = sender["value"]["thumbnail"]["url"]
- except:
- pass
- try:
- banner = sender["value"]["cover"]["url"]
- except:
- pass
- try:
- title = sender["value"]["title"]
- except:
- pass
- try:
- link = sender["canonical_url"].replace("#",":").replace("lbry://", "")
- except:
- link = ""
- url_of_comment = out["permanent_url"].replace("#",":").replace("lbry://", "https://odysee.com/")
- print("SENDER NAME: "+title)
- print("SENDER BANNER: "+banner)
- print("SENDER ICON: "+icon)
- print("SENDER LINK: "+link)
- print("COMMENT LINK: "+url_of_comment)
- DATA_C.append([
- float(t["amount"]), # 0
- title, # 1
- "", # 2
- icon, # 3
- banner, # 4
- link, # 5
- url_of_comment # 6
- ])
- except:
- pass
- # Fetching comments from the publication
- print("FETCHING COMMENTS ...")
- # Old code, no longer supported by the new SDK
-
- # comments = subprocess.check_output([SDK,
- # "comment", "list", out["claim_id"],
- # '--page=1',
- # '--page_size='+str(fetch), # AMOUNT OF COMMENTS TO LOAD
- # '--include_replies'])
- params = {
- "claim_id": out["claim_id"],
- "page": 1,
- "page_size": fetch,
- "sort_by": 3,
- "top_level": False,
- }
-
- comments = comment_request("comment.List", params)["result"]
- # Trying to read the list
- # comments = json.loads(comments)
- print( str(comments["total_items"])+" COMMENTS FOUND" )
- if 'items' in comments:
- for c in comments["items"]:
- # Each comment will be checked for the amount
- if c["support_amount"] > lbc:
- # So we found a supporter comment
- try:
- print(c["channel_name"], "SUPPORT: "+str(c["support_amount"]),
- "["+c["comment"][:20]+"...]")
- except:
- print("ANONYMOUS", "SUPPORT: "+str(c["support_amount"]),
- "["+c["comment"][:20]+"...]")
-
- # We need to get the image of the channel.
- # and make a pretty picture for this comment.
- icon = ""
- banner = ""
- title = c["channel_name"]
-
-
- if "channel_id" in c:
- print("FETCHING DATA ABOUT SENDER ...")
- sender = subprocess.check_output([SDK,
- "resolve", c["channel_name"]+"#"+c["channel_id"]])
- sender = json.loads(sender)
- sender = sender[c["channel_name"]+"#"+c["channel_id"]]
- try:
- icon = sender["value"]["thumbnail"]["url"]
- except:
- pass
- try:
- banner = sender["value"]["cover"]["url"]
- except:
- pass
- try:
- title = sender["value"]["title"]
- except:
- pass
- try:
- link = sender["canonical_url"].replace("#",":").replace("lbry://", "")
- except:
- link = ""
-
- url_of_comment = out["permanent_url"].replace("#",":").replace("lbry://", "https://odysee.com/")\
- +"?lc="+c["comment_id"]
-
- print("SENDER NAME: "+title)
- print("SENDER BANNER: "+banner)
- print("SENDER ICON: "+icon)
- print("SENDER LINK: "+link)
- print("COMMENT LINK: "+url_of_comment)
- # Because we checked earlier for the support not related
- # to the comments. The supported comment ( if chose to filter
- # by more then 0 LBC ) will be already added but without the
- # comments string. So we need to delete it first.
- found = False
- for item in DATA_C:
- if c["support_amount"] in item\
- and title in item\
- and icon in item\
- and banner in item\
- and out["permanent_url"].replace("#",":").replace("lbry://", "https://odysee.com/") in item\
- and link in item:
- found = item
- break
- if found:
- DATA_C.remove(found)
-
- DATA_C.append([
- c["support_amount"], # 0
- title, # 1
- c["comment"], # 2
- icon, # 3
- banner, # 4
- link, # 5
- url_of_comment # 6
-
- ])
- # Now that we have all the comments ready in the DATA_C list
- # we can start drawing the comments.
- # The idea is to have a .md formatted text that you can add in
- # the end of the article. With images uploaded and ready to go.
- def roundrect(layer, x, y, width, height, r):
- # This function will trace a round rectangle
-
- if width < r*2:
- width = r*2
- if height < r*2:
- height = r*2
-
- layer.move_to(x,y+r)
- layer.arc(x+r, y+r, r, math.pi, 3*math.pi/2)
- layer.arc(x+width-r, y+r, r, 3*math.pi/2, 0)
- layer.arc(x+width-r, y+height-r, r, 0, math.pi/2)
- layer.arc(x+r, y+height-r, r, math.pi/2, math.pi)
- layer.close_path()
- def get_image(url, save_url):
- # This function will download an image and save it
- # as a PNG image
-
- filename = "images/"+str(save_url)
- # First let's download the image
-
- http = urllib3.PoolManager()
- r = http.request('GET', url, preload_content=False)
- with open(filename, 'wb') as out:
- while True:
- data = r.read(1024)
- if not data:
- break
- out.write(data)
- r.release_conn()
- # Then converting to png
- im = Image.open(filename)
- im.save(filename+".png")
- # REMOVING THE TEMPORARY ONE
- os.remove(filename)
-
- def fit(load2, width, height):
- # This will scale a layer to fit into an
- # area.
- newimage = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
- imagedraw = cairo.Context(newimage)
-
-
- factor = 1
- if (load2.get_height()*(width/load2.get_width()))\
- < height:
- factor = height / load2.get_height()
- else:
- factor = width / load2.get_width()
- #factor = 0.1
- imagedraw.scale(factor, factor)
- dx = (width/2)/factor -(load2.get_width() /2)
- dy = (height/2)/factor -(load2.get_height()/2)
- imagedraw.set_source_surface(load2, dx, dy)
- imagedraw.paint()
- return newimage
- MD = "# Supporters:\n\n"
- MD = MD + "*Showing hyper-comments and other support with more than "+str(lbc)+" LBC from the last "+str(amount)+" publications. "
- MD = MD + "Including publications by other channels of my account. It's done automatically using* [this](https://notabug.org/jyamihud/LBRY-Supported-Comments) *script.*\n\n"
- for n, c in enumerate(reversed(sorted(DATA_C))):
- print("DRAWING IMAGE FOR COMMENT NUMBER", n)
-
- image_width = 1100
- image_height = 200
-
- # Now let's make the cairo object
- surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, image_width, image_height)
- layer = cairo.Context(surface)
- layer.select_font_face("Dyuthi", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
-
- # BACKGROUND
- roundrect(layer, 0, 0, image_width, image_height, 50)
- layer.set_source_rgba(0.21,0.22,0.29,1) # BACKGROUND IS #353849
- layer.fill()
- # AROUND THING
- roundrect(layer, 10, 10, image_width-20, image_height-20, 40)
- layer.set_line_width(20)
- layer.set_source_rgba(1,0.72,0,1) # YELLOW THING AROUND
- layer.stroke()
- # CLIP 1
- roundrect(layer, 20, 20, image_width-40, image_height-40, 30)
- layer.clip()
-
- # Banner
- try:
- if c[4]:
- get_image(c[4], str(n)+"banner")
- banner = cairo.ImageSurface.create_from_png("images/"+str(n)+"banner.png")
- banner = fit(banner, image_width-40, image_height-40)
- layer.set_source_surface(banner, 20, 20)
- layer.paint()
- # REMOVING THE BANNER ORIGNINAL
- os.remove("images/"+str(n)+"banner.png")
- except:
- pass
-
- # MAKING BANNER LESS VISIBLE
- roundrect(layer, 0, 0, image_width, image_height, 50)
- layer.set_source_rgba(0.21,0.22,0.29,0.75)
- layer.fill()
-
-
- #NAME OF THE CHANNEL
- layer.set_source_rgba(1,0.72,0,1) # YELLOW THING AROUND
- layer.set_font_size(60)
- layer.move_to(210,80)
- if c[1]:
- layer.show_text(c[1])
- else:
- layer.set_font_size(30)
- layer.show_text("Help... For some reason I can't get the channel resolved. Line: 149")
- # TODO: See the message on top. It's something to do with
- # line 149. Please help. I can't understand the problem.
- # It used to work. But no longer works.
-
- c[1] = "[Channel Name]"
- #LBC AMOUNT
- lbc = cairo.ImageSurface.create_from_png("images/lbc.png")
- layer.set_source_surface(lbc, 210, 110)
- layer.paint()
-
- layer.set_source_rgba(1,1,1,1) # WHITE
- layer.set_font_size(55)
- layer.move_to(260,150)
- layer.show_text(str(c[0]))
- # COMMENTED WITH:
- if c[2]:
- layer.set_source_rgba(1,0.72,0,1) # YELLOW THING AROUND
- layer.set_font_size(30)
- layer.move_to(800,150)
- layer.show_text("Commented with:")
- # CLIP 2 ICON
- roundrect(layer, 25, 25, 150, 150, 25)
- layer.clip()
-
- # Icon
- if c[3]:
- try:
- get_image(c[3], str(n)+"icon")
- icon = cairo.ImageSurface.create_from_png("images/"+str(n)+"icon.png")
- icon = fit(icon, 150, 150)
- layer.set_source_surface(icon, 25, 25)
- layer.paint()
- # REMOVING THE BANNER ORIGNINAL
- os.remove("images/"+str(n)+"icon.png")
- except:
- print("Icon Failed!")
-
- # SAVE TO IMAGE
- print("SAVING IMAGE ...")
- try:
- os.mkdir("images")
- except:
- pass
- surface.write_to_png("images/"+str(n)+".png")
- print("SAVED AS: images/"+str(n)+".png")
- # Uploading the IMAGE to the LBRY network
- # We gonna need the images to be online
- if not "-t" in sys.argv:
-
- print("UPLOADING IMAGE: images/"+str(n)+".png TO LBRY ...")
- lbryname = ""
- good = "qwertyuiopasdfghjklzxcvbnm-_QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
- for i in range(30):
- lbryname = lbryname + random.choice(good)
- img = subprocess.check_output([
- SDK,
- "publish",
- "--name="+lbryname,
- "--bid=0.001",
- "--file_path="+os.getcwd()+"/images/"+str(n)+".png"])
- img = json.loads(img)
- imageurl = img['outputs'][0]['permanent_url'].replace("lbry://","https://spee.ch/")
- print("PUBLISHED : "+imageurl)
- else:
- # If testing mode
- imageurl = "images/"+str(n)+".png"
-
- # Adding the comment to the MD file
-
- MD = MD + "[![]("+imageurl+")]("+c[6]+")\n"
- if c[2]: # If there are any comments
- for line in c[2].split("\n"):
- bad_site = False
-
- # This feature will implement a redirect
- # or warning if a sender wants to link to
- # a website which are known to be nasty.
- filt = {
- "youtube.com/":"yewtu.be/",
- "youtu.be/":"yewtu.be/",
- "twitter.com/":"nitter.net/",
- "reddit.com/":"teddit.net/"
- }
- for site in filt:
- if site in line:
- line = line.replace(site, filt[site])
- bad_site = True
-
-
- MD = MD + "> "+line+"\n"
- if bad_site:
- MD = MD + "\n\n*This line included links to unsafe and non-free internet dis-services. The link was automatically edited to load a Free / Private alternative front end.*\n"
- MD = MD + "\n\n*I want to show appreciation to ["+c[1]+"](https://odysee.com/"+c[5]+") for supporting me with "+str(c[0])+" LBC coins. "
- MD = MD + "You can send some of your LBC to ["+c[1]+"](https://odysee.com/"+c[5]+") or follow ["+c[1]+"](https://odysee.com/"+c[5]+") to return the favor.*"
-
- MD = MD + "\n\n\n__________\n"
-
- print("----------- THE MD IS -------------")
- print(MD)
- print("-------------THE END---------------")
- savemd = open("output.md", "w")
- savemd.write(MD)
- savemd.close()
- print("SAVED AS: output.md")
- # Show preview if it's a test.
- if "-t" in sys.argv:
- os.system("xdg-open output.md")
|