mirror of
https://github.com/tildeclub/ttbp.git
synced 2026-03-17 08:10:17 +00:00
create setup.py and ttbp package
This commit is contained in:
0
ttbp/__init__.py
Normal file
0
ttbp/__init__.py
Normal file
1147
ttbp/_ttbp.py
Normal file
1147
ttbp/_ttbp.py
Normal file
File diff suppressed because it is too large
Load Diff
69
ttbp/chatter.py
Normal file
69
ttbp/chatter.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
|
||||
DEFAULT_LANG = {
|
||||
"greet":[
|
||||
"hi",
|
||||
"hey",
|
||||
"howdy",
|
||||
"good morning",
|
||||
"good afternoon",
|
||||
"good day",
|
||||
"good evening",
|
||||
"welcome back",
|
||||
"nice to see you"
|
||||
],
|
||||
"bye":[
|
||||
"see you later, space cowboy",
|
||||
"bye, townie",
|
||||
"until next time, friend",
|
||||
"come back whenever"
|
||||
],
|
||||
"friend":[
|
||||
"friend",
|
||||
"pal",
|
||||
"buddy",
|
||||
"townie"
|
||||
],
|
||||
"months":{
|
||||
"01":"january",
|
||||
"02":"february",
|
||||
"03":"march",
|
||||
"04":"april",
|
||||
"05":"may",
|
||||
"06":"june",
|
||||
"07":"july",
|
||||
"08":"august",
|
||||
"09":"september",
|
||||
"10":"october",
|
||||
"11":"november",
|
||||
"12":"december"
|
||||
}
|
||||
}
|
||||
|
||||
if os.path.exists("/home/endorphant/lib/python/chatterlib.json"):
|
||||
with open("/home/endorphant/lib/python/chatterlib.json", 'r') as f:
|
||||
LANG = json.load(f)
|
||||
else:
|
||||
LANG = DEFAULT_LANG
|
||||
|
||||
def say(keyword):
|
||||
'''
|
||||
takes a keyword and randomly returns from language dictionary to match that keyword
|
||||
|
||||
returns None if keyword doesn't exist
|
||||
|
||||
TODO: validate keyword?
|
||||
'''
|
||||
|
||||
return random.choice(LANG.get(keyword))
|
||||
|
||||
def month(num):
|
||||
'''
|
||||
takes a MM and returns lovercase full name of that month
|
||||
|
||||
TODO: validate num?
|
||||
'''
|
||||
|
||||
return LANG["months"].get(num)
|
||||
9
ttbp/config/banner-beta.txt
Normal file
9
ttbp/config/banner-beta.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
__________________________________________________________
|
||||
| |
|
||||
| the tilde.town |
|
||||
| ____ ____ ____ _ ____ ____ _ _ ____ _ _ _ ____ |
|
||||
| |___ |___ |___ | [__ |___ |\ | | __ | |\ | |___ |
|
||||
| | |___ |___ |___ ___] |___ | \| |__] | | \| |___ |
|
||||
| |
|
||||
| ver 0.9.2 (almost stable) |
|
||||
|__________________________________________________________|
|
||||
8
ttbp/config/banner.txt
Normal file
8
ttbp/config/banner.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
__________________________________________________________
|
||||
| |
|
||||
| the tilde.town |
|
||||
| ____ ____ ____ _ ____ ____ _ _ ____ _ _ _ ____ |
|
||||
| |___ |___ |___ | [__ |___ |\ | | __ | |\ | |___ |
|
||||
| | |___ |___ |___ ___] |___ | \| |__] | | \| |___ |
|
||||
| ver 0.9.2 (almost stable) |
|
||||
|__________________________________________________________|
|
||||
3
ttbp/config/defaults/footer.txt
Normal file
3
ttbp/config/defaults/footer.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
12
ttbp/config/defaults/header.txt
Normal file
12
ttbp/config/defaults/header.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>$USER on TTBP</title>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="meta">
|
||||
<h1><a href="#">~$USER</a>@<a href="/~endorphant/ttbp">TTBP</a></h1>
|
||||
</div>
|
||||
|
||||
<div id="tlogs">
|
||||
42
ttbp/config/defaults/style.css
Normal file
42
ttbp/config/defaults/style.css
Normal file
@@ -0,0 +1,42 @@
|
||||
body {
|
||||
background-color: #E0B0FF;
|
||||
font-family: courier
|
||||
}
|
||||
|
||||
#meta {
|
||||
top: -.5em;
|
||||
position: fixed;
|
||||
height: 3.5em;
|
||||
float: left;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
background-color: #e0b0ff;
|
||||
}
|
||||
|
||||
#tlogs {
|
||||
margin-top: 5em;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.entry {
|
||||
border: 1px dotted white;
|
||||
padding: .4em;
|
||||
margin-bottom:-4em;
|
||||
}
|
||||
|
||||
.entry p {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.entry h5 {
|
||||
text-align: right;
|
||||
margin-top: .2em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
background-color: black;
|
||||
color: #e0b0ff;
|
||||
font-size: 90%;
|
||||
border: 1px dotted white;
|
||||
padding: 4px;
|
||||
}
|
||||
437
ttbp/core.py
Normal file
437
ttbp/core.py
Normal file
@@ -0,0 +1,437 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
'''
|
||||
ttbp: tilde town blogging platform
|
||||
(also known as the feels engine)
|
||||
a console-based blogging program developed for tilde.town
|
||||
copyright (c) 2016 ~endorphant (endorphant@tilde.town)
|
||||
|
||||
core.py:
|
||||
this is a core handler for some ttbp standalone/output functions
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
the complete codebase is available at:
|
||||
https://github.com/modgethanc/ttbp
|
||||
'''
|
||||
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
import re
|
||||
import mistune
|
||||
import json
|
||||
|
||||
import chatter
|
||||
|
||||
SOURCE = os.path.join("/home", "endorphant", "projects", "ttbp", "bin")
|
||||
USER = os.path.basename(os.path.expanduser("~"))
|
||||
PATH = os.path.join("/home", USER, ".ttbp")
|
||||
|
||||
LIVE = "http://tilde.town/~"
|
||||
WWW = os.path.join(PATH, "www")
|
||||
CONFIG = os.path.join(PATH, "config")
|
||||
DATA = os.path.join(PATH, "entries")
|
||||
FEED = os.path.join(SOURCE, "www", "index.html")
|
||||
DOCS = os.path.join(SOURCE, "www", "help.html")
|
||||
NOPUB = os.path.join(CONFIG, "nopub")
|
||||
SETTINGS = {}
|
||||
|
||||
HEADER = ""
|
||||
FOOTER = ""
|
||||
FILES = []
|
||||
|
||||
def load(ttbprc={}):
|
||||
'''
|
||||
get all them globals set up!!
|
||||
'''
|
||||
|
||||
global HEADER
|
||||
global FOOTER
|
||||
global SETTINGS
|
||||
|
||||
HEADER = open(os.path.join(CONFIG, "header.txt")).read()
|
||||
FOOTER = open(os.path.join(CONFIG, "footer.txt")).read()
|
||||
SETTINGS = ttbprc
|
||||
|
||||
load_files()
|
||||
|
||||
def reload_ttbprc(ttbprc={}):
|
||||
'''
|
||||
reloads new ttbprc into current session
|
||||
'''
|
||||
|
||||
global SETTINGS
|
||||
|
||||
SETTINGS = ttbprc
|
||||
|
||||
def load_files():
|
||||
'''
|
||||
file loader
|
||||
|
||||
* reads user's nopub file
|
||||
* loads all valid filenames that are not excluded in nopub to global files list
|
||||
'''
|
||||
|
||||
global FILES
|
||||
|
||||
FILES = []
|
||||
|
||||
for filename in os.listdir(DATA):
|
||||
if nopub(filename):
|
||||
link = os.path.join(WWW, os.path.splitext(os.path.basename(filename))[0]+".html")
|
||||
if os.path.exists(link):
|
||||
subprocess.call(["rm", link])
|
||||
continue
|
||||
filename = os.path.join(DATA, filename)
|
||||
if os.path.isfile(filename) and valid(filename):
|
||||
FILES.append(filename)
|
||||
|
||||
FILES.sort()
|
||||
FILES.reverse()
|
||||
|
||||
|
||||
## html outputting
|
||||
|
||||
def write(outurl="default.html"):
|
||||
'''
|
||||
main page renderer
|
||||
|
||||
* takes everything currently in FILES and writes a single non-paginated html
|
||||
file
|
||||
* calls write_page() on each file to make permalinks
|
||||
'''
|
||||
|
||||
outfile = open(os.path.join(WWW, outurl), "w")
|
||||
|
||||
outfile.write("<!--generated by the tilde.town blogging platform on "+time.strftime("%d %B %y")+"\nhttp://tilde.town/~endorphant/ttbp/-->\n\n")
|
||||
|
||||
for line in HEADER:
|
||||
outfile.write(line)
|
||||
|
||||
outfile.write("\n")
|
||||
|
||||
for filename in FILES:
|
||||
write_page(filename)
|
||||
for line in write_entry(filename):
|
||||
outfile.write(line)
|
||||
|
||||
outfile.write("\n")
|
||||
|
||||
for line in FOOTER:
|
||||
outfile.write(line)
|
||||
|
||||
outfile.close()
|
||||
|
||||
return os.path.join(LIVE+USER,os.path.basename(os.path.realpath(WWW)),outurl)
|
||||
|
||||
def write_page(filename):
|
||||
'''
|
||||
permalink generator
|
||||
|
||||
* makes a page out of a single entry for permalinking, using filename/date as
|
||||
url
|
||||
'''
|
||||
|
||||
outurl = os.path.join(WWW, "".join(parse_date(filename))+".html")
|
||||
outfile = open(outurl, "w")
|
||||
|
||||
outfile.write("<!--generated by the tilde.town blogging platform on "+time.strftime("%d %B %y")+"\nhttp://tilde.town/~endorphant/ttbp/-->\n\n")
|
||||
|
||||
for line in HEADER:
|
||||
outfile.write(line)
|
||||
|
||||
outfile.write("\n")
|
||||
|
||||
for line in write_entry(filename):
|
||||
outfile.write(line)
|
||||
|
||||
outfile.write("\n")
|
||||
|
||||
for line in FOOTER:
|
||||
outfile.write(line)
|
||||
|
||||
outfile.close()
|
||||
|
||||
return outurl
|
||||
|
||||
def write_entry(filename):
|
||||
'''
|
||||
entry text generator
|
||||
|
||||
* dump given file into entry format by parsing file as markdown
|
||||
* return as list of strings
|
||||
'''
|
||||
|
||||
date = parse_date(filename)
|
||||
|
||||
entry = [
|
||||
"\t\t<p><a name=\""+date[0]+date[1]+date[2]+"\"></a><br /><br /></p>\n",
|
||||
"\t\t<div class=\"entry\">\n",
|
||||
"\t\t\t<h5><a href=\"#"+date[0]+date[1]+date[2]+"\">"+date[2]+"</a> "+chatter.month(date[1])+" "+date[0]+"</h5>\n"
|
||||
#"\t\t\t<P>"
|
||||
]
|
||||
|
||||
raw = []
|
||||
rawfile = open(os.path.join(DATA, filename), "r")
|
||||
|
||||
for line in rawfile:
|
||||
raw.append(line)
|
||||
rawfile.close()
|
||||
|
||||
entry.append("\t\t\t"+mistune.markdown("".join(raw), escape=False, hard_wrap=False))
|
||||
|
||||
#for line in raw:
|
||||
#entry.append(line+"\t\t\t")
|
||||
#if line == "\n":
|
||||
# entry.append("</p>\n\t\t\t<p>")
|
||||
|
||||
#entry.append("</p>\n")
|
||||
entry.append("\t\t\t<p style=\"font-size:.6em; font-color:#808080; text-align: right;\"><a href=\""+"".join(date)+".html\">permalink</a></p>\n")
|
||||
entry.append("\n\t\t</div>\n")
|
||||
|
||||
return entry
|
||||
|
||||
def write_global_feed(blogList):
|
||||
'''
|
||||
main ttbp index printer
|
||||
|
||||
* sources README.md for documentation
|
||||
* takes incoming list of formatted blog links for all publishing blogs and
|
||||
prints to blog feed
|
||||
'''
|
||||
|
||||
outfile = open(FEED, "w")
|
||||
|
||||
## header
|
||||
outfile.write("""\
|
||||
<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2//EN\">
|
||||
<html>
|
||||
<head>
|
||||
<title>tilde.town feels engine</title>
|
||||
<link rel=\"stylesheet\" href=\"style.css\" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="meta">
|
||||
<h1>tilde.town feels engine</h1>
|
||||
|
||||
<h2><a href="https://github.com/modgethanc/ttbp">github
|
||||
repo</a> | <a
|
||||
href="http://tilde.town/~endorphant/blog/20160510.html">state
|
||||
of the ttbp</a></h2>
|
||||
<!--<p>curious? run <b>~endorphant/bin/ttbp</b> while logged in to tilde.town.</p>
|
||||
<p>it's still a little volatile. let me know if anything breaks.</p>---></div>
|
||||
<p> </p>
|
||||
""")
|
||||
|
||||
## docs
|
||||
outfile.write("""\
|
||||
<div class="docs">""")
|
||||
outfile.write(mistune.markdown(open(os.path.join(SOURCE, "..", "README.md"), "r").read()))
|
||||
outfile.write("""\
|
||||
</div>""")
|
||||
|
||||
## feed
|
||||
outfile.write("""\
|
||||
<p> </p>
|
||||
<div class=\"feed\">
|
||||
<h3>live feels-sharing:</h3>
|
||||
<ul>""")
|
||||
for blog in blogList:
|
||||
outfile.write("""
|
||||
<li>"""+blog+"""</li>\
|
||||
""")
|
||||
|
||||
## footer
|
||||
outfile.write("""
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""")
|
||||
|
||||
outfile.close()
|
||||
|
||||
## misc helpers
|
||||
|
||||
def meta(entries = FILES):
|
||||
'''
|
||||
metadata generator
|
||||
|
||||
* takes a list of filenames and returns a 2d list:
|
||||
[0] absolute path
|
||||
[1] mtime
|
||||
[2] wc -w
|
||||
[3] timestamp "DD month YYYY at HH:MM"
|
||||
[4] entry date YYYY-MM-DD
|
||||
[5] author
|
||||
|
||||
* sorted in reverse date order by [4]
|
||||
'''
|
||||
|
||||
meta = []
|
||||
|
||||
for filename in entries:
|
||||
mtime = os.path.getmtime(filename)
|
||||
try:
|
||||
wc = subprocess.check_output(["wc","-w",filename], stderr=subprocess.STDOUT).split()[0]
|
||||
except subprocess.CalledProcessError:
|
||||
wc = "???"
|
||||
timestamp = time.strftime("%Y-%m-%d at %H:%M", time.localtime(mtime))
|
||||
date = "-".join(parse_date(filename))
|
||||
author = os.path.split(os.path.split(os.path.split(os.path.split(filename)[0])[0])[0])[1]
|
||||
|
||||
meta.append([filename, mtime, wc, timestamp, date, author])
|
||||
|
||||
#meta.sort(key = lambda filename:filename[4])
|
||||
#meta.reverse()
|
||||
|
||||
return meta
|
||||
|
||||
def valid(filename):
|
||||
'''
|
||||
filename validator
|
||||
|
||||
* check if the filename is YYYYMMDD.txt
|
||||
'''
|
||||
|
||||
filesplit = os.path.splitext(os.path.basename(filename))
|
||||
|
||||
if filesplit[1] != ".txt":
|
||||
return False
|
||||
|
||||
pattern = '^((19|20)\d{2})(0[1-9]|1[0-2])(0[1-9]|1\d|2\d|3[01])$'
|
||||
|
||||
if not re.match(pattern, filesplit[0]):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def parse_date(file):
|
||||
'''
|
||||
parses date out of pre-validated filename
|
||||
|
||||
* assumes a filename of YYYYMMDD.txt
|
||||
* returns a list:
|
||||
[0] 'YYYY'
|
||||
[1] 'MM'
|
||||
[2] 'DD'
|
||||
'''
|
||||
|
||||
rawdate = os.path.splitext(os.path.basename(file))[0]
|
||||
|
||||
date = [rawdate[0:4], rawdate[4:6], rawdate[6:]]
|
||||
|
||||
return date
|
||||
|
||||
def find_ttbps():
|
||||
'''
|
||||
returns a list of users with a ttbp by checking for a valid ttbprc
|
||||
'''
|
||||
|
||||
users = []
|
||||
|
||||
for townie in os.listdir("/home"):
|
||||
if os.path.exists(os.path.join("/home", townie, ".ttbp", "config", "ttbprc")):
|
||||
users.append(townie)
|
||||
|
||||
return users
|
||||
|
||||
def publishing(username = USER):
|
||||
'''
|
||||
checks .ttbprc for whether or not user opted for www publishing
|
||||
'''
|
||||
|
||||
ttbprc = {}
|
||||
|
||||
if username == USER:
|
||||
ttbprc = SETTINGS
|
||||
|
||||
else:
|
||||
ttbprc = json.load(open(os.path.join("/home", username, ".ttbp", "config", "ttbprc")))
|
||||
|
||||
return ttbprc.get("publishing")
|
||||
|
||||
def www_neighbors():
|
||||
'''
|
||||
takes a list of users with publiishing turned on and prepares it for www output
|
||||
'''
|
||||
|
||||
userList = []
|
||||
|
||||
for user in find_ttbps():
|
||||
if not publishing(user):
|
||||
continue
|
||||
|
||||
userRC = json.load(open(os.path.join("/home", user, ".ttbp", "config", "ttbprc")))
|
||||
|
||||
url = ""
|
||||
if userRC["publish dir"]:
|
||||
url = LIVE+user+"/"+userRC["publish dir"]
|
||||
|
||||
lastfile = ""
|
||||
files = os.listdir(os.path.join("/home", user, ".ttbp", "entries"))
|
||||
files.sort()
|
||||
for filename in files:
|
||||
if valid(filename):
|
||||
lastfile = os.path.join("/home", user, ".ttbp", "entries", filename)
|
||||
|
||||
if lastfile:
|
||||
last = os.path.getctime(lastfile)
|
||||
timestamp = time.strftime("%Y-%m-%d at %H:%M", time.localtime(last)) + " (utc"+time.strftime("%z")[0]+time.strftime("%z")[2]+")"
|
||||
else:
|
||||
timestamp = ""
|
||||
last = 0
|
||||
|
||||
userList.append(["<a href=\""+url+"\">~"+user+"</a> "+timestamp, last])
|
||||
|
||||
# sort user by most recent entry
|
||||
userList.sort(key = lambda userdata:userdata[1])
|
||||
userList.reverse()
|
||||
sortedUsers = []
|
||||
for user in userList:
|
||||
sortedUsers.append(user[0])
|
||||
|
||||
write_global_feed(sortedUsers)
|
||||
|
||||
def nopub(filename):
|
||||
'''
|
||||
checks to see if given filename is in user's NOPUB
|
||||
'''
|
||||
|
||||
exclude = []
|
||||
|
||||
if os.path.isfile(NOPUB):
|
||||
for line in open(NOPUB, "r"):
|
||||
exclude.append(line.rstrip())
|
||||
|
||||
return os.path.basename(filename) in exclude
|
||||
|
||||
#############
|
||||
#############
|
||||
#############
|
||||
|
||||
def test():
|
||||
load()
|
||||
|
||||
metaTest = meta()
|
||||
|
||||
for x in metaTest:
|
||||
print(x)
|
||||
1148
ttbp/ttbp.py
Normal file
1148
ttbp/ttbp.py
Normal file
File diff suppressed because it is too large
Load Diff
6
ttbp/update.py
Normal file
6
ttbp/update.py
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import core
|
||||
|
||||
core.load()
|
||||
print("\n blog updated at "+core.write("index.html")+"\n")
|
||||
219
ttbp/util.py
Normal file
219
ttbp/util.py
Normal file
@@ -0,0 +1,219 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
'''
|
||||
util.py: frequently used terminal and text processing utilities
|
||||
copyright (c) 2016 ~endorphant (endorphant@tilde.town)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
'''
|
||||
|
||||
import inflect
|
||||
import time
|
||||
import random
|
||||
import colorama
|
||||
|
||||
## misc globals
|
||||
BACKS = ['back', 'b', 'q']
|
||||
NAVS = ['u', 'd']
|
||||
|
||||
## color stuff
|
||||
colorama.init()
|
||||
textcolors = [ colorama.Fore.RED, colorama.Fore.GREEN, colorama.Fore.YELLOW, colorama.Fore.BLUE, colorama.Fore.MAGENTA, colorama.Fore.WHITE, colorama.Fore.CYAN]
|
||||
lastcolor = colorama.Fore.RESET
|
||||
|
||||
p = inflect.engine()
|
||||
|
||||
def set_rainbow():
|
||||
'''
|
||||
prints a random terminal color code
|
||||
'''
|
||||
|
||||
global lastcolor
|
||||
|
||||
color = lastcolor
|
||||
while color == lastcolor:
|
||||
color = random.choice(textcolors)
|
||||
|
||||
lastcolor = color
|
||||
|
||||
print(color)
|
||||
|
||||
def reset_color():
|
||||
'''
|
||||
prints terminal color code reset
|
||||
'''
|
||||
|
||||
print(colorama.Fore.RESET)
|
||||
|
||||
def attach_rainbow():
|
||||
'''
|
||||
returns a random terminal color code, presumably to be 'attached' to a string
|
||||
'''
|
||||
|
||||
global lastcolor
|
||||
|
||||
color = lastcolor
|
||||
while color == lastcolor:
|
||||
color = random.choice(textcolors)
|
||||
|
||||
lastcolor = color
|
||||
return color
|
||||
|
||||
def attach_reset():
|
||||
'''
|
||||
returns terminal color code reset, presumably to be 'attached' to a string
|
||||
'''
|
||||
|
||||
return colorama.Style.RESET_ALL
|
||||
|
||||
def hilight(text):
|
||||
'''
|
||||
takes a string and highlights it on return
|
||||
'''
|
||||
|
||||
return colorama.Style.BRIGHT+text+colorama.Style.NORMAL
|
||||
|
||||
def rainbow(txt):
|
||||
'''
|
||||
Takes a string and makes every letter a different color.
|
||||
'''
|
||||
|
||||
rainbow = ""
|
||||
for letter in txt:
|
||||
rainbow += attach_rainbow() + letter
|
||||
|
||||
rainbow += attach_reset()
|
||||
|
||||
return rainbow
|
||||
|
||||
def pretty_time(time):
|
||||
'''
|
||||
human-friendly time formatter
|
||||
|
||||
takes an integer number of seconds and returns a phrase that describes it,
|
||||
using the largest possible figure, rounded down (ie, time=604 returns '10
|
||||
minutes', not '10 minutes, 4 seconds' or '604 seconds')
|
||||
'''
|
||||
|
||||
m, s = divmod(time, 60)
|
||||
if m > 0:
|
||||
h, m = divmod(m, 60)
|
||||
if h > 0:
|
||||
d, h = divmod(h, 24)
|
||||
if d > 0:
|
||||
w, d = divmod(d, 7)
|
||||
if w > 0:
|
||||
mo, w = divmod(w, 4)
|
||||
if mo > 0:
|
||||
return p.no("month", mo)
|
||||
else:
|
||||
return p.no("week", w)
|
||||
else:
|
||||
return p.no("day", d)
|
||||
else:
|
||||
return p.no("hour", h)
|
||||
else:
|
||||
return p.no("minute", m)
|
||||
else:
|
||||
return p.no("second", s)
|
||||
|
||||
def genID(digits=5):
|
||||
'''
|
||||
returns a string-friendly string of digits, which can start with 0
|
||||
'''
|
||||
|
||||
id = ""
|
||||
x = 0
|
||||
while x < digits:
|
||||
id += str(random.randint(0,9))
|
||||
x += 1
|
||||
|
||||
return id
|
||||
|
||||
def print_menu(menu, rainbow=False):
|
||||
'''
|
||||
A pretty menu handler that takes an incoming lists of
|
||||
options and prints them nicely.
|
||||
|
||||
Set rainbow=True for colorized menus.
|
||||
'''
|
||||
|
||||
i = 0
|
||||
for x in menu:
|
||||
line = []
|
||||
if rainbow is not False:
|
||||
line.append(attach_rainbow())
|
||||
line.append("\t[ ")
|
||||
if i < 10:
|
||||
line.append(" ")
|
||||
line.append(str(i)+" ] "+x)
|
||||
line.append(attach_reset())
|
||||
print("".join(line))
|
||||
i += 1
|
||||
|
||||
def list_select(options, prompt):
|
||||
'''
|
||||
Given a list and query prompt, returns either False as an
|
||||
eject flag, or an integer index of the list Catches cancel
|
||||
option from list defined by BACKS; otherwise, retries on
|
||||
ValueError or IndexError.
|
||||
'''
|
||||
|
||||
ans = ""
|
||||
invalid = True
|
||||
|
||||
choice = raw_input("\n"+prompt)
|
||||
|
||||
if choice in BACKS:
|
||||
return False
|
||||
|
||||
if choice in NAVS:
|
||||
return choice
|
||||
|
||||
try:
|
||||
ans = int(choice)
|
||||
except ValueError:
|
||||
return list_select(options, prompt)
|
||||
|
||||
try:
|
||||
options[ans]
|
||||
except IndexError:
|
||||
return list_select(options, prompt)
|
||||
|
||||
return ans
|
||||
|
||||
def input_yn(query):
|
||||
'''
|
||||
Given a query, returns boolean True or False by processing y/n input
|
||||
'''
|
||||
|
||||
try:
|
||||
ans = raw_input(query+" [y/n] ")
|
||||
except KeyboardInterrupt:
|
||||
input_yn(query)
|
||||
|
||||
while ans not in ["y", "n"]:
|
||||
ans = raw_input("'y' or 'n' please: ")
|
||||
|
||||
if ans == "y":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user