aboutsummaryrefslogblamecommitdiff
path: root/telem.lua
blob: 140c930a55744e0f3cff14a97d502533aa48d6a2 (plain) (tree)
1
2
3
4
5
6
7
8
                 
                              
 
                         
                  
                    

                         


                          

                       




                          
                                                                   

                            










                                 
                                                                         
                                                         















                                                     
                                                                      


                                                
                                                                                       
                                                                                          

                                                              
                                                                        
                                                         




















                                                                           


                                                                       
                                                                  





                                                                                        
                   

                                         




                                                                             



                                      
                                                   
                                       



                                                                   
                                 

                           
                                                         











                                                  
                
                                       
                                       


                                        
                                                                





                                     
                                                      


                   
                                                       

   



                                                              


                                                                             
                                                                          


















                                                                                                                

                                   
                                          


                                     
                             
                           



                                                                       






                                                 
                                                                               














                                                                                     



                                
                           

   




                                                    



                                                               
                           





                                                               
                           


                        










                                                          
                           



                                  
                                 

                                 
                                  


                                          
                                          




                                                                                   
                                                          
                                               

                                                    
                           












                                               
                                                                        

   

                                                               



                                                                           
                               
                                                 
                                                                                                            





                         




                                          
                                           




                                                                                   






                                                          
                   
                                                       





                      
                 
                                

   




                            
                                                       

















                                                  
                      


                                
                      
                        
                        
                                











                                    
#!/usr/bin/lua5.2
telemver = "0.3 // 2018-05-04"

file = require("pl.file")
io = require("io")
lfs = require("lfs")
os = require("os")
path = require("pl.path")
string = require("string")
table = require("table")

_BBS_ROOT = "/var/bbs/"

bad_topics = {}
bad_topics["."] = true
bad_topics[".."] = true
bad_topics["topic"] = true

-- TODO: This is, obviously, not secure and will need to be updated
username = os.getenv("USER")

current_board = ""
current_thread_index = nil

function cat_file(filename)
	io.input(filename)
	welcome = io.read("*all")
	io.input(io.stdin)
	io.write(welcome)
end

function show_welcome()
	print("::::::::::: This is telem ver."..telemver.." :::::::::::")
	cat_file(path.join(_BBS_ROOT, "docs", "welcome"))
end

function show_prompt()
	io.write("["..current_board.."] COMMAND :> ")
end

function getchar()
	local char
	os.execute("/bin/stty -icanon")
	char = io.read(1)
	os.execute("/bin/stty icanon")
	return char
end

function get_threads(board)
	local threads = {}
	for topic in lfs.dir(path.join(_BBS_ROOT, "boards", board)) do
		if bad_topics[topic] == nil then
			thread = {}
			thread.filename = topic
			thread.directory = path.join(_BBS_ROOT, "boards", board, topic)
			_, _, timestamp, thread.author = string.find(topic, "(%d+)-(%g+)")
			thread.timestamp = tonumber(timestamp)

			io.input(path.join(thread.directory, "subject"))
			thread.subject = io.read("*line")
			io.input(io.stdin)

			posts = get_posts(thread)
			thread.post_count = #posts
			thread.updated = 0
			for _, post in ipairs(posts) do
				if post.timestamp > thread.updated then
					thread.updated = post.timestamp
				end
			end
			table.insert(threads, thread)
			
		end
	end
	table.sort(threads, function(x,y) return x.updated > y.updated end)
	return threads
end

function get_posts(thread)
	local posts = {}
	for reply in lfs.dir(thread.directory) do
		if string.sub(reply, 1,1) == "." then goto continue end
		if reply == "subject" then goto continue end
		post = {}
		post.filename = path.join(thread.directory, reply)
		if reply == "original" then
			post.author = thread.author
			post.timestamp = thread.timestamp
		else
			_, _, timestamp, post.author = string.find(reply, "(%d+)-(%a+)")
			post.timestamp = tonumber(timestamp)
		end
		table.insert(posts, post)
		::continue::
	end
	table.sort(posts, function(x,y) return x.timestamp < y.timestamp end)
	return posts
end

function do_board()
	-- Creates a new (empty) board

	-- Get details
	io.write("New board name? (Max 18 chars) ")
	board = string.upper(io.read())
	if string.len(board) > 18 then
		print("Board names must be 18 characters or less!")
		return
	end
	io.write("Description? ")
	desc = io.read()
	-- Create directory
	board_dir = path.join(_BBS_ROOT, "boards", board)
	lfs.mkdir(board_dir)
	-- Write topic file
	topic_file = path.join(board_dir, "topic")
	file.write(topic_file, desc)
	-- Update representation of BBS
	boards[board] = true
	table.insert(board_names, board)
	table.sort(board_names)
	-- Done!
	print("Board created.")
end

function do_go()
	io.write("Go to which board? ")
	board = string.upper(io.read())
	if board == "" then
		do_list()
	elseif boards[board] == nil then
		print("No such board!  Hit `l` to list boards.")
	else
		current_board = board
	end
end

function do_help()
	cat_file(path.join(_BBS_ROOT, "docs", "help"))
end

function do_help2()
	cat_file(path.join(_BBS_ROOT, "docs", "help2"))
end

function do_unimplemented()
	print("Sorry, this command is not (yet) implemented!")
end

function do_list()
	for _,b in pairs(board_names) do
		posts = -3 -- Don't want to count "topic" file or "." or ".."
		for topic in lfs.dir(path.join(_BBS_ROOT, "boards", b)) do
			posts = posts +1
		end
		print(string.format("%s    [%d posts]", b, posts))
	end
end

function do_messages()
	if current_board == "" then
		print("Not at any board")
	else
		threads = get_threads(current_board)
		for i, thread in ipairs(threads) do
			print(tostring(i), os.date("%x %H:%M", thread.timestamp), thread.author, thread.subject)
--, tostring(thread.post_count))
		end
	end
	current_thread_index = threads
end

function do_new()
	if current_board == "" then
		print("Not at any board!")
		return
	end
	-- Get subject for new thread
	io.write("Subject? ")
	subject = io.read()
	if string.len(subject) > 48 then
		print("Thread subjects must be 48 characters or less!")
		return
	end
	-- Save body of post to temp file
	filename = os.tmpname()
	os.execute("$EDITOR " .. filename)
	-- TODO: show and confirm
	-- Make thread dir
	timestamp = tostring(os.time())
	thread_dir = timestamp .. "-" .. username
	thread_path = path.join(_BBS_ROOT, "boards", current_board, thread_dir)
	lfs.mkdir(thread_path)
	-- Write subject file
	file.write(path.join(thread_path, "subject"),
		subject)
	-- Move post file
	post_file = thread_dir -- first post and thread directory names are the same!
	newpath = path.join(thread_path, post_file)
	ret, str = file.move(filename, newpath)
	if not ret then
		print(str)
	end
	-- Done!
	print("Post submitted.")
end

-- Type stuff below

function do_type_first()
	current_thread_index = 1
	do_type_show_post()
end

function do_type_last()
	current_thread_index = #current_thread_posts
	do_type_show_post()
end

function do_type_next()
	if current_thread_index ~= #current_thread_posts then
		current_thread_index = current_thread_index + 1
	end
	do_type_show_post()
end

function do_type_prev()
	if current_thread_index ~= 1 then
		current_thread_index = current_thread_index - 1
	end
	do_type_show_post()
end

function do_type_reply()
	filename = os.tmpname()
	os.execute("$EDITOR " .. filename)
	timestamp = tostring(os.time())
	newfilename = timestamp .. "-" .. username
	newpath = path.join(thread.directory, newfilename)
	ret, str = file.move(filename, newpath)
	if not ret then
		print(str)
	end
	current_thread_posts = get_posts(thread)
	current_thread_index = #current_thread_posts
	do_type_show_post()
end

type_dispatch = {}
type_dispatch["f"] = do_type_first
type_dispatch["l"] = do_type_last
type_dispatch["n"] = do_type_next
type_dispatch["p"] = do_type_prev
type_dispatch["r"] = do_type_reply
type_dispatch["d"] = function() return end

function do_type()
	io.write("Display which thread? ")
	local thread_id = tonumber(io.read())
	if not thread_id or thread_id < 0 or thread_id > #current_thread_index then
		print("Invalid thread index!")
		return
	end
	thread = current_thread_index[tonumber(thread_id)]
	current_thread_subject = thread.subject
	current_thread_posts = get_posts(thread)
	current_thread_index = #current_thread_posts
	do_type_show_post()
	repeat
		show_type_prompt()
		c = getchar()
		io.write("\n")
		if type_dispatch[c] == nil then
			print("Eh?")
		else	
			type_dispatch[c]()
		end
	until c == "d"
end

function show_type_prompt()
	io.write("[f]irst, [n]ext, [p]rev, [l]last, [r]eply, [d]one > ")
end

function do_type_show_post()
	local post = current_thread_posts[current_thread_index]
	print("SUBJECT:    " .. current_thread_subject)
	print("AUTHOR:     " .. post.author)
	print("POSTED:     " .. os.date("%H:%M %B %d, %Y", post.timestamp))
	print("--------------------------------")
	cat_file(post.filename)
	print("--------------------------------")
	print(string.format("Viewing post %d of %d in thread", current_thread_index, #current_thread_posts))
end

function do_quit()
	print("Goodbye!")
end

function do_reply()
	if current_board == "" then
		print("Not at any board!")
		return
	end
	io.write("Reply to which thread? ")
	local thread_id = tonumber(io.read())
	if not thread_id or thread_id < 0 or thread_id > #current_thread_index then
		print("Invalid thread index!")
		return
	end
	thread = current_thread_index[tonumber(thread_id)]
	current_thread_subject = thread.subject
	current_thread_posts = get_posts(thread)
	current_thread_index = #current_thread_posts
	do_type_reply()
end

function do_rules()
	cat_file(path.join(_BBS_ROOT, "docs", "rules"))
end

function do_stub()
	print("Stub!")
end

function do_who()
	os.execute("/usr/bin/w")
end

-- MAIN PROGRAM BODY BELOW

show_welcome()

boards, board_names = {}, {}
for board in lfs.dir(path.join(_BBS_ROOT, "boards")) do
	if string.sub(board, 1, 1) ~= "." then
		boards[board] = true
		table.insert(board_names, board)
	end
end
table.sort(board_names)
message_index = {}

-- Build dispatch table mapping chars to functions
dispatch = {}
dispatch["h"] = do_help
dispatch["g"] = do_go
dispatch["l"] = do_list
dispatch["m"] = do_messages
dispatch["q"] = do_quit
dispatch["t"] = do_type
dispatch["?"] = do_help2
dispatch["!"] = do_rules
dispatch["w"] = do_who
dispatch["s"] = do_unimplemented
dispatch["="] = do_unimplemented
dispatch["M"] = do_unimplemented
dispatch["n"] = do_new
dispatch["r"] = do_reply
dispatch["b"] = do_board
dispatch["c"] = do_unimplemented

-- Infinite loop of command dispatch
repeat
	show_prompt()
	c = getchar()
	io.write("\n")
	if dispatch[c] == nil then
		print("What?")
	else	
		dispatch[c]()
	end
until c == "q"