Opens a Jira ticket for each message your people report, and adds later reports of the same sender to the ticket already open.
Uses
GmailJira
Turn reported suspicious mail into Jira tickets
Whenever new mail arrives in Gmail, the messages your people have reported as suspicious are found by a search you choose, and each one becomes a ticket in the Jira project your responders already work from.
Every line of the ticket is copied from the message: the sender address, the name shown, whether that address is on one of your own domains, the subject, the hosts its links point to and the Gmail message id. Nothing is rated or scored: a conclusion would be invention. It does note when a name you list as commonly impersonated appears in the sender name or subject while the address is not yours, and it records only the host part of each link. One ticket per sender, not per report: later reports of the same sender become comments on the ticket already open. A message is labelled as triaged only after its ticket is written, so a failed run repeats a report rather than losing it.
You need a Gmail label or search that identifies reported mail and a Jira project for the tickets.
reportSearch to blank.Set reportSearch to blank.jiraProjectKey to blank.Set jiraProjectKey to blank.ourDomains to blank.Set ourDomains to blank.impersonatedNames to blank.Set impersonatedNames to blank.triagedLabel to Reported mail triaged.Set triagedLabel to Reported mail triaged.issueType to Task.Set issueType to Task.maxReportsPerRun to 10.Set maxReportsPerRun to 10.reportSearch, -label:, @triagedLabel, ] with nothing and save it as reportQuery.Join a list of [reportSearch, -label:, @triagedLabel, ] with nothing and save it as reportQuery.reportLimit to maxReportsPerRun multiplied by 1.Set reportLimit to maxReportsPerRun multiplied by 1.,, ourDomains with "Join a list of [,, ourDomains with
replaced by , with replaced by in lowercase, ,] with nothing and save it as ourDomainList.impersonatedNames with "Split impersonatedNames with
replaced by , by , and save it as nameList.reportQuery. Save it as reported.List emails matching reportQuery. Save it as reported.handledCount to 0.Set handledCount to 0.ticketCount to 0.Set ticketCount to 0.commentCount to 0.Set commentCount to 0.message in reported.emails:For each message in reported.emails:handledCount is greater than or equal to reportLimitIf handledCount is greater than or equal to reportLimitreportErrorIf a step below fails, save the error as reportErrormessage.id and save it as messageId.Trim message.id and save it as messageId.messageId is emptyIf messageId is emptymessage.from and save it as fromHeader.Trim message.from and save it as fromHeader.fromHeader is emptyIf fromHeader is emptyA reported message arrived with no sender address; skipped..Show in the output: A reported message arrived with no sender address; skipped..addressPart to fromHeader.Set addressPart to fromHeader.displayName to blank.Set displayName to blank.fromHeader contains <If fromHeader contains <fromHeader split by < split by > and save it as addressPart.Get the first item of the last item of fromHeader split by < split by > and save it as addressPart.fromHeader split by < with replaced by " and save it as displayName.Trim the first item of fromHeader split by < with replaced by " and save it as displayName.addressPart with replaced by with \ replaced by with replaced by " lowercase and save it as senderKey.Make addressPart with replaced by with \ replaced by with replaced by " lowercase and save it as senderKey.senderKey doesn't contain @If senderKey doesn't contain @The From line of message , messageId, holds no address, so there is nothing to group it by; skipped.] joined by nothing.Show in the output: a list of [The From line of message , messageId, holds no address, so there is nothing to group it by; skipped.] joined by nothing.senderKey contains :// or senderKey contains /If senderKey contains :// or senderKey contains /The From line of message , messageId, is not shaped like an address; skipped.] joined by nothing.Show in the output: a list of [The From line of message , messageId, is not shaped like an address; skipped.] joined by nothing.senderKey is greater than 120If the length of senderKey is greater than 120The sender address on message , messageId, is too long to use as a ticket title; skipped.] joined by nothing.Show in the output: a list of [The sender address on message , messageId, is too long to use as a ticket title; skipped.] joined by nothing.senderKey split by @ and save it as senderDomain.Get the last item of senderKey split by @ and save it as senderDomain.ourDomainList contains a list of [,, senderDomain, ,] joined by nothing and save it as ownDomain.Check whether ourDomainList contains a list of [,, senderDomain, ,] joined by nothing and save it as ownDomain.subject to (no subject).Set subject to (no subject).subjectMissingIf a step below fails, save the error as subjectMissingmessage.subject and save it as subjectText.Trim message.subject and save it as subjectText.subjectText isn't empty and subjectText is not nullIf subjectText isn't empty and subjectText is not nullsubject to subjectText.Change subject to subjectText.A reported message arrived with no subject..Show in the output: A reported message arrived with no subject..subjectLink in all web addresses in subject:For each subjectLink in all web addresses in subject:subjectLink with [link removed] in subject and save it as subject. Replace subjectLink with [link removed] in subject and save it as subject.subject contains ://If subject contains ://subject to (subject withheld: it carried a web address).Change subject to (subject withheld: it carried a web address).bodyText to blank.Set bodyText to blank.bodyMissingIf a step below fails, save the error as bodyMissingmessage.body and save it as bodyValue.Trim message.body and save it as bodyValue.bodyValue is not nullIf bodyValue is not nullbodyText to bodyValue.Change bodyText to bodyValue.A reported message arrived with no readable text..Show in the output: A reported message arrived with no readable text..namesUsed.Start an empty list and save it as namesUsed.candidate in nameList:For each candidate in nameList:candidate and save it as nameText. Trim candidate and save it as nameText.nameText is empty or nameText is null If nameText is empty or nameText is nulldisplayName contains nameText (ignoring case) and save it as inName. Check whether displayName contains nameText (ignoring case) and save it as inName.subject contains nameText (ignoring case) and save it as inSubject. Check whether subject contains nameText (ignoring case) and save it as inSubject.inName is yes or inSubject is yes and ownDomain is no If inName is yes or inSubject is yes and ownDomain is nonameText to the end of namesUsed and save it as namesUsed.Add nameText to the end of namesUsed and save it as namesUsed.nameLine to None of the names in 'impersonatedNames' appear in the sender name or the subject..Set nameLine to None of the names in 'impersonatedNames' appear in the sender name or the subject..namesUsed isn't emptyIf namesUsed isn't emptyNames from 'impersonatedNames' that appear in the sender name or the subject: , namesUsed joined by , , . The address itself is not on one of your own domains.] with nothing and save it as nameLine.Join a list of [Names from 'impersonatedNames' that appear in the sender name or the subject: , namesUsed joined by , , . The address itself is not on one of your own domains.] with nothing and save it as nameLine.linkHosts.Start an empty list and save it as linkHosts.linkCount to 0.Set linkCount to 0.linkUrl in all web addresses in bodyText:For each linkUrl in all web addresses in bodyText:linkCount is greater than or equal to 8 If linkCount is greater than or equal to 8linkUrl split by :// with :// and save it as afterScheme. Join the items from index 1 onward of linkUrl split by :// with :// and save it as afterScheme.afterScheme split by / and save it as hostPart. Get the first item of afterScheme split by / and save it as hostPart.hostPart split by ? and save it as hostPart. Get the first item of hostPart split by ? and save it as hostPart.hostPart split by # and save it as hostPart. Get the first item of hostPart split by # and save it as hostPart.hostPart split by @ and save it as hostPart. Get the last item of hostPart split by @ and save it as hostPart.hostPart split by : and save it as hostPart. Get the first item of hostPart split by : and save it as hostPart.hostPart lowercase and save it as linkHost. Make hostPart lowercase and save it as linkHost.linkHost is empty or linkHost doesn't contain . or linkHost contains : or… If linkHost is empty or linkHost doesn't contain . or linkHost contains : or…linkHosts contains linkHost If linkHosts contains linkHostlinkHost to the end of linkHosts and save it as linkHosts. Add linkHost to the end of linkHosts and save it as linkHosts.linkCount. Add 1 to linkCount.hostLine to No web addresses were found in the text of the message..Set hostLine to No web addresses were found in the text of the message..linkHosts isn't emptyIf linkHosts isn't emptyHosts the message text links to: , linkHosts joined by , , . Only the host part of each link is recorded here; the rest of each address is left out of the ticket on purpose, because it can carry a code that identifies the person who was written to.] with nothing and save it as hostLine.Join a list of [Hosts the message text links to: , linkHosts joined by , , . Only the host part of each link is recorded here; the rest of each address is left out of the ticket on purpose, because it can carry a code that identifies the person who was written to.] with nothing and save it as hostLine.[sender: , senderKey, ]] with nothing and save it as marker.Join a list of [[sender: , senderKey, ]] with nothing and save it as marker.project = , @jiraProjectKey, AND summary ~ , @senderKey, ORDER BY created DESC] with nothing and save it as jql.Join a list of [project = , @jiraProjectKey, AND summary ~ , @senderKey, ORDER BY created DESC] with nothing and save it as jql.jql. Save it as candidates.Find issues matching jql. Save it as candidates.openTicket to blank.Set openTicket to blank.issue in candidates.issues:For each issue in candidates.issues:issue.summary and save it as issueSummary. Trim issue.summary and save it as issueSummary.issueSummary contains marker If issueSummary contains markerissue.key and save it as issueKey.Trim issue.key and save it as issueKey.issueKey isn't empty and issueKey is not nullIf issueKey isn't empty and issueKey is not nullopenTicket to issueKey.Change openTicket to issueKey.subject to 100 characters and save it as subjectShort.Shorten subject to 100 characters and save it as subjectShort.ownDomainLine to The address is not on one of the domains listed in 'ourDomains'..Set ownDomainLine to The address is not on one of the domains listed in 'ourDomains'..ownDomain is yesIf ownDomain is yesownDomainLine to The address is on one of the domains listed in 'ourDomains'..Change ownDomainLine to The address is on one of the domains listed in 'ourDomains'..nameShown to displayName.Set nameShown to displayName.displayName is emptyIf displayName is emptynameShown to (none).Change nameShown to (none).Sender address: , senderKey, "Join a list of [Sender address: , senderKey,
Name shown on the message: , nameShown,
Sender domain: , senderDomain,
, ownDomainLine] with nothing and save it as sectionSender.sectionSubject to Subject as received: subjectShort.Set sectionSubject to Subject as received: subjectShort.sectionNames to nameLine.Set sectionNames to nameLine.sectionHosts to hostLine.Set sectionHosts to hostLine.Gmail message id: , messageId, "Join a list of [Gmail message id: , messageId,
Picked up on: , today's date] with nothing and save it as sectionIds.sectionNote to Every line above is copied from the message itself. Nothing here reads the body into the ticket, and no step decides what the message is - that is the reader's job..Set sectionNote to Every line above is copied from the message itself. Nothing here reads the body into the ticket, and no step decides what the message is - that is the reader's job..sectionSender, sectionSubject, sectionNames, sectionHosts, sectionIds, sectionNote] with a blank line and save it as ticketBody.Join a list of [sectionSender, sectionSubject, sectionNames, sectionHosts, sectionIds, sectionNote] with a blank line and save it as ticketBody.bodyLink in all web addresses in ticketBody:For each bodyLink in all web addresses in ticketBody:bodyLink with [link removed] in ticketBody and save it as ticketBody. Replace bodyLink with [link removed] in ticketBody and save it as ticketBody.ticketBody contains ://If ticketBody contains ://ticketBody to The details of this report carried a web address and were left out. Open the message in the mailbox instead..Change ticketBody to The details of this report carried a web address and were left out. Open the message in the mailbox instead..openTicket isn't emptyIf openTicket isn't emptyopenTicket: 'Another message from this sender was reported.Comment on openTicket: 'Another message from this sender was reported.
ticketBody'.commentCount.Add 1 to commentCount.Reported mail , marker, , subjectShort] joined by nothing' in jiraProjectKey.Create the issue 'a list of [Reported mail , marker, , subjectShort] joined by nothing' in jiraProjectKey.ticketBodyticketCount.Add 1 to ticketCount.triagedLabel to email messageId.Add label triagedLabel to email messageId.handledCount.Add 1 to handledCount.reportError as text.Show in the output: Could not raise a ticket for one reported message: reportError as text.Reports handled: , handledCount as text, (new tickets: , ticketCount as text, , added to a ticket already open: , commentCount as text, )] joined by nothing.Show in the output: a list of [Reports handled: , handledCount as text, (new tickets: , ticketCount as text, , added to a ticket already open: , commentCount as text, )] joined by nothing.