Free Competitors integration
This commit is contained in:
parent
f8ed6e56c8
commit
49cea62407
6 changed files with 6518 additions and 18 deletions
6139
fcdata.json
Normal file
6139
fcdata.json
Normal file
File diff suppressed because it is too large
Load diff
BIN
icons/fc.png
Normal file
BIN
icons/fc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
|
@ -34,6 +34,7 @@ def Set():
|
|||
print(clr["tdyl"]+"--domain"+clr["norm"]+" - Let the server know the clearnet domain.")
|
||||
print(clr["tdyl"]+"--tor"+clr["norm"]+" - Let the server know the tor domain.")
|
||||
print(clr["tdyl"]+"--email"+clr["norm"]+" - Set Email SMTP account for internal automatic messages.")
|
||||
print(clr["tdyl"]+"--account"+clr["norm"]+" - Set the Main Account, for the footer and stuff.")
|
||||
print(clr["tdyl"]+"--port"+clr["norm"]+" - Set port where to run the website.")
|
||||
print(clr["tdyl"]+"--css"+clr["norm"]+" - Set a CSS file.")
|
||||
print(clr["tdyl"]+"--css_edit"+clr["norm"]+" - edit a CSS file.")
|
||||
|
@ -44,6 +45,7 @@ def Set():
|
|||
print()
|
||||
print(clr["tdyl"]+"--editor"+clr["norm"]+" - Set editor. Default nano.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--fc_api"+clr["norm"]+" - API for software Free Competitors search.")
|
||||
|
||||
def Accounts():
|
||||
|
||||
|
|
|
@ -173,6 +173,10 @@ def validate(cookie):
|
|||
return Accounts[account]
|
||||
return {}
|
||||
|
||||
def isHuman(cookie):
|
||||
|
||||
return ( cookie in ProbablyHumanCookies and cookie in KnownCookies ) or validate(cookie)
|
||||
|
||||
def moderates(moderator, user):
|
||||
|
||||
Accounts = accounts()
|
||||
|
@ -327,7 +331,36 @@ def previewsToSize(text):
|
|||
# A thousand character article is about 4 articles.
|
||||
return len(text)/2200
|
||||
|
||||
def mastohead(mastodon):
|
||||
|
||||
try:
|
||||
if "/" in mastodon:
|
||||
mastodon = mastodon.replace("https://", "").replace("http://", "")
|
||||
mastodon = mastodon.split("/")[1]+"@"+mastodon.split("/")[0]
|
||||
except: pass
|
||||
|
||||
if not mastodon.startswith("@"): mastodon = "@"+mastodon
|
||||
return mastodon
|
||||
|
||||
def mastolink(mastodon):
|
||||
|
||||
return "https://"+mastodon[1:].split("@")[1]+"/@"+mastodon[1:].split("@")[0]
|
||||
|
||||
|
||||
def isFreeSoftware(app):
|
||||
|
||||
with open("fcdata.json") as json_file:
|
||||
licenses = json.load(json_file).get("licenses",[])
|
||||
|
||||
if "licenses" in app and app["licenses"]:
|
||||
all_licenses = licenses
|
||||
for al in all_licenses: # Making longer loop once
|
||||
for l in app["licenses"]:
|
||||
if l in [al.get("licenseId",""),al.get("name","")]\
|
||||
and al.get("isFsfLibre", False):
|
||||
return True
|
||||
|
||||
|
||||
###
|
||||
|
||||
def MainPage(server):
|
||||
|
@ -402,7 +435,7 @@ def MainPage(server):
|
|||
|
||||
html = html + '</div>'
|
||||
|
||||
html = html + Footer()
|
||||
html = html + Footer(server)
|
||||
|
||||
html = html + LoginButton(server)
|
||||
|
||||
|
@ -483,6 +516,7 @@ def ListPage(server, tab):
|
|||
if To < len(Articles)-1:
|
||||
html = html + Button(str(page+1), tab+"?page="+str(page+1), "right")
|
||||
|
||||
html = html + Footer(server)
|
||||
html = html + LoginButton(server)
|
||||
|
||||
send(server, html, 200)
|
||||
|
@ -728,7 +762,7 @@ def ArticlePage(server, url):
|
|||
if str(n) == comment_edit and moderates(user.get("username"), comment.get("username")):
|
||||
html = html + CommentEditInput(server, comment, url, n, user)
|
||||
else:
|
||||
html = html + Comment(comment, url, n, user)
|
||||
html = html + Comment(comment, url, n, user, comments=comments)
|
||||
|
||||
# Needed to extend the suggestion for pages with many comments
|
||||
commentsTextLength += previewsToSize(comment.get("text", ""))
|
||||
|
@ -775,6 +809,7 @@ def ArticlePage(server, url):
|
|||
html = html + ""
|
||||
|
||||
|
||||
html = html + Footer(server)
|
||||
html = html + LoginButton(server)
|
||||
|
||||
send(server, html, 200)
|
||||
|
@ -814,7 +849,7 @@ def AccountPage(server, account):
|
|||
html = html + '</center>'
|
||||
|
||||
# Protecting emails and stuff from scrubbers
|
||||
if server.cookie in ProbablyHumanCookies and server.cookie in KnownCookies:
|
||||
if isHuman(server.cookie):
|
||||
|
||||
# Website
|
||||
|
||||
|
@ -849,12 +884,9 @@ def AccountPage(server, account):
|
|||
# It could be mastodon url and not handle.
|
||||
|
||||
try:
|
||||
if "/" in mastodon:
|
||||
mastodon = mastodon.replace("https://", "").replace("http://", "")
|
||||
mastodon = mastodon.split("/")[1]+"@"+mastodon.split("/")[0]
|
||||
|
||||
if not mastodon.startswith("@"): mastodon = "@"+mastodon
|
||||
mastolink = "https://"+mastodon[1:].split("@")[1]+"/@"+mastodon[1:].split("@")[0]
|
||||
|
||||
mastodon = mastohead(mastodon)
|
||||
mastolink = mastolink(mastodon)
|
||||
|
||||
html = html + '<center>'
|
||||
html = html + '<img style="vertical-align: middle" src="/icon/mastodon">'
|
||||
|
@ -1530,10 +1562,14 @@ def EditorPage(server):
|
|||
|
||||
###
|
||||
|
||||
def Button(text, link, icon="", image=""):
|
||||
def Button(text, link, icon="", image="", rel=""):
|
||||
|
||||
# rel tag is useful for mastodon website verifications
|
||||
reltext = ""
|
||||
if rel: reltext = 'rel="'+rel+'" '
|
||||
|
||||
html = """
|
||||
<a class="button" href=\""""+link+"""">"""
|
||||
<a """+reltext+"""class="button" href=\""""+link+"""">"""
|
||||
if icon:
|
||||
html = html + """
|
||||
<img alt="[icon """+icon+"""]" src="/icon/"""+icon+"""" style="vertical-align: middle">"""
|
||||
|
@ -1590,7 +1626,7 @@ def ArticlePreview(article, Tabs, cookie=""):
|
|||
|
||||
return html
|
||||
|
||||
def Footer():
|
||||
def Footer(server):
|
||||
|
||||
html = """
|
||||
|
||||
|
@ -1599,6 +1635,21 @@ def Footer():
|
|||
"""
|
||||
html = html + Button("Powered with BDServer", "https://codeberg.org/blenderdumbass/BDServer/", "codeberg")
|
||||
|
||||
|
||||
|
||||
config = Set.Load()
|
||||
account = config.get("main_account", "")
|
||||
Accounts = accounts()
|
||||
if account in Accounts:
|
||||
|
||||
if isHuman(server.cookie):
|
||||
email = Accounts[account].get("email")
|
||||
if email:
|
||||
html = html + Button("Contact Admin", "mailto:"+email, "frase")
|
||||
|
||||
mastodon = Accounts[account].get("mastodon")
|
||||
if mastodon:
|
||||
html = html + Button("Mastodon", mastolink(mastohead(mastodon)), "mastodon", rel="me")
|
||||
|
||||
html = html + """
|
||||
|
||||
|
@ -1862,7 +1913,7 @@ def CommentEditInput(server, comment, url, n=0, user=None, request=None):
|
|||
|
||||
return html
|
||||
|
||||
def Comment(comment, url, n=0, user=None, request=False):
|
||||
def Comment(comment, url, n=0, user={}, request=False, comments=[]):
|
||||
|
||||
if not request:
|
||||
html = '<div class="dark_box" id="comment_'+str(n)+'">'
|
||||
|
@ -1870,7 +1921,12 @@ def Comment(comment, url, n=0, user=None, request=False):
|
|||
html = '<div class="dark_box" id="request_'+str(n)+'">'
|
||||
|
||||
account = comment.get("username", "Anonymous User")
|
||||
html = html + User(account) + '<br>\n'
|
||||
html = html + User(account)
|
||||
|
||||
if not request:
|
||||
html = html + ' <a href="#comment_'+str(n)+'">c:'+str(n)+'</a>'
|
||||
|
||||
html = html + '<br>\n'
|
||||
|
||||
if request:
|
||||
html = html + '<center><sup><b> Pending Approval </b></sup></center><br>'
|
||||
|
@ -1883,7 +1939,7 @@ def Comment(comment, url, n=0, user=None, request=False):
|
|||
html = html + warning
|
||||
html = html + '</summary>'
|
||||
|
||||
html = html + markdown.convert(Safe(comment.get("text", "")), False)+'<br>'
|
||||
html = html + markdown.convert(Safe(comment.get("text", "")), False, comments={"url":url,"comments":comments})+'<br>'
|
||||
|
||||
if warning:
|
||||
html = html + '</details>'
|
||||
|
@ -2016,6 +2072,189 @@ def Error(server, text="Some Error Happened."):
|
|||
|
||||
send(server, html, 501)
|
||||
|
||||
|
||||
def FreeCompetitor(free, nonfree):
|
||||
|
||||
html = """
|
||||
|
||||
<div class="article_box">
|
||||
|
||||
"""
|
||||
|
||||
html = html + '<h1><img alt="[icon fc]" src="/icon/fc" style="vertical-align: middle">'
|
||||
html = html + free.get("names", ["Software"])[0]+'</h1>'
|
||||
|
||||
icon = free.get("links", {}).get("icon", "")
|
||||
if icon:
|
||||
html = html + '<center><img alt="[thumbnail]" style="min-width:80%;" src="'+icon+'"></center>'
|
||||
|
||||
text = '<br><br>To replace <b>'+nonfree.get("names", ["The Software"])[0]+'</b>'
|
||||
text = text +' you can use <b>'+free.get("names", ["Software"])[0]+'</b>'
|
||||
text = text +' since it <a href="https://www.gnu.org/philosophy/free-sw.html">respects the user\'s freedom</a> and '
|
||||
|
||||
features = []
|
||||
for feature in free.get("generic_name", []):
|
||||
if feature.startswith("*"):
|
||||
features.append(feature[1:])
|
||||
|
||||
oformats = []
|
||||
for f in free.get("formats_read", []):
|
||||
if f.startswith("*"):
|
||||
oformats.append(f[1:])
|
||||
|
||||
sformats = []
|
||||
for f in free.get("formats_write", []):
|
||||
if f.startswith("*"):
|
||||
sformats.append(f[1:])
|
||||
|
||||
onetworks = []
|
||||
for f in free.get("networks_read", []):
|
||||
if f.startswith("*"):
|
||||
onetworks.append(f[1:])
|
||||
|
||||
snetworks = []
|
||||
for f in free.get("networks_write", []):
|
||||
if f.startswith("*"):
|
||||
snetworks.append(f[1:])
|
||||
|
||||
|
||||
if features:
|
||||
text = text + 'is also a'
|
||||
for n, feature in enumerate(features):
|
||||
AND = " "
|
||||
if n != 0: AND = ', '
|
||||
if n and n == len(features)-1: AND = ' and '
|
||||
text = text + AND + feature + " software"
|
||||
|
||||
text = text +"."
|
||||
if any((oformats, sformats, onetworks, snetworks)):
|
||||
text = text + "<br><br>Also it "
|
||||
|
||||
if oformats:
|
||||
text = text + 'reads '+str(len(oformats))+' of the same formats as '
|
||||
text = text + '<b>'+nonfree.get("names", ["The Software"])[0]+'</b>'
|
||||
text = text + ' such as: '
|
||||
|
||||
|
||||
|
||||
for n, f in enumerate(oformats):
|
||||
|
||||
if n == 4: break
|
||||
|
||||
AND = " "
|
||||
if n: AND = ', '
|
||||
if n and ( n == len(oformats)-1 or n == 3 ): AND = ' and '
|
||||
|
||||
text = text + AND + f.upper()
|
||||
|
||||
if sformats:
|
||||
|
||||
text = text + ' and '
|
||||
|
||||
else:
|
||||
|
||||
text = text + '.'
|
||||
|
||||
if sformats:
|
||||
text = text + 'saves to '+str(len(sformats))+' of the same formats as '
|
||||
text = text + '<b>'+nonfree.get("names", ["The Software"])[0]+'</b>'
|
||||
text = text + ' such as: '
|
||||
|
||||
|
||||
|
||||
for n, f in enumerate(sformats):
|
||||
|
||||
if n == 4: break
|
||||
|
||||
AND = " "
|
||||
if n: AND = ', '
|
||||
if n and ( n == len(sformats)-1 or n == 3 ): AND = ' and '
|
||||
|
||||
text = text + AND + f.upper()
|
||||
|
||||
text = text + '.'
|
||||
|
||||
if any((oformats, sformats)) and any((onetworks, snetworks)):
|
||||
text = text + "<br><br>Also it "
|
||||
|
||||
|
||||
if onetworks:
|
||||
text = text + 'can access data from '+str(len(onetworks))+' of the same network protocols as '
|
||||
text = text + '<b>'+nonfree.get("names", ["The Software"])[0]+'</b>'
|
||||
text = text + ' such as: '
|
||||
|
||||
|
||||
|
||||
for n, f in enumerate(onetworks):
|
||||
|
||||
if n == 4: break
|
||||
|
||||
AND = " "
|
||||
if n: AND = ', '
|
||||
if n and ( n == len(onetworks)-1 or n == 3 ): AND = ' and '
|
||||
|
||||
text = text + AND + f.upper()
|
||||
|
||||
if snetworks:
|
||||
|
||||
text = text + ' and '
|
||||
|
||||
else:
|
||||
|
||||
text = text + '.'
|
||||
|
||||
if snetworks:
|
||||
text = text + 'can publish data to '+str(len(snetworks))+' of the same network protocols as '
|
||||
text = text + '<b>'+nonfree.get("names", ["The Software"])[0]+'</b>'
|
||||
text = text + ' such as: '
|
||||
|
||||
|
||||
|
||||
for n, f in enumerate(snetworks):
|
||||
|
||||
if n == 4: break
|
||||
|
||||
AND = " "
|
||||
if n: AND = ', '
|
||||
if n and ( n == len(snetworks)-1 or n == 3 ): AND = ' and '
|
||||
|
||||
text = text + AND + f.upper()
|
||||
|
||||
text = text + '.'
|
||||
|
||||
text = text + '<br><br>'
|
||||
|
||||
if free.get("issues", []):
|
||||
|
||||
text = text + 'Unfortunately though '
|
||||
text = text +'<b>'+free.get("names", ["Software"])[0]+'</b>'
|
||||
text = text +' is not without issues: <br><center>'
|
||||
|
||||
for issue in free.get("issues", []):
|
||||
text = text + '<div class="toot">'
|
||||
text = text + '<img alt="[icon bug]" src="/icon/bug" style="vertical-align: middle"> '+issue+"<br>"
|
||||
text = text + '</div><br>'
|
||||
|
||||
text = text + '</center>'
|
||||
|
||||
text = text + '<center>'
|
||||
for link in free.get("links", {}):
|
||||
if link == "icon":
|
||||
continue
|
||||
name = link.replace("git", "source code").replace("fsd", "FSD")
|
||||
if name.lower() == name:
|
||||
name = name[0].upper()+name[1:]
|
||||
text = text + Button(name, free["links"][link], "internet")
|
||||
text = text + '</center>'
|
||||
|
||||
html = html + text
|
||||
|
||||
html = html + '</div>'
|
||||
html = html + '<br><br>'
|
||||
|
||||
|
||||
return html
|
||||
|
||||
|
||||
###
|
||||
|
||||
|
@ -2345,7 +2584,7 @@ def UpdatePublicationRights(server):
|
|||
def DoComment(server):
|
||||
|
||||
# Limiting bots from commenting
|
||||
if not server.cookie:
|
||||
if not isHuman(server.cookie):
|
||||
AccessDenied(server)
|
||||
return
|
||||
|
||||
|
@ -2880,6 +3119,9 @@ def Search(server):
|
|||
Articles = allArticles()
|
||||
Tabs = tabs()
|
||||
config = Set.Load()
|
||||
|
||||
# Free Competitors support
|
||||
FreeCompetitors = config.get("free_competitors", "")
|
||||
|
||||
# Generating <head>
|
||||
html = head(title = "Search",
|
||||
|
@ -2904,11 +3146,13 @@ def Search(server):
|
|||
searchpost = server.parsed.get("post",[""])[0]
|
||||
searchdescription = server.parsed.get("description",[""])[0]
|
||||
searchcomments = server.parsed.get("comments",[""])[0]
|
||||
searchfc = server.parsed.get("fc",[""])[0]
|
||||
|
||||
# Supporting legacy search links
|
||||
if not any([searchtitle,
|
||||
searchauthor,
|
||||
searchpost,
|
||||
searchfc,
|
||||
searchdescription,
|
||||
searchcomments
|
||||
]):
|
||||
|
@ -2916,11 +3160,15 @@ def Search(server):
|
|||
searchpost = True
|
||||
searchdescription = True
|
||||
searchcomments = True
|
||||
searchfc = True
|
||||
|
||||
|
||||
checkedtitle = ""
|
||||
if searchtitle: checkedtitle = " checked "
|
||||
|
||||
checkedfc = ""
|
||||
if searchfc: checkedfc = " checked "
|
||||
|
||||
checkedauthor = ""
|
||||
if searchauthor: checkedauthor = " checked "
|
||||
|
||||
|
@ -2932,6 +3180,8 @@ def Search(server):
|
|||
|
||||
checkedcomments = ""
|
||||
if searchcomments: checkedcomments = " checked "
|
||||
|
||||
|
||||
|
||||
html = html + """
|
||||
|
||||
|
@ -2948,6 +3198,15 @@ def Search(server):
|
|||
</button>
|
||||
|
||||
<br>
|
||||
|
||||
"""
|
||||
if FreeCompetitors:
|
||||
html = html + """
|
||||
<div class="button">
|
||||
<input type="checkbox" """+checkedfc+""" name="fc"> Free Software
|
||||
</div>"""
|
||||
|
||||
html = html + """
|
||||
|
||||
<div class="button">
|
||||
<input type="checkbox" """+checkedtitle+""" name="title"> Title
|
||||
|
@ -3045,7 +3304,16 @@ def Search(server):
|
|||
if To < len(list(counted))-1:
|
||||
html = html + Button(str(page+1), urlNoPage+"&page="+str(page+1), "right")
|
||||
|
||||
|
||||
if searchfc:
|
||||
|
||||
html = html + '<center><div class="toot">'
|
||||
html = html + ' The Free Software Search is Powered by '
|
||||
html = html + Button("Free Competitors", "https://notabug.org/jyamihud/FreeCompetitors", "fc")
|
||||
html = html + '</div></center>'
|
||||
|
||||
|
||||
|
||||
html = html + """
|
||||
<br>
|
||||
<br>
|
||||
|
@ -3055,7 +3323,33 @@ def Search(server):
|
|||
<div class="flexity">
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# Free Competitors Search
|
||||
|
||||
if searchfc:
|
||||
|
||||
try:
|
||||
fcdata = API.Get(FreeCompetitors+"/json/"+text.replace(" ", "+"))
|
||||
except:
|
||||
fcdata = {}
|
||||
|
||||
fcMatch = 0.5
|
||||
|
||||
# If we found a match in software
|
||||
if fcdata.get("found", {}).get("match", 0) >= fcMatch:
|
||||
|
||||
fcSearchMatch = max( x[0] for x in fcdata.get("suggestions",[])) * 0.2
|
||||
|
||||
for soft in fcdata.get("suggestions",[]):
|
||||
if fcSearchMatch > soft[0] or not isFreeSoftware(soft[1]):
|
||||
continue
|
||||
|
||||
if soft[1].get("names", [""])[0] in fcdata.get("found", {}).get("data", {}).get("names", []):
|
||||
continue
|
||||
|
||||
html = html + FreeCompetitor(soft[1], fcdata.get("found", {}).get("data",{}))
|
||||
|
||||
|
||||
|
||||
rendered = 0
|
||||
for n, article in enumerate(counted):
|
||||
|
|
|
@ -66,6 +66,16 @@ def Set():
|
|||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Title Wasn't Specified!")
|
||||
print('Use: $ python3 run.py --set --title "My Website"')
|
||||
|
||||
if "--account" in sys.argv:
|
||||
try:
|
||||
account = sys.argv[ sys.argv.index("--account") + 1]
|
||||
if "--" in account: 1/0 # Failing this for the error message.
|
||||
MainAccount(account)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Account Wasn't Specified!")
|
||||
print('Use: $ python3 run.py --set --account blenderdumbass')
|
||||
|
||||
if "--tagline" in sys.argv:
|
||||
try:
|
||||
tagline = sys.argv[ sys.argv.index("--tagline") + 1]
|
||||
|
@ -172,6 +182,17 @@ def Set():
|
|||
if "--css_edit" in sys.argv:
|
||||
config = Load()
|
||||
os.system("nano "+config.get("css", "default.css"))
|
||||
|
||||
|
||||
if "--fc_api" in sys.argv:
|
||||
try:
|
||||
api = sys.argv[ sys.argv.index("--fc_api") + 1]
|
||||
if "--" in api: 1/0 # Failing this for the error message.
|
||||
FreeCompetitors(api)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" API Link Wasn't Specified!")
|
||||
print('Use: $ python3 run.py --set --fc_api https://sudo.madiator.com')
|
||||
|
||||
def Title(title):
|
||||
|
||||
|
@ -332,3 +353,20 @@ def TabRows(tab_rows):
|
|||
|
||||
print(clr["bold"]+clr["tbyl"]+str(tab_rows)+clr["norm"]+" is set as amount of rows to render tabs.")
|
||||
|
||||
def MainAccount(account):
|
||||
|
||||
data = Load()
|
||||
data["main_account"] = account
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+account+clr["norm"]+" is set as main account.")
|
||||
|
||||
def FreeCompetitors(api):
|
||||
|
||||
data = Load()
|
||||
data["free_competitors"] = api
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+api+clr["norm"]+" is set as Free Competitors API.")
|
||||
|
|
|
@ -8,6 +8,7 @@ import urllib.request
|
|||
import urllib.parse
|
||||
from urllib import request, parse
|
||||
|
||||
from modules import Render
|
||||
|
||||
preicons = os.listdir("/home/vcs/Software/VCStudio/settings/themes/Default/icons")
|
||||
icons = []
|
||||
|
@ -399,7 +400,7 @@ def search_convert(s):
|
|||
r = r + i
|
||||
return r
|
||||
|
||||
def convert(filename, isfile=True, fullpath=False):
|
||||
def convert(filename, isfile=True, fullpath=False, comments={}):
|
||||
|
||||
|
||||
if fullpath:
|
||||
|
@ -452,6 +453,32 @@ def convert(filename, isfile=True, fullpath=False):
|
|||
if i[-1].startswith("http"):
|
||||
tag = '<a href="'+i[-1][:-1]+'">'
|
||||
ctag = "</a>"
|
||||
if i[-1].startswith("c:"):
|
||||
|
||||
try: N = int(i[-1].replace("c:",""))
|
||||
except:N = None
|
||||
try:
|
||||
comment = comments.get("comments", [])[N]
|
||||
URL = comments.get("url","")
|
||||
COMMENTS = comments.get("comments", [])
|
||||
comment = Render.Comment(comment, URL, n=N, comments=COMMENTS)
|
||||
|
||||
TEXT = """
|
||||
<details>
|
||||
<summary class="button">
|
||||
<img src="/icon/frase" style="vertical-align: middle">
|
||||
c:"""+str(N)+"""
|
||||
</summary>
|
||||
|
||||
"""+comment+"""
|
||||
|
||||
</details>
|
||||
"""
|
||||
|
||||
i[-1] = TEXT.replace("\n", "")
|
||||
except Exception as e: print(e)
|
||||
|
||||
|
||||
textReturn = textReturn + tag + i[-1] + ctag
|
||||
|
||||
elif i[0] == "text_cm":
|
||||
|
|
Loading…
Add table
Reference in a new issue