Merge branch 'client-init' of cmccabe/linkulator2 into master

This commit is contained in:
cmccabe 2019-11-22 07:44:19 -05:00 committed by Gitea
commit 91cf9fe8c6
1 changed files with 134 additions and 56 deletions

View File

@ -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()