Advertisement
1lann

Chat Assistant Central Server

Aug 16th, 2014
411
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.93 KB | None | 0 0
  1. -- Chat Assistant
  2.  
  3. print("1lann's Chat Assistant Central Server")
  4. print("Please don't touch me :(")
  5.  
  6. local chat = peripheral.wrap("right")
  7. local modem = peripheral.wrap("top")
  8. local drive = peripheral.wrap("left")
  9. local operatingChannel = 27158
  10. local successChannel = 27159
  11.  
  12. if not chat then
  13.     error("Could not find chatbox!")
  14. end
  15.  
  16. if modem then
  17.     modem.closeAll()
  18.     modem.open(successChannel)
  19.     print("Modem ready")
  20. else
  21.     print("Warning: No modem found")
  22. end
  23.  
  24. if drive then
  25.     print("Disk drive detected")
  26.     if drive.getAudioTitle() then
  27.         print("Loaded with disk: "..drive.getAudioTitle())
  28.         drive.stopAudio()
  29.     else
  30.         print("No music disk found!")
  31.     end
  32. else
  33.     print("No disk drive detected")
  34. end
  35.  
  36. local database = {}
  37. local localDatabase = {}
  38. local messageQueue = {}
  39. local pendingReminders = {}
  40. local musicLength = 190
  41. local musicClock = musicLength * -1
  42. local successCalls = {}
  43. local orderedKeys = {}
  44.  
  45. local searchAndTrim = function(message, searches, limit)
  46.     if not limit then
  47.         limit = #message + 1
  48.     end
  49.     for k, v in pairs(searches) do
  50.         local startPos, endPos = message:lower():find(v)
  51.         local match = nil
  52.         local err, msg = pcall(function() match = message:lower():match(v) end)
  53.         if endPos and startPos <= limit then
  54.             if err then
  55.                 return true, message:sub(endPos, -1), {match}, startPos
  56.             else
  57.                 return true, message:sub(endPos, -1), {}, startPos
  58.             end
  59.         end
  60.     end
  61.     return false, message
  62. end
  63.  
  64. local saveDatabase = function()
  65.     local f = io.open("/chat-config", "w")
  66.     f:write(textutils.serialize(database))
  67.     f:close()
  68. end
  69.  
  70. local commands = {}
  71. commands["remind"] = function(time, units, player, message)
  72.     if time ~= nil and time > 0 then
  73.         if not database[player] then
  74.             database[player] = {}
  75.         end
  76.         if not database[player].reminders then
  77.             database[player].reminders = {}
  78.         end
  79.         if #database[player].reminders >= 3 then
  80.             return true, {"Sorry, I can only store 3 reminders at once.", "To cancel a reminder, you can \"cancel reminder\" it"}
  81.         end
  82.         if units == "sec" then
  83.             table.insert(database[player].reminders, {message, os.clock() + time})
  84.             saveDatabase()
  85.             return true, {"Okay, I will remind you in " .. time .. " seconds.", "Use \"cancel reminder\" to cancel your reminder"}
  86.         else
  87.             table.insert(database[player].reminders, {message, os.clock() + time*60})
  88.             saveDatabase()
  89.             return true, {"Okay, I will remind you in " .. time .. " minutes.", "Use \"cancel reminder\" to cancel your reminder"}
  90.         end
  91.     else
  92.         return true, {"Sorry, I couldn't understand", "when you wanted to be reminded :("}
  93.     end
  94. end
  95.  
  96. local matches = {}
  97. matches["1remind"] = function(player, message)
  98.     if searchAndTrim(message, {" in %d+"}, 3) then
  99.         local _, search = searchAndTrim(message, {" in %d"}, 3)
  100.         local secSuccess, secSearch, secMatch = searchAndTrim(search, {"(%d+) secs,? ", "(%d+) sec,? ", "(%d+) seconds,? ", "(%d+) second,? "}, 3)
  101.         local minSuccess, minSearch, minMatch = searchAndTrim(search, {"(%d+) min,? ", "(%d+) minute,? ", "(%d+) minutes,? ", "(%d+)mins,? "}, 3)
  102.         if secSuccess then
  103.             local remindSuccess, search = searchAndTrim(secSearch, {" remind me ", " reminder "}, 8)
  104.             if remindSuccess then
  105.                 _, search = searchAndTrim(search, {" to "}, 12)
  106.                 return commands.remind(tonumber(secMatch[1]), "sec", player, search:sub(2, -1))
  107.             end
  108.         elseif minSuccess then
  109.             local remindSuccess, search = searchAndTrim(minSearch, {" remind me ", " reminder "}, 8)
  110.             if remindSuccess then
  111.                 _, search = searchAndTrim(search, {" to "}, 12)
  112.                 return commands.remind(tonumber(minMatch[1]), "min", player, search:sub(2, -1))
  113.             end
  114.         end
  115.     elseif searchAndTrim(message, {" remind me ", " reminder "}, 10) then
  116.         if searchAndTrim(message, {" cancel "}, 10) then
  117.             return
  118.         end
  119.  
  120.         local _, search = searchAndTrim(message, {" remind me ", " reminder "})
  121.         local secSuccess, secSearch, secMatch = searchAndTrim(search, {" in (%d+) secs,? ", " in (%d+) sec,? ", " in (%d+) seconds,? ", " in (%d+) second,? "}, 3)
  122.         local minSuccess, minSearch, minMatch = searchAndTrim(search, {" in (%d+) min,? ", " in (%d+) minute,? ", " in (%d+) minutes,? ", " in (%d+)mins,? "}, 3)
  123.         if secSuccess then
  124.             _, search = searchAndTrim(secSearch, {" to "}, 12)
  125.             return commands.remind(tonumber(secMatch[1]), "sec", player, search:sub(2, -1))
  126.         elseif minSuccess then
  127.             _, search = searchAndTrim(minSearch, {" to "}, 12)
  128.             return commands.remind(tonumber(minMatch[1]), "min", player, search:sub(2, -1))
  129.         else
  130.             local searchSuccess, search = searchAndTrim(search, {" to "}, 12)
  131.             if searchSuccess then
  132.                 local secSuccess, _, secMatch, secStart = searchAndTrim(search, {" in (%d+) secs", " in (%d+) sec", " in (%d+) seconds", " in (%d+) second"})
  133.                 local minSuccess, _, minMatch, minStart = searchAndTrim(search, {" in (%d+) min", " in (%d+) minute", " in (%d+) minutes", " in (%d+)mins"})
  134.                 if secSuccess then
  135.                     return commands.remind(tonumber(secMatch[1]), "sec", player, search:sub(2, secStart - 1))
  136.                 elseif minSuccess then
  137.                     return commands.remind(tonumber(minMatch[1]), "min", player, search:sub(2, minStart - 1))
  138.                 else
  139.                     return true, {"Sorry, I couldn't understand", "when you wanted to be reminded :("}
  140.                 end
  141.             else
  142.                 return true, {"Sorry, I couldn't understand", "when you wanted to be reminded :("}
  143.             end
  144.         end
  145.     end
  146. end
  147.  
  148. matches["cancel-reminder"] = function(player, message)
  149.     if searchAndTrim(message, {" cancel reminder", " dismiss reminder", " delete reminder"}) then
  150.         if database[player] and database[player].reminders then
  151.             if #database[player].reminders < 1 then
  152.                 return true, {"You don't have any reminders set!"}
  153.             elseif #database[player].reminders == 1 then
  154.                 local reminderText = database[player].reminders[1][1]
  155.                 table.remove(database[player].reminders, 1)
  156.                 saveDatabase()
  157.                 return true, {"Alright, your reminder:", reminderText, "has been cancelled."}
  158.             else
  159.                 local _, search = searchAndTrim(message, {" cancel reminder", " dismiss reminder"})
  160.                 if searchAndTrim(search, {"%d+"}) then
  161.                     local _, _, match = searchAndTrim(search, {"(%d+)"})
  162.                     if tonumber(match[1]) <= #database[player].reminders then
  163.                         local reminderText = database[player].reminders[tonumber(match[1])][1]
  164.                         table.remove(database[player].reminders, tonumber(match[1]))
  165.                         saveDatabase()
  166.                         return true, {"Alright, your reminder:", reminderText, "has been cancelled."}
  167.                     else
  168.                         return true, {"Sorry, I could not find that reminder index!"}
  169.                     end
  170.                 end
  171.                 local returnTable = {}
  172.                 table.insert(returnTable, "Here are your active reminders: ")
  173.                 for k,v in pairs(database[player].reminders) do
  174.                     table.insert(returnTable, "#"..tostring(k)..": "..v[1])
  175.                 end
  176.                 table.insert(returnTable, "What reminder number would you like to cancel?")
  177.                 table.insert(returnTable, "You can append \"#\" or \"number\" as a prefix")
  178.                 return true, returnTable
  179.             end
  180.         end
  181.     elseif #localDatabase[player] > 0 and localDatabase[player][#localDatabase[player]][1] == "cancel-reminder" then
  182.         if searchAndTrim(message, {"^%d+", "^# ?%d+", "^number ?%d+"}) then
  183.             local _, _, match = searchAndTrim(message, {"(%d+)"})
  184.             if tonumber(match[1]) <= #database[player].reminders then
  185.                 local reminderText = database[player].reminders[tonumber(match[1])][1]
  186.                 table.remove(database[player].reminders, tonumber(match[1]))
  187.                 saveDatabase()
  188.                 return true, {"Alright, your reminder:", reminderText, "has been cancelled."}, true
  189.             else
  190.                 return true, {"Sorry, I could not find that reminder index!"}
  191.             end
  192.         end
  193.     end
  194. end
  195.  
  196. matches["list-reminders"] = function(player, message)
  197.     if searchAndTrim(message, {" what reminders? ", " what are my %S* ?reminder", " list reminder"}) then
  198.         if database[player] and database[player].reminders then
  199.             if #database[player].reminders < 1 then
  200.                 return true, {"You currently have no set reminders!"}
  201.             elseif #database[player].reminders == 1 then
  202.                 return true, {"You have 1 active reminder:", database[player].reminders[1][1], "Use \"cancel reminder\" to cancel a reminder"}
  203.             else
  204.                 local returnTable = {}
  205.                 table.insert(returnTable, "Your active reminders: ")
  206.                 for k,v in pairs(database[player].reminders) do
  207.                     table.insert(returnTable, "#"..k..": "..v[1])
  208.                 end
  209.                 table.insert(returnTable, "Use \"cancel reminder\" to cancel a reminder")
  210.                 return true, returnTable
  211.             end
  212.         end
  213.     end
  214. end
  215.  
  216. matches["compliment"] = function(player, message)
  217.     if #localDatabase[player] > 0 and (os.clock() - localDatabase[player][#localDatabase[player]][2]) < 30 then
  218.         if searchAndTrim(message, {"thank", "kudo", "cool", "nice", "wow", "awesome"}) then
  219.             return true, {"If that was a compliment directed", "towards me, Thank you! :D"}
  220.         end
  221.     end
  222. end
  223.  
  224. matches["time"] = function(player, message)
  225.     if searchAndTrim(message, {" what ", " whats ", " what's ", " what is "}) then
  226.         local _, search = searchAndTrim(message, {" what ", " whats ", " what's ", " what is "})
  227.         if searchAndTrim(search, {" time"}, 15) then
  228.             _, search = searchAndTrim(search, {" time"}, 15)
  229.             if searchAndTrim(search, {" for ", " in "}, 8) then
  230.                 return true, {"Sorry, I cannot provide location based time,", "I can only provide in game time"}
  231.             else
  232.                 return true, {"The current in-game time is " .. textutils.formatTime(os.time())}
  233.             end
  234.         end
  235.     end
  236. end
  237.  
  238. matches["help"] = function(player, message)
  239.     if #localDatabase[player] > 0 and (os.clock() - localDatabase[player][#localDatabase[player]][2]) < 30 then
  240.         if searchAndTrim(message, {" what is this", " whats that", " what's that", " what is chat", " what's chat", " what are you"}) then
  241.             return true, {"Hello there, I'm Chat Assistant!",
  242.             "I'm made by 1lann and am still in",
  243.             "development. Anyways, thanks",
  244.             "for trying me out! :D"}
  245.         end
  246.     end
  247. end
  248.  
  249. matches["1stopwatch"] = function(player, message)
  250.     if searchAndTrim(message, {" start stopwatch", " start counting", " stopwatch start", " start the stopwatch"}) then
  251.         localDatabase[player].stopwatch = {}
  252.         localDatabase[player].stopwatch.running = true
  253.         localDatabase[player].stopwatch.start = os.clock()
  254.         return true, {"Alright, I have started the stopwatch."}
  255.     elseif searchAndTrim(message, {" stop stopwatch", " quit stopwatch", " stop counting", " quit counting",
  256.         " stop the stopwatch", " quit the stopwatch", " stopwatch stop", " stopwatch end", " stopwatch quit",
  257.         " end counting", " end stopwatch"}) then
  258.         if localDatabase[player].stopwatch then
  259.             if localDatabase[player].stopwatch.running then
  260.                 localDatabase[player].stopwatch.running = false
  261.                 localDatabase[player].stopwatch.start = os.clock() - localDatabase[player].stopwatch.start
  262.                 local reading = math.ceil(localDatabase[player].stopwatch.start*100 - 0.5)/100
  263.                 return true, {"Okay, I stopped the stopwatch", "The stopwatch reads: "..reading.." seconds"}
  264.             else
  265.                 local reading = math.ceil(localDatabase[player].stopwatch.start*100 - 0.5)/100
  266.                 return true, {"The stopwatch has already been stopped", "The stopwatch reads: "..reading.." seconds"}
  267.             end
  268.         else
  269.  
  270.         end
  271.     elseif searchAndTrim(message, {" how much time", " stopwatch", " time passed"}) then
  272.         if localDatabase[player].stopwatch then
  273.             if localDatabase[player].stopwatch.running then
  274.                 local rawReading = os.clock() - localDatabase[player].stopwatch.start
  275.                 local reading = math.ceil(rawReading*100 - 0.5)/100
  276.                 return true, {"The running stopwatch reads: "..reading.." seconds"}
  277.             else
  278.                 local reading = math.ceil(localDatabase[player].stopwatch.start*100 - 0.5)/100
  279.                 return true, {"The stopped stopwatch reads: "..reading.." seconds"}
  280.             end
  281.         else
  282.             return true, {"You have never started the stopwatch!"}
  283.         end
  284.     end
  285. end
  286.  
  287. local alphabet = "abcdefghijklmnopqrstuvwxyz"
  288.  
  289. matches["math"] = function(player, message)
  290.     local matchSuccess, _, search = searchAndTrim(message, {" what's (%(*%d.+)$", " what is (%(*%d.+)$", " whats (%(*%d.+)$", "^%S+ (%(*%d.+)$", "^(%(*%d.+)$", " what does (%(*%d.+)$", " what is (%(*%d.+)$"})
  291.     if matchSuccess then
  292.         search = search[1]
  293.     end
  294.     if matchSuccess and (search:find("%+") or search:find("%*") or search:find("%^") or search:find("%/") or search:find("%-")) then
  295.         search = search:gsub("?", "")
  296.         search = search:gsub("=", "")
  297.         for i = 1, #alphabet do
  298.             search = search:gsub(alphabet:sub(i, i), "")
  299.         end
  300.  
  301.         local func = loadstring("return "..search)
  302.         local result = nil
  303.         if func then
  304.             if pcall(function() result = func() end) then
  305.                 return true, {"The answer is: "..tostring(result)}
  306.             else
  307.                 return true, {"Sorry, I couldn't calculate that!", "Make sure you don't use any", "alphabetical letters in it"}
  308.             end
  309.         else
  310.             return true, {"Sorry, I couldn't process that calculation!", "Make sure you don't use any", "alphabetical letters in it"}
  311.         end
  312.     end
  313. end
  314.  
  315. matches["1music"] = function(player, message)
  316.     if searchAndTrim(message, {" play ", " listen to "}) then
  317.         local _, search = searchAndTrim(message, {" play ", " listen to ", " start "})
  318.         if drive then
  319.             if searchAndTrim(search, {" music", " some music", " song", " some song", " the music", " the song", tostring(drive.hasAudio())}, 3) then
  320.                 if drive.getAudioTitle() and (os.clock() - musicClock) < musicLength then
  321.                     return true, {"Music is already playing!"}, false, true
  322.                 elseif drive.getAudioTitle() then
  323.                     return true, {"Now playing: "..drive.getAudioTitle()}, false, true, function()
  324.                         musicClock = os.clock()
  325.                         drive.playAudio()
  326.                     end
  327.                 else
  328.                     return true, {"I don't have any music in", "my disk drive to play :("}, false, true
  329.                 end
  330.             else
  331.                 return true, {"Sorry, I currently can only play", "the disk in my disk drive :("}
  332.             end
  333.         else
  334.             return true, {"Sorry, I don't have a disk drive", "to play music with :("}, false, true
  335.         end
  336.     elseif searchAndTrim(message, {" what ", " whats ", " what's ", " what is "}) then
  337.         local _, search = searchAndTrim(message, {" what ", " whats ", " what's ", " what is "})
  338.         if searchAndTrim(search, {" song", " this song", " the song", " music", " this music", " playing", " the music", " the current song", " this current song", " currently play"}, 3) then
  339.             if drive and drive.getAudioTitle() and (os.clock() - musicClock) < musicLength then
  340.                 return true, {"The currently playing song is", drive.getAudioTitle()}
  341.             else
  342.                 return true, {"There currently is no music playing!"}, false, true
  343.             end
  344.         end
  345.     elseif searchAndTrim(message, {" stop ", " pause "}) then
  346.         local _, search = searchAndTrim(message, {" stop ", " pause "})
  347.         if searchAndTrim(search, {" play", " music", " the music", " the song", " this music", " this song"}, 3) then
  348.             if drive and drive.getAudioTitle() and (os.clock() - musicClock) < musicLength then
  349.                 return true, {"Ok, music stopped"}, false, true, function()
  350.                     drive.stopAudio()
  351.                     musicClock = musicLength * -1
  352.                 end
  353.             else
  354.                 return true, {"There currently is no music playing!"}, false, true
  355.             end
  356.         end
  357.     end
  358. end
  359.  
  360. if fs.exists("/chat-config") then
  361.     local f = io.open("/chat-config")
  362.     database = textutils.unserialize(f:read("*a"))
  363.     f:close()
  364. end
  365.  
  366. if database then
  367.     print("Database loaded!")
  368. else
  369.     database = {}
  370.     print("Database failed to be loaded!")
  371. end
  372.  
  373. print("Running chat assistant")
  374.  
  375. local tell = function(player, messages, successCallback, localAction)
  376.     table.insert(messageQueue, {player, "---- Chat Assistant ----", successCallback, localAction})
  377.     for k,v in pairs(messages) do
  378.         table.insert(messageQueue, {player, v, nil, localAction})
  379.     end
  380. end
  381.  
  382. for k,v in pairs(database) do
  383.     if v.reminders and #v.reminders > 0 then
  384.         for i = 1, #v.reminders do
  385.             database[k].reminders[i][2] = 0
  386.         end
  387.     end
  388. end
  389.  
  390. local checkTimer = os.startTimer(1)
  391.  
  392. local success, messages, clear, localAction, callbackFunc = nil, nil, nil, nil, nil
  393.  
  394. local function mainLoop()
  395.     while true do
  396.         local e, side, player, message = os.pullEvent()
  397.         if e == "chat_message" or e == "chatbox_command" then
  398.             if message:sub(1, 2) == "$$" then
  399.                 message = message:sub(3, -1)
  400.             end
  401.  
  402.             message = " " .. message
  403.  
  404.             if not localDatabase[player] then
  405.                 localDatabase[player] = {}
  406.             end
  407.  
  408.             if message == "cancel" then
  409.                 table.insert(localDatabase[player], {"clear", os.clock()})
  410.             end
  411.  
  412.             for _, key in pairs(orderedKeys) do
  413.                 local err, msg = pcall(function() success, messages, clear, localAction, callbackFunc = matches[key](player, message) end)
  414.                 if not err then
  415.                     print("An error has occured while running: "..key)
  416.                     print(msg)
  417.                 else
  418.                     if success then
  419.                         tell(player, messages, callbackFunc, localAction)
  420.                         if clear then
  421.                             table.insert(localDatabase[player], {"clear", os.clock()})
  422.                         else
  423.                             table.insert(localDatabase[player], {key, os.clock()})
  424.                         end
  425.                         break
  426.                     end
  427.                 end
  428.                 success, messages, clear, localAction, callbackFunc = nil, nil, nil, nil
  429.             end
  430.         elseif e == "timer" and side == checkTimer then
  431.             checkTimer = os.startTimer(1)
  432.             for k,v in pairs(database) do
  433.                 if v.reminders and #v.reminders > 0 then
  434.                     for i = 1, #v.reminders do
  435.                         if v.reminders[#v.reminders - i + 1] and os.clock() >= v.reminders[#v.reminders - i + 1][2] then
  436.                             if (not pendingReminders[tostring(#v.reminders - i + 1)]) or pendingReminders[tostring(#v.reminders - i + 1)] < os.clock() then
  437.                                 pendingReminders[tostring(#v.reminders - i + 1)] = os.clock() + 5
  438.                                 tell(k, {"Reminder: " .. v.reminders[#v.reminders - i + 1][1]},
  439.                                 function()
  440.                                     pendingReminders[tostring(#v.reminders - i + 1)] = nil
  441.                                     table.remove(database[k].reminders, #v.reminders - i + 1)
  442.                                     saveDatabase()
  443.                                 end)
  444.                             end
  445.                         end
  446.                     end
  447.                 end
  448.             end
  449.         end
  450.     end
  451. end
  452.  
  453. local function sendLoop()
  454.     while true do
  455.         sleep(0.6)
  456.         if #messageQueue > 0 then
  457.             local result = nil
  458.             while not pcall(function() result = chat.tell(messageQueue[1][1], messageQueue[1][2]) end) do
  459.                 sleep(0.2)
  460.             end
  461.             if result then
  462.                 if messageQueue[1][3] then
  463.                     local err, msg = pcall(messageQueue[1][3])
  464.                     if not err then
  465.                         print("Error while running callback")
  466.                         print(msg)
  467.                     end
  468.                 end
  469.             elseif modem and not messageQueue[1][4] then
  470.                 local newID = tostring({}):sub(8, -1)
  471.                 if messageQueue[1][3] then
  472.                     successCalls[newID] = messageQueue[1][3]
  473.                 end
  474.                 modem.transmit(operatingChannel, operatingChannel, {
  475.                     "chat-assistant-network", newID, messageQueue[1][1], messageQueue[1][2]
  476.                 })
  477.             end
  478.             table.remove(messageQueue, 1)
  479.         end
  480.     end
  481. end
  482.  
  483. local function receiveLoop()
  484.     while true do
  485.         local event, side, channel, reply, message = os.pullEvent()
  486.         if event == "modem_message" and channel == successChannel and type(message) == "string" then
  487.             if successCalls[message] then
  488.                 local err, msg = pcall(successCalls[message])
  489.                 if not err then
  490.                     print("Error while running remote callback")
  491.                     print(msg)
  492.                 end
  493.                 successCalls[message] = nil
  494.             end
  495.         end
  496.     end
  497. end
  498.  
  499. for k in pairs(matches) do
  500.     table.insert(orderedKeys, k)
  501. end
  502.  
  503. table.sort(orderedKeys)
  504.  
  505. parallel.waitForAny(mainLoop, sendLoop, receiveLoop)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement