Mail & Documents
Send Nimbus Mail, manage domain addresses, and issue paper documents from another resource.
Nimbus Mail is the mail app on Facade, Lumen and Solace. Every Nimbus account has an address such as [email protected], and players who own or rent a domain on Namestead make addresses on it, such as [email protected], that several characters share. Other resources send mail to characters or addresses, such as receipts and notices, read a character's addresses, and manage addresses on a domain, such as giving every officer access to [email protected].
Documents are paper. A printout, a photocopy or an issued original is a laptop_document item that only holds the document's serial, and the server keeps the page itself. Other resources hand out official originals, such as an impound notice or a prescription, and check whether a serial is a real one.
To open Mail on the player's screen from the client, see Open Mail.
The mail exports only exist while Config.Mail.enabled is true in shared/unlocked/mail.lua. Mail needs the Nimbus cloud: with Config.Settings.cloud.mode set to "drive" there are no Nimbus accounts, so characters have no address. Domain addresses also need Config.Domains.enabled and Config.Mail.domains.enabled.
The document exports only exist while
Config.Documents.enabledistrueinshared/unlocked/documents.lua.With either off, calling its exports fails, so a resource that works without them wraps its calls in
pcall.
General Information
Every export on this page is a server export.
A character is passed by their identifier. A recipient can be a character identifier, which becomes their Nimbus address, or any address. Addresses are turned to lower case.
Exports marked Yields wait on the database. Call them from a thread, an event handler or a command, not while your script loads.
Types / Classes
MailResourceMailis intypes/mail.lua, andLaptopDocument,DocumentBlock,DocumentRun,LaptopDocumentViewandDocumentKindare intypes/documents.luainsidezyke_laptop.LaptopDocumentIssueandLaptopDocumentCheckare inserver/locked/documents/exports.lua.
#Send Mail
Sends mail from a resource. Yields. Returns the mail's message key, or
niland"invalid"or"noRecipients". A refused mail also prints a warning in the server console.
toandccare a character identifier or address, or a list of them, each cut tolimits.recipients(20 by default). There is no BCC.- A character without a Nimbus account is skipped. When nobody is left, it returns
"noRecipients"."invalid"is returned for an invalidfromaddress, a subject or body that isn't a string or is overlimits.subjectorlimits.bodycharacters, or a file that is invalid or overlimits.attachmentBytes. Files pastlimits.attachments(5 by default) are dropped.- Mail to an address nobody has still returns a message key. It is not delivered, and no bounce is sent.
fromisConfig.Mail.systemSender.address([email protected]) when left out, andfromNameis the system sender's name only for that address. Replies to an address nobody has bounce, so anoreply@sender stays one. Sent from a domain address, a copy goes into that address's Sent.- The body is plain text, shown exactly as written, unless
markupistrue. Markup uses**bold**,*italic*,__underline__,~~strike~~and[text](https://...), with-,1.and>at the start of a line for lists and quotes, and a backslash before a character to show it as it is.Example:
lua---@param to string | string[] @ Character identifiers or addresses ---@param mail MailResourceMail ---@return string? messageKey ---@return string? err @ "invalid" or "noRecipients" local messageKey, err = exports["zyke_laptop"]:SendMail(identifier, { from = "[email protected]", fromName = "Benny's Original Motor Works", subject = "Your car is ready", body = "Hi,\n\nYour **Sultan RS** is ready to pick up.", markup = true, cc = "[email protected]", files = {{name = "invoice.txt", mime = "text/plain", data = "Paint job: $4,500"}}, })
| Field | Default | Description |
|---|---|---|
from | Config.Mail.systemSender.address | The sender's address. It doesn't have to exist. |
fromName | The system sender's name, or none | The name the mail is shown from, up to limits.displayName characters. |
subject | "" | One line, up to limits.subject characters. |
body | "" | Up to limits.body characters. |
markup | false | true keeps the body's formatting marks. |
cc | None | A character identifier or address, or a list of them. |
files | None | {name, mime?, data}. data is text, or a data URL for a picture. mime is "application/octet-stream" when left out or invalid. |
#Get Mail Address
Returns the character's Nimbus address, or
nilwhen they have no Nimbus account. Yields the first time a character is looked up.Example:
lua---@param identifier string ---@return string? address local address = exports["zyke_laptop"]:GetMailAddress(identifier)
#Get Mail Addresses
Returns every address the character reads and sends from: their Nimbus address first, then every domain address they have access to whose domain is still registered, by address. Returns an empty table when they have no Nimbus account. Yields.
Example:
lua---@param identifier string ---@return string[] addresses local addresses = exports["zyke_laptop"]:GetMailAddresses(identifier)
#Create Mailbox
Makes an address on a domain registered on Namestead and gives
membersaccess to it at once. It doesn't check who owns the domain or thereservednames, so a script can make[email protected]on a domain it set up. The address counts towards the domain'smaxMailboxes. Yields.Returns
true, orfalseand an error:"invalid"for an invalid address,"notFound"when the domain isn't registered or domain addresses are off,"taken"when the address exists, and"limit"when the domain hasdomains.maxMailboxesaddresses (25 by default).The address lives as long as the domain. A rental that runs out suspends it, and a released domain deletes it with its mail.
Example:
lua---@param address string ---@param members? string[] @ Character identifiers ---@param name? string @ The name mail from it goes out with ---@return boolean created ---@return string? err @ "invalid", "notFound", "taken" or "limit" local created, err = exports["zyke_laptop"]:CreateMailbox("[email protected]", {identifier}, "LSPD Dispatch")
#Delete Mailbox
Deletes a domain address with its members, settings, auto-reply and every mail it held. Returns
falsefor an address that isn't a domain address. Yields.Example:
lua---@param address string ---@return boolean deleted local deleted = exports["zyke_laptop"]:DeleteMailbox("[email protected]")
#Set Mailbox Access
Gives a character access to a domain address, or takes it away, and updates their open Mail. Returns
truewhen it changed, andfalsefor an address that isn't a domain address or a character who already had, or didn't have, access. Yields.Example:
lua---@param address string ---@param identifier string ---@param hasAccess boolean ---@return boolean changed exports["zyke_laptop"]:SetMailboxAccess("[email protected]", identifier, true)
#Get Mailbox Access
Returns the identifiers of every character with access to a domain address, or
nilfor an address that isn't a domain address.Example:
lua---@param address string ---@return string[]? members local members = exports["zyke_laptop"]:GetMailboxAccess("[email protected]")
#Documents
A document's serial looks like DC-7F3K-92QD. Each one has a kind: "original" from IssueDocument, "print" from a laptop's printer, or "copy" from a photocopy. A copy has its own serial and shows its original's content. Documents nobody looked at for retentionDays (120 by default) are deleted, and their paper then reads blank.
#Issue Document
Writes an original and hands its paper to the player as
copiespapers of the same original. WithoutplyIdit only writes the document. Yields.Returns the serial, or
niland an error:
"invalid":issueris missing, or nothing readable is left inblocks,textandfields."tooLarge": the document is overlimits.bytes(49152 by default) as JSON."noItem": the paper itemConfig.Documents.itemis missing from the inventory."noRoom": the player can't carry the papers. When the document was already written, the serial is returned with"noRoom"."failed": the document could not be written.The issuer and serial are printed on the paper, with the issuing character's name when
issuedByis given.metadatais added to the item's metadata, and may replace itslabelanddescriptionbut never the document. Photocopies never get it.Example:
lua---@param plyId? integer ---@param data LaptopDocumentIssue ---@return string? serial ---@return string? err @ "invalid", "tooLarge", "noItem", "noRoom" or "failed" local serial, err = exports["zyke_laptop"]:IssueDocument(plyId, { issuer = "Los Santos Police Department", issuerId = "police", issuedBy = identifier, title = "Vehicle impound notice", blocks = { {k = "p", s = "title", r = {{t = "Impound notice"}}}, {k = "p", r = {{t = "Your vehicle was impounded for "}, {t = "parking on the sidewalk", b = true}, {t = "."}}}, }, fields = {{"Plate", "46EEK572"}, {"Fee", "$500"}}, size = "letter", footer = "Mission Row Police Station", numbers = "fr", copies = 1, metadata = {plate = "46EEK572"}, })
| Field | Default | Description |
|---|---|---|
issuer | Required | The organisation printed on it, up to 100 characters. |
issuerId | None | Your own key for the issuer, such as a job name, up to 64 characters. VerifyDocument returns it. |
issuedBy | None | Identifier of the character who issued it. Their name is shown on it. |
title | "" | Up to 120 characters. The paper item's label. |
blocks | None | The document's blocks. |
text | None | Plain text instead of blocks. A blank line starts a new paragraph. |
fields | None | {{label, value}, ...}, a two-column table with bold labels after the text. Up to 60 rows. |
size | "letter" | "letter", "a4" or "legal". |
o | Portrait | "l" for landscape. |
m | The paper's own | {top, right, bottom, left} margins in pixels at 96 per inch, 216 at most. |
bg | White | Page colour as #rrggbb. |
header / footer | None | Text in the page margins, up to 160 characters. |
numbers | None | Page numbers: "h" header or "f" footer, then "l" left, "c" centre or "r" right, such as "fr". |
copies | 1 | Papers handed to the player, 1 to 10. |
pages | Counted from break blocks | Pages it fills, up to limits.pages. |
metadata | None | More item metadata for your own use, such as a prescription id. |
#Blocks
A block is a table with k, and up to 400 blocks are kept. Blocks and fields that aren't valid are dropped.
k | Fields |
|---|---|
"p" | A paragraph. r its runs, s its style ("title", "h1", "h2" or "small"), a its alignment ("c", "r" or "j"), ls its line spacing (1, 1.5 or 2, 1.15 when left out) and il its indent level, 0 to 8. |
"ul" / "ol" | A bulleted or numbered list. items is a list of runs, lv each item's nesting level, 0 to 8, plus a and ls. |
"table" | rows = {{runs, runs}, ...}, up to 60 rows of 8 cells. |
"img" | A picture. src is an https address, w its width in percent of the line, and a its alignment. Up to limits.images (8 by default), from the imageHosts the config allows. |
"break" | A page break. |
A run is {t = "text"} with any of b bold, i italic, u underline, s strikethrough set to true, z the size in points (8 to 48), f the font ("inter", "ubuntu", "serif" or "mono", a sans when left out), and c and h the text and highlight colours as #rrggbb.
#Verify Document
Tells whether a serial is an original from
IssueDocumentor a photocopy of one, so a police check or a pharmacy can trust it. A laptop's own printout, or a copy of one, is never valid. Returns{valid = false}for an unknown serial. Yields when the document isn't cached.For a valid document,
serial,title,issuer,issuerId,issuedByNameandissuedAt(Unix seconds) are the original's, andcopyistruefor a photocopy. An invalid one only haskindandtitle.Example:
lua---@param serial string ---@return LaptopDocumentCheck check @ {valid, kind?, serial?, copy?, title?, issuer?, issuerId?, issuedByName?, issuedAt?} local check = exports["zyke_laptop"]:VerifyDocument("DC-7F3K-92QD") if (check.valid and check.issuerId == "police" and not check.copy) then -- An original from the police end
#Get Document
Returns what a paper says:
{id, kind, title, document, issuer?, issuedByName?, createdAt, sourceId?}, wheredocumentis theLaptopDocumentandsourceIdthe original's serial on a copy. Returnsnilfor an unknown serial. Yields when the document isn't cached.Example:
lua---@param serial string ---@return LaptopDocumentView? view local view = exports["zyke_laptop"]:GetDocument("DC-7F3K-92QD")
#Show Document
Shows a document on the player's screen, outside the laptop, as if they used its paper. Counts as the document being looked at, which keeps it from the retention sweep. Returns
falsefor an unknown serial. Yields when the document isn't cached.Example:
lua---@param plyId integer ---@param serial string ---@return boolean shown exports["zyke_laptop"]:ShowDocument(plyId, "DC-7F3K-92QD")
A paper item's serial is in its metadata as document, so a resource reads the paper a player hands over from the item and checks it with VerifyDocument.
#Server Local Events
These are local server events. Listen with AddEventHandler on the server. They cannot block an action.
#Mail Sent
Fires when mail is sent, by a player, a resource, an auto-reply or a bounce.
authoris the character who wrote it,nilfor a resource.deliveredholds the addresses it reached.sentAtis in milliseconds.Example:
lua---@param data {from: string, author?: string, to: string[], cc: string[], bcc: string[], subject: string, kind: MailKind, messageKey: string, delivered: string[], sentAt: integer} AddEventHandler("zyke_laptop:mail:Sent", function(data) print(data.from, data.subject, #data.delivered) end)
#Mail Received
Fires once for every address a mail reached.
identifiersare the characters who read that address.folderis"inbox"or"spam".Example:
lua---@param data {address: string, identifiers: string[], from: string, subject: string, folder: MailFolder, kind: MailKind, messageKey: string, sentAt: integer} AddEventHandler("zyke_laptop:mail:Received", function(data) print(data.address, data.from, data.subject) end)
#Document Issued
Fires when
IssueDocumenthanded out an original.plyIdisnilwhen no player was given one.Example:
lua---@param plyId? integer ---@param serial string ---@param issuer string ---@param issuerId? string AddEventHandler("zyke_laptop:documents:Issued", function(plyId, serial, issuer, issuerId) end)
#Document Printed
Fires when a player sends a print from a laptop to a printer. The paper is taken from the tray then, and the printout waits at the printer until the player collects it.
Example:
lua---@param plyId integer ---@param serial string ---@param printerId integer ---@param copies integer AddEventHandler("zyke_laptop:documents:Printed", function(plyId, serial, printerId, copies) end)
#Document Copied
Fires when a player photocopies a paper at a printer.
Example:
lua---@param plyId integer ---@param copySerial string ---@param originalSerial string ---@param copies integer AddEventHandler("zyke_laptop:documents:Copied", function(plyId, copySerial, originalSerial, copies) end)
#Printer Placed
Fires when a player sets a printer down.
Example:
lua---@param plyId integer ---@param printerId integer AddEventHandler("zyke_laptop:documents:PrinterPlaced", function(plyId, printerId) end)