Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- (*
- batchMailer.app
- Send an individual email with an optional attachment to each of the addresses listed in email.txt. Inserts emailers name when present in email.txt file.
- Input files are in the bulkMailerContents folder in your home folder. I use the free version of bbedit to create and edit all these files.
- Download here:
- https://www.barebones.com/products/bbedit/index.html
- (1) You need to specify the email addresses to send email to in file emails.txt.
- example:
- #
- # Where a leading # is a comment line.
- #
- # Enter one or more valid email addresses
- # Examples are from Snow White and the Seven Dwarfs:
- # Doc, Happy, Grumpy, Bashful, Sneezy, Sleepy, and Dopey.
- # ( dwarves )
- #
- Sneezy Dwarf <sneezy.dwarf@noname.info>
- Sleepy Dwarf <sleepydwarf@noname.net>
- grumpy.dwarf@noname.net
- (2) The file content.txt contains the text of your message.
- To get a name insertion, you need to include the real name before the email address and include a greeting in the content.txt file. You need
- to start the file with a word like Dear followed by a colon (:). For email with a name followed by the email address in <>, the name will be
- inserted before the first colen (:). For email address from file emails.txt with a name followed by the email address in <>, the name will be
- inserted before the fist colen (:) in the content.txt file.
- For example, with an email sent to Sneezy Dwarf <sneezy.dwarf@noname.info> the name Sneezy Dwarfis inserted.
- Dear:
- will become
- Dear Sneezy Dwarf:
- (3) subject.txt contains the subject line.
- (4) To send an attachment, you will have to modify the Applescript code below.
- Look for "set attachmentFile". See the "underlined" code.
- I left in AppleScript log statements for debugging. Select view message tab when running from debugger.
- Copyright 2020 Robert Janku
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
- OR OTHER DEALINGS IN THE SOFTWARE.
- *)
- on run
- global debug
- set debug to 7
- set ht to character id 9
- set lf to character id 10
- -- Write a message into the event log.
- log " --- Starting on " & ((current date) as string) & " --- "
- -- name of attachment file. --
- -- note folders are separated with a colon (:)
- -- file excerpt.pdf is in folder bulkMailerContents which is in your home folder.
- try
- set attachmentFile to (path to home folder as text) & "bulkMailerContents:" & "contents.zip" as alias
- -- --------------
- on error errMsg number n
- -- could be what user wants. Doesn't want to send an attachment.
- set attachmentFile to ""
- log "==> No go with Attachment File. " & errMsg & " with number " & n
- end try
- -- email sender address --
- if debug ≥ 4 then
- tell application "Mail"
- log "count of account is " & (count of account)
- log item 1 of every account
- log "Email addresses is: " & email addresses of item 1 of every account
- end tell
- end if
- tell application "Mail"
- if (count of account) = 0 then
- display dialog "You have not set up your Apple Mail account. Please define a email account for yourself." & return & "Good bye." giving up after 25
- -- ------------------------ good bye. ------------>"
- return 1
- end if
- set theSender to email addresses of item 1 of every account
- if (count of account) > 1 then
- display dialog "You have multiple Mail accounts. Account " & theSender & " will be used." giving up after 25
- end if
- end tell
- if debug ≥ 4 then log "Resulting email addresses to be used is " & theSender
- -- subject line --
- try
- set subjectFile to (path to home folder as text) & "bulkMailerContents:subject.txt" as alias
- -- read in the subject text
- set subjectText to (read subjectFile as «class utf8») as text
- on error errMsg number n
- log "==> No go in with reading subject from file. " & errMsg & " with number " & n
- -- ask user to cough up subject text
- set returnedInfo to display dialog "subject not found in ~/bulkMailerContents/subject.txt." & return & "Type your Subject" default answer ""
- set subjectText to (text returned of returnedInfo)
- if debug ≥ 4 then log "subjectText is " & return & subjectText
- end try
- set subjectText to adjustCharacters(subjectText)
- if debug ≥ 4 then log "after adusting linends. subjectText is " & return & subjectText
- -- list of email address(s) --
- try
- set emailFile to (path to home folder as text) & "bulkMailerContents:emails.txt" as alias
- -- read in all the emails
- set emailFullText to (read emailFile as «class utf8») as text
- on error errMsg number n
- display dialog "Could not find file containing email content from file bulkMailerContents:emails.txt. " & errMsg & return & "Good bye." giving up after 25
- log "==> No go in with locating email list file. " & errMsg & " with number " & n & return & " Good bye."
- -- ------------------------ good bye. ------------>"
- return 1
- end try
- if debug ≥ 4 then log "emailFullText is " & return & emailFullText
- set emailFullText to adjustCharacters(emailFullText)
- -- split on lf. resulting in a list
- set emailList to textToList(emailFullText, lf)
- set emailList to items 1 thru -2 of emailList -- trim last item to get rid of null last item
- if debug ≥ 4 then log " count is " & (count of emailList) & " emailList is " & return & emailList
- -- gather all email addresses found so we can ask user if we found the correct list of emails.
- -- Found out the hardwary.
- set emailCleanedList to {}
- if debug ≥ 7 then
- log "count of emailCleanedList is " & (count of emailCleanedList)
- log emailCleanedList
- end if
- set myCount to 0
- set endMarker to "##end##"
- if debug ≥ 7 then
- log " endMarker is " & endMarker
- log "(length of endMarker) is " & (length of endMarker)
- end if
- repeat with aNamedEmail in emailList
- -- keep track of current record number
- set myCount to myCount + 1
- if debug ≥ 4 then log "----> aNamedEmail is " & aNamedEmail
- -- Check for end marker,"##end##", (skip all following records).
- if debug ≥ 4 then
- try
- if debug ≥ 4 then log "(length of aNamedEmail) is " & (length of aNamedEmail)
- log "characters 1 thru (length of endMarker) of aNamedEmail is " & characters 1 thru (length of endMarker) of aNamedEmail
- end try
- end if
- if (length of aNamedEmail) ≥ (length of endMarker) then
- if debug ≥ 7 then log "aNamedEmail is long enough to look for ##end## input marker"
- -- done this convoluted way to see what each segment yields.
- if ((characters 1 thru (length of endMarker) of aNamedEmail) as text is endMarker) then
- if debug ≥ 4 then log "End of processing marker found at record number " & myCount & ". Skip rest of records."
- -- Done scanning since found end marker.
- exit repeat
- end if
- end if
- -- Check for null line
- -- Check for comment line
- -- Check for a blank line
- -- the # needs to be the first character in the record, so look at line before trimmed.
- set trimmedName to trim_line(aNamedEmail, " ", 0)
- if debug ≥ 4 then log " after trim; length of trimmedName is " & length of trimmedName & " trimmedName is " & trimmedName
- if trimmedName is not " " and length of aNamedEmail ≥ 1 and character 1 of aNamedEmail is not "#" then
- copy aNamedEmail to the end of emailCleanedList
- if debug ≥ 4 then log " ====> displayEmailText of " & myCount & " is " & aNamedEmail
- if debug ≥ 7 then
- log " count of emailCleanedList is " & (count of emailCleanedList)
- log emailCleanedList
- end if
- end if
- end repeat
- if debug ≥ 4 then log emailCleanedList
- -- gather email addresses for printing
- set displayEmailText to ""
- repeat with emailName in emailCleanedList
- set displayEmailText to displayEmailText & emailName & return
- end repeat
- display dialog ("oK to email these folks?" & return & displayEmailText)
- -- fyi. Saying no quits this applescript.
- -- get message text --
- try
- set contentFile to (path to home folder as text) & "bulkMailerContents:content.txt" as alias
- set emailContent to (read contentFile as «class utf8») as text
- on error errMsg number n
- display dialog "Could not find file containing the message text. " & return & "Create file ~/bulkMailerContents/content.txt." & return & errMsg & return & " Good bye." giving up after 25
- log "==> No go no content File. " & errMsg & " with number " & n
- -- ------------------------ good bye. ------------>"
- return 1
- end try
- -- convert lineends to lf.
- set emailContent to adjustCharacters(emailContent)
- -- Send an email to each address.
- set warningEmailCount to 100
- set numberOfSends to 0
- set userAnswered to 0
- repeat with anEmailAddress in emailCleanedList
- if debug ≥ 4 then log "--------------> length of anEmailAddress is " & length of anEmailAddress & " anEmailAddress is " & anEmailAddress
- -- do one send
- sendMail(anEmailAddress, theSender, subjectText, emailContent, attachmentFile)
- if debug ≥ 5 then log "back in run after sending the email."
- -- Check to display warning message as needed.
- set numberOfSends to numberOfSends + 1
- if numberOfSends ≥ warningEmailCount and userAnswered = 0 then
- display dialog "You have sent " & warningEmailCount & " emails. Last email to " & trimmedName & return & "Many ISPs limit the number of emails you may send." & return & "Do you want to risk sending more emails? (You will not be asked again.)"
- set userAnswered to 1
- end if
- end repeat
- if debug ≥ 1 then log "All done! Have a good day from batchMailer."
- end run
- -- ============================== Common Subroutines ==========================
- -- ------------------------------------------------------
- (*
- Recipiet
- Sender
- Subject
- theContent = the content of the email message. It is assumed that the first line startes with "Dear:". This is changed to include the sender name when suppied.
- example:
- sender is Green Fox <green@domain.info>
- theContents is
- Dear:
- ...
- Modify theContents to
- Dear Green Fox:
- ...
- File = points to the file to attach
- *)
- on sendMail(toRecipiet, theSender, theSubject, theContent, aFile)
- global debug
- if debug ≥ 5 then log "in ~~~ sendMail ~~~"
- if debug ≥ 5 then log "toRecipiet is " & toRecipiet
- set buildContent to theContent
- -- Did the email address contian the person's name?
- -- split out name
- set nameAndAddress to textToList(toRecipiet, "<")
- if (count of nameAndAddress) is 1 then
- else
- -- set adjustedContent to alterString(theContent, ":", first item of nameAndAddress & ":")
- set theOffset to offset of ":" in theContent
- if theOffset > 1 then
- set buildContent to text 1 thru (theOffset - 1) of theContent
- log " buildContent is " & buildContent
- set buildContent to buildContent & " " & trim_line(first item of nameAndAddress, " ", 2)
- log " buildContent is " & buildContent
- set buildContent to buildContent & text theOffset thru -1 of theContent
- log " buildContent is " & buildContent
- end if
- end if
- tell application "Mail"
- set msg to make new outgoing message with properties {subject:theSubject, content:buildContent as rich text, visible:true, sender:theSender}
- tell msg to make new to recipient at end of every to recipient with properties {address:toRecipiet}
- -- ignore file not found.
- try
- tell msg to make new attachment with properties {file name:aFile as alias}
- on error errMsg number n
- -- use may not have wanted to send an attachment
- log "==> No go with Attachment File. " & return & " " & errMsg & " with number " & n
- end try
- send msg
- end tell
- end sendMail
- -- ------------------------------------------------------
- (*
- Symbol Meaning Hex Used
- CR Carriage Return 0d classic Macintosh
- LF Line Feed 0a UNIX
- CR/LF Carriage Return/Line Feed 0d0a MS-DOS, Windows, OS/2
- 8230 U+2026 E2 80 A6 … Horizontal Ellipsis
- https://www.charset.org/utf-8/9
- …
- https://www.toptal.com/designers/htmlarrows/punctuation/horizontal-ellipsis/
- *)
- on adjustCharacters(normalHtml)
- global debug
- set ht to character id 9 -- horizontal tab
- set lf to character id 10
- set ellipsis1 to character id 226
- set ellipsis2 to character id 128
- set ellipsis3 to character id 166
- if debug ≥ 3 then log "in --- adjustCharacters() ---"
- -- for some reason web broswers are having difficulty with utf-8 E2 80 A6
- -- so convert to a HTML entity. does work in <pre>
- set normalHtml to alterString(normalHtml, ellipsis1 & ellipsis2 & ellipsis3, "…")
- -- don't let Windoze confuse us. convert Return LineFeed to lf
- set normalHtml to alterString(normalHtml, return & lf, lf)
- -- might as will convert classic macOS return to lf. We will have to look for less things.
- set normalHtml to alterString(normalHtml, return, lf)
- if debug ≥ 3 then hexDumpFormatOne("adjustCharacters: after altering characters normalHtml", normalHtml)
- return normalHtml
- end adjustCharacters
- -- ------------------------------------------------------
- (*
- alterString
- thisText is the input string to change
- delim is what string to change. It doesn't have to be a single character.
- replacement is the new string
- returns the changed string.
- *)
- on alterString(thisText, delim, replacement)
- global debug
- if debug ≥ 5 then log "in ~~~ alterString ~~~"
- set resultList to {}
- set {tid, my text item delimiters} to {my text item delimiters, delim}
- try
- set resultList to every text item of thisText
- set text item delimiters to replacement
- set resultString to resultList as string
- set my text item delimiters to tid
- on error
- set my text item delimiters to tid
- end try
- return resultString
- end alterString
- -- ------------------------------------------------------
- (*
- length of inputLfBuffer & " in hex " & integerToHex(length of inputLfBuffer)
- *)
- on getIntegerAndHex(aNumber)
- global debug
- if debug ≥ 5 then log "in ~~~ getIntegerAndHex ~~~"
- return aNumber & " in Hex " & integerToHex(aNumber)
- end getIntegerAndHex
- (*
- http://krypted.com/mac-os-x/to-hex-and-back/
- 0 2 4 6 8 a c e 0 2 4 6 8 a c e
- 0000000: 3c 703e 5369 6d70 6c65 2070 7574 2c20 <p>Simple put,
- *)
- on hexDumpFormatOne(textMessage, hex)
- global debug
- set aNul to character id 1
- if debug ≥ 7 then log "in ~~~ hexDumpFormatOne ~~~"
- if debug ≥ 8 then log " hexDumpFormatOne: input string is " & return & hex
- -- -r -p
- set displayValue to aNul & hex
- set toUnix to "/bin/echo -n " & (quoted form of displayValue) & " | xxd "
- if debug ≥ 8 then log " hexDumpFormatOne: toUnix is " & toUnix
- try
- set fromUnix to do shell script toUnix
- -- two hex digits
- set displayText to replaceCharacter(fromUnix, 10, " ")
- if debug ≥ 8 then
- log " hexDumpFormatOne: " & return & displayText
- log " hexDumpFormatOne: length of displayText is " & length of displayText
- end if
- -- one character
- set displayText to replaceCharacter(displayText, 51, " ")
- if debug ≥ 8 then
- log " hexDumpFormatOne: " & return & displayText
- log " hexDumpFormatOne: almost there ..... length of displayText is " & length of displayText
- end if
- log "variable " & textMessage & " in hex is " & return & " 0 2 4 6 8 a c e 0 2 4 6 8 a c e" & return & displayText
- on error errMsg number n
- log " hexDumpFormatOne: ==> convert hex string to string failed. " & errMsg & " with number " & n
- end try
- if debug ≥ 8 then
- log "leaving ~.~ hexDumpFormatOne ~.~"
- end if
- end hexDumpFormatOne
- -- ------------------------------------------------------
- (*
- https://macscripter.net/viewtopic.php?id=43713
- *)
- on integerToHex(nDec)
- global debug
- if debug ≥ 5 then log "in ~~~ integerToHex ~~~"
- try
- set nHex to do shell script "perl -e 'printf(\"%X\", " & nDec & ")'" --> "F0"
- on error errMsg number n
- log "==> convert integer to hex. " & errMsg & " with number " & n
- set nHex to ""
- end try
- return nHex
- end integerToHex
- -- ------------------------------------------------------
- (*
- StefanK in https://macscripter.net/viewtopic.php?id=43852
- Replaces one or more characters based on the length of theCharacter.
- Big Warning!!!
- ==============
- This on block is called by hexDumpFormatOne().
- Therefor, you may not call hexDumpFormatOne() from this on block.
- If you so so, you get yourself into an endless loop.
- Use hexDumpFormatZero() instead.
- script -k <output file name>
- osascript /Applications/applescriptFiles/workwithclipboardV13-HTML.app
- use Activity Monito to stop osascript
- *)
- on replaceCharacter(theText, theOffset, theCharacter)
- global debug
- if debug ≥ 7 then log "in ~~~ replaceCharacter ~~~"
- if debug ≥ 7 then
- log " theOffset is " & getIntegerAndHex(theOffset) & " with theCharacter >" & theCharacter & "< length of theText is " & getIntegerAndHex(length of theText)
- log "theText is " & theText
- end if
- set theOutput to theText -- ready to return if need be.
- repeat 1 times
- -- sanity checks
- if theOffset ≤ 0 then
- display dialog "No character to replace at " & theOffset & " with character " & theCharacter & " in " & theText giving up after 10
- log "==> Adjust theOffset to be wihin the string."
- exit repeat -------------- return ---------->
- end if
- if (theOffset - (length of theCharacter)) ≤ 0 then
- display dialog "Too near the front of the buffer. " & theOffset & " with character " & theCharacter & " in " & theText giving up after 10
- log "==> Too near the front of the buffer. "
- exit repeat -------------- return ---------->
- end if
- if (theOffset + (length of theCharacter) - 1) > (length of theText) then
- display dialog "To near the end of the buffer. " & theOffset & " with character " & theCharacter & " in " & theText giving up after 10
- log "==> Too near the end of the buffer. "
- log " " & "theOffset is " & theOffset & " with theCharacter >" & theCharacter & "< in " & theText
- log "length of buffer is " & getIntegerAndHex(length of theText)
- exit repeat -------------- return ---------->
- end if
- if debug ≥ 7 then
- log "theOffset is " & getIntegerAndHex(theOffset)
- log "theCharacter is " & theCharacter
- end if
- try
- -- what if we are at the end of the buffer. We cannot get any remainder text.
- if theOffset ≥ (length of theText) then
- set theOutput to (text 1 thru (theOffset - 1) of theText) & theCharacter
- else
- set theOutput to (text 1 thru (theOffset - 1) of theText) & theCharacter & (text (theOffset + (length of theCharacter)) thru -1 of theText)
- end if
- on error errMsg number n
- log "==> No go. " & errMsg & " with number " & n
- exit repeat -------------- return ---------->
- end try
- end repeat
- return theOutput
- end replaceCharacter
- -- ------------------------------------------------------
- (*
- splitTextToList seems to be what you are trying to do
- thisText is the input string
- delim is what to split on
- results returned in a list
- Total hack. We know splitTextToList strips of delim so add it back.
- *)
- on splitTextToList(thisText, delim)
- global debug
- if debug ≥ 5 then log "in ~~~ splitTextToList ~~~"
- set returnedList to textToList(thisText, delim)
- set resultArray to {}
- copy item 1 of returnedList to the end of the resultArray
- repeat with i from 2 to (count of returnedList) in returnedList
- set newElement to delim & item i of returnedList
- copy newElement to the end of the resultArray
- end repeat
- return resultArray
- end splitTextToList
- -- ------------------------------------------------------
- (*
- textToList seems to be what you are trying to do
- thisText is the input string
- delim is what to split on
- returns a list of strings.
- - textToList was found here:
- - http://macscripter.net/viewtopic.php?id=15423
- *)
- on textToList(thisText, delim)
- global debug
- if debug ≥ 5 then log "in ~~~ textToList ~~~"
- set resultList to {}
- set {tid, my text item delimiters} to {my text item delimiters, delim}
- try
- set resultList to every text item of thisText
- set my text item delimiters to tid
- on error
- set my text item delimiters to tid
- end try
- return resultList
- end textToList
- -- ------------------------------------------------------
- (*
- Foound here:
- https://www.macosxautomation.com/applescript/sbrt/sbrt-06.html
- Text Manipulation
- Manipulating text strings is one of the most common tasks performed in scripts. The following sub-routines perform some of the most common text manipulation tasks.
- Trim Line
- The following sub-routine can be used to trim text from the beginning or end of a string. It has three passed parameters:
- The text to trim
- The characters to trim from the passed text
- The direction indicator
- The direction indicator has three possible numeric values:
- 0, which tells the routine to trim the indicated characters from the beginning of the passed string:
- set this_text to "----1----"
- trim_line(this_text, "-", 0)
- --> returns: "1----"
- 1, which tells the routine to trim the indicated characters from the end of the passed string:
- set this_text to "12345.txt"
- trim_line(this_text, ".txt", 1)
- --> returns: "12345"
- 2, which tells the routine to trim the indicated characters from both ends of the passed string:
- set this_text to "*-*-fred*-*-"
- trim_line(this_text, "*-", 2)
- --> returns: "fred"
- *)
- on trim_line(this_text, trim_chars, trim_indicator)
- -- 0 = beginning, 1 = end, 2 = both
- set x to the length of the trim_chars
- -- TRIM BEGINNING
- if the trim_indicator is in {0, 2} then
- repeat while this_text begins with the trim_chars
- try
- set this_text to characters (x + 1) thru -1 of this_text as string
- on error
- -- the text contains nothing but the trim characters
- return ""
- end try
- end repeat
- end if
- -- TRIM ENDING
- if the trim_indicator is in {1, 2} then
- repeat while this_text ends with the trim_chars
- try
- set this_text to characters 1 thru -(x + 1) of this_text as string
- on error
- -- the text contains nothing but the trim characters
- return ""
- end try
- end repeat
- end if
- return this_text
- end trim_line
- (* non-working code
- repeat with aName in emailList
- log "--------------> aName is " & aName
- if displayEmailCount = 0 then
- set displayEmailText to displayEmailText & " " & aName
- else
- set displayEmailText to displayEmailText & ", " & aName
- end if
- set displayEmailCount to displayEmailCount + 1
- -- five email addresses per line
- if displayEmailCount ≥ 1 then
- set displayEmailText to displayEmailText & return
- set displayEmailCount to 0
- end if
- end repeat
- *)
- (*
- log (characters 1 thru (length of endMarker) of aName is endMarker)
- log (characters 1 thru (length of endMarker) of aName) as text
- log "class of endMarker is " & class of endMarker
- set evaluatedExpression to (characters 1 thru (length of endMarker) of aName) as text
- log "evaluatedExpression is " & evaluatedExpression
- log "class of characters... " & class of evaluatedExpression
- *)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement