Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- THIS IS FREEWARE!!!
- -- Written and designed by PaymentOption (Grim Reaper)
- -- 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.
- -- Parallel concept application:
- -- Every 5 seconds update the contacts with a timer.
- -- os.startTimer( 5 )
- -- function UpdateContacts()
- --[[
- while true do
- -- Wait for an event.
- local event = os.pullEvent()
- -- If the timer triggered.
- if event == "timer" then
- -- Reload the contacts and restart the timer.
- LoadContacts()
- os.starTimer(5)
- end
- end
- end
- ...
- while true do
- parallel.waitForAny( UpdateContacts, RunShell, etc. )
- end
- ]]--
- -- Simple mail system addition to shell.
- -- Concept Top Down Design:
- -- Establish a program that will listen for incoming rednet activity and check if said activity meets the email format.
- -- If the rednet message is an email, alert the user.
- -- Use a directory for stored mail.
- -- Console based commands:
- -- mail send <ID> would trigger a prompt that said something like: "Please type your message below."
- -- Get as many lines as necessary with the limit read function designed earlier.
- -- Format for an email:
- -- !GRIM_REAPER_MAIL_SYSTEM_2012_!MESSAGE
- -- Additional features:
- -- Contacts:
- -- Stored in a table: tContacts[n] = { Name, ID }
- -- Function to read in contacts from a specified directory (probably mail/contacts/)
- -- Variables --
- -- Globals --
- sMailFormat = "!jdf#0dfj!" -- This will be the string at the beginning of each mail message the signifies it as a mail message.
- tContacts = {}
- nContactUpdateTime = 3 -- The amount of seconds before updating the contacts table in during "parallel" execution.
- sMailDirectory = "mail" -- Feel free to change this directory for your needs.
- sContactsDirectory = sMailDirectory .. "/contacts" -- Feel free to change this too.
- -------------
- -- IMPORTANT: the contacts directory string contains the full path of the contacts directory. To get the name of the directory, use fs.getName()
- ---------------
- -- BORROWED FUNCTIONS --
- -- Borrowed from: http://lua-users.org/wiki/SplitJoin
- -- Compatibility: Lua-5.1
- function split(str, pat)
- local t = {} -- NOTE: use {n = 0} in Lua-5.0
- local fpat = "(.-)" .. pat
- local last_end = 1
- local s, e, cap = str:find(fpat, 1)
- while s do
- if s ~= 1 or cap ~= "" then
- table.insert(t,cap)
- end
- last_end = e+1
- s, e, cap = str:find(fpat, last_end)
- end
- if last_end <= #str then
- cap = str:sub(last_end)
- table.insert(t, cap)
- end
- return t
- end
- ------------------------
- -- Existance Checks (Check for existing directories, create directories if not, etc.) --
- -- CheckAllDirectories()
- -- Checks if all the directories that need to exist, exit.
- -- If a directory isn't found then it is created.
- -- !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!
- -- The catch will be checking if the contacts directory exists within the mail directory. Also, if any directories within the contacts directory
- -- will throw errors.
- function CheckAllDirectories()
- local bMail_Check = false -- If the mail directory is setup properly.
- local bContacts_Check = false -- If the contacts directory is setup properly.
- -- First check if the mail directory is setup properly.
- if fs.exists( sMailDirectory ) and fs.isDir( sMailDirectory ) then
- local tMailDir_Contents = fs.list( sMailDirectory ) -- Get the contents of the mail directory so we can check its contents for the contacts directory.
- -- Go through the contents of the mail directory and try to find the contacts directory.
- for fileNumber,fileName in ipairs( tMailDir_Contents ) do
- -- If we found the contacts directory, break out of the loop.
- if fileName == fs.getName( sContactsDirectory ) then
- bMail_Check = true
- break
- end
- end
- -- If the mail directory is setup improperly.
- if not bMail_Check then
- -- Report the failure to the user.
- error( "Mail directory checking failed.\nDelete " .. sMailDirectory .. " and try again." )
- end
- -- If the mail directory doesn't appear to exist, or is not a directory.
- else
- -- Attempt to create the directory so we can continue the program.
- fs.makeDir( sMailDirectory )
- end
- -- Next check if the contacts directory is setup properly.
- if fs.exists( sContactsDirectory ) and fs.isDir( sContactsDirectory ) then
- local tContactDir_Contents = fs.list( sContactsDirectory ) -- Get the contents of the contacts directory to check for directories in it.
- -- Go through the contents of the contacts directory and look for directories.
- for fileIndex, fileName in ipairs( tContactDir_Contents ) do
- -- If we find a directory then the contacts check failed; break out of the loop.
- if fs.isDir( sContactsDirectory .. "/" .. fileName ) then
- bContacts_Check = false
- break
- end
- -- After every iteration, if there was no exception then the loop won't be terminated, so the check is true until false.
- bContacts_Check = true
- end
- -- If the contacts directory is setup improperly.
- if not bContacts_Check then
- -- Report the failure to the user.
- error( "Contacts directory checking failed.\nDelete " .. sContactsDirectory .. " and try again." )
- end
- else
- -- Assuming that the mail directory check passed.
- fs.makeDir( sContactsDirectory )
- end
- end
- -- CheckIfContactExists( sContactName )
- -- Checks if the contact name entered matches an ID in the contacts table so we can send
- -- the message to the correct ID.
- -- RETURNS:
- -- True and the ID if the name exists in the contacts table.
- -- False and nil if the name does not exist in the contacts table.
- function CheckIfContactExists_Name( sContactName )
- -- Check if the ID exists within the contacts table.
- for contactIndex,contactSubtable in ipairs( tContacts ) do
- -- If we found a match in the contacts table with the contact name given.
- if contactSubtable.Name == sContactName then
- -- Return true and the ID of the contact given.
- return true, contactSubtable.ID
- end
- end
- -- If the loop completed checking and found no results, then
- -- the contact probably doesn't exist in our table.
- return false, nil
- end
- function CheckIfContactExists_ID( nContactID )
- if nContactID and type( nContactID ) == "number" then
- -- Run through all of the contacts in the contacts table.
- for i=1, #tContacts do
- if tContacts[i].ID == nContactID then
- return true, tContacts[i].Name
- end
- end
- return false, nil
- else
- return false, nil
- end
- end
- -- CheckIfMessageIsMail( sMessage )
- -- Checks if the given string is in the mail format.
- -- RETURNS:
- -- True and the mail without the formatting if the message is mail.
- -- False and nil and if the message is not mail.
- function CheckIfReceivedMessageIsMail( sMessage )
- -- Check if the mail format string is in only in the begging of the message,
- -- just in case it shows up in other places in the message.
- if string.find( sMessage, sMailFormat, 1, string.len( sMailFormat ) ) then
- -- Return true and the message without the formatting.
- local sMessage_Unformatted = string.sub( sMessage, 1, string.len( sMailFormat ) )
- return true, sMessage_Unformatted
- -- If the mail format was not found in the message at the begging onf the message.
- else
- -- Report the lack of mail formatting; this message is just a regular rednet message.
- return false, nil
- end
- end
- --------------------
- -- Loading Methods (load information from files) --
- -- LoadContacts()
- -- Loads all of the contact information from the contact directory into the following formatted table:
- -- tContacts[n] = { Name, ID }
- -- EXAMPLE:
- -- tContacts[3] = { Name = "Bob", ID = 45 }
- function LoadContacts()
- -- Get the contents of the contacts directory and clear the contacts table.
- local tContactDir_Contents = fs.list( sContactsDirectory )
- local contactFile = nil
- local contactNumber = 0
- tContacts = {}
- -- Go through the contacts directory contens and read the information. Filename = Name, Contents = ID
- for fileIndex,fileName in ipairs( tContactDir_Contents ) do
- -- Open the next contact file in the loop, read the id from it, then close it.
- contactFile = fs.open( sContactsDirectory .. "/" .. fileName, "r" )
- contactNumber = contactFile.readLine()
- contactFile.close()
- -- Add the new contact in a record.
- tContacts[#tContacts+1] = { Name = fileName, ID = tonumber( contactNumber ) }
- end
- end
- -- UpdateContacts()
- -- 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.
- -- The function will reload the contacts to make sure any changes are saved.
- function UpdateContacts()
- while true do
- local event = os.pullEvent()
- if event == "timer" then
- -- Restart the timer for contacts updating and reload the contacts.
- LoadContacts()
- os.startTimer( nContactUpdateTime )
- end
- end
- end
- -- ReadMail( sMessageName )
- -- NOTE:
- --[[
- Mail is written to files with the ID of the sender on the first line.
- ]]--
- -- Prints out the contents of a message and scrolls if it is too long.
- -- Finds the name of the mssage and checks if it exists, then gets the contents.
- -- It determines the length of the message against how much space is left on the screen.
- function ReadMail( sMessageName )
- local cursorPosX, cursorPosY = term.getCursorPos()
- local messageFile = nil
- local nScreenWidth, nScreenHeight = term.getSize()
- -- Check if the message really exsists in the mail directory.
- if fs.exists( sMailDirectory .. "/" .. sMessageName ) then
- local bCutOffMessage = false -- Whether or not we'll have to cut the message short so we can scroll.
- local nLinesLeft_ToPrint = 0 -- If the message is cut off, this will be how many lines we have left to print.
- -- Get the message into a string that we can print.
- messageFile = fs.open( sMailDirectory .. "/" .. sMessageName, "r" )
- local sMessageContents = messageFile.readAll()
- messageFile.close()
- -- Calculate the amount of lines we have to print on, just in case we need to "Press any key to continue" things.
- local nLinesLeft_OnScreen = nScreenHeight - cursorPosY - 1 -- Subtract 1 to compensate for "Press any key to continue"
- local nLinesInMessage = 0
- local tLines = {}
- local nFromID = 0
- local sFromName_Contact = ""
- local bFromContact = false
- -- Fill the lines tabel with every occurence of the endline marker "\n".
- tLines = split( sMessageContents, "\n" )
- nFromID = tonumber( tLines[1] ); table.remove( tLines, 1 ); table.remove( tLines, #tLines )
- nLinesInMessage = #tLines
- bFromContact, sFromName_Contact = CheckIfContactExists_ID( nFromID )
- -- Check if the amount of lines in the message is greater than the amount of lines left to print on.
- if nLinesInMessage > nLinesLeft_OnScreen then
- bCutOffMessage = true
- nLinesLeft_ToPrint = nLinesInMessage - nLinesLeft_OnScreen
- end
- -- Continue to draw the lines until there are no more; stop as many times as necessary.
- if bFromContact then
- print( "From: " .. sFromName_Contact )
- print( "------" .. string.rep( "-", string.len( sFromName_Contact ) ) )
- else
- print( "From: " .. nFromID )
- print( "------" .. string.rep( "-", string.len( tostring( nFromID ) ) ) )
- end
- local nLine = 1
- while nLine ~= nLinesInMessage do
- nLine = nLine + textutils.pagedPrint( tLines[nLine], ( nScreenHeight - 3 ) - nLine )
- end
- end
- end
- -- WriteMessage( nFromID, sMessage )
- -- Creates a new message file in the mail directory and writes the message contents
- -- to the file in proper format under the name message#. # = The amount of messages + 1
- function WriteMail( nFromID, sMessage )
- -- Get the amount of messages so we can calculate the name of the newest message.
- local nMessageCount = #( fs.list( sMailDirectory ) ) - 1 -- Subtract 1 to compensate for the contacts directory.
- local sMessageName = "message" .. nMessageCount + 1
- -- Open the file and write the message contents.
- local messageFile = fs.open( sMailDirectory .. "/" .. sMessageName, "w" )
- messageFile.writeLine( nFromID .. "\n" .. sMessage .. "\n" .. [[\n]] .. "\n" .. [[\n]] )
- messageFile.close()
- return sMessageName
- end
- -------------------
- -- Listening Methods (Listen for messages) --
- -- Parallel functions:
- -- To be ran in parallel with the shell.
- function ListenForMail()
- -- Open the nearest modem.
- local tSides = rs.getSides()
- for index,side in ipairs( tSides ) do
- if peripheral.isPresent( side ) and peripheral.getType( side ) == "modem" then
- rednet.open( side )
- end
- end
- while true do
- local event, sender, message = os.pullEvent()
- if event == "rednet_message" then
- if CheckIfReceivedMessageIsMail( message ) then
- local sMessage = string.sub( message, string.len( sMailFormat ) + 1, string.len( message ) )
- local sMessageName = WriteMail( sender, sMessage )
- local xPos, yPos = term.getCursorPos()
- term.setCursorPos( 1, yPos + 1 )
- print( "Received and saved mail as " .. sMailDirectory .. "/" .. sMessageName )
- term.setCursorPos( xPos, yPos )
- end
- end
- end
- end
- function RunShell()
- while true do
- shell.run( "shell" )
- end
- end
- function MainLoop()
- while true do
- parallel.waitForAny( ListenForMail, UpdateContacts, RunShell )
- end
- end
- ----------------------
- CheckAllDirectories()
- LoadContacts()
- term.clear(); term.setCursorPos( 1,1 )
- print( "Mail utility started." )
- sleep( 1 )
- term.clear(); term.setCursorPos( 1,1 )
- MainLoop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement