Advertisement
PaymentOption

Mail Utility

Sep 6th, 2012
533
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.56 KB | None | 0 0
  1. -- THIS IS FREEWARE!!!
  2. -- Written and designed by PaymentOption (Grim Reaper)
  3.  
  4. -- Messages must be written to files with two \n's at the end of the message, just to ensure that the whole message is split when reading.
  5.  
  6. -- Parallel concept application:
  7. -- Every 5 seconds update the contacts with a timer.
  8. -- os.startTimer( 5 )
  9. -- function UpdateContacts()
  10. --[[
  11.     while true do
  12.         -- Wait for an event.
  13.         local event = os.pullEvent()
  14.        
  15.         -- If the timer triggered.
  16.         if event == "timer" then
  17.             -- Reload the contacts and restart the timer.
  18.             LoadContacts()
  19.             os.starTimer(5)
  20.         end
  21.     end
  22.    end
  23.    
  24.    ...
  25.    
  26.    while true do
  27.     parallel.waitForAny( UpdateContacts, RunShell, etc. )
  28.    end
  29. ]]--
  30.  
  31. -- Simple mail system addition to shell.
  32. -- Concept Top Down Design:
  33. -- Establish a program that will listen for incoming rednet activity and check if said activity meets the email format.
  34. -- If the rednet message is an email, alert the user.
  35. -- Use a directory for stored mail.
  36. -- Console based commands:
  37. -- mail send <ID> would trigger a prompt that said something like: "Please type your message below."
  38. -- Get as many lines as necessary with the limit read function designed earlier.
  39.  
  40. -- Format for an email:
  41. -- !GRIM_REAPER_MAIL_SYSTEM_2012_!MESSAGE
  42.  
  43. -- Additional features:
  44. -- Contacts:
  45. --  Stored in a table: tContacts[n] = { Name, ID }
  46. --  Function to read in contacts from a specified directory (probably mail/contacts/)
  47.  
  48. -- Variables --
  49. -- Globals --
  50. sMailFormat = "!jdf#0dfj!" -- This will be the string at the beginning of each mail message the signifies it as a mail message.
  51.  
  52. tContacts = {}
  53. nContactUpdateTime = 3 -- The amount of seconds before updating the contacts table in during "parallel" execution.
  54. sMailDirectory = "mail" -- Feel free to change this directory for your needs.
  55. sContactsDirectory = sMailDirectory .. "/contacts" -- Feel free to change this too.
  56. -------------
  57. -- IMPORTANT: the contacts directory string contains the full path of the contacts directory. To get the name of the directory, use fs.getName()
  58. ---------------
  59.  
  60. -- BORROWED FUNCTIONS --
  61. -- Borrowed from: http://lua-users.org/wiki/SplitJoin
  62. -- Compatibility: Lua-5.1
  63. function split(str, pat)
  64.    local t = {}  -- NOTE: use {n = 0} in Lua-5.0
  65.    local fpat = "(.-)" .. pat
  66.    local last_end = 1
  67.    local s, e, cap = str:find(fpat, 1)
  68.    while s do
  69.       if s ~= 1 or cap ~= "" then
  70.      table.insert(t,cap)
  71.       end
  72.       last_end = e+1
  73.       s, e, cap = str:find(fpat, last_end)
  74.    end
  75.    if last_end <= #str then
  76.       cap = str:sub(last_end)
  77.       table.insert(t, cap)
  78.    end
  79.    return t
  80. end
  81. ------------------------
  82.  
  83. -- Existance Checks (Check for existing directories, create directories if not, etc.) --
  84.  
  85. -- CheckAllDirectories()
  86. -- Checks if all the directories that need to exist, exit.
  87. -- If a directory isn't found then it is created.
  88. -- !WARNING! If a directory already exists but isn't the right one, then the function will try to catch it, but it might cause problems later!
  89. -- The catch will be checking if the contacts directory exists within the mail directory. Also, if any directories within the contacts directory
  90. -- will throw errors.
  91. function CheckAllDirectories()
  92.     local bMail_Check = false -- If the mail directory is setup properly.
  93.     local bContacts_Check = false -- If the contacts directory is setup properly.
  94.    
  95.     -- First check if the mail directory is setup properly.
  96.     if fs.exists( sMailDirectory ) and fs.isDir( sMailDirectory ) then
  97.         local tMailDir_Contents = fs.list( sMailDirectory ) -- Get the contents of the mail directory so we can check its contents for the contacts directory.
  98.        
  99.         -- Go through the contents of the mail directory and try to find the contacts directory.
  100.         for fileNumber,fileName in ipairs( tMailDir_Contents ) do
  101.             -- If we found the contacts directory, break out of the loop.
  102.             if fileName == fs.getName( sContactsDirectory ) then
  103.                 bMail_Check = true
  104.                 break
  105.             end
  106.         end
  107.        
  108.         -- If the mail directory is setup improperly.
  109.         if not bMail_Check then
  110.             -- Report the failure to the user.
  111.             error( "Mail directory checking failed.\nDelete " .. sMailDirectory .. " and try again." )
  112.         end
  113.     -- If the mail directory doesn't appear to exist, or is not a directory.
  114.     else
  115.         -- Attempt to create the directory so we can continue the program.
  116.         fs.makeDir( sMailDirectory )
  117.     end
  118.    
  119.     -- Next check if the contacts directory is setup properly.
  120.     if fs.exists( sContactsDirectory ) and fs.isDir( sContactsDirectory ) then
  121.         local tContactDir_Contents = fs.list( sContactsDirectory ) -- Get the contents of the contacts directory to check for directories in it.
  122.        
  123.         -- Go through the contents of the contacts directory and look for directories.
  124.         for fileIndex, fileName in ipairs( tContactDir_Contents ) do
  125.             -- If we find a directory then the contacts check failed; break out of the loop.
  126.             if fs.isDir( sContactsDirectory .. "/" .. fileName ) then
  127.                 bContacts_Check = false
  128.                 break
  129.             end
  130.            
  131.             -- After every iteration, if there was no exception then the loop won't be terminated, so the check is true until false.
  132.             bContacts_Check = true
  133.         end
  134.        
  135.         -- If the contacts directory is setup improperly.
  136.         if not bContacts_Check then
  137.             -- Report the failure to the user.
  138.             error( "Contacts directory checking failed.\nDelete " .. sContactsDirectory .. " and try again." )
  139.         end
  140.     else
  141.         -- Assuming that the mail directory check passed.
  142.         fs.makeDir( sContactsDirectory )
  143.     end
  144. end
  145.  
  146. -- CheckIfContactExists( sContactName )
  147. -- Checks if the contact name entered matches an ID in the contacts table so we can send
  148. -- the message to the correct ID.
  149. -- RETURNS:
  150. -- True and the ID if the name exists in the contacts table.
  151. -- False and nil if the name does not exist in the contacts table.
  152. function CheckIfContactExists_Name( sContactName )
  153.     -- Check if the ID exists within the contacts table.
  154.     for contactIndex,contactSubtable in ipairs( tContacts ) do
  155.         -- If we found a match in the contacts table with the contact name given.
  156.         if contactSubtable.Name == sContactName then
  157.             -- Return true and the ID of the contact given.
  158.             return true, contactSubtable.ID
  159.         end
  160.     end
  161.    
  162.     -- If the loop completed checking and found no results, then
  163.     -- the contact probably doesn't exist in our table.
  164.     return false, nil
  165. end
  166.  
  167. function CheckIfContactExists_ID( nContactID )
  168.     if nContactID and type( nContactID ) == "number" then
  169.         -- Run through all of the contacts in the contacts table.
  170.         for i=1, #tContacts do
  171.             if tContacts[i].ID == nContactID then
  172.                 return true, tContacts[i].Name
  173.             end
  174.         end
  175.        
  176.         return false, nil
  177.     else
  178.         return false, nil
  179.     end
  180. end
  181.  
  182. -- CheckIfMessageIsMail( sMessage )
  183. -- Checks if the given string is in the mail format.
  184. -- RETURNS:
  185. -- True and the mail without the formatting if the message is mail.
  186. -- False and nil and if the message is not mail.
  187. function CheckIfReceivedMessageIsMail( sMessage )
  188.     -- Check if the mail format string is in only in the begging of the message,
  189.     -- just in case it shows up in other places in the message.
  190.     if string.find( sMessage, sMailFormat, 1, string.len( sMailFormat ) ) then
  191.         -- Return true and the message without the formatting.
  192.         local sMessage_Unformatted = string.sub( sMessage, 1, string.len( sMailFormat ) )
  193.         return true, sMessage_Unformatted
  194.     -- If the mail format was not found in the message at the begging onf the message.
  195.     else
  196.         -- Report the lack of mail formatting; this message is just a regular rednet message.
  197.         return false,  nil
  198.     end
  199. end
  200. --------------------
  201.  
  202. -- Loading Methods (load information from files) --
  203.  
  204. -- LoadContacts()
  205. -- Loads all of the contact information from the contact directory into the following formatted table:
  206. -- tContacts[n] = { Name, ID }
  207. -- EXAMPLE:
  208. -- tContacts[3] = { Name = "Bob", ID = 45 }
  209. function LoadContacts()
  210.     -- Get the contents of the contacts directory and clear the contacts table.
  211.     local tContactDir_Contents = fs.list( sContactsDirectory )
  212.     local contactFile = nil
  213.     local contactNumber = 0
  214.     tContacts = {}
  215.    
  216.     -- Go through the contacts directory contens and read the information. Filename = Name, Contents = ID
  217.     for fileIndex,fileName in ipairs( tContactDir_Contents ) do
  218.         -- Open the next contact file in the loop, read the id from it, then close it.
  219.         contactFile = fs.open( sContactsDirectory .. "/" .. fileName, "r" )
  220.         contactNumber = contactFile.readLine()
  221.         contactFile.close()
  222.        
  223.         -- Add the new contact in a record.
  224.         tContacts[#tContacts+1] = { Name = fileName, ID = tonumber( contactNumber ) }
  225.     end
  226. end
  227.  
  228. -- UpdateContacts()
  229. -- Every 3 seconds a timer will be triggered and the event will be caught by this function whilst in parallel with the shell and other functions.
  230. -- The function will reload the contacts to make sure any changes are saved.
  231. function UpdateContacts()
  232.     while true do
  233.         local event = os.pullEvent()
  234.        
  235.         if event == "timer" then
  236.             -- Restart the timer for contacts updating and reload the contacts.
  237.             LoadContacts()
  238.             os.startTimer( nContactUpdateTime )
  239.         end
  240.     end
  241. end
  242.  
  243. -- ReadMail( sMessageName )
  244. -- NOTE:
  245. --[[
  246.     Mail is written to files with the ID of the sender on the first line.
  247. ]]--
  248. -- Prints out the contents of a message and scrolls if it is too long.
  249. -- Finds the name of the mssage and checks if it exists, then gets the contents.
  250. -- It determines the length of the message against how much space is left on the screen.
  251. function ReadMail( sMessageName )
  252.     local cursorPosX, cursorPosY = term.getCursorPos()
  253.     local messageFile = nil
  254.     local nScreenWidth, nScreenHeight = term.getSize()
  255.    
  256.     -- Check if the message really exsists in the mail directory.
  257.     if fs.exists( sMailDirectory .. "/" .. sMessageName ) then
  258.         local bCutOffMessage = false -- Whether or not we'll have to cut the message short so we can scroll.
  259.         local nLinesLeft_ToPrint = 0 -- If the message is cut off, this will be how many lines we have left to print.
  260.        
  261.         -- Get the message into a string that we can print.
  262.         messageFile = fs.open( sMailDirectory .. "/" .. sMessageName, "r" )
  263.         local sMessageContents = messageFile.readAll()
  264.         messageFile.close()
  265.        
  266.         -- Calculate the amount of lines we have to print on, just in case we need to "Press any key to continue" things.
  267.         local nLinesLeft_OnScreen = nScreenHeight - cursorPosY - 1 -- Subtract 1 to compensate for "Press any key to continue"
  268.         local nLinesInMessage = 0
  269.         local tLines = {}
  270.         local nFromID = 0
  271.         local sFromName_Contact = ""
  272.         local bFromContact = false
  273.        
  274.         -- Fill the lines tabel with every occurence of the endline marker "\n".
  275.         tLines = split( sMessageContents, "\n" )
  276.         nFromID = tonumber( tLines[1] ); table.remove( tLines, 1 ); table.remove( tLines, #tLines )
  277.         nLinesInMessage = #tLines
  278.         bFromContact, sFromName_Contact = CheckIfContactExists_ID( nFromID )
  279.        
  280.    
  281.         -- Check if the amount of lines in the message is greater than the amount of lines left to print on.
  282.         if nLinesInMessage > nLinesLeft_OnScreen then
  283.             bCutOffMessage = true
  284.             nLinesLeft_ToPrint = nLinesInMessage - nLinesLeft_OnScreen
  285.         end
  286.        
  287.         -- Continue to draw the lines until there are no more; stop as many times as necessary.
  288.         if bFromContact then
  289.             print( "From: " .. sFromName_Contact )
  290.             print( "------" .. string.rep( "-", string.len( sFromName_Contact ) ) )
  291.         else
  292.             print( "From: " .. nFromID )
  293.             print( "------" .. string.rep( "-", string.len( tostring( nFromID ) ) ) )
  294.         end
  295.        
  296.         local nLine = 1
  297.         while nLine ~= nLinesInMessage do
  298.             nLine = nLine + textutils.pagedPrint( tLines[nLine], ( nScreenHeight - 3 ) - nLine )
  299.         end
  300.     end
  301. end
  302.  
  303. -- WriteMessage( nFromID, sMessage )
  304. -- Creates a new message file in the mail directory and writes the message contents
  305. -- to the file in proper format under the name message#. # = The amount of messages + 1
  306. function WriteMail( nFromID, sMessage )
  307.     -- Get the amount of messages so we can calculate the name of the newest message.
  308.     local nMessageCount = #( fs.list( sMailDirectory ) ) - 1 -- Subtract 1 to compensate for the contacts directory.
  309.     local sMessageName = "message" .. nMessageCount + 1
  310.    
  311.     -- Open the file and write the message contents.
  312.     local messageFile = fs.open( sMailDirectory .. "/" .. sMessageName, "w" )
  313.     messageFile.writeLine( nFromID .. "\n" .. sMessage .. "\n" .. [[\n]] .. "\n" .. [[\n]] )
  314.     messageFile.close()
  315.    
  316.     return sMessageName
  317. end
  318. -------------------
  319.  
  320. -- Listening Methods (Listen for messages) --
  321. -- Parallel functions:
  322. -- To be ran in parallel with the shell.
  323. function ListenForMail()
  324.     -- Open the nearest modem.
  325.     local tSides = rs.getSides()
  326.     for index,side in ipairs( tSides ) do
  327.         if peripheral.isPresent( side ) and peripheral.getType( side ) == "modem" then
  328.             rednet.open( side )
  329.         end
  330.     end
  331.    
  332.     while true do
  333.         local event, sender, message = os.pullEvent()
  334.        
  335.         if event == "rednet_message" then
  336.             if CheckIfReceivedMessageIsMail( message ) then
  337.                 local sMessage = string.sub( message, string.len( sMailFormat ) + 1, string.len( message ) )
  338.                 local sMessageName = WriteMail( sender, sMessage )
  339.                
  340.                 local xPos, yPos = term.getCursorPos()
  341.                 term.setCursorPos( 1, yPos + 1 )
  342.                 print( "Received and saved mail as " .. sMailDirectory .. "/" .. sMessageName )
  343.                 term.setCursorPos( xPos, yPos )
  344.             end
  345.         end
  346.     end
  347. end
  348.  
  349. function RunShell()
  350.     while true do
  351.         shell.run( "shell" )
  352.     end
  353. end
  354.  
  355. function MainLoop()
  356.     while true do
  357.         parallel.waitForAny( ListenForMail, UpdateContacts, RunShell )
  358.     end
  359. end
  360. ----------------------
  361.  
  362. CheckAllDirectories()
  363. LoadContacts()
  364. term.clear(); term.setCursorPos( 1,1 )
  365. print( "Mail utility started." )
  366. sleep( 1 )
  367. term.clear(); term.setCursorPos( 1,1 )
  368. MainLoop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement