Merge branch 'client-init' of cmccabe/linkulator2 into master
This commit is contained in:
commit
91cf9fe8c6
190
linkulator
190
linkulator
|
@ -2,15 +2,16 @@
|
|||
|
||||
## If this script contains bugs, blame cmccabe.
|
||||
|
||||
import glob
|
||||
import getpass
|
||||
import glob
|
||||
import os
|
||||
import signal
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
from shutil import which
|
||||
import signal
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
username = getpass.getuser()
|
||||
|
||||
|
@ -32,12 +33,12 @@ A few important points about Linkulator:
|
|||
to deceive others.
|
||||
* Link/reply threads disappear if the original link poster deletes their post; or if you
|
||||
put their username in your ignore file.
|
||||
* Your ~/.linkulator/linkulator_data file must be readable by others, or nobody else will
|
||||
* Your ~/.linkulator/linkulator.data file must be readable by others, or nobody else will
|
||||
see your contributions.
|
||||
* Linkulator may not work outside of Linux systems.
|
||||
"""
|
||||
|
||||
pipe_count = 4 ## A PROPERLY FORMATED LINE IN linkulator.data HAS EXACTLY FOUR PIPES.
|
||||
pipe_count = 4 ## A PROPERLY FORMATED LINE IN linkulator.data HAS EXACTLY FOUR PIPES.
|
||||
|
||||
link_data = []
|
||||
## username, datestamp, parent-id, category, link-url, link-title
|
||||
|
@ -50,7 +51,7 @@ ignore_names = []
|
|||
# IGNORE NAMES.
|
||||
def parse_ignore_file():
|
||||
global ignore_names
|
||||
p = Path(Path.home(), '.linkulator/ignore')
|
||||
p = Path(Path.home(), ".linkulator/ignore")
|
||||
if p.exists():
|
||||
s = p.read_text()
|
||||
l = s.splitlines()
|
||||
|
@ -63,7 +64,7 @@ def build_menu():
|
|||
## WHENEVER THIS FUNCTION IS CALLED, THE DATA IS REFRESHED FROM FILES. SINCE
|
||||
## DISK IO IS PROBABLY THE HEAVIEST PART OF THIS SCRIPT, DON'T DO THIS OFTEN.
|
||||
|
||||
linkulator_files = glob.glob('/home/*/.linkulator/linkulator.data')
|
||||
linkulator_files = glob.glob("/home/*/.linkulator/linkulator.data")
|
||||
|
||||
if len(linkulator_files) == 0:
|
||||
print("It looks link there are no links yet. Run 'linkulator -p' to add one.")
|
||||
|
@ -75,24 +76,24 @@ def build_menu():
|
|||
linkulator_lines = []
|
||||
for filename in linkulator_files:
|
||||
with open(filename) as f:
|
||||
file_owner = filename.split('/')[2]
|
||||
file_owner = filename.split("/")[2]
|
||||
if file_owner in ignore_names:
|
||||
continue ## IGNORE NAMES IN ignore_file
|
||||
continue ## IGNORE NAMES IN ignore_file
|
||||
for line in f:
|
||||
if line.count("|") != pipe_count:
|
||||
continue ## IGNORE LINES THAT AREN'T FORMATTED PROPERLY.
|
||||
line = line.rstrip('\n')
|
||||
split_line = line.split('|')
|
||||
split_line.insert(0,file_owner)
|
||||
linkulator_lines.append(split_line) ## creating a list of lists
|
||||
continue ## IGNORE LINES THAT AREN'T FORMATTED PROPERLY.
|
||||
line = line.rstrip("\n")
|
||||
split_line = line.split("|")
|
||||
split_line.insert(0, file_owner)
|
||||
linkulator_lines.append(split_line) ## creating a list of lists
|
||||
|
||||
i=1
|
||||
i = 1
|
||||
for idx, line in enumerate(linkulator_lines):
|
||||
if line[2] == "": # CREATE/INSERT PARENT ID:
|
||||
if line[2] == "": # CREATE/INSERT PARENT ID:
|
||||
linkulator_lines[idx].insert(0, i)
|
||||
i=i+1
|
||||
else: ## NOT PARENT, SO NO PARENT ID
|
||||
linkulator_lines[idx].insert(0,"")
|
||||
i = i + 1
|
||||
else: ## NOT PARENT, SO NO PARENT ID
|
||||
linkulator_lines[idx].insert(0, "")
|
||||
link_data = linkulator_lines
|
||||
|
||||
## THIS IS SUPPOSED TO SORT ALL LINKS BY CREATION DATE. NEED TO CONFIRM THAT IT WORKS.
|
||||
|
@ -101,7 +102,7 @@ def build_menu():
|
|||
global categories
|
||||
global category_counts
|
||||
categories = []
|
||||
category_counts.clear() ## CLEAR SO WE DON'T DOUBLE-COUNT IF FNC RUN MORE THAN ONCE.
|
||||
category_counts.clear() ## CLEAR SO WE DON'T DOUBLE-COUNT IF FNC RUN MORE THAN ONCE.
|
||||
for line in link_data:
|
||||
if line[4] not in categories and line[4] != "":
|
||||
categories.append(line[4])
|
||||
|
@ -114,7 +115,7 @@ def print_categories():
|
|||
print("Current link post categories include: ")
|
||||
for i in categories:
|
||||
print(" * " + i + "(" + str(category_counts[i]) + ")")
|
||||
# print(categories)
|
||||
# print(categories)
|
||||
view_category_contents()
|
||||
|
||||
|
||||
|
@ -131,12 +132,14 @@ def view_category_contents():
|
|||
if line[4] == view_cat:
|
||||
print("ID:", line[0], "|", line[6], "|", line[5], "|", line[1])
|
||||
|
||||
pid = input("Enter a post ID to see its thread, \"m\" to return to the main menu, or hit [Enter] to quit: ")
|
||||
if pid == "": ## HARMLESS BUT UNINTENDED
|
||||
graceful_exit() ## ABILITY HERE IS THAT USERS
|
||||
elif pid =="m" or pid == "M": ## CAN PUT ANY PID IN, NOT JUST
|
||||
print_categories() ## FROM WITHIN THIS CATEGORY.
|
||||
return()
|
||||
pid = input(
|
||||
'Enter a post ID to see its thread, "m" to return to the main menu, or hit [Enter] to quit: '
|
||||
)
|
||||
if pid == "": ## HARMLESS BUT UNINTENDED
|
||||
graceful_exit() ## ABILITY HERE IS THAT USERS
|
||||
elif pid == "m" or pid == "M": ## CAN PUT ANY PID IN, NOT JUST
|
||||
print_categories() ## FROM WITHIN THIS CATEGORY.
|
||||
return ()
|
||||
else:
|
||||
view_thread(pid)
|
||||
|
||||
|
@ -154,25 +157,34 @@ def view_thread(post_id):
|
|||
|
||||
if parent_id == "":
|
||||
print("Sorry, no thread found with that ID.")
|
||||
print_categories() ## THIS IS NOT A GOOD END POINT. SHOULD ASK USER TO RE-ENTER THEIR CHOICE.
|
||||
print_categories() ## THIS IS NOT A GOOD END POINT. SHOULD ASK USER TO RE-ENTER THEIR CHOICE.
|
||||
for line in link_data:
|
||||
if line[1] == parent_user and line[2] == parent_timestamp:
|
||||
ftime = time.strftime("%b %d %Y", time.gmtime(float(parent_timestamp))) ## UGGHH...
|
||||
print("\nLink: ", line[6] + " (" + line[5] + "),\n posted by " + line[1] + " on " + ftime)
|
||||
ftime = time.strftime(
|
||||
"%b %d %Y", time.gmtime(float(parent_timestamp))
|
||||
) ## UGGHH...
|
||||
print(
|
||||
"\nLink: ",
|
||||
line[6] + " (" + line[5] + "),\n posted by " + line[1] + " on " + ftime,
|
||||
)
|
||||
|
||||
print("\nReplies:")
|
||||
i = 0
|
||||
for line in link_data:
|
||||
if line[3] == parent_id:
|
||||
print(line[1] + ": " + line[6])
|
||||
i = i+1
|
||||
i = i + 1
|
||||
if i == 0:
|
||||
print("(No replies yet. Be the first!)\n")
|
||||
|
||||
next_step = input("Type 'R' to reply, 'B' to view in " + browser + ", 'M' for main menu, or anything else to quit: ")
|
||||
next_step = input(
|
||||
"Type 'R' to reply, 'B' to view in "
|
||||
+ browser
|
||||
+ ", 'M' for main menu, or anything else to quit: "
|
||||
)
|
||||
if next_step == "M" or next_step == "m":
|
||||
print_categories()
|
||||
elif next_step =="B" or next_step == "b":
|
||||
elif next_step == "B" or next_step == "b":
|
||||
view_link_in_browser(url, post_id)
|
||||
elif next_step == "R" or next_step == "r":
|
||||
reply(parent_user, parent_timestamp, post_id)
|
||||
|
@ -182,16 +194,24 @@ def view_thread(post_id):
|
|||
|
||||
def view_link_in_browser(url, post_id):
|
||||
if which(browser) is None:
|
||||
print("Sorry, " + browser + " is not installed on your system. Ask your sysadmin to install it.")
|
||||
print(
|
||||
"Sorry, "
|
||||
+ browser
|
||||
+ " is not installed on your system. Ask your sysadmin to install it."
|
||||
)
|
||||
view_thread(post_id)
|
||||
|
||||
if url.startswith("gopher://") or url.startswith("https://") or url.startswith("http://"):
|
||||
subprocess.call(['lynx', url])
|
||||
if (
|
||||
url.startswith("gopher://")
|
||||
or url.startswith("https://")
|
||||
or url.startswith("http://")
|
||||
):
|
||||
subprocess.call(["lynx", url])
|
||||
else:
|
||||
print("Sorry, that url doesn't start with gopher://, http:// or https://")
|
||||
tryAnyway = input("Do you want to try it in", browser, "anyway? Y/[N] ")
|
||||
if tryAnyway == "Y" or tryAnyway == "y":
|
||||
subprocess.call(['lynx', url])
|
||||
subprocess.call(["lynx", url])
|
||||
view_thread(post_id)
|
||||
|
||||
|
||||
|
@ -202,16 +222,20 @@ def post_link():
|
|||
while link_url == "":
|
||||
link_url = input("URL: ")
|
||||
if "|" in link_url:
|
||||
print("Pipes, \"|\", are illegal characters in Linkulator. Please try again.")
|
||||
print(
|
||||
'Pipes, "|", are illegal characters in Linkulator. Please try again.'
|
||||
)
|
||||
link_url = ""
|
||||
elif link_url == "":
|
||||
graceful_exit()
|
||||
|
||||
|
||||
link_category = ""
|
||||
while link_category == "":
|
||||
link_category = input("Category: ")
|
||||
if "|" in link_category:
|
||||
print("Pipes, \"|\", are illegal characters in Linkulator. Please try again.")
|
||||
print(
|
||||
'Pipes, "|", are illegal characters in Linkulator. Please try again.'
|
||||
)
|
||||
link_category = ""
|
||||
elif link_category == "":
|
||||
graceful_exit()
|
||||
|
@ -220,45 +244,50 @@ def post_link():
|
|||
while link_title == "":
|
||||
link_title = input("Title: ")
|
||||
if "|" in link_title:
|
||||
print("Pipes, \"|\", are illegal characters in Linkulator. Please try again.")
|
||||
print(
|
||||
'Pipes, "|", are illegal characters in Linkulator. Please try again.'
|
||||
)
|
||||
link_title = ""
|
||||
elif link_title == "":
|
||||
graceful_exit()
|
||||
|
||||
timestamp = str(time.time())
|
||||
filename = '/home/' + username + '/.linkulator/linkulator.data'
|
||||
filename = "/home/" + username + "/.linkulator/linkulator.data"
|
||||
if os.path.exists(filename):
|
||||
append_write = 'a' # append if already exists
|
||||
append_write = "a" # append if already exists
|
||||
else:
|
||||
append_write = 'w+' # make a new file if not
|
||||
append_write = "w+" # make a new file if not
|
||||
with open(filename, append_write) as file:
|
||||
file.write(timestamp + '||' + link_category + '|' + link_url + '|' + link_title + "\n")
|
||||
file.write(
|
||||
timestamp + "||" + link_category + "|" + link_url + "|" + link_title + "\n"
|
||||
)
|
||||
print("Link added!")
|
||||
graceful_exit()
|
||||
|
||||
|
||||
|
||||
def reply(owner, tstamp, post_id):
|
||||
global username
|
||||
|
||||
comment = input("Enter your comment: ")
|
||||
|
||||
filename = '/home/' + username + '/.linkulator/linkulator.data'
|
||||
filename = "/home/" + username + "/.linkulator/linkulator.data"
|
||||
if os.path.exists(filename):
|
||||
append_write = 'a' # append if already exists
|
||||
append_write = "a" # append if already exists
|
||||
else:
|
||||
append_write = 'w+' # make a new file if not
|
||||
append_write = "w+" # make a new file if not
|
||||
with open(filename, append_write) as file:
|
||||
timestamp = str(time.time())
|
||||
file.write(timestamp + '|' + owner + "+" + tstamp + '|||' + comment + "\r")
|
||||
timestamp = str(time.time())
|
||||
file.write(timestamp + "|" + owner + "+" + tstamp + "|||" + comment + "\r")
|
||||
|
||||
x = input('Reply added. Hit [Enter] to return to thread.')
|
||||
x = input("Reply added. Hit [Enter] to return to thread.")
|
||||
build_menu()
|
||||
view_thread(post_id)
|
||||
|
||||
|
||||
|
||||
def search(keyword):
|
||||
print("Doesn't work yet. Would be searching title, category, comment for ", keyword)
|
||||
|
||||
|
||||
## PSEUDOCODE:
|
||||
## results_found = ""
|
||||
## for line in link_data:
|
||||
|
@ -275,6 +304,52 @@ def search(keyword):
|
|||
## next_step = input("Enter ID to view thread, "M" for main menu, or [Enter] to quit: ")
|
||||
## if next_step...
|
||||
|
||||
|
||||
def is_readable(st_mode: int) -> bool:
|
||||
"""Checks the provided mode is group and other readable, returns true if this is the case
|
||||
|
||||
Check if 700 is readable:
|
||||
>>> is_readable(16832)
|
||||
False
|
||||
|
||||
Check if 755 is readable:
|
||||
>>> is_readable(16877)
|
||||
True
|
||||
"""
|
||||
if bool(st_mode & stat.S_IRGRP) & bool(st_mode & stat.S_IROTH):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def init():
|
||||
"""Performs startup checks to ensure environment is set up for use
|
||||
|
||||
Creates necessary data directory and data file. If they exist, no error
|
||||
occurs.
|
||||
Checks that the data directory and data file are group and other readable.
|
||||
Sets some correct permissions if they are not.
|
||||
Other errors may raise an exception.
|
||||
"""
|
||||
dir_p = Path(Path.home(), ".linkulator")
|
||||
file_p = Path(dir_p, "linkulator.data")
|
||||
|
||||
dir_p.mkdir(mode=0o755, exist_ok=True)
|
||||
file_p.touch(mode=0o644, exist_ok=True)
|
||||
|
||||
if not is_readable(dir_p.stat().st_mode):
|
||||
print(
|
||||
"Warning: %s is not group or other readable - changing permissions"
|
||||
% str(dir_p)
|
||||
)
|
||||
dir_p.chmod(0o755)
|
||||
if not is_readable(file_p.stat().st_mode):
|
||||
print(
|
||||
"Warning: %s is not group or other readable - changing permissions"
|
||||
% str(file_p)
|
||||
)
|
||||
file_p.chmod(0o644)
|
||||
|
||||
|
||||
def graceful_exit():
|
||||
print("\n\nThank you for linkulating. Goodbye.\n")
|
||||
exit(0)
|
||||
|
@ -282,11 +357,14 @@ def graceful_exit():
|
|||
|
||||
def signal_handler(sig, frame):
|
||||
graceful_exit()
|
||||
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
|
||||
def parse_command():
|
||||
args = sys.argv[1:]
|
||||
init()
|
||||
if not len(args):
|
||||
print("----------")
|
||||
print("LINKULATOR")
|
||||
|
@ -302,5 +380,5 @@ def parse_command():
|
|||
print("Unknown command: {}".format(args[0]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
parse_command()
|
||||
|
|
Loading…
Reference in New Issue