Advertisement
1lann

Chat Assistant Beta

Oct 17th, 2015
196
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 31.38 KB | None | 0 0
  1. -- Chat Assistant Admin Edition
  2.  
  3. local version = "v0.1.8 Admin Edition"
  4.  
  5. print("1lann's Chat Assistant Central Server")
  6. print(version)
  7. print("Please don't touch me :(")
  8.  
  9. local chat = peripheral.wrap("right")
  10. --local modem = peripheral.wrap("top")
  11. local drive = peripheral.wrap("left")
  12. local operatingChannel = 27158
  13. local successChannel = 27159
  14. local wrapLength = 36
  15. local pmOnly = true
  16.  
  17. if not chat then
  18.     error("Could not find chatbox!")
  19. end
  20.  
  21. chat.setLabel("CA")
  22.  
  23. if modem then
  24.     modem.closeAll()
  25.     modem.open(successChannel)
  26.     print("Modem ready")
  27. else
  28.     print("Warning: No modem found")
  29. end
  30.  
  31. if drive then
  32.     print("Disk drive detected")
  33.     if drive.getAudioTitle() then
  34.         print("Loaded with disk: "..drive.getAudioTitle())
  35.         drive.stopAudio()
  36.     else
  37.         print("No music disk found!")
  38.     end
  39. else
  40.     print("No disk drive detected")
  41. end
  42.  
  43. local database = {}
  44. local localDatabase = {}
  45. local messageQueue = {}
  46. local receiveQueue = {}
  47. local pendingReminders = {}
  48. local musicLength = 190
  49. local musicClock = musicLength * -1
  50. local successCalls = {}
  51. local orderedKeys = {}
  52.  
  53. local searchAndTrim = function(message, searches, limit)
  54.     if not limit then
  55.         limit = #message + 1
  56.     end
  57.     for k, v in pairs(searches) do
  58.         local startPos, endPos = message:lower():find(v)
  59.         local match = nil
  60.         local err, msg = pcall(function() match = message:lower():match(v) end)
  61.         if endPos and startPos <= limit then
  62.             if err then
  63.                 return true, message:sub(endPos, -1), {match}, startPos
  64.             else
  65.                 return true, message:sub(endPos, -1), {}, startPos
  66.             end
  67.         end
  68.     end
  69.     return false, message
  70. end
  71.  
  72. local saveDatabase = function()
  73.     local f = io.open("/chat-config", "w")
  74.     f:write(textutils.serialize(database))
  75.     f:close()
  76. end
  77.  
  78. local textWrap = function(message)
  79.     local messages = {}
  80.     local currentMessage = ""
  81.     for word in message:gmatch("%S+") do
  82.         if #(currentMessage .. word) > wrapLength then
  83.             table.insert(messages, currentMessage)
  84.  
  85.             if word ~= "`" then
  86.                 currentMessage = word .. " "
  87.             end
  88.         elseif word == "`" then
  89.             table.insert(messages, currentMessage)
  90.             currentMessage = ""
  91.         else
  92.             currentMessage = currentMessage .. word .. " "
  93.         end
  94.     end
  95.  
  96.     if #currentMessage > 0 then
  97.         table.insert(messages, currentMessage)
  98.     end
  99.  
  100.     return messages
  101. end
  102.  
  103. local randomLoadingMessage = function()
  104.     local messages = {
  105.         "Alright, let me just look that up...",
  106.         "I'll look that up for you...",
  107.         "One moment...",
  108.         "Sure, I'll find that for you..."
  109.     }
  110.  
  111.     return messages[math.random(1, #messages)]
  112. end
  113.  
  114. local tell = function(player, messages, successCallback, localAction, shouldSay)
  115.     for k,v in pairs(messages) do
  116.         if k == 1 then
  117.             table.insert(messageQueue, {player, v, successCallback, localAction, shouldSay})
  118.         else
  119.             table.insert(messageQueue, {player, v, nil, localAction, shouldSay})
  120.         end
  121.     end
  122.     os.queueEvent("ca_send_message")
  123. end
  124.  
  125. local function trim(s)
  126.     return s:match("^%s*(.-)%s*$")
  127. end
  128.  
  129. local commands = {}
  130. commands["remind"] = function(time, units, player, message)
  131.     if time ~= nil and time > 0 then
  132.         if not database[player] then
  133.             database[player] = {}
  134.         end
  135.         if not database[player].reminders then
  136.             database[player].reminders = {}
  137.         end
  138.         if #database[player].reminders >= 3 then
  139.             return true, {"Sorry, I can only store 3 reminders at once.", "To cancel a reminder, you can \"cancel reminder\" it"}
  140.         end
  141.         if units == "sec" then
  142.             table.insert(database[player].reminders, {message, os.clock() + time})
  143.             saveDatabase()
  144.             return true, {"Okay, I will remind you in " .. time .. " seconds.", "Use \"cancel reminder\" to cancel your reminder"}
  145.         else
  146.             table.insert(database[player].reminders, {message, os.clock() + time*60})
  147.             saveDatabase()
  148.             return true, {"Okay, I will remind you in " .. time .. " minutes.", "Use \"cancel reminder\" to cancel your reminder"}
  149.         end
  150.     else
  151.         return true, {"Sorry, I couldn't understand", "when you wanted to be reminded :("}
  152.     end
  153. end
  154.  
  155. commands["remote-services"] = function(player, post, shouldSay)
  156.     print(player .. " requested remote services.")
  157.  
  158.     tell(player, {randomLoadingMessage()}, nil, nil, shouldSay)
  159.  
  160.     local resp = http.post("http://chatbox.chuie.io/query", post)
  161.     if not resp then
  162.         return true, {"Sorry, I could not connect to the", "remote services to fulfill your query!"}
  163.     end
  164.     local response = textutils.unserialize(resp.readAll())
  165.     resp.close()
  166.  
  167.     if response.error then
  168.         local serverError = textWrap(response.error)
  169.         table.insert(serverError, 1, "The server responded with an error:")
  170.         return true, serverError
  171.     else
  172.         return true, textWrap(response.success)
  173.     end
  174. end
  175.  
  176. commands["irl-time"] = function(player, location, shouldSay)
  177.     location = location:gsub(" now%?", "")
  178.     location = location:gsub("%?", "")
  179.     location = location:gsub(" right ", "")
  180.  
  181.     if #trim(location) == 0 then
  182.         return false
  183.     end
  184.  
  185.     local post = "query_type=time&location=" .. textutils.urlEncode(trim(location))
  186.  
  187.     return commands["remote-services"](player, post, shouldSay)
  188. end
  189.  
  190. commands["weather"] = function(player, location, time, shouldSay)
  191.     location = location:gsub(" today", "")
  192.     location = location:gsub(" now%?", "")
  193.     location = location:gsub("%?", "")
  194.     location = location:gsub(" right ", "")
  195.     location = location:gsub(" tomorrow", "")
  196.  
  197.     if #trim(location) == 0 then
  198.         return false
  199.     end
  200.  
  201.     local post = "query_type=weather&location=" ..
  202.         textutils.urlEncode(trim(location)) .. "&time=" .. time
  203.  
  204.     return commands["remote-services"](player, post, shouldSay)
  205. end
  206.  
  207. commands["define"] = function(player, word, shouldSay)
  208.     if #word == 0 then
  209.         return false
  210.     end
  211.  
  212.     local post = "query_type=definition&word=" .. textutils.urlEncode(word)
  213.  
  214.     return commands["remote-services"](player, post, shouldSay)
  215. end
  216.  
  217. commands["convert"] = function(player, conversion, shouldSay)
  218.     if conversion:find("%d") > 10 then
  219.         return false
  220.     end
  221.  
  222.     if #conversion == 0 then
  223.         return false
  224.     end
  225.  
  226.     local post = "query_type=conversion&conversion=" .. textutils.urlEncode(conversion)
  227.  
  228.     return commands["remote-services"](player, post, shouldSay)
  229. end
  230.  
  231. local matches = {}
  232. matches["1remind"] = function(player, message)
  233.     if searchAndTrim(message, {" in %d+"}, 6) then
  234.         local _, search = searchAndTrim(message, {" in %d"}, 6)
  235.         local secSuccess, secSearch, secMatch = searchAndTrim(search, {"(%d+) secs,? ", "(%d+) sec,? ", "(%d+) seconds,? ", "(%d+) second,? "}, 3)
  236.         local minSuccess, minSearch, minMatch = searchAndTrim(search, {"(%d+) min,? ", "(%d+) minute,? ", "(%d+) minutes,? ", "(%d+)mins,? "}, 3)
  237.         if secSuccess then
  238.             local remindSuccess, search = searchAndTrim(secSearch, {" remind me ", " reminder "}, 8)
  239.             if remindSuccess then
  240.                 _, search = searchAndTrim(search, {" to "}, 12)
  241.                 return commands.remind(tonumber(secMatch[1]), "sec", player, search:sub(2, -1))
  242.             end
  243.         elseif minSuccess then
  244.             local remindSuccess, search = searchAndTrim(minSearch, {" remind me ", " reminder "}, 8)
  245.             if remindSuccess then
  246.                 _, search = searchAndTrim(search, {" to "}, 12)
  247.                 return commands.remind(tonumber(minMatch[1]), "min", player, search:sub(2, -1))
  248.             end
  249.         end
  250.     elseif searchAndTrim(message, {" remind me ", " reminder "}, 6) then
  251.         if searchAndTrim(message, {" cancel "}, 10) then
  252.             return
  253.         end
  254.  
  255.         local _, search = searchAndTrim(message, {" remind me ", " reminder "}, 6)
  256.         local secSuccess, secSearch, secMatch = searchAndTrim(search, {" in (%d+) secs,? ", " in (%d+) sec,? ", " in (%d+) seconds,? ", " in (%d+) second,? "}, 3)
  257.         local minSuccess, minSearch, minMatch = searchAndTrim(search, {" in (%d+) min,? ", " in (%d+) minute,? ", " in (%d+) minutes,? ", " in (%d+)mins,? "}, 3)
  258.         if secSuccess then
  259.             _, search = searchAndTrim(secSearch, {" to "}, 12)
  260.             return commands.remind(tonumber(secMatch[1]), "sec", player, search:sub(2, -1))
  261.         elseif minSuccess then
  262.             _, search = searchAndTrim(minSearch, {" to "}, 12)
  263.             return commands.remind(tonumber(minMatch[1]), "min", player, search:sub(2, -1))
  264.         else
  265.             local searchSuccess, search = searchAndTrim(search, {" to "}, 12)
  266.             if searchSuccess then
  267.                 local secSuccess, _, secMatch, secStart = searchAndTrim(search, {" in (%d+) secs", " in (%d+) sec", " in (%d+) seconds", " in (%d+) second"})
  268.                 local minSuccess, _, minMatch, minStart = searchAndTrim(search, {" in (%d+) min", " in (%d+) minute", " in (%d+) minutes", " in (%d+)mins"})
  269.                 if secSuccess then
  270.                     return commands.remind(tonumber(secMatch[1]), "sec", player, search:sub(2, secStart - 1))
  271.                 elseif minSuccess then
  272.                     return commands.remind(tonumber(minMatch[1]), "min", player, search:sub(2, minStart - 1))
  273.                 else
  274.                     return true, {"Sorry, I couldn't understand", "when you wanted to be reminded :("}
  275.                 end
  276.             else
  277.                 return true, {"Sorry, I couldn't understand", "when you wanted to be reminded :("}
  278.             end
  279.         end
  280.     end
  281. end
  282.  
  283. matches["cancel-reminder"] = function(player, message)
  284.     if searchAndTrim(message, {" cancel reminder", " dismiss reminder", " delete reminder"}, 6) then
  285.         if database[player] and database[player].reminders then
  286.             if #database[player].reminders < 1 then
  287.                 return true, {"You don't have any reminders set!"}
  288.             elseif #database[player].reminders == 1 then
  289.                 local reminderText = database[player].reminders[1][1]
  290.                 table.remove(database[player].reminders, 1)
  291.                 saveDatabase()
  292.                 return true, {"Alright, your reminder:", reminderText, "has been cancelled."}
  293.             else
  294.                 local _, search = searchAndTrim(message, {" cancel reminder", " dismiss reminder"})
  295.                 if searchAndTrim(search, {"%d+"}) then
  296.                     local _, _, match = searchAndTrim(search, {"(%d+)"})
  297.                     if tonumber(match[1]) <= #database[player].reminders then
  298.                         local reminderText = database[player].reminders[tonumber(match[1])][1]
  299.                         table.remove(database[player].reminders, tonumber(match[1]))
  300.                         saveDatabase()
  301.                         return true, {"Alright, your reminder:", reminderText, "has been cancelled."}
  302.                     else
  303.                         return true, {"Sorry, I could not find that reminder index!"}
  304.                     end
  305.                 end
  306.                 local returnTable = {}
  307.                 table.insert(returnTable, "Here are your active reminders: ")
  308.                 for k,v in pairs(database[player].reminders) do
  309.                     table.insert(returnTable, "#"..tostring(k)..": "..v[1])
  310.                 end
  311.                 table.insert(returnTable, "What reminder number would you like to cancel?")
  312.                 table.insert(returnTable, "You can append \"#\" or \"number\" as a prefix")
  313.                 return true, returnTable
  314.             end
  315.         end
  316.     elseif #localDatabase[player] > 0 and localDatabase[player][#localDatabase[player]][1] == "cancel-reminder" then
  317.         if searchAndTrim(message, {"^%d+", "^# ?%d+", "^number ?%d+"}) then
  318.             local _, _, match = searchAndTrim(message, {"(%d+)"})
  319.             if tonumber(match[1]) <= #database[player].reminders then
  320.                 local reminderText = database[player].reminders[tonumber(match[1])][1]
  321.                 table.remove(database[player].reminders, tonumber(match[1]))
  322.                 saveDatabase()
  323.                 return true, {"Alright, your reminder:", reminderText, "has been cancelled."}, true
  324.             else
  325.                 return true, {"Sorry, I could not find that reminder index!"}
  326.             end
  327.         end
  328.     end
  329. end
  330.  
  331. matches["list-reminders"] = function(player, message)
  332.     if searchAndTrim(message, {" what reminders? ", " what are my %S* ?reminder", " list reminder"}, 6) then
  333.         if database[player] and database[player].reminders then
  334.             if #database[player].reminders < 1 then
  335.                 return true, {"You currently have no set reminders!"}
  336.             elseif #database[player].reminders == 1 then
  337.                 return true, {"You have 1 active reminder:", database[player].reminders[1][1], "Use \"cancel reminder\" to cancel a reminder"}
  338.             else
  339.                 local returnTable = {}
  340.                 table.insert(returnTable, "Your active reminders: ")
  341.                 for k,v in pairs(database[player].reminders) do
  342.                     table.insert(returnTable, "#"..k..": "..v[1])
  343.                 end
  344.                 table.insert(returnTable, "Use \"cancel reminder\" to cancel a reminder")
  345.                 return true, returnTable
  346.             end
  347.         end
  348.     end
  349. end
  350.  
  351. matches["compliment"] = function(player, message)
  352.     if #localDatabase[player] > 0 and (os.clock() - localDatabase[player][#localDatabase[player]][2]) < 30 then
  353.         if searchAndTrim(message, {"thank", "kudo", "cool", "nice", "wow", "awesome"}) then
  354.             return true, {"If that was a compliment directed", "towards me, Thank you! :D"}
  355.         end
  356.     end
  357. end
  358.  
  359. matches["time"] = function(player, message, shouldSay)
  360.     if searchAndTrim(message, {" what ", " whats ", " what's ", " what is "}, 6) then
  361.         local _, search = searchAndTrim(message, {" what ", " whats ", " what's ", " what is "}, 6)
  362.         if searchAndTrim(search, {" time", " date", " day"}, 15) then
  363.             _, search = searchAndTrim(search, {" time", " date", " day"}, 15)
  364.             if searchAndTrim(search, {" for ", " in ", " is it in ", " is it for "}, 8) then
  365.                 _, location = searchAndTrim(search, {" for ", " in ", " is it in ", " is it for "}, 8)
  366.                 return commands["irl-time"](player, location, shouldSay)
  367.             else
  368.                 return true, {"The current in-game time is " .. textutils.formatTime(os.time())}
  369.             end
  370.         end
  371.     -- elseif searchAndTrim(message, {" time ", " date ", " day "}, 5) then
  372.     --  _, search = searchAndTrim(message, {" time ", " date ", " day "}, 5)
  373.     --  if searchAndTrim(search, {" for ", " in ", " is it in ", " is it for "}, 8) then
  374.     --      _, location = searchAndTrim(search, {" for ", " in ", " is it in ", " is it for "}, 8)
  375.     --      return commands["irl-time"](player, location, shouldSay)
  376.     --  else
  377.     --      return true, {"The current in-game time is " .. textutils.formatTime(os.time())}
  378.     --  end
  379.     end
  380. end
  381.  
  382. matches["help"] = function(player, message)
  383.     if #localDatabase[player] > 0 and (os.clock() - localDatabase[player][#localDatabase[player]][2]) < 100 then
  384.         if searchAndTrim(message, {" what is this", " whats that", " what's that", " what is chat", " what's chat", " what are you", " what is that", " what is ca"}) then
  385.             return true, {
  386.             "Hello there, I'm CA, short for Chat Assistant.",
  387.             "My creator is 1lann. If you would like a list",
  388.             "of what I can do, ask \"what can ca do?\".",
  389.             "Thanks for trying me out!",
  390.             "Running " .. version}
  391.         end
  392.     end
  393. end
  394.  
  395. matches["help-cando"] = function(player, message)
  396.     if searchAndTrim(message, {" what can ca do"}) then
  397.         return true, {
  398.         "Here are some examples of what you can ask:",
  399.         "What is the time?",
  400.         "What is the time in New York?",
  401.         "How's the weather in London?",
  402.         "What's tomorrow's forecast for Beijing?",
  403.         "Lookup how old is Obama?",
  404.         "What does assistant mean?",
  405.         "Define astute",
  406.         "What is 24 C to F?",
  407.         "What is 300 g in lb?",
  408.         "What is 200 USD in GBP?",
  409.         "",
  410.         "You can also ask questions without everyone",
  411.         "seeing by prefixing ##.",
  412.         }
  413.     end
  414. end
  415.  
  416. matches["1stopwatch"] = function(player, message)
  417.     if searchAndTrim(message, {" start stopwatch", " start counting", " stopwatch start", " start the stopwatch"}, 6) then
  418.         localDatabase[player].stopwatch = {}
  419.         localDatabase[player].stopwatch.running = true
  420.         localDatabase[player].stopwatch.start = os.clock()
  421.         return true, {"Alright, I have started the stopwatch."}
  422.     elseif searchAndTrim(message, {" stop stopwatch", " quit stopwatch", " stop counting", " quit counting",
  423.         " stop the stopwatch", " quit the stopwatch", " stopwatch stop", " stopwatch end", " stopwatch quit",
  424.         " end counting", " end stopwatch"}, 10) then
  425.         if localDatabase[player].stopwatch then
  426.             if localDatabase[player].stopwatch.running then
  427.                 localDatabase[player].stopwatch.running = false
  428.                 localDatabase[player].stopwatch.start = os.clock() - localDatabase[player].stopwatch.start
  429.                 local reading = math.ceil(localDatabase[player].stopwatch.start*100 - 0.5)/100
  430.                 return true, {"Okay, I stopped the stopwatch", "The stopwatch reads: "..reading.." seconds"}
  431.             else
  432.                 local reading = math.ceil(localDatabase[player].stopwatch.start*100 - 0.5)/100
  433.                 return true, {"The stopwatch has already been stopped", "The stopwatch reads: "..reading.." seconds"}
  434.             end
  435.         else
  436.  
  437.         end
  438.     elseif searchAndTrim(message, {" how much time", " stopwatch", " time passed"}) then
  439.         if localDatabase[player].stopwatch then
  440.             if localDatabase[player].stopwatch.running then
  441.                 local rawReading = os.clock() - localDatabase[player].stopwatch.start
  442.                 local reading = math.ceil(rawReading*100 - 0.5)/100
  443.                 return true, {"The running stopwatch reads: "..reading.." seconds"}
  444.             else
  445.                 local reading = math.ceil(localDatabase[player].stopwatch.start*100 - 0.5)/100
  446.                 return true, {"The stopped stopwatch reads: "..reading.." seconds"}
  447.             end
  448.         else
  449.             return true, {"You have never started the stopwatch!"}
  450.         end
  451.     end
  452. end
  453.  
  454. local alphabet = "abcdefghijklmnopqrstuvwxyz"
  455.  
  456. matches["math"] = function(player, message)
  457.     local matchSuccess, _, search = searchAndTrim(message, {" what's (%(*%d.+)$", " what is (%(*%d.+)$", " whats (%(*%d.+)$", "^%S+ (%(*%d.+)$", "^(%(*%d.+)$", " what does (%(*%d.+)$", " what is (%(*%d.+)$"})
  458.     if matchSuccess then
  459.         search = search[1]
  460.     end
  461.     if matchSuccess and (search:find("%+") or search:find("%*") or search:find("%^") or search:find("%/") or search:find("%-")) then
  462.         search = search:gsub("%?", "")
  463.         search = search:gsub("=", "")
  464.         for i = 1, #alphabet do
  465.             search = search:gsub(alphabet:sub(i, i), "")
  466.         end
  467.  
  468.         local func = loadstring("return "..search)
  469.         local result = nil
  470.         if func then
  471.             if pcall(function() result = func() end) then
  472.                 return true, {"The answer is: "..tostring(result)}
  473.             else
  474.                 return true, {"Sorry, I couldn't calculate that!", "Make sure you don't use any", "alphabetical letters in it"}
  475.             end
  476.         else
  477.             return true, {"Sorry, I couldn't process that calculation!", "Make sure you don't use any", "alphabetical letters in it"}
  478.         end
  479.     end
  480. end
  481.  
  482. matches["weather"] = function(player, message, shouldSay)
  483.     if searchAndTrim(message, {" what ", " whats ", " what's ", " what is ", " how ", " how's ", " how is "}, 6) then
  484.         local _, search = searchAndTrim(message, {" what ", " whats ", " what's ", " what is ", " how ", " how's ", " how is "}, 6)
  485.         if searchAndTrim(search, {" weather ", " forecast ", " temperature ", " hot ", " cold "}, 8) then
  486.             _, search = searchAndTrim(search, {" weather ", " forecast ", " temperature ", " hot ", " cold "}, 8)
  487.             if searchAndTrim(search, {" for ", " in ", " at "}, 10) then
  488.                 _, location = searchAndTrim(search, {" for ", " in ", " at "}, 10)
  489.                 if searchAndTrim(search, {" tomorrow"}, 40) then
  490.                     return commands["weather"](player, location, "tomorrow", shouldSay)
  491.                 else
  492.                     return commands["weather"](player, location, "today", shouldSay)
  493.                 end
  494.             end
  495.         elseif searchAndTrim(search, {" now ", " today", " tomorrow"}, 8) then
  496.             _, search = searchAndTrim(search, {" now ", " today", " tomorrow"}, 8)
  497.             if searchAndTrim(search, {" weather ", " forecast ", " temperature ", " hot ", " cold "}, 8) then
  498.                 _, search = searchAndTrim(search, {" weather ", " forecast ", " temperature ", " hot ", " cold "}, 8)
  499.                 if searchAndTrim(search, {" for ", " in ", " at "}, 10) then
  500.                     _, location = searchAndTrim(search, {" for ", " in ", " at "}, 10)
  501.                     if searchAndTrim(message, {" tomorrow"}) then
  502.                         return commands["weather"](player, location, "tomorrow", shouldSay)
  503.                     else
  504.                         return commands["weather"](player, location, "today", shouldSay)
  505.                     end
  506.                 end
  507.             end
  508.         end
  509.     elseif searchAndTrim(message, {" weather ", " forecast ", " temperature "}, 6) then
  510.         local _, search = searchAndTrim(message, {" weather ", " forecast ", " temperature "}, 6)
  511.         if searchAndTrim(search, {" for ", " in ", " at "}, 10) then
  512.             _, location = searchAndTrim(search, {" for ", " in ", " at "}, 10)
  513.             if searchAndTrim(search, {" tomorrow"}, 40) then
  514.                 return commands["weather"](player, location, "tomorrow", shouldSay)
  515.             else
  516.                 return commands["weather"](player, location, "today", shouldSay)
  517.             end
  518.         end
  519.     elseif searchAndTrim(message, {" will ", " is "}, 10) then
  520.         if searchAndTrim(message, {" rain", " sun", " snow", " clear", " hot ", " cold "}, 30) then
  521.             _, search = searchAndTrim(message, {" rain", " sun", " snow", " clear", " hot ", " cold "}, 30)
  522.             if searchAndTrim(search, {" for ", " in ", " at "}, 20) then
  523.                 _, location = searchAndTrim(search, {" for ", " in ", " at "}, 20)
  524.                 if searchAndTrim(search, {" tomorrow"}, 40) then
  525.                     return commands["weather"](player, location, "tomorrow", shouldSay)
  526.                 else
  527.                     return commands["weather"](player, location, "today", shouldSay)
  528.                 end
  529.             end
  530.         end
  531.     end
  532. end
  533.  
  534. matches["define"] = function(player, message, shouldSay)
  535.     if searchAndTrim(message, {" define ", " definition ", " meaning "}, 20) then
  536.         local _, search = searchAndTrim(message, {" define ", " definition ", " meaning "}, 20)
  537.             local word = search:gsub(" of ", "")
  538.             word = word:gsub(" the ", "")
  539.             word = word:gsub("%?", "")
  540.             word = word:gsub("%.", "")
  541.             return commands["define"](player, trim(word), shouldSay)
  542.     elseif searchAndTrim(message, {" what does "}, 20) then
  543.         local _, search = searchAndTrim(message, {" what does "}, 20)
  544.         if searchAndTrim(search, {" mean"}) then
  545.             local word = message:match("does (.+) mean")
  546.             if not word then
  547.                 return false
  548.             end
  549.  
  550.             if trim(word) == "that" then
  551.                 return false
  552.             end
  553.             return commands["define"](player, trim(word), shouldSay)
  554.         end
  555.     end
  556. end
  557.  
  558. matches["convert"] = function(player, message, shouldSay)
  559.     if message:match("%d+%.%d+.+ to .+") then
  560.         return commands["convert"](player, message, shouldSay)
  561.     elseif message:match("%d+%.%d+.+ in .+") then
  562.         return commands["convert"](player, message, shouldSay)
  563.     elseif message:match("%d+.+ to .+") then
  564.         return commands["convert"](player, message, shouldSay)
  565.     elseif message:match("%d+.+ in .+") then
  566.         return commands["convert"](player, message, shouldSay)
  567.     end
  568. end
  569.  
  570. matches["1music"] = function(player, message)
  571.     if searchAndTrim(message, {" play ", " listen to "}, 6) then
  572.         local _, search = searchAndTrim(message, {" play ", " listen to ", " start "})
  573.         if drive then
  574.             if searchAndTrim(search, {" music", " some music", " song", " some song", " the music", " the song", tostring(drive.hasAudio())}, 3) then
  575.                 if drive.getAudioTitle() and (os.clock() - musicClock) < musicLength then
  576.                     return true, {"Music is already playing!"}, false, true
  577.                 elseif drive.getAudioTitle() then
  578.                     return true, {"Now playing: "..drive.getAudioTitle()}, false, true, function()
  579.                         musicClock = os.clock()
  580.                         drive.playAudio()
  581.                     end
  582.                 else
  583.                     return true, {"I don't have any music in", "my disk drive to play :("}, false, true
  584.                 end
  585.             else
  586.                 return true, {"Sorry, I currently can only play", "the disk in my disk drive :("}
  587.             end
  588.         else
  589.             return true, {"Sorry, I don't have a disk drive", "to play music with :("}, false, true
  590.         end
  591.     elseif searchAndTrim(message, {" what ", " whats ", " what's ", " what is "}, 6) then
  592.         local _, search = searchAndTrim(message, {" what ", " whats ", " what's ", " what is "})
  593.         if searchAndTrim(search, {" song", " this song", " the song", " music", " this music", " playing", " the music", " the current song", " this current song", " currently play"}, 3) then
  594.             if drive and drive.getAudioTitle() and (os.clock() - musicClock) < musicLength then
  595.                 return true, {"The currently playing song is", drive.getAudioTitle()}
  596.             else
  597.                 return true, {"There currently is no music playing!"}, false, true
  598.             end
  599.         end
  600.     elseif searchAndTrim(message, {" stop ", " pause "}) then
  601.         local _, search = searchAndTrim(message, {" stop ", " pause "})
  602.         if searchAndTrim(search, {" play", " music", " the music", " the song", " this music", " this song"}, 3) then
  603.             if drive and drive.getAudioTitle() and (os.clock() - musicClock) < musicLength then
  604.                 return true, {"Ok, music stopped"}, false, true, function()
  605.                     drive.stopAudio()
  606.                     musicClock = musicLength * -1
  607.                 end
  608.             else
  609.                 return true, {"There currently is no music playing!"}, false, true
  610.             end
  611.         end
  612.     end
  613. end
  614.  
  615. matches["joke"] = function(player, message)
  616.     if searchAndTrim(message, {" tell me a joke", " tell a joke", " give me a joke", " give a joke"}, 4) then
  617.         local jokes = {'Did you hear about the guy whose whole left side was cut off? He\'s all right now.',
  618.             "I'm reading a book about anti-gravity. It's impossible to put down.",
  619.             "I wondered why the baseball was getting bigger. Then it hit me.",
  620.             "It's not that the man did not know how to juggle, he just didn't have the balls to do it.",
  621.             "I'm glad I know sign language, it's pretty handy.",
  622.             "My friend's bakery burned down last night. Now his business is toast.",
  623.             "Why did the cookie cry? It was feeling crumby.",
  624.             "I used to be a banker, but I lost interest.",
  625.             "A drum and a symbol fall off a cliff",
  626.             "Why do seagulls fly over the sea? Because they aren't bay-gulls!",
  627.             "Why did the fireman wear red, white, and blue suspenders? To hold his pants up.",
  628.             "Why didn't the crab share his food? Because crabs are territorial animals, that don't share anything.",
  629.             "Why was the javascript developer sad? Because he didn't Node how to Express himself.",
  630.             "What do I look like? A JOKE MACHINE!?",
  631.             "How did the hipster burn the roof of his mouth? He ate the pizza before it was cool.",
  632.             "Why is it hard to make puns for kleptomaniacs? They are always taking things literally.",
  633.             "Why do mermaid wear sea-shells? Because b-shells are too small.",
  634.             "I'm a humorless, cold hearted, machine.",
  635.             "Two fish in a tank. One looks to the other and says 'Can you even drive this thing???'",
  636.             "Two fish swim down a river, and hit a wall. One says: 'Dam!'",
  637.             "What's funnier than a monkey dancing with an elephant? Two monkeys dancing with an elephant.",
  638.             "How did Darth Vader know what Luke was getting for Christmas? He felt his presents.",
  639.             "What's red and bad for your teeth? A Brick.",
  640.             "What's orange and sounds like a parrot? A Carrot.",
  641.             "What do you call a cow with no legs? Ground beef",
  642.             "Two guys walk into a bar. You'd think the second one would have noticed.",
  643.             "What is a centipedes's favorite Beatle song?  I want to hold your hand, hand, hand, hand...",
  644.             "What do you call a chicken crossing the road? Poultry in moton. ",
  645.             "Did you hear about the Mexican train killer?  He had locomotives",
  646.             "What do you call a fake noodle?  An impasta",
  647.             "How many tickles does it take to tickle an octupus? Ten-tickles!",
  648.             "At the rate law schools are turning them out, by 2050 there will be more lawyers than humans."
  649.         }
  650.  
  651.         return true, textWrap(jokes[math.random(1, #jokes)])
  652.     end
  653. end
  654.  
  655. matches["zlookup"] = function(player, message, shouldSay)
  656.     if searchAndTrim(message, {" wolfram ", " wolframalpha ", " wa ", " wa, ", " lookup ",
  657.         " wa: ", " lookup: ", " search for ", " search "}, 4) then
  658.         local _, query = searchAndTrim(message, {" wolfram ", " wolframalpha ", " wa ", " wa, ", " lookup ",
  659.         " wa: ", " lookup: ", " search for ", " search "}, 4)
  660.  
  661.         if #trim(query) == 0 then
  662.             return false
  663.         end
  664.  
  665.         local post = "query_type=wolframalpha&query=" .. textutils.urlEncode(trim(query))
  666.  
  667.         return commands["remote-services"](player, post, shouldSay)
  668.     end
  669. end
  670.  
  671.  
  672. if fs.exists("/chat-config") then
  673.     local f = io.open("/chat-config")
  674.     database = textutils.unserialize(f:read("*a"))
  675.     f:close()
  676. end
  677.  
  678. if database then
  679.     print("Database loaded!")
  680. else
  681.     database = {}
  682.     print("Database failed to be loaded!")
  683. end
  684.  
  685. print("Running chat assistant")
  686.  
  687. for k,v in pairs(database) do
  688.     if v.reminders and #v.reminders > 0 then
  689.         for i = 1, #v.reminders do
  690.             database[k].reminders[i][2] = 0
  691.         end
  692.     end
  693. end
  694.  
  695. local checkTimer = os.startTimer(1)
  696.  
  697. local success, messages, clear, localAction, callbackFunc = nil, nil, nil, nil, nil
  698.  
  699. local function mainLoop()
  700.     while true do
  701.         local e, side, player, message = os.pullEvent()
  702.         if e == "chat_message" or e == "chatbox_command" then
  703.             if message:sub(1, 2) == "$$" then
  704.                 message = message:sub(3, -1)
  705.             end
  706.  
  707.             message = " " .. message .. " "
  708.  
  709.             if not localDatabase[player] then
  710.                 localDatabase[player] = {}
  711.             end
  712.  
  713.             if message == "cancel" then
  714.                 table.insert(localDatabase[player], {"clear", os.clock()})
  715.             end
  716.  
  717.             local shouldSay = e == "chat_message"
  718.  
  719.             if player == "Ale32bit" then
  720.                 shouldSay = false
  721.             end
  722.  
  723.             table.insert(receiveQueue, {player, message, shouldSay})
  724.  
  725.             os.queueEvent("ca_receive_message")
  726.         elseif e == "timer" and side == checkTimer then
  727.             checkTimer = os.startTimer(1)
  728.             for k,v in pairs(database) do
  729.                 if v.reminders and #v.reminders > 0 then
  730.                     for i = 1, #v.reminders do
  731.                         if v.reminders[#v.reminders - i + 1] and os.clock() >= v.reminders[#v.reminders - i + 1][2] then
  732.                             if (not pendingReminders[tostring(#v.reminders - i + 1)]) or pendingReminders[tostring(#v.reminders - i + 1)] < os.clock() then
  733.                                 pendingReminders[tostring(#v.reminders - i + 1)] = os.clock() + 5
  734.                                 tell(k, {"Reminder: " .. v.reminders[#v.reminders - i + 1][1]},
  735.                                 function()
  736.                                     pendingReminders[tostring(#v.reminders - i + 1)] = nil
  737.                                     table.remove(database[k].reminders, #v.reminders - i + 1)
  738.                                     saveDatabase()
  739.                                 end)
  740.                             end
  741.                         end
  742.                     end
  743.                 end
  744.             end
  745.         end
  746.     end
  747. end
  748.  
  749. local function messageReceiveLoop()
  750.     while true do
  751.         os.pullEvent("ca_receive_message")
  752.         while #receiveQueue > 0 do
  753.             player, message, shouldSay = unpack(receiveQueue[1])
  754.  
  755.             for _, key in pairs(orderedKeys) do
  756.                 local err, msg = pcall(function() success, messages, clear, localAction, callbackFunc = matches[key](player, message, shouldSay) end)
  757.                 if not err then
  758.                     print("An error has occured while running: "..key)
  759.                     print(msg)
  760.                     tell(player, {"Sorry! An error occured while executing", "your request."}, nil, nil, shouldSay)
  761.                 else
  762.                     if success then
  763.                         if key == "1remind" then
  764.                             shouldSay = false
  765.                         end
  766.  
  767.                         tell(player, messages, callbackFunc, localAction, shouldSay)
  768.                         if clear then
  769.                             table.insert(localDatabase[player], {"clear", os.clock()})
  770.                         else
  771.                             table.insert(localDatabase[player], {key, os.clock()})
  772.                         end
  773.                         break
  774.                     end
  775.                 end
  776.                 success, messages, clear, localAction, callbackFunc = nil, nil, nil, nil
  777.             end
  778.  
  779.             table.remove(receiveQueue, 1)
  780.         end
  781.     end
  782. end
  783.  
  784. local function sendLoop()
  785.     while true do
  786.         os.pullEvent("ca_send_message")
  787.         while #messageQueue > 0 do
  788.             local result = nil
  789.             sleep(0.1)
  790.             if messageQueue[1][5] and not pmOnly then
  791.                 pcall(function() result = chat.say(messageQueue[1][2]) end)
  792.             else
  793.                 pcall(function() result = chat.tell(messageQueue[1][1], messageQueue[1][2]) end)
  794.             end
  795.             if result then
  796.                 if messageQueue[1][3] then
  797.                     local err, msg = pcall(messageQueue[1][3])
  798.                     if not err then
  799.                         print("Error while running callback")
  800.                         print(msg)
  801.                     end
  802.                 end
  803.             elseif modem and not messageQueue[1][4] then
  804.                 local newID = tostring({}):sub(8, -1)
  805.                 if messageQueue[1][3] then
  806.                     successCalls[newID] = messageQueue[1][3]
  807.                 end
  808.                 modem.transmit(operatingChannel, operatingChannel, {
  809.                     "chat-assistant-network", newID, messageQueue[1][1], messageQueue[1][2]
  810.                 })
  811.             end
  812.             table.remove(messageQueue, 1)
  813.         end
  814.     end
  815. end
  816.  
  817. local function receiveLoop()
  818.     while true do
  819.         local event, side, channel, reply, message = os.pullEvent()
  820.         if event == "modem_message" and channel == successChannel and type(message) == "string" then
  821.             if successCalls[message] then
  822.                 local err, msg = pcall(successCalls[message])
  823.                 if not err then
  824.                     print("Error while running remote callback")
  825.                     print(msg)
  826.                 end
  827.                 successCalls[message] = nil
  828.             end
  829.         end
  830.     end
  831. end
  832.  
  833. for k in pairs(matches) do
  834.     table.insert(orderedKeys, k)
  835. end
  836.  
  837. table.sort(orderedKeys)
  838.  
  839. parallel.waitForAny(mainLoop, sendLoop, receiveLoop, messageReceiveLoop)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement