Trystan_C_C

Gmail-Server

Nov 23rd, 2012
169
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 17.32 KB | None | 0 0
  1. --[[
  2.         Gmail-Server            PaymentOption
  3.                                 Google on BlackWolfCC
  4.                                 23 November 2012
  5.                                
  6.         Mail server script to correspond and handle
  7.         mail requests within a 50 block radius
  8.         (or larger depending on CC config file)
  9.         from the Gmail-Client script.
  10.        
  11.         Basic Model:
  12.                 - Client -> Server -> Receiver -> Server(Confirmation)
  13.                 -> Client(Confirmation) or Stored to resend until
  14.                 confirmation received.
  15.                
  16.         Message Model:
  17.                 {sender = (number), receiver = (number),
  18.                  message = (table), subject = (string),
  19.                  attachment = {code = (string)}}
  20.                 - Attachments will only be sent if the attachment table
  21.                   exists within the message table provided.
  22.                 - The whole message will be in a table, and therefore
  23.                   will be serialized and unserialized upon sending and
  24.                   receiving.
  25. ]]--
  26.  
  27. -- Variables --
  28. local unsentMessagesDirectory = "Gmail-Server/Unsent_Mail" -- The directory in which
  29.                                                            -- mail that hasn't been
  30.                                                            -- sent is stored.
  31. local listenTimeout = 10 -- The time in seconds for the server to wait and listen
  32.                          -- for incoming mail before attempting to resend all
  33.                          -- unsent messages.
  34. local confirmationTimeout = 0.3 -- The time in seconds for the server to wait
  35.                                 -- for a confirmation message from a receiver.
  36. local confirmationFormat = "ConfirmedReception" -- The message that must be
  37.                                                 -- received from a receiver to
  38.                                                 -- confirm a successful message
  39.                                                 -- send.
  40. local sentMessageCount = 0 -- The number of messages that have been sent.
  41. local screenWidth, screenHeight = term.getSize() -- The x and y dimensions of the
  42.                                                  -- screen we're on.
  43. local exitOption = {label = "Exit", xPos = 1, yPos = screenHeight}
  44. -- Variables --
  45.  
  46.  
  47.  
  48. -- Rednet Functions --
  49. -- Opens or close any modem found on this computer. An error is thrown if
  50. -- no modem on the computer was found.
  51. -- Params : open - A boolean value that determines whehter or not the modem is
  52. --                 opened or closed.
  53. -- Returns: nil
  54. function openOrCloseModem(open)
  55.         for sideIndex, side in pairs(rs.getSides()) do
  56.                 if peripheral.isPresent(side) and peripheral.getType(side) == "modem" then
  57.                         if open then
  58.                                 rednet.open(side)
  59.                         else
  60.                                 rednet.close(side)
  61.                         end
  62.                         return
  63.                 end
  64.         end
  65.        
  66.         error("No modem found. Aborting.")
  67. end
  68.  
  69. -- Sends a message provided in its unserialized for in a serialized format
  70. -- to the receiver. After sending a message the function waits for 0.3 seconds
  71. -- for a confirmation; if received the function returns true; if not, false.
  72. -- Params : unserializedMessage - The unserializedMessage table.
  73. -- Returns: true or false - Depending on whether or not a confirmation was
  74. --                          received from the receiver.
  75. function sendUnserializedMessage(unserializedMessage)
  76.         local receiverID = unserializedMessage.receiver
  77.         rednet.send(receiverID, textutils.serialize(unserializedMessage))
  78.        
  79.         local confirmationID, confirmationMessage = rednet.receive(confirmationTimeout)
  80.         if confirmationID == receiverID and confirmationMessage == confirmationFormat then
  81.                 sentMessageCount = sentMessageCount + 1
  82.                 return true
  83.         end
  84.        
  85.         return false
  86. end
  87.  
  88. -- Resends all of the unsent messages from the unsent messages directory from
  89. -- top to bottom, waiting for confirmations after each one. If an unsent
  90. -- message is resent and the server receives a confirmation of reception, then
  91. -- the unsent message will be delete from the unsent messages directory.
  92. -- Params : nil
  93. -- Returns: nil
  94. function resendUnsentMessages()
  95.         local unsentMessagesDirectoryContents = fs.list(unsentMessagesDirectory)
  96.         for index, unsentMessageName in ipairs(unsentMessagesDirectoryContents) do
  97.                 local unserializedMessage = loadUnsentMessageInUnserializedFormat(unsentMessageName)
  98.                
  99.                 if sendUnserializedMessage(unserializedMessage) then
  100.                         deleteUnsentMessage(unsentMessageName)
  101.                 end
  102.         end
  103. end
  104.  
  105. -- Listens for incoming messages for 10 seconds, then resends all unsent messages
  106. -- , only to re-listen for incoming messages to send. This function runs in an infinite
  107. -- loop. This should be used as the main function for listening.
  108. -- Params : nil
  109. -- Returns: nil
  110. function listenForIncomingMail()
  111.         local listenTimer = os.startTimer(listenTimeout)
  112.         local updateTimer = os.startTimer(1) -- The amount of time to wait before
  113.                                             -- updating the screen.
  114.        
  115.         while true do
  116.                 -- Redraw the main screen with the updated information.
  117.                 drawMainScreen()
  118.                
  119.                 local event, incomingID, incomingSerializedMessage, p3 = os.pullEvent()
  120.                 if event == "rednet_message" then
  121.                         local unserializedMessage = textutils.unserialize(incomingSerializedMessage)
  122.                        
  123.                         -- Check if the two 'sender' and 'receiver' components of the message
  124.                         -- received exist; if so, then send the message; if not, throw it away.
  125.                         if unserializedMessage.receiver and unserializedMessage.sender then
  126.                                 if not sendUnserializedMessage(unserializedMessage) then
  127.                                         -- Save the message because it was not sent (the server did)
  128.                                         -- not receive a confirmation message.
  129.                                         saveUnsentMessage(unserializedMessage)
  130.                                 end
  131.                         end
  132.                 elseif event == "timer" then
  133.                         -- If the timeout for listening has expired, then attempt to resend
  134.                         -- all unsent messages. Afterwards, restart the timer.
  135.                         if incomingID == listenTimer then
  136.                                 resendUnsentMessages()
  137.                         -- If the timeout for updating the screen has expired, then
  138.                         -- redraw the current statistics on teh screen.
  139.                         elseif incomingID == updateTimer then
  140.                                 drawMainScreen()
  141.                         end
  142.                 -- If the event was a mouse click, then check if we are to exit
  143.                 -- the program by returning false.
  144.                 elseif event == "mouse_click" then
  145.                         -- Check if the click was on the exit button.
  146.                         -- In this case, the incoming message will be the
  147.                         -- xPos for the click that occurred.
  148.                         if checkIfClickWasOnTextAtPos(incomingSerializedMessage, p3, exitOption.xPos, exitOption.yPos, exitOption.label) then
  149.                                 return false
  150.                         end
  151.                 end
  152.                 -- Reset timers.
  153.                 listenTimer = os.startTimer(listenTimeout)
  154.                 updateTimer = os.startTimer(1)
  155.         end
  156. end
  157. -- Rednet Functions --
  158.  
  159.  
  160.  
  161. -- UI Functions --
  162. -- Clears the screen with the given color. If no color is provided, then black
  163. -- will be used instead.
  164. -- Params : color - The color to clear the screen with.
  165. -- Returns: nil
  166. function clearScreen(color)
  167.         term.setBackgroundColor(color or colors.black)
  168.         term.clear()
  169.         term.setCursorPos(1, 1)
  170. end
  171.  
  172. -- Prints the google logo to the position located. If no position was
  173. -- provided, then it will default to the top center of the screen.
  174. -- Params : xPos, yPos - The x and y coordinates for the logo to be drawn.
  175. -- Returns: nil
  176. function printLogo(xPos, yPos)
  177.         if not xPos and not yPos then
  178.                 xPos = screenWidth/2 - ("Google"):len()/2
  179.                 yPos = 1
  180.         end
  181.         term.setCursorPos(xPos, yPos)
  182.        
  183.         local logoColors = {colors.blue, colors.red, colors.yellow, colors.blue,
  184.                             colors.green, colors.red} -- The colors for the logo
  185.                                                       -- that we're printing.
  186.         local logo = "Google" -- The logo to be printed.
  187.         for index = 1, logo:len() do
  188.                 term.setTextColor(logoColors[index])
  189.                 term.write(logo:sub(index, index))
  190.         end
  191. end
  192.  
  193. -- Prints the Gmail-Server title to the top of the screen at the given coordinates,
  194. -- or the center of the screen on line two by default.
  195. -- Params : xPos, yPos - The x and y coordinates of the title to be drawn.
  196. -- Returns: nil
  197. function printTitle(xPos, yPos)
  198.         if not xPos and not yPos then
  199.                 xPos = screenWidth/2 - ("Gmail-Server"):len()/2
  200.                 yPos = 2
  201.         end
  202.         term.setCursorPos(xPos, yPos)
  203.        
  204.         local titleColors = {colors.blue, colors.red, colors.yellow, colors.blue,
  205.                             colors.green, colors.red, colors.blue, colors.red,
  206.                             colors.yellow, colors.blue, colors.green, colors.red}
  207.                             -- The colors for the title to be printed.
  208.         local title = "Gmail-Server"
  209.         for index = 1, title:len() do
  210.                 term.setTextColor(titleColors[index])
  211.                 term.write(title:sub(index, index))
  212.         end
  213. end
  214.  
  215. -- Prints the number of unsent messages to the screen. This is based off the
  216. -- size of the unsent messages directory.
  217. -- Params : xPos, yPos - The x and y coordinates for this information to be
  218. --                       drawn at.
  219. -- Returns: nil
  220. function printNumberOfUnsentMessages(xPos, yPos)
  221.         term.setCursorPos(xPos, yPos)
  222.         term.write("Unsent Messages: " .. #fs.list(unsentMessagesDirectory))
  223. end
  224.  
  225. -- Prints the number of messages that have been successfullt sent. This is
  226. -- based off of the 'sentMessagesCount' variable.
  227. -- Params : xPos, yPos - The x and y coordinates for this information to be
  228. --                       drawn at.
  229. -- Returns: nil
  230. function printNumberOfSentMessages(xPos, yPos)
  231.         term.setCursorPos(xPos, yPos)
  232.         term.write("Sent Messages  : " .. sentMessageCount)
  233. end
  234.  
  235. -- Prints the current time in the Minecraft world. By default, however, this
  236. -- information will be written at the bottom right of the screen.
  237. -- Params : xPos, yPos - The x and y coordinates for this information to be
  238. --                       drawn at.
  239. -- Returns: nil
  240. function printCurrentTimeInMinecraft(xPos, yPos)
  241.         local time = textutils.formatTime(os.time(), true)
  242.         if not xPos and not yPos then
  243.                 xPos = screenWidth/2 - ("Current Time: " .. time):len()/2
  244.                 yPos = screenHeight
  245.         end
  246.         term.setCursorPos(xPos, yPos)
  247.         term.write("Current Time: " .. time)
  248. end
  249.  
  250. -- Prints the exit option for the script.
  251. -- Params : nil
  252. -- Returns: nil
  253. function printExitOption()
  254.         term.setCursorPos(exitOption.xPos, exitOption.yPos)
  255.        
  256.         local logoColors = {colors.blue, colors.red, colors.yellow, colors.blue,
  257.                            } -- The colors for the exit option that we're printing.
  258.         for characterIndex = 1, exitOption.label:len() do
  259.                 term.setTextColor(logoColors[characterIndex])
  260.                 term.write(exitOption.label:sub(characterIndex, characterIndex))
  261.         end
  262.        
  263.         -- Reset the colors.
  264.         term.setTextColor(colors.black)
  265. end
  266.  
  267. -- Draws the main screen for the server script.
  268. -- Params : nil
  269. -- Returns: nil
  270. function drawMainScreen()
  271.         -- Clear the screen then print all of the necessary information,
  272.         -- including the exit button.
  273.         clearScreen(colors.white)
  274.         printLogo()
  275.         printTitle()
  276.         printNumberOfUnsentMessages(2, 4)
  277.         printNumberOfSentMessages(2, 5)
  278.         printCurrentTimeInMinecraft()
  279.         printExitOption()
  280. end
  281. -- UI Functions --
  282.  
  283.  
  284.  
  285. -- Click Functions --
  286. -- Checks if the click at the given position is on top of the text at another
  287. -- given position provided.
  288. -- Params : xClickPos, yClickPos - The x and y coordinates of the click.
  289. --          xTextPos, yTextPos   - The x and y coordinates of the text.
  290. --          text                 - The text to be checked for clicks on.
  291. -- Returns: true or false - Whether or not the click was on the text provided.
  292. function checkIfClickWasOnTextAtPos(xClickPos, yClickPos, xTextPos, yTextPos, text)
  293.         if xClickPos >= xTextPos and xClickPos <= xTextPos + text:len() then
  294.                 if yClickPos == yTextPos then
  295.                         return true
  296.                 end
  297.         end
  298.        
  299.         return false
  300. end
  301. -- Click Functions --
  302.  
  303.  
  304.  
  305. -- File Functions --
  306. -- Stores the given message in the unsent messages directory. If there is a
  307. -- message with the same receiver/sender and the same subject, then this
  308. -- function will rename the message to the same thing with an appended count
  309. -- of the amount of messages with the same signatures. However, when this
  310. -- message is read from the file, it will be sent without the appended count.
  311. -- Params : message - The message table to be serialized and stored.
  312. -- Returns: nil
  313. function saveUnsentMessage(message)
  314.         -- Generate a name for the new file and check it against the existing
  315.         -- contents of the unsent messages directory in the case of a duplicate
  316.         -- message.
  317.         local unsentMessagePath = message.receiver .. '_' .. message.sender .. "_false"
  318.         _, unsentMessagePath = checkForDuplicateFileInDirectory(unsentMessagesDirectory, unsentMessagePath)
  319.        
  320.         -- Get a file handle on the path for the new unsent message, then save
  321.         -- the message.
  322.         local unsentMessageFileHandle = fs.open(unsentMessagesDirectory .. '/' .. unsentMessagePath, 'w')
  323.         unsentMessageFileHandle.write(textutils.serialize(message))
  324.         unsentMessageFileHandle.close()
  325. end
  326.  
  327. -- Checks for a file with the same name as the one provided.
  328. -- Params : directory, fileName - The path of the file to check the name of for
  329. --                                duplicates/the directory to check.
  330. -- Returns: true, newFileName or false, fileName - The new file name in the case of
  331. --                                                 a duplicate file name.
  332. function checkForDuplicateFileInDirectory(directory, fileName)
  333.         local timesAppeared = getNumberOfTimesFileAppearsInDirectory(directory, fileName)
  334.         if timesAppeared > 0 then
  335.                 local newName = fileName .. timesAppeared
  336.                 return true, newName
  337.         end
  338.        
  339.         return false, fileName
  340. end
  341.  
  342. -- Checks the amount of times the same file name appears in a directory.
  343. -- Params : directory, fileName - The directory and file name to check.
  344. -- Returns: timesAppeared - The number of times the same file name appears.
  345. function getNumberOfTimesFileAppearsInDirectory(directory, fileName)
  346.         local timesAppeared = 0
  347.         for index, item in pairs(fs.list(directory)) do
  348.                 if item:find(fileName) then
  349.                         timesAppeared = timesAppeared + 1
  350.                 end
  351.         end
  352.        
  353.         return timesAppeared
  354. end
  355.  
  356. -- Loads a message from the unsent messages directory and returns the message
  357. -- in its UNSERIALIZED form.
  358. -- Params : unsentMessageName - The name of the message to get from the directory.
  359. -- Returns: unsentMessage - The unsent message table.
  360. function loadUnsentMessageInUnserializedFormat(unsentMessageName)
  361.         local unsentMessageFileHandle = fs.open(unsentMessagesDirectory .. '/' .. unsentMessageName, 'r')
  362.         local unsentMessage = textutils.unserialize(unsentMessageFileHandle.readAll())
  363.         unsentMessageFileHandle.close()
  364.        
  365.         return unsentMessage
  366. end
  367.  
  368. -- Delets an unsent message.
  369. -- Params : unsentMessageName - The name of the message to delete.
  370. -- Returns: nil
  371. function deleteUnsentMessage(unsentMessageName)
  372.         fs.delete(unsentMessagesDirectory .. '/' .. unsentMessageName)
  373. end
  374. -- File Functions --
  375.  
  376. openOrCloseModem(true) -- Open any modem found on this computer.
  377. -- Setup all necessary directories properly if they have not been so already.
  378. if not fs.isDir("/Gmail-Server") then
  379.         fs.makeDir("/Gmail-Server")
  380. end
  381. if not fs.isDir(unsentMessagesDirectory) then
  382.         fs.makeDir(unsentMessagesDirectory)
  383. end
  384.  
  385. -- Main Loop -------------------------------------------------------------------
  386. if not listenForIncomingMail() then
  387.         openOrCloseModem(false)
  388.         clearScreen(colors.black)
  389.         term.setTextColor(colors.yellow)
  390.         print(os.version())
  391. end
  392. --------------------------------------------------------------------------------
Advertisement
Add Comment
Please, Sign In to add comment