Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Modular Chatbox Command Interpreter
- Made by LDDestroier
- This version uses native Coroutines API instead of StateMachine.
- pastebin get 5tJM4Bf8 mcci
- --]]
- local progname = fs.getName(shell.getRunningProgram())
- local baseDir = ".mcci" --all other directories are combined with this one, you see
- local commandDir = fs.combine(baseDir,"cmd") --a folder
- local commandHelpDir = fs.combine(baseDir,"cmdhelp") --a folder
- local chatlogDir = fs.combine(baseDir,"chatlog") --a file
- local banDir = fs.combine(baseDir,"banlist")
- local adminDir = fs.combine(baseDir,"adminlist")
- local configDir = fs.combine(baseDir,"config")
- local chatEvents = {"chat_message","chatbox_command","chat"}
- local _
- local useMoarPeriphs = (peripheral.find("chatbox_admin") or peripheral.find("chatbox") or peripheral.find("chat_box")) and true or false --either moarperipherals/computronics xor peripherals++
- if not fs.exists(commandDir) then fs.makeDir(commandDir) end
- if not fs.exists(commandHelpDir) then fs.makeDir(commandHelpDir) end
- local adminUserTable = { --only one who can use admin commands. default values.
- "EldidiStroyrr",
- "Notch",
- }
- if pairs ~= _G.pairs then
- pairs = function(tbl)
- if type(tbl) ~= "table" then
- error("expected a table here, but got "..type(tbl))
- else
- return _G.pairs(tbl)
- end
- end
- end
- local doRestart = false
- local adminUser = function(u)
- if type(u) ~= "string" then return false end
- for a = 1, #adminUserTable do
- if adminUserTable[a]:lower() == u:lower() then
- return true
- end
- end
- return false
- end
- --[[
- local getEvents = function(...)
- local arg = table.pack(...)
- local output
- while true do
- output = {os.pullEvent()}
- for a = 1, #arg do
- if output[1] == arg[a] then
- return table.unpack(output)
- end
- end
- end
- end
- --]]
- --here's a more experimental getEvents...
- getEvents = function(...)
- local arg, funky, output = table.pack(...), {}
- if #arg <= 1 then
- return os.pullEvent(table.unpack(arg))
- else
- for a = 1, #arg do
- funky[a] = function() output = {os.pullEvent(arg[a])} end
- end
- parallel.waitForAny(table.unpack(funky))
- return table.unpack(output)
- end
- end
- local mcci = {
- prefix = "(>",
- ignoreErrors = true,
- mcciLabel = "MCCI",
- }
- local chatbox
- local usingAdminChatbox = false
- local chatDelay = 0.1 --seconds. regular chatboxes in P+1 will error if you use them too fast
- local chatbox = peripheral.find("chatbox_admin") or peripheral.find("chatbox") or peripheral.find("chatBox") or peripheral.find("chat_box")
- local cLabel
- local setLabel = function(newLabel)
- cLabel = newLabel
- if chatbox.setLabel then
- chatbox.setLabel(newLabel)
- elseif chatbox.setName then
- chatbox.setName(newLabel)
- end
- end
- if useMoarPeriphs then setLabel(mcci.mcciLabel) end
- local SAY = commands.tellraw and function(user,message)
- commands.tellraw("@a",'{"text":"['..cLabel..'] '..message..'"}')
- end or chatbox.say
- local WHISPER = commands.tellraw and function(user,message)
- commands.tellraw(user,'{"text":"['..cLabel..'] '..message..'"}')
- end or chatbox.tell
- local parseListenEvent = function(evt)
- return evt[3 - (useMoarPeriphs and 0 or 1)], evt[4 - (useMoarPeriphs and 0 or 1)]
- end
- local LISTEN = function(username,checkPrefix)
- local evt, user, message
- while true do
- evt = {getEvents(table.unpack(chatEvents))}
- user, message = parseListenEvent(evt)
- if (user == (username or user)) then
- if checkPrefix then
- if message:sub(1,#tostring(checkPrefix)) == checkPrefix then
- message = message:sub(#tostring(checkPrefix)+1)
- break
- end
- else
- break
- end
- end
- end
- return user, message
- end
- local checkit = function(check, ...) --checks if the first argument is equal to one of the others
- local list = {...}
- for a = 1, #list do
- if list[a] == check then
- return true
- end
- end
- return false
- end
- local explode = function(div,str)
- if (div=='') then return false end
- local pos,arr = 0,{}
- for st,sp in function() return string.find(str,div,pos,false) end do
- table.insert(arr,string.sub(str,pos,st-1))
- pos = sp + 1
- end
- table.insert(arr,string.sub(str,pos))
- return arr
- end
- local chatlog = {}
- local blacklist = {}
- local whitelist = {}
- local userActions = {}
- local userActionNames = {}
- local addToChatlog = function(user,message) --what the fuck do you think it does
- if chatlog[1] then
- if chatlog[#chatlog][1] == user and chatlog[#chatlog][2] == message then
- chatlog[#chatlog] = {user,message,chatlog[#chatlog][3]+1}
- else
- chatlog[#chatlog+1] = {user,message,1}
- end
- else
- chatlog[#chatlog+1] = {user,message,1}
- end
- end
- local writeChatlog = function() --appends chat output to the chat log
- prettyOutput = "("..chatlog[#chatlog][3] or "0"..") <"..chatlog[#chatlog][1].."> "..chatlog[#chatlog][2]
- local file = fs.open(chatlogDir,"a")
- file.writeLine(prettyOutput)
- file.close()
- end
- local saveBanlist = function()
- local file = fs.open(banDir,"w")
- for k,v in pairs(blacklist) do
- file.writeLine(k)
- file.writeLine(v)
- file.writeLine("")
- end
- file.close()
- end
- local loadBanlist = function()
- blacklist = {}
- local file = fs.open(banDir,"r")
- local name,reason = ""
- while name do
- name = file.readLine()
- if name then
- reason = file.readLine()
- file.readLine() --blank space
- blacklist[name] = reason
- end
- end
- file.close()
- return blacklist
- end
- local saveAdminlist = function()
- local file = fs.open(adminDir,"w")
- for a = 1, #adminUserTable do
- file.writeLine(adminUserTable[a])
- end
- file.close()
- end
- local loadAdminlist = function()
- adminUserTable = {}
- local file = fs.open(adminDir,"r")
- local line = ""
- while line do
- line = file.readLine()
- if line then
- adminUserTable[#adminUserTable+1] = line
- end
- end
- file.close()
- return adminUserTable
- end
- local saveConfig = function()
- local file = fs.open(configDir,"w")
- file.writeLine("prefix:"..textutils.serialize(mcci.prefix))
- file.writeLine("mcciLabel:"..textutils.serialize(mcci.mcciLabel))
- file.writeLine("ignoreErrors:"..tostring(mcci.ignoreErrors))
- file.close()
- end
- local fixInput = function(input)
- if tonumber(input) then
- return tonumber(input)
- elseif input == "false" then
- return false
- elseif input == "true" then
- return true
- elseif textutils.unserialize(input) then
- return textutils.unserialize(input)
- else
- return tostring(input)
- end
- end
- loadConfig = function()
- local file = fs.open(configDir,"r")
- local line = ""
- while line do
- line = file.readLine()
- if line then
- local name = line:sub(1,line:find(":")-1)
- local value = line:sub(line:find(":")+1)
- value = fixInput(value)
- mcci[name] = value
- end
- end
- file.close()
- end
- local scr_x, scr_y = term.getSize()
- local render = function()
- term.setTextColor(colors.white)
- term.setBackgroundColor(colors.black)
- term.clear()
- term.setCursorPos(1,1)
- print("("..mcci.mcciLabel.."/Prefix='"..mcci.prefix.."')")
- print("(ignoreErrors="..tostring(mcci.ignoreErrors)..")")
- print(("-"):rep(scr_x))
- print("Active Users:")
- for k,v in pairs(userActions) do
- print("-"..k.." ("..userActionNames[k]..")")
- end
- end
- local initCommand = function(u,cmd,...)
- local yarg = table.pack(...)
- local golly_G = setmetatable({}, {__index=(_ENV or getfenv())})
- golly_G.chatbox = chatbox
- golly_G.WHISPER = WHISPER
- golly_G.SAY = SAY
- golly_G.LISTEN = LISTEN
- golly_G._USERNAME = u
- golly_G._PREFIX = mcci.prefix
- golly_G._ADMIN = adminUser(u)
- golly_G.pairs = pairs
- userActions[u] = coroutine.create(function()
- local func = loadfile(fs.combine(commandDir,cmd))
- func = setfenv(func, golly_G)
- local output = {pcall( function() return func(table.unpack(yarg)) end )}
- if mcci.ignoreErrors and (output[1] == false) then
- error(output[2])
- end
- return table.unpack(output)
- end)
- userActionNames[u] = cmd
- render()
- end
- local dCommands,dCommandsHelp
- dCommandsHelp = {
- ["belay"] = mcci.prefix.."belay [playername] ; Belays all commands or a specific player's.",
- ["ban"] = mcci.prefix.."ban playername ; Bans a player from using this chat command interpreter.",
- ["unban"] = mcci.prefix.."unban playername ; Unbans a player who was banned from using this command interpreter.",
- ["banlist"] = mcci.prefix.."banlist ; Lists every player banned from using this command interpreter.",
- ["help"] = mcci.prefix.."help [commandname] ; Gives a more detailed description of a command.",
- ["update"] = mcci.prefix.."update ; Redownloads the MCCI program and overwrites the source if it's different.",
- ["restart"] = mcci.prefix.."restart ; Restarts the program. One would usually do this after updating.",
- ["checkadmin"] = mcci.prefix.."checkadmin [username] ; Without argument, checks if you are an admin. Also can check if another user is an admin.",
- ["admin"] = mcci.prefix.."admin username ; Sets a username as an administrator.",
- ["sudo"] = mcci.prefix.."sudo username command ; Runs a command as a specific user. Can't be used to abdicate.",
- ["abdicate"] = mcci.prefix.."abdicate ; Use with no arguments to tick yourself off the list of admins. Use with care, there's no confirming.",
- }
- dCommands = { --default commands
- ["belay"] = function(u,username)
- if adminUser(u) then
- if username then
- if userActions[username] then
- userActions[username] = nil
- WHISPER(u,username.."'s command has been belayed.")
- WHISPER(username,"Your command has been belayed. Ready.")
- render()
- else
- WHISPER(u,"That user isn't executing a cancelable command.")
- end
- else
- WHISPER(u,"All commands have been belayed.")
- for k,v in pairs(userActions) do
- WHISPER(k,"Your command has been belayed. Ready.")
- end
- userActions = {}
- render()
- end
- else
- WHISPER(k,"You can't do that!")
- end
- end,
- ["ban"] = function(u,baduser,...)
- local arg = table.pack(...)
- if adminUser(u) then
- if adminUser(baduser) then
- WHISPER(u,"Very funny. You can't ban admins.")
- elseif not baduser then
- WHISPER(u,"Do "..mcci.prefix.."ban [username]")
- else
- if blacklist[baduser] then
- WHISPER(u,"That dude was already banned.")
- else
- blacklist[baduser] = table.concat(arg," ") or "Unspecified reason."
- userActions[baduser] = nil
- userActionNames[baduser] = nil
- render()
- WHISPER(u,"'"..baduser.."' has been banned from making commands.")
- WHISPER(baduser,"You are hereby BANNED from making MCCI commands!")
- end
- end
- else
- WHISPER(u,"You can't do that!")
- end
- end,
- ["banlist"] = function(u)
- WHISPER(u,"## USERS BANNED FROM THIS CHAT AI ##")
- for k,v in pairs(blacklist) do
- WHISPER(u," * "..k.." ("..v..")")
- end
- end,
- ["unban"] = function(u,baduser)
- if adminUser(u) then
- if adminUser(baduser) then
- WHISPER(u,"Admins can't be banned to begin with, you dolt.")
- elseif (not baduser) then
- WHISPER(u,"Do "..mcci.prefix.."unban [username]")
- else
- if (not blacklist[baduser]) then
- WHISPER(u,"That dude isn't banned.")
- else
- blacklist[baduser] = nil
- WHISPER(u,"'"..baduser.."' has been unbanned from making commands.")
- WHISPER(baduser,"You've been unbanned from making commands.")
- end
- end
- else
- if blacklist[gooduser] then
- WHISPER(u,"You can't do that!")
- else
- WHISPER(u,"You couldn't do that even if "..gooduser.." were banned!")
- end
- end
- end,
- ["order"] = function(u)
- WHISPER(u,"What, do I look like a pizzeria to you?")
- end,
- ["help"] = function(u,cmd)
- if cmd then
- if dCommands[cmd] then
- WHISPER(u,dCommandsHelp[cmd])
- else
- if not fs.exists(fs.combine(commandDir,cmd)) then
- WHISPER(u,"That command doesn't exist, internally or externally.")
- else
- if fs.exists(fs.combine(commandHelpDir,cmd)) then
- local file = fs.open(fs.combine(commandHelpDir,cmd),"r")
- local cont = file.readAll(file)
- file.close()
- cont = cont:gsub("$PREFIX",mcci.prefix):gsub("\n"," ; ")
- WHISPER(u,cont)
- else
- WHISPER(u,"That external command does not have a help file.")
- end
- end
- end
- else
- local list = {}
- local cmdlist = fs.list(commandDir)
- for k,v in pairs(dCommands) do
- list[#list+1] = k
- end
- for a = 1, #cmdlist do
- list[#list+1] = cmdlist[a]
- end
- for a = 1, #list do
- WHISPER(u,mcci.prefix..list[a])
- end
- end
- end,
- ["update"] = function(u)
- if adminUser(u) then
- WHISPER(u,"Downloading latest version...")
- local foyle = http.get("https://pastebin.com/raw/5tJM4Bf8")
- if foyle then
- local cont = foyle.readAll()
- local mccifoyle = fs.open(shell.getRunningProgram(),"r")
- local mccicont = mccifoyle.readAll()
- mccifoyle.close()
- if mccicont == cont then
- WHISPER(u,"You were using the latest version. Aborting.")
- else
- local file = fs.open(shell.getRunningProgram(),"w")
- file.write(cont)
- file.close()
- WHISPER(u,"Update complete. Use "..mcci.prefix.."restart to run the latest version.")
- end
- end
- else
- WHISPER(u,"Yeah, try that again as an admin, doofus.")
- end
- end,
- ["restart"] = function(u)
- if adminUser(u) then
- doRestart = true
- end
- end,
- ["checkadmin"] = function(u,user)
- if not user then
- if adminUser(u) then
- WHISPER(u,"You're an admin.")
- else
- WHISPER(u,"You're decidedly not an admin.")
- end
- else
- if adminUser(user) then
- WHISPER(u,"'"..user.."' is an admin.")
- else
- WHISPER(u,"'"..user.."' is decidedly not an admin.")
- end
- end
- end,
- ["admin"] = function(u,user)
- if not user then
- if adminUser(u) then
- WHISPER(u,"Expected an argument with who you wish to make an admin, dunce.")
- else
- WHISPER(u,"Only admins can make other people admins, you twat.")
- end
- else
- if adminUser(u) then
- if adminUser(user) then
- if user == u then
- WHISPER(u,"Oh my god, stop it.")
- else
- WHISPER(u,"That person is already an admin.")
- end
- else
- table.insert(adminUserTable,user)
- WHISPER(u,"'"..user.."' is now an admin.")
- end
- else
- WHISPER(u,"This is an admin command, lamebrain.")
- end
- end
- end,
- ["abdicate"] = function(u,user)
- if adminUser(u) then
- if user then
- WHISPER(u,"You can't abdicate another user from their adminship.")
- else
- for a = 1, #adminUserTable do
- if adminUserTable[a] == u then
- table.remove(adminUserTable,a)
- WHISPER(u,"Lo, you have abdicated the throne of adminship.")
- return
- end
- end
- WHISPER(u,"That's weird. You weren't on the list, but you still did this command.")
- end
- else
- if user then
- WHISPER(u,"If you really don't like an admin, don't go complaining to me.")
- else
- WHISPER(u,"Abdicate from what? Only admins can do this!")
- end
- end
- end,
- ["sudo"] = function(u,user,cmd,...)
- if adminUser(u) then
- if cmd == "abdicate" then
- WHISPER(u,"You can't use sudo to abdicate people!")
- else
- if user then
- if userActions[user] then
- WHISPER(u,"That user is already in a command.")
- else
- if cmd then
- initCommand(user,cmd,...)
- WHISPER(u,"Ran '"..cmd.."' as '"..user.."'.")
- else
- WHISPER(u,"Sudo requires a second argument for the command.")
- end
- end
- else
- WHISPER(u,"Sudo requires a first argument for the user.")
- end
- end
- else
- WHISPER(u,"Sudo is an admin's command!")
- end
- end,
- }
- local handleDcommands = function(u,f,...) --heh heh heh
- local arg = table.pack(...)
- if dCommands[f] then
- return true, dCommands[f](u,table.unpack(arg))
- else
- return false
- end
- end
- local handleNormalCommands = function(u,cmd,...)
- local yarg = table.pack(...)
- local cmdlist = fs.list(commandDir)
- for a = 1, #cmdlist do
- if cmdlist[a] == cmd then
- initCommand(u,cmd,table.unpack(yarg))
- if useMoarPeriphs then setLabel(mcci.mcciLabel..":"..userActionNames[u]) end
- return true
- end
- end
- return false
- end
- local fuckingDoIt = function(user,message)
- -- local user, message
- local isDcommand, isNormalCommand
- -- while true do
- -- user, message = LISTEN()
- -- sleep(0) --to ensure that chatbox output comes AFTER player chat
- addToChatlog(user,message)
- writeChatlog()
- loadBanlist()
- loadAdminlist()
- loadConfig()
- if useMoarPeriphs then setLabel(mcci.mcciLabel) end
- render()
- if not blacklist[user] then
- if message:sub(1,#mcci.prefix) == mcci.prefix then
- message = message:sub(#mcci.prefix+1)
- if userActions[user] then
- if message == "nvm" then
- userActions[user] = nil
- userActionNames[user] = nil
- WHISPER(user,"Command cancelled. Ready.")
- render()
- else
- return "followup_command_"..user, message
- end
- else
- isNormalCommand = handleNormalCommands(user,table.unpack(explode(" ",message)))
- if not isNormalCommand then
- isDcommand = handleDcommands(user,table.unpack(explode(" ",message)))
- if doRestart then
- return
- else
- if not isDcommand then
- WHISPER(user,"No such command exists, internally or externally.")
- end
- end
- end
- end
- end
- saveBanlist()
- saveAdminlist()
- saveConfig()
- end
- -- end
- end
- if not fs.exists(adminDir) then
- saveAdminlist()
- end
- if not fs.exists(banDir) then
- saveBanlist()
- end
- if not fs.exists(configDir) then
- saveConfig()
- end
- loadAdminlist()
- loadBanlist()
- loadConfig()
- render()
- local doBreak, vres, grodyList --grodyList is the list of coroutines that will be purged after each queuing
- local rootEvent = {}
- local followUpEvent = {}
- local doUserActions = function()
- while true do
- rootEvent = {os.pullEvent()}
- if checkit(rootEvent[1], table.unpack(chatEvents)) then
- followUpEvent = {fuckingDoIt(parseListenEvent(rootEvent))}
- end
- grodyList = {}
- for k,v in pairs(userActions) do
- if useMoarPeriphs then setLabel(mcci.mcciLabel..":"..userActionNames[k]) end
- if (coroutine.status(v) == "dead") then
- if useMoarPeriphs then setLabel(mcci.mcciLabel) end
- WHISPER(k,"Ready.")
- grodyList[#grodyList+1] = k
- else
- _,vres = coroutine.resume(v,table.unpack(rootEvent))
- end
- end
- for a = 1, #grodyList do
- userActions[grodyList[a]] = nil
- userActionNames[grodyList[a]] = nil
- end
- grodyList = {}
- if #followUpEvent > 0 then
- for k,v in pairs(userActions) do
- if useMoarPeriphs then setLabel(mcci.mcciLabel..":"..userActionNames[k]) end
- _,vres = coroutine.resume(v,table.unpack(rootEvent))
- if (coroutine.status(v) == "dead") then
- if useMoarPeriphs then setLabel(mcci.mcciLabel) end
- WHISPER(k,"Ready.")
- grodyList[#grodyList+1] = k
- end
- end
- end
- for a = 1, #grodyList do
- userActions[grodyList[a]] = nil
- userActionNames[grodyList[a]] = nil
- end
- render()
- if doRestart then return true end
- end
- end
- doUserActions()
- if doRestart then
- SAY("MCCI Restarting.")
- shell.run(shell.getRunningProgram())
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement