2016-04-30 03:17:06 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
|
2016-05-22 02:18:25 +00:00
|
|
|
'''
|
|
|
|
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)
|
|
|
|
|
|
|
|
ttbp.py:
|
|
|
|
the main console interface
|
|
|
|
|
2016-06-15 01:51:49 +00:00
|
|
|
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:
|
2016-05-22 02:18:25 +00:00
|
|
|
|
2016-06-15 01:51:49 +00:00
|
|
|
The above copyright notice and this permission notice shall be
|
|
|
|
included in all copies or substantial portions of the Software.
|
2016-05-22 02:18:25 +00:00
|
|
|
|
2016-06-15 01:51:49 +00:00
|
|
|
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.
|
2016-05-22 02:18:25 +00:00
|
|
|
|
|
|
|
the complete codebase is available at:
|
|
|
|
https://github.com/modgethanc/ttbp
|
|
|
|
'''
|
2017-11-21 02:48:32 +00:00
|
|
|
from __future__ import absolute_import
|
2016-05-22 02:18:25 +00:00
|
|
|
|
2016-04-30 03:17:06 +00:00
|
|
|
import os
|
2018-03-16 05:30:48 +00:00
|
|
|
import sys
|
2016-05-01 17:44:49 +00:00
|
|
|
import tempfile
|
|
|
|
import subprocess
|
|
|
|
import time
|
2016-05-01 22:43:12 +00:00
|
|
|
import json
|
2017-12-04 04:05:04 +00:00
|
|
|
from email.mime.text import MIMEText
|
2017-11-26 02:39:58 +00:00
|
|
|
import datetime
|
2018-02-23 20:28:25 +00:00
|
|
|
from six.moves import input
|
2016-05-01 17:44:49 +00:00
|
|
|
|
2016-05-03 17:14:53 +00:00
|
|
|
import inflect
|
2016-04-30 03:17:06 +00:00
|
|
|
|
2017-11-21 06:02:10 +00:00
|
|
|
from . import config
|
2017-11-21 02:48:32 +00:00
|
|
|
from . import core
|
|
|
|
from . import chatter
|
2017-12-04 03:37:43 +00:00
|
|
|
from . import gopher
|
2017-11-21 02:48:32 +00:00
|
|
|
from . import util
|
|
|
|
|
2019-06-07 14:27:39 +00:00
|
|
|
__version__ = "0.12.2"
|
2016-05-23 02:42:31 +00:00
|
|
|
__author__ = "endorphant <endorphant@tilde.town)"
|
|
|
|
|
2016-05-03 17:14:53 +00:00
|
|
|
p = inflect.engine()
|
2016-05-01 22:26:26 +00:00
|
|
|
|
|
|
|
## ui globals
|
2017-11-21 06:02:10 +00:00
|
|
|
BANNER = util.attach_rainbow() + config.BANNER + util.attach_reset()
|
2016-05-20 03:19:02 +00:00
|
|
|
SPACER = "\n"
|
2016-05-01 18:10:04 +00:00
|
|
|
INVALID = "please pick a number from the list of options!\n\n"
|
|
|
|
DUST = "sorry about the dust, but this part is still under construction. check back later!\n\n"
|
2016-05-09 02:59:48 +00:00
|
|
|
QUITS = ['exit', 'quit', 'q', 'x']
|
|
|
|
EJECT = "eject button fired! going home now."
|
2016-05-31 07:20:15 +00:00
|
|
|
RAINBOW = False
|
2016-05-01 18:10:04 +00:00
|
|
|
|
2016-05-01 22:26:26 +00:00
|
|
|
## ref
|
2017-12-06 03:50:45 +00:00
|
|
|
EDITORS = ["nano", "vim", "vi", "emacs", "pico", "ed", "micro"]
|
2016-05-02 15:26:51 +00:00
|
|
|
SUBJECTS = ["help request", "bug report", "feature suggestion", "general comment"]
|
2017-12-31 16:52:56 +00:00
|
|
|
DEFAULT_SETTINGS = {
|
|
|
|
"editor": "nano",
|
|
|
|
"publish dir": None,
|
|
|
|
"gopher": False,
|
2018-01-02 06:21:48 +00:00
|
|
|
"publishing": False,
|
|
|
|
"rainbows": False,
|
2018-03-23 04:11:40 +00:00
|
|
|
"post as nopub": False,
|
2017-12-31 16:52:56 +00:00
|
|
|
}
|
2016-05-01 22:26:26 +00:00
|
|
|
|
2017-12-31 17:52:23 +00:00
|
|
|
## user globals
|
|
|
|
SETTINGS = {
|
|
|
|
"editor": "nano",
|
|
|
|
"publish dir": None,
|
|
|
|
"gopher": False,
|
2018-01-02 06:21:48 +00:00
|
|
|
"publishing": False,
|
2018-03-23 04:11:40 +00:00
|
|
|
"rainbows": False,
|
|
|
|
"post as nopub": False,
|
2017-12-31 17:52:23 +00:00
|
|
|
}
|
|
|
|
|
2016-10-10 21:53:02 +00:00
|
|
|
## ttbp specific utilities
|
|
|
|
|
2019-04-28 16:08:47 +00:00
|
|
|
def menu_handler(options, prompt, pagify=10, page=0, rainbow=False, top=""):
|
2016-10-10 21:53:02 +00:00
|
|
|
'''
|
|
|
|
This menu handler takes an incoming list of options, pagifies to a
|
|
|
|
pre-set value, and queries via the prompt. Calls print_menu() and
|
|
|
|
list_select() as helpers.
|
|
|
|
|
|
|
|
'top' is an optional list topper, to be passed to redraw()
|
|
|
|
'''
|
|
|
|
|
|
|
|
optCount = len(options)
|
|
|
|
total = optCount / pagify
|
|
|
|
|
|
|
|
# don't display empty pages
|
|
|
|
if optCount % pagify == 0:
|
|
|
|
total = total - 1
|
|
|
|
|
2019-07-10 04:36:35 +00:00
|
|
|
if 0:
|
|
|
|
# temporary fix for menu bug when fewer than 10 entries
|
|
|
|
#if total < 1:
|
2018-01-02 06:21:48 +00:00
|
|
|
util.print_menu(options, SETTINGS.get("rainbows", False))
|
2016-11-08 23:38:15 +00:00
|
|
|
return util.list_select(options, prompt)
|
2016-10-10 21:53:02 +00:00
|
|
|
|
|
|
|
else:
|
2018-02-28 18:16:51 +00:00
|
|
|
return page_helper(options, prompt, pagify, rainbow, page, int(total), top)
|
2016-10-10 21:53:02 +00:00
|
|
|
|
|
|
|
def page_helper(options, prompt, pagify, rainbow, page, total, top):
|
|
|
|
'''
|
|
|
|
A helper to process pagination.
|
2016-11-08 23:38:15 +00:00
|
|
|
|
|
|
|
'pagify' is the number of entries per page of display
|
|
|
|
'page' is the current page number
|
|
|
|
'total' is the total number of pages
|
|
|
|
'top' is displyed after the banner redraw
|
2016-10-10 21:53:02 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
## make short list
|
|
|
|
x = 0 + page * pagify
|
|
|
|
y = x + pagify
|
|
|
|
optPage = options[x:y]
|
|
|
|
|
2018-01-02 06:21:48 +00:00
|
|
|
util.print_menu(optPage, SETTINGS.get("rainbows", False))
|
2018-02-28 18:16:51 +00:00
|
|
|
print("\n\t( page {page} of {total}; type 'u' or 'd' to scroll up and down)".format(page=page+1, total=total+1))
|
2016-10-10 21:53:02 +00:00
|
|
|
|
|
|
|
ans = util.list_select(optPage, prompt)
|
|
|
|
|
|
|
|
if ans in util.NAVS:
|
|
|
|
error = ""
|
|
|
|
if ans == 'u':
|
|
|
|
if page == 0:
|
|
|
|
error = "can't scroll up anymore!\n\n> "
|
|
|
|
else:
|
|
|
|
page = page - 1
|
|
|
|
else:
|
|
|
|
if page == total:
|
|
|
|
error = "can't scroll down anymore!\n\n> "
|
|
|
|
else:
|
|
|
|
page = page + 1
|
|
|
|
redraw(error+top)
|
|
|
|
return page_helper(options, prompt, pagify, rainbow, page, total, top)
|
|
|
|
|
2016-11-08 23:38:15 +00:00
|
|
|
elif ans is False:
|
|
|
|
return ans
|
|
|
|
|
|
|
|
else:
|
|
|
|
# shift answer to refer to index from original list
|
|
|
|
ans = ans + page * pagify
|
2019-04-28 16:08:47 +00:00
|
|
|
# return the (shifted) answer and the current page
|
|
|
|
# alternatively, we can recompute the current page at a call site
|
|
|
|
return (page, ans)
|
2016-05-01 18:10:04 +00:00
|
|
|
|
|
|
|
def redraw(leftover=""):
|
2016-05-22 04:45:04 +00:00
|
|
|
'''
|
|
|
|
screen clearing
|
|
|
|
|
|
|
|
* clears the screen and reprints the banner, plus whatever leftover text to be hilights
|
|
|
|
'''
|
|
|
|
|
2016-05-01 18:10:04 +00:00
|
|
|
os.system("clear")
|
|
|
|
print(BANNER)
|
|
|
|
print(SPACER)
|
|
|
|
if leftover:
|
2017-12-31 16:52:56 +00:00
|
|
|
print("> {leftover}\n".format(leftover=leftover))
|
2016-04-30 03:17:06 +00:00
|
|
|
|
2017-12-31 16:52:56 +00:00
|
|
|
def main():
|
2016-05-22 04:45:04 +00:00
|
|
|
'''
|
|
|
|
main engine head
|
|
|
|
|
|
|
|
* called on program start
|
|
|
|
* calls config check
|
|
|
|
* proceeds to main menu
|
|
|
|
* handles ^c and ^d ejects
|
|
|
|
'''
|
|
|
|
|
2016-05-20 03:35:34 +00:00
|
|
|
redraw()
|
|
|
|
print("""
|
2016-05-15 04:22:05 +00:00
|
|
|
if you don't want to be here at any point, press <ctrl-d> and it'll all go away.
|
|
|
|
just keep in mind that you might lose anything you've started here.\
|
|
|
|
""")
|
|
|
|
|
2016-05-11 03:54:44 +00:00
|
|
|
try:
|
2016-05-20 03:35:34 +00:00
|
|
|
print(check_init())
|
2016-05-11 03:54:44 +00:00
|
|
|
except EOFError:
|
|
|
|
print(stop())
|
2016-05-20 03:35:34 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
redraw()
|
|
|
|
|
|
|
|
while 1:
|
|
|
|
try:
|
|
|
|
print(main_menu())
|
|
|
|
except EOFError:
|
|
|
|
print(stop())
|
|
|
|
break
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
redraw(EJECT)
|
|
|
|
else:
|
|
|
|
break
|
2016-04-30 03:34:43 +00:00
|
|
|
|
|
|
|
def stop():
|
2016-05-22 04:45:04 +00:00
|
|
|
'''
|
2018-01-03 03:28:04 +00:00
|
|
|
returns an exit message.
|
2016-05-22 04:45:04 +00:00
|
|
|
'''
|
|
|
|
|
2016-05-20 03:35:34 +00:00
|
|
|
return "\n\n\t"+chatter.say("bye")+"\n\n"
|
2016-04-30 03:34:43 +00:00
|
|
|
|
|
|
|
def check_init():
|
2016-05-22 04:45:04 +00:00
|
|
|
'''
|
2018-01-03 03:28:04 +00:00
|
|
|
user environment validation
|
2016-05-22 04:45:04 +00:00
|
|
|
|
|
|
|
* checks for presence of ttbprc
|
|
|
|
* checks for last run version
|
|
|
|
'''
|
|
|
|
|
2016-05-20 03:35:34 +00:00
|
|
|
print("\n\n")
|
|
|
|
if os.path.exists(os.path.join(os.path.expanduser("~"),".ttbp")):
|
2018-02-20 00:15:21 +00:00
|
|
|
if config.USER == "endorphant":
|
|
|
|
print("hey boss! :D\n")
|
|
|
|
else:
|
|
|
|
print("{greeting}, {user}".format(greeting=chatter.say("greet"),
|
|
|
|
user=config.USER))
|
2016-10-10 21:53:02 +00:00
|
|
|
|
2018-02-28 22:29:10 +00:00
|
|
|
load_settings = load_user_settings()
|
|
|
|
|
2016-10-10 21:53:02 +00:00
|
|
|
## ttbp env validation
|
2018-01-02 06:21:48 +00:00
|
|
|
if not user_up_to_date():
|
|
|
|
update_user_version()
|
2018-02-25 05:16:37 +00:00
|
|
|
|
2018-02-28 22:29:10 +00:00
|
|
|
if not valid_setup(load_settings):
|
2016-10-10 21:53:02 +00:00
|
|
|
setup_repair()
|
2017-12-31 17:28:42 +00:00
|
|
|
else:
|
2018-02-23 20:28:25 +00:00
|
|
|
input("press <enter> to explore your feels.\n\n")
|
2016-05-20 03:35:34 +00:00
|
|
|
|
2016-05-31 07:20:15 +00:00
|
|
|
core.load(SETTINGS)
|
2016-05-20 03:35:34 +00:00
|
|
|
|
|
|
|
return ""
|
|
|
|
else:
|
|
|
|
return init()
|
2016-04-30 03:34:43 +00:00
|
|
|
|
|
|
|
def init():
|
2018-01-03 03:28:04 +00:00
|
|
|
"""Initializes new user by setting up ~/.ttbp directory and config file.
|
|
|
|
"""
|
2016-05-22 04:45:04 +00:00
|
|
|
|
2016-05-01 23:34:20 +00:00
|
|
|
try:
|
2018-03-23 04:11:40 +00:00
|
|
|
input(config.intro_prompt)
|
2016-05-01 23:34:20 +00:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
print("\n\nthanks for checking in! i'll always be here.\n\n")
|
2016-05-02 15:26:51 +00:00
|
|
|
quit()
|
2016-05-01 23:34:20 +00:00
|
|
|
|
2017-12-31 17:52:23 +00:00
|
|
|
print("\nokay! gimme a second to get you set up!")
|
|
|
|
|
|
|
|
time.sleep(1)
|
|
|
|
print("...")
|
2018-03-22 17:30:32 +00:00
|
|
|
time.sleep(.5)
|
2017-12-31 17:52:23 +00:00
|
|
|
|
2016-05-22 04:45:04 +00:00
|
|
|
## record user in source list
|
2017-11-21 06:02:10 +00:00
|
|
|
users = open(config.USERFILE, 'a')
|
|
|
|
users.write(config.USER+"\n")
|
2016-05-01 23:34:20 +00:00
|
|
|
users.close()
|
2016-05-22 04:45:04 +00:00
|
|
|
|
2017-11-29 17:20:37 +00:00
|
|
|
#subprocess.call(['chmod', 'a+w', config.USERFILE])
|
2017-11-22 05:12:49 +00:00
|
|
|
|
2016-05-22 04:45:04 +00:00
|
|
|
## make .ttbp directory structure
|
2017-12-31 17:52:23 +00:00
|
|
|
print("\ngenerating feels at {path}...".format(path=config.PATH).rstrip())
|
2017-11-21 06:02:10 +00:00
|
|
|
subprocess.call(["mkdir", config.PATH])
|
|
|
|
subprocess.call(["mkdir", config.USER_CONFIG])
|
2018-03-16 02:41:22 +00:00
|
|
|
subprocess.call(["mkdir", config.MAIN_FEELS])
|
2016-05-15 04:22:05 +00:00
|
|
|
|
2017-11-21 06:02:10 +00:00
|
|
|
versionFile = os.path.join(config.PATH, "version")
|
2016-09-08 23:11:34 +00:00
|
|
|
open(versionFile, "w").write(__version__)
|
|
|
|
|
2016-05-22 04:45:04 +00:00
|
|
|
## create header file
|
2016-05-02 03:26:12 +00:00
|
|
|
header = gen_header()
|
2017-11-21 06:02:10 +00:00
|
|
|
headerfile = open(os.path.join(config.USER_CONFIG, "header.txt"), 'w')
|
2016-05-02 03:26:12 +00:00
|
|
|
for line in header:
|
|
|
|
headerfile.write(line)
|
|
|
|
headerfile.close()
|
2016-05-15 04:22:05 +00:00
|
|
|
|
2016-05-22 04:45:04 +00:00
|
|
|
## copy footer and default stylesheet
|
2017-11-21 06:39:38 +00:00
|
|
|
with open(os.path.join(config.USER_CONFIG, 'footer.txt'), 'w') as f:
|
2017-11-21 06:02:10 +00:00
|
|
|
f.write(config.DEFAULT_FOOTER)
|
2017-11-21 06:39:38 +00:00
|
|
|
with open(os.path.join(config.USER_CONFIG, 'style.css'), 'w') as f:
|
2017-11-21 06:02:10 +00:00
|
|
|
f.write(config.DEFAULT_STYLE)
|
2016-05-01 23:34:20 +00:00
|
|
|
|
2016-05-22 04:45:04 +00:00
|
|
|
## run user-interactive setup and load core engine
|
2018-03-22 17:30:32 +00:00
|
|
|
time.sleep(0.5)
|
2017-12-31 17:52:23 +00:00
|
|
|
print("done setting up feels!")
|
|
|
|
print("\nthese are the default settings. you can change any of them now, or change them later at any time!!")
|
2016-05-01 23:34:20 +00:00
|
|
|
setup()
|
2016-05-31 07:20:15 +00:00
|
|
|
core.load(SETTINGS)
|
2016-05-02 03:26:12 +00:00
|
|
|
|
2018-03-23 04:11:40 +00:00
|
|
|
input("""
|
|
|
|
|
|
|
|
you're all good to go, {friend}! if you have any questions about how things
|
|
|
|
work here, check out the documentation from the main menu, ask in IRC, or
|
|
|
|
drop ~endorphant a line!
|
|
|
|
|
|
|
|
hit <enter> to continue.
|
|
|
|
""".format(friend=chatter.say("friend")))
|
|
|
|
|
2016-05-01 17:44:49 +00:00
|
|
|
return ""
|
|
|
|
|
2016-05-02 03:26:12 +00:00
|
|
|
def gen_header():
|
2016-05-22 04:45:04 +00:00
|
|
|
'''
|
|
|
|
header generator
|
|
|
|
|
|
|
|
builds header to insert username
|
|
|
|
'''
|
|
|
|
|
2016-05-15 04:22:05 +00:00
|
|
|
header ="""
|
|
|
|
<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2//EN\">
|
|
|
|
<html>
|
|
|
|
<head>
|
2016-05-22 04:45:04 +00:00
|
|
|
<!--- this header automatically generated by ttbp initialization on """+time.strftime("%Y-%m-%d %h:m")+""" --->
|
2017-11-21 06:02:10 +00:00
|
|
|
<title>~"""+config.USER+""" on TTBP</title>
|
2016-05-15 04:22:05 +00:00
|
|
|
<link rel=\"stylesheet\" href=\"style.css\" />
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div id=\"meta\">
|
2017-11-21 06:02:10 +00:00
|
|
|
<h1><a href=\"index.html#\">~"""+config.USER+"""</a>@<a href=\"/~endorphant/ttbp\">TTBP</a></h1>
|
2016-05-15 04:22:05 +00:00
|
|
|
</div>
|
2016-05-20 03:35:34 +00:00
|
|
|
|
2016-05-15 04:22:05 +00:00
|
|
|
<!---put your custom html here-->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!---don't put anything after this line-->
|
|
|
|
<div id=\"tlogs\">\
|
|
|
|
"""
|
2016-05-02 03:26:12 +00:00
|
|
|
return header
|
|
|
|
|
2018-02-28 22:29:10 +00:00
|
|
|
def valid_setup(load_settings):
|
2016-10-10 21:53:02 +00:00
|
|
|
'''
|
2017-12-31 16:52:56 +00:00
|
|
|
Checks to see if user has a valid ttbp environment.
|
2016-10-10 21:53:02 +00:00
|
|
|
'''
|
|
|
|
|
2018-02-28 22:29:10 +00:00
|
|
|
if not load_settings:
|
2016-10-10 21:53:02 +00:00
|
|
|
return False
|
|
|
|
|
2017-12-31 16:52:56 +00:00
|
|
|
for option in iter(DEFAULT_SETTINGS):
|
|
|
|
if option != "publish dir" and SETTINGS.get(option, None) is None:
|
2016-10-10 21:53:02 +00:00
|
|
|
return False
|
|
|
|
|
2017-12-31 16:52:56 +00:00
|
|
|
if core.publishing():
|
|
|
|
if SETTINGS.get("publish dir", None) is None:
|
|
|
|
print("CONFIG ERROR! publishing is enabled but no directory is set")
|
2016-10-10 21:53:02 +00:00
|
|
|
return False
|
|
|
|
|
2017-12-31 16:52:56 +00:00
|
|
|
if (not os.path.exists(config.WWW) or
|
|
|
|
not os.path.exists(os.path.join(config.PUBLIC,
|
|
|
|
SETTINGS.get("publish dir")))):
|
|
|
|
print("something's weird with your publishing directories. let's try rebuilding them!")
|
|
|
|
|
|
|
|
update_publishing()
|
2017-12-05 04:18:42 +00:00
|
|
|
|
2016-10-10 21:53:02 +00:00
|
|
|
return True
|
|
|
|
|
2018-02-28 22:29:10 +00:00
|
|
|
def load_user_settings():
|
|
|
|
"""attempts to load user's ttbprc; returns settings dict if valie, otherwise
|
|
|
|
returns false"""
|
|
|
|
|
|
|
|
global SETTINGS
|
|
|
|
|
|
|
|
if not os.path.isfile(config.TTBPRC):
|
|
|
|
return False
|
|
|
|
|
|
|
|
try:
|
|
|
|
SETTINGS = json.load(open(config.TTBPRC))
|
|
|
|
except ValueError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
core.load(SETTINGS)
|
|
|
|
|
|
|
|
return SETTINGS
|
|
|
|
|
2016-05-31 07:20:15 +00:00
|
|
|
def setup_repair():
|
2016-05-22 04:45:04 +00:00
|
|
|
'''
|
2016-05-31 07:20:15 +00:00
|
|
|
setup repair function
|
2016-05-22 04:45:04 +00:00
|
|
|
|
|
|
|
* calls setup()
|
|
|
|
* handles ^c
|
|
|
|
'''
|
|
|
|
|
2017-12-31 16:52:56 +00:00
|
|
|
global SETTINGS
|
|
|
|
|
2017-12-31 17:52:23 +00:00
|
|
|
print("\nyour ttbp configuration doesn't look right. let me try to fix it....\n\n")
|
2017-12-31 16:52:56 +00:00
|
|
|
|
2018-01-02 06:21:48 +00:00
|
|
|
time.sleep(1)
|
|
|
|
|
2017-12-31 16:52:56 +00:00
|
|
|
settings_map = {
|
|
|
|
"editor": select_editor,
|
|
|
|
"publishing": select_publishing,
|
|
|
|
"publish dir": select_publish_dir,
|
2018-01-02 06:21:48 +00:00
|
|
|
"gopher": gopher.select_gopher,
|
2018-02-28 22:29:10 +00:00
|
|
|
"rainbows": toggle_rainbows,
|
|
|
|
"post as nopub": toggle_pub_default
|
2017-12-31 16:52:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for option in iter(settings_map):
|
|
|
|
if SETTINGS.get(option, None) is None:
|
|
|
|
SETTINGS.update({option: "NOT SET"})
|
|
|
|
SETTINGS.update({option: settings_map[option]()})
|
|
|
|
|
|
|
|
update_publishing()
|
|
|
|
core.reload_ttbprc(SETTINGS)
|
|
|
|
save_settings()
|
|
|
|
|
2017-12-31 17:52:23 +00:00
|
|
|
print("...")
|
2018-03-22 17:30:32 +00:00
|
|
|
time.sleep(0.5)
|
2018-02-23 20:28:25 +00:00
|
|
|
input("\nyou're all good to go, "+chatter.say("friend")+"! hit <enter> to continue.\n\n")
|
2016-05-01 22:43:12 +00:00
|
|
|
|
2016-05-01 22:26:26 +00:00
|
|
|
def setup():
|
2016-05-22 04:45:04 +00:00
|
|
|
'''
|
|
|
|
master setup function
|
|
|
|
|
|
|
|
* editor selection
|
2017-12-05 04:18:42 +00:00
|
|
|
* publishing toggle (publish/unpublish as needed)
|
2016-05-22 04:45:04 +00:00
|
|
|
* directory selection
|
2017-12-05 04:18:42 +00:00
|
|
|
* gopher opt in/out
|
2016-05-22 04:45:04 +00:00
|
|
|
|
|
|
|
TODO: break this out better?
|
|
|
|
'''
|
|
|
|
|
2016-05-01 22:26:26 +00:00
|
|
|
global SETTINGS
|
|
|
|
|
2017-12-06 03:50:45 +00:00
|
|
|
menuOptions = []
|
|
|
|
settingList = sorted(list(SETTINGS))
|
|
|
|
|
|
|
|
for setting in settingList:
|
|
|
|
menuOptions.append(setting + ": \t" + str(SETTINGS.get(setting)))
|
2018-01-02 06:21:48 +00:00
|
|
|
util.print_menu(menuOptions, SETTINGS.get("rainbows", False))
|
2017-12-06 03:50:45 +00:00
|
|
|
|
|
|
|
try:
|
2018-02-23 20:28:25 +00:00
|
|
|
choice = input("\npick a setting to change (or type 'q' to exit): ")
|
2017-12-06 03:50:45 +00:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
redraw(EJECT)
|
|
|
|
return SETTINGS
|
|
|
|
|
2018-03-23 04:17:33 +00:00
|
|
|
if choice is not "":
|
|
|
|
|
|
|
|
if choice in QUITS:
|
|
|
|
redraw()
|
|
|
|
return SETTINGS
|
|
|
|
|
|
|
|
# editor selection
|
|
|
|
if settingList[int(choice)] == "editor":
|
|
|
|
SETTINGS.update({"editor": select_editor()})
|
|
|
|
redraw("text editor set to: {editor}".format(editor=SETTINGS["editor"]))
|
|
|
|
save_settings()
|
|
|
|
return setup()
|
|
|
|
|
|
|
|
# publishing selection
|
|
|
|
elif settingList[int(choice)] == "publishing":
|
|
|
|
SETTINGS.update({"publishing":select_publishing()})
|
|
|
|
core.reload_ttbprc(SETTINGS)
|
|
|
|
update_publishing()
|
|
|
|
redraw("publishing set to {publishing}".format(publishing=SETTINGS.get("publishing")))
|
|
|
|
save_settings()
|
|
|
|
return setup()
|
|
|
|
|
|
|
|
# publish dir selection
|
|
|
|
elif settingList[int(choice)] == "publish dir":
|
|
|
|
publish_dir = select_publish_dir()
|
|
|
|
SETTINGS.update({"publish dir": publish_dir})
|
|
|
|
#update_publishing()
|
|
|
|
|
|
|
|
if publish_dir is None:
|
|
|
|
redraw("sorry, i can't set a publish directory for you if you don't have html publishing enabled. please enable publishing to continue.")
|
|
|
|
else:
|
|
|
|
redraw("publishing your entries to {url}/index.html".format(
|
|
|
|
url="/".join([config.LIVE+config.USER,
|
|
|
|
str(SETTINGS.get("publish dir"))])))
|
|
|
|
save_settings()
|
|
|
|
return setup()
|
|
|
|
|
|
|
|
# gopher opt-in
|
|
|
|
elif settingList[int(choice)] == "gopher":
|
|
|
|
SETTINGS.update({'gopher': gopher.select_gopher()})
|
|
|
|
redraw('gopher publishing set to: {gopher}'.format(gopher=SETTINGS['gopher']))
|
|
|
|
update_gopher()
|
|
|
|
save_settings()
|
|
|
|
return setup()
|
|
|
|
|
|
|
|
# rainbow menu selection
|
|
|
|
elif settingList[int(choice)] == "rainbows":
|
|
|
|
SETTINGS.update({"rainbows": toggle_rainbows()})
|
|
|
|
redraw("rainbow menus set to {rainbow}".format(rainbow=SETTINGS.get("rainbows")))
|
|
|
|
save_settings()
|
|
|
|
return setup()
|
|
|
|
|
|
|
|
#nopub toggling
|
|
|
|
elif settingList[int(choice)] == "post as nopub":
|
|
|
|
SETTINGS.update({"post as nopub": toggle_pub_default()})
|
|
|
|
redraw("posting default set to {nopub}".format(nopub=SETTINGS.get("post as nopub")))
|
|
|
|
save_settings()
|
|
|
|
return setup()
|
|
|
|
|
|
|
|
input("\nyou're all good to go, {friend}! hit <enter> to continue.\n\n".format(friend=chatter.say("friend")))
|
2017-12-06 03:50:45 +00:00
|
|
|
redraw()
|
2016-05-01 22:43:12 +00:00
|
|
|
|
2018-03-23 04:17:33 +00:00
|
|
|
return SETTINGS
|
2018-01-02 06:21:48 +00:00
|
|
|
|
2018-03-23 04:17:33 +00:00
|
|
|
else:
|
|
|
|
redraw("now changing your settings. press <ctrl-c> if you didn't mean to do this.")
|
2018-02-28 17:23:34 +00:00
|
|
|
return setup()
|
|
|
|
|
2017-12-06 03:50:45 +00:00
|
|
|
def save_settings():
|
|
|
|
"""
|
|
|
|
Save current settings.
|
|
|
|
"""
|
|
|
|
|
|
|
|
ttbprc = open(config.TTBPRC, "w")
|
|
|
|
ttbprc.write(json.dumps(SETTINGS, sort_keys=True, indent=2, separators=(',',':')))
|
|
|
|
ttbprc.close()
|
|
|
|
|
2016-05-01 17:44:49 +00:00
|
|
|
## menus
|
|
|
|
|
2016-04-30 03:17:06 +00:00
|
|
|
def main_menu():
|
2016-05-22 04:45:04 +00:00
|
|
|
'''
|
|
|
|
main navigation menu
|
|
|
|
'''
|
|
|
|
|
2016-05-01 22:26:26 +00:00
|
|
|
menuOptions = [
|
2018-10-13 01:34:10 +00:00
|
|
|
"record some feels",
|
2018-03-12 03:46:15 +00:00
|
|
|
"manage your feels",
|
2016-05-03 17:46:54 +00:00
|
|
|
"check out your neighbors",
|
2016-05-05 00:04:16 +00:00
|
|
|
"browse global feels",
|
2018-10-13 01:34:10 +00:00
|
|
|
"visit your subscriptions",
|
2016-10-10 21:53:02 +00:00
|
|
|
"scribble some graffiti",
|
2016-05-03 17:46:54 +00:00
|
|
|
"change your settings",
|
|
|
|
"send some feedback",
|
2016-05-23 02:42:31 +00:00
|
|
|
"see credits",
|
|
|
|
"read documentation"]
|
2016-05-22 04:45:04 +00:00
|
|
|
|
2016-10-10 21:53:02 +00:00
|
|
|
print("you're at ttbp home. remember, you can always press <ctrl-c> to come back here.\n")
|
2018-01-02 06:21:48 +00:00
|
|
|
util.print_menu(menuOptions, SETTINGS.get("rainbows", False))
|
2016-05-01 17:44:49 +00:00
|
|
|
|
2016-05-01 22:26:26 +00:00
|
|
|
try:
|
2018-02-23 20:28:25 +00:00
|
|
|
choice = input("\ntell me about your feels (or type 'q' to exit): ")
|
2016-05-01 22:26:26 +00:00
|
|
|
except KeyboardInterrupt:
|
2016-05-09 02:59:48 +00:00
|
|
|
redraw(EJECT)
|
2016-05-01 22:26:26 +00:00
|
|
|
return main_menu()
|
2016-05-01 17:44:49 +00:00
|
|
|
|
|
|
|
if choice == '0':
|
2016-05-01 23:34:20 +00:00
|
|
|
redraw()
|
|
|
|
today = time.strftime("%Y%m%d")
|
2018-03-16 02:41:22 +00:00
|
|
|
write_entry(os.path.join(config.MAIN_FEELS, today+".txt"))
|
2016-05-31 07:20:15 +00:00
|
|
|
core.www_neighbors()
|
2016-05-01 17:44:49 +00:00
|
|
|
elif choice == '1':
|
2018-03-13 21:40:09 +00:00
|
|
|
intro = "here are some options for managing your feels:"
|
|
|
|
redraw(intro)
|
|
|
|
review_menu(intro)
|
|
|
|
core.load_files()
|
2016-05-05 00:04:16 +00:00
|
|
|
elif choice == '2':
|
2016-05-31 07:20:15 +00:00
|
|
|
users = core.find_ttbps()
|
2017-12-31 16:52:56 +00:00
|
|
|
prompt = "the following {usercount} {are} recording feels on ttbp:".format(
|
|
|
|
usercount=p.no("user", len(users)),
|
|
|
|
are=p.plural("is", len(users)))
|
2016-11-08 23:38:15 +00:00
|
|
|
redraw(prompt)
|
|
|
|
view_neighbors(users, prompt)
|
2016-05-05 00:04:16 +00:00
|
|
|
elif choice == '3':
|
2016-10-10 21:53:02 +00:00
|
|
|
redraw("most recent global entries")
|
2018-10-13 01:14:28 +00:00
|
|
|
view_global_feed()
|
2016-05-05 00:04:16 +00:00
|
|
|
elif choice == '4':
|
2018-10-14 17:25:07 +00:00
|
|
|
intro = "your subscriptions list is private; no one but you will know who you're following.\n\n> here are some options for your subscriptions:"
|
2018-10-13 01:34:10 +00:00
|
|
|
redraw(intro)
|
|
|
|
subscription_handler(intro)
|
2016-05-31 07:20:15 +00:00
|
|
|
elif choice == '5':
|
2018-10-13 01:34:10 +00:00
|
|
|
graffiti_handler()
|
|
|
|
elif choice == '6':
|
2016-05-23 02:42:31 +00:00
|
|
|
redraw("now changing your settings. press <ctrl-c> if you didn't mean to do this.")
|
2017-12-06 03:50:45 +00:00
|
|
|
core.load(setup()) # reload settings to core
|
2018-10-13 01:34:10 +00:00
|
|
|
elif choice == '7':
|
2016-10-10 21:53:02 +00:00
|
|
|
redraw("you're about to send mail to ~endorphant about ttbp")
|
2016-05-31 07:20:15 +00:00
|
|
|
feedback_menu()
|
2018-10-13 01:34:10 +00:00
|
|
|
elif choice == '8':
|
2016-05-11 03:54:44 +00:00
|
|
|
redraw()
|
|
|
|
show_credits()
|
2018-10-13 01:34:10 +00:00
|
|
|
elif choice == '9':
|
2018-03-23 03:16:45 +00:00
|
|
|
subprocess.call(["lynx", os.path.join(config.INSTALL_PATH, "..", "doc", "manual.html")])
|
2016-05-23 02:42:31 +00:00
|
|
|
redraw()
|
2016-05-09 02:59:48 +00:00
|
|
|
elif choice in QUITS:
|
2016-05-01 17:44:49 +00:00
|
|
|
return stop()
|
|
|
|
else:
|
2016-05-01 18:10:04 +00:00
|
|
|
redraw(INVALID)
|
2016-05-01 17:44:49 +00:00
|
|
|
|
|
|
|
return main_menu()
|
2016-04-30 03:17:06 +00:00
|
|
|
|
2016-05-01 17:44:49 +00:00
|
|
|
def feedback_menu():
|
2016-05-22 04:45:04 +00:00
|
|
|
'''
|
|
|
|
feedback handling menu
|
|
|
|
|
|
|
|
* selects feedback type
|
|
|
|
* calls feedback writing function
|
|
|
|
'''
|
|
|
|
|
2018-01-02 06:21:48 +00:00
|
|
|
util.print_menu(SUBJECTS, SETTINGS.get("rainbows", False))
|
2018-02-23 20:28:25 +00:00
|
|
|
choice = input("\npick a category for your feedback: ")
|
2016-04-30 03:17:06 +00:00
|
|
|
|
2016-05-01 17:44:49 +00:00
|
|
|
cat = ""
|
2016-05-03 17:14:53 +00:00
|
|
|
if choice in ['0', '1', '2', '3']:
|
2016-05-01 22:26:26 +00:00
|
|
|
cat = SUBJECTS[int(choice)]
|
2018-02-23 20:28:25 +00:00
|
|
|
entered = input("""
|
2017-12-31 16:52:56 +00:00
|
|
|
composing a {mail_category} to ~endorphant.
|
2016-05-15 04:22:05 +00:00
|
|
|
|
|
|
|
press <enter> to open an external text editor. mail will be sent once you save and quit.
|
|
|
|
|
2017-12-31 16:52:56 +00:00
|
|
|
""".format(mail_category=cat))
|
2016-05-15 04:22:05 +00:00
|
|
|
redraw(send_feedback(entered, cat))
|
2016-05-01 18:10:04 +00:00
|
|
|
return
|
2016-05-01 17:44:49 +00:00
|
|
|
else:
|
2016-05-01 18:10:04 +00:00
|
|
|
redraw(INVALID)
|
2016-05-01 17:44:49 +00:00
|
|
|
|
|
|
|
return feedback_menu()
|
|
|
|
|
2016-10-10 21:53:02 +00:00
|
|
|
def review_menu(intro=""):
|
|
|
|
'''
|
|
|
|
submenu for reviewing feels.
|
|
|
|
'''
|
|
|
|
|
|
|
|
menuOptions = [
|
|
|
|
"read over feels",
|
2018-03-12 02:30:10 +00:00
|
|
|
"modify feels publishing",
|
|
|
|
"backup your feels",
|
2018-03-17 02:38:01 +00:00
|
|
|
"import a feels backup",
|
2018-03-15 20:41:41 +00:00
|
|
|
"bury some feels",
|
2018-03-12 02:30:10 +00:00
|
|
|
"delete feels by day",
|
2018-03-13 21:40:09 +00:00
|
|
|
"purge all feels",
|
2018-03-16 05:30:48 +00:00
|
|
|
"wipe feels account"
|
2016-10-10 21:53:02 +00:00
|
|
|
]
|
|
|
|
|
2018-01-02 06:21:48 +00:00
|
|
|
util.print_menu(menuOptions, SETTINGS.get("rainbows", False))
|
2016-10-10 21:53:02 +00:00
|
|
|
|
2018-03-16 02:41:22 +00:00
|
|
|
choice = util.list_select(menuOptions, "what would you like to do with your feels? (or 'q' to return home) ")
|
2016-10-10 21:53:02 +00:00
|
|
|
|
2018-03-12 02:30:10 +00:00
|
|
|
top = ""
|
2018-03-16 02:41:22 +00:00
|
|
|
hasfeels = len(os.listdir(config.MAIN_FEELS)) > 0
|
2018-03-13 21:40:09 +00:00
|
|
|
nofeels = "you don't have any feels to work with, "+chatter.say("friend")+"\n\n> "
|
2018-03-12 02:30:10 +00:00
|
|
|
|
2016-10-10 21:53:02 +00:00
|
|
|
if choice is not False:
|
|
|
|
if choice == 0:
|
2018-03-13 21:40:09 +00:00
|
|
|
if hasfeels:
|
|
|
|
redraw("your recorded feels, listed by date:")
|
|
|
|
view_feels(config.USER)
|
|
|
|
else:
|
|
|
|
top = nofeels
|
2016-10-10 21:53:02 +00:00
|
|
|
elif choice == 1:
|
2018-03-13 21:40:09 +00:00
|
|
|
if hasfeels:
|
|
|
|
redraw("publishing status of your feels:")
|
|
|
|
list_nopubs(config.USER)
|
|
|
|
else:
|
|
|
|
top = nofeels
|
2018-03-12 02:30:10 +00:00
|
|
|
elif choice == 2:
|
2018-03-13 21:40:09 +00:00
|
|
|
if hasfeels:
|
|
|
|
redraw("FEELS BACKUP")
|
|
|
|
backup_feels()
|
|
|
|
else:
|
|
|
|
top = nofeels
|
2018-03-12 02:30:10 +00:00
|
|
|
elif choice == 3:
|
2018-03-17 02:38:01 +00:00
|
|
|
redraw("loading feels backup")
|
|
|
|
load_backup()
|
2018-03-15 20:41:41 +00:00
|
|
|
elif choice == 4:
|
2018-03-22 17:46:41 +00:00
|
|
|
if hasfeels:
|
|
|
|
redraw("burying feels")
|
|
|
|
bury_feels()
|
|
|
|
else:
|
|
|
|
top = nofeels
|
2018-03-17 02:38:01 +00:00
|
|
|
elif choice == 5:
|
2018-03-15 20:17:21 +00:00
|
|
|
if hasfeels:
|
|
|
|
redraw("deleting feels")
|
|
|
|
delete_feels()
|
|
|
|
else:
|
|
|
|
top = nofeels
|
2018-03-17 02:38:01 +00:00
|
|
|
elif choice == 6:
|
2018-03-13 21:40:09 +00:00
|
|
|
if hasfeels:
|
|
|
|
redraw("!!!PURGING ALL FEELS!!!")
|
|
|
|
purge_feels()
|
|
|
|
else:
|
|
|
|
top = nofeels
|
2018-03-16 05:30:48 +00:00
|
|
|
elif choice == 7:
|
|
|
|
redraw("!!! WIPING FEELS ACCOUNT !!!")
|
|
|
|
wipe_account()
|
2016-10-10 21:53:02 +00:00
|
|
|
else:
|
|
|
|
redraw()
|
|
|
|
return
|
|
|
|
|
2018-03-12 02:30:10 +00:00
|
|
|
redraw(top+intro)
|
|
|
|
return review_menu(intro)
|
2016-10-10 21:53:02 +00:00
|
|
|
|
2018-10-13 01:34:10 +00:00
|
|
|
def subscription_handler(intro=""):
|
|
|
|
'''
|
|
|
|
submenu for managing subscriptions
|
|
|
|
'''
|
|
|
|
|
2018-10-13 19:29:49 +00:00
|
|
|
if not os.path.exists(config.SUBS):
|
|
|
|
subprocess.call(["touch", config.SUBS])
|
|
|
|
subprocess.call(["chmod", "600", config.SUBS])
|
|
|
|
|
2018-10-13 22:50:44 +00:00
|
|
|
subs_raw = []
|
|
|
|
if os.path.isfile(config.SUBS):
|
|
|
|
for line in open(config.SUBS, "r"):
|
|
|
|
subs_raw.append(line.rstrip())
|
|
|
|
|
|
|
|
subs = []
|
|
|
|
all_users = core.find_ttbps()
|
|
|
|
for name in subs_raw:
|
|
|
|
if name in all_users:
|
|
|
|
subs.append(name)
|
|
|
|
|
2018-10-13 01:34:10 +00:00
|
|
|
menuOptions = [
|
|
|
|
"view subscribed feed",
|
|
|
|
"manage subscriptions"
|
|
|
|
]
|
|
|
|
|
|
|
|
util.print_menu(menuOptions, SETTINGS.get("rainbows", False))
|
|
|
|
|
|
|
|
choice = util.list_select(menuOptions, "what would you like to do with your subscriptions? (or 'q' to return home) ")
|
|
|
|
|
|
|
|
top = ""
|
|
|
|
|
|
|
|
if choice is not False:
|
|
|
|
if choice == 0:
|
2018-10-13 22:50:44 +00:00
|
|
|
if len(subs) > 0:
|
2018-10-14 19:00:51 +00:00
|
|
|
prompt = "most recent entries from your subscribed pals:"
|
|
|
|
redraw(prompt)
|
|
|
|
view_subscribed_feed(subs, prompt)
|
2018-10-13 22:50:44 +00:00
|
|
|
else:
|
|
|
|
intro = "it doesn't look like you have any subscriptions to see! add pals with 'manage subscriptions' here."
|
2018-10-13 01:34:10 +00:00
|
|
|
elif choice == 1:
|
2018-10-14 17:16:46 +00:00
|
|
|
prompt = "options for managing your subscriptions:"
|
|
|
|
redraw(prompt)
|
|
|
|
subscription_manager(subs, prompt)
|
2018-10-13 01:34:10 +00:00
|
|
|
else:
|
|
|
|
redraw()
|
|
|
|
return
|
|
|
|
|
|
|
|
redraw(top+intro)
|
|
|
|
return subscription_handler(intro)
|
|
|
|
|
2019-04-28 16:08:47 +00:00
|
|
|
def view_neighbors(users, prompt, page=0):
|
2016-05-23 02:42:31 +00:00
|
|
|
'''
|
|
|
|
generates list of all users on ttbp, sorted by most recent post
|
|
|
|
|
|
|
|
* if user is publishing, list publish directory
|
|
|
|
'''
|
2016-05-02 15:26:51 +00:00
|
|
|
|
2016-05-03 17:52:07 +00:00
|
|
|
userList = []
|
|
|
|
|
2016-05-23 02:42:31 +00:00
|
|
|
## assumes list of users passed in all have valid config files
|
2016-05-02 15:26:51 +00:00
|
|
|
for user in users:
|
|
|
|
userRC = json.load(open(os.path.join("/home", user, ".ttbp", "config", "ttbprc")))
|
2016-05-23 02:42:31 +00:00
|
|
|
|
|
|
|
## retrieve publishing url, if it exists
|
2016-05-23 00:52:27 +00:00
|
|
|
url="\t\t\t"
|
2016-05-20 15:06:42 +00:00
|
|
|
if userRC.get("publish dir"):
|
2017-11-21 06:02:10 +00:00
|
|
|
url = config.LIVE+user+"/"+userRC.get("publish dir")
|
2016-05-23 02:42:31 +00:00
|
|
|
|
|
|
|
## find last entry
|
2018-02-25 05:16:37 +00:00
|
|
|
try:
|
|
|
|
files = os.listdir(os.path.join("/home", user, ".ttbp", "entries"))
|
|
|
|
except OSError:
|
|
|
|
files = []
|
2016-05-09 02:59:48 +00:00
|
|
|
files.sort()
|
2016-05-23 02:42:31 +00:00
|
|
|
lastfile = ""
|
2016-05-09 02:59:48 +00:00
|
|
|
for filename in files:
|
2016-05-11 04:10:04 +00:00
|
|
|
if core.valid(filename):
|
2016-05-09 02:59:48 +00:00
|
|
|
lastfile = os.path.join("/home", user, ".ttbp", "entries", filename)
|
|
|
|
|
2016-05-23 02:42:31 +00:00
|
|
|
## generate human-friendly timestamp
|
2016-05-10 16:14:53 +00:00
|
|
|
ago = "never"
|
2016-05-09 02:59:48 +00:00
|
|
|
if lastfile:
|
|
|
|
last = os.path.getctime(lastfile)
|
2016-05-10 16:14:53 +00:00
|
|
|
since = time.time()-last
|
|
|
|
ago = util.pretty_time(int(since)) + " ago"
|
2016-05-09 02:59:48 +00:00
|
|
|
else:
|
|
|
|
last = 0
|
|
|
|
|
2016-05-23 02:42:31 +00:00
|
|
|
## some formatting handwavin
|
|
|
|
urlpad = ""
|
|
|
|
if ago == "never":
|
|
|
|
urlpad = "\t"
|
2016-05-03 17:52:07 +00:00
|
|
|
|
2016-05-23 02:42:31 +00:00
|
|
|
userpad = ""
|
|
|
|
if len(user) < 7:
|
|
|
|
userpad = "\t"
|
2016-05-09 02:59:48 +00:00
|
|
|
|
2017-12-31 16:52:56 +00:00
|
|
|
userList.append(["\t~{user}{userpad}\t({ago}){urlpad}\t{url}".format(user=user,
|
|
|
|
userpad=userpad, ago=ago, urlpad=urlpad, url=url), last, user])
|
2016-05-23 02:42:31 +00:00
|
|
|
|
|
|
|
|