Compare commits
10 Commits
24fa3e25d2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5b20a71de | ||
|
|
a902f5a685 | ||
|
|
153ba885c8 | ||
|
|
4ff791d9fe | ||
|
|
2df6e67ffb | ||
|
|
8165ee9970 | ||
|
|
47e698a4fe | ||
|
|
62a11e258a | ||
|
|
df99a03936 | ||
|
|
6c82570829 |
23
LICENSE.txt
Normal file
23
LICENSE.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
send_mail
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Handmade Cities
|
||||
|
||||
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.
|
||||
45
README.md
Normal file
45
README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
------
|
||||
**DISCLAIMER: This project is very much a Work In Progress. It is for meetup hosts pioneering Handmade Meetups. Hosts are expected to be programmers comfortable with command-line programs. You can get help or give feedback in the [`#meetups`](https://discord.com/channels/239737791225790464/424523844016144394) channel of the [Handmade Network Discord](https://discord.gg/hmn).**
|
||||
------
|
||||
# send_mail
|
||||
`send_mail` lets you email a [meetup group](https://handmadecities.com/meetups) using Handmade Cities branding. You can send an invite to your meetup group like so:
|
||||
|
||||
`> python3 send_mail.py --subject SUBJECT --body BODY`
|
||||
|
||||
where `SUBJECT` is the subject of your email and `BODY` is the file containing its contents. Plain text or HTML is supported. Your email will automatically include the Handmade Cities brand along with unsubscribe links!
|
||||
|
||||
Note that you can dump the mailing list of your meetup group so far:
|
||||
|
||||
`> python3 send_mail.py --dump`
|
||||
|
||||
This list grows automatically as more people sign up for your city.
|
||||
## app.ini
|
||||
`send_mail` requires a companion `app.ini` file. It has the secret credentials to be able to dump the mailing list and otherwise send real emails.
|
||||
|
||||
Below is how the file might look like. (Secret stuff is censored as `xxx`):
|
||||
|
||||
```
|
||||
; User Constants - Update as needed
|
||||
[DEFAULT]
|
||||
LIVE = false
|
||||
TEST_EMAIL = abner@terminal.click
|
||||
SENDER_NAME = Abner Coimbre
|
||||
|
||||
; Handmade Cities Credentials - DON'T TOUCH
|
||||
[hmc]
|
||||
API_URL = https://api.handmadecities.com/v1/meetups/subscribers
|
||||
SHARED_SECRET = xxx
|
||||
CITY = Seattle
|
||||
|
||||
; Postmark App Credentials - NO TOUCHY TOUCHY
|
||||
[postmark]
|
||||
SERVER_TOKEN = xxx
|
||||
API_URL = https://api.postmarkapp.com/email/batchWithTemplates
|
||||
TEMPLATE_ID = xxx
|
||||
SENDER_EMAIL = abner@handmadecities.com
|
||||
MESSAGE_STREAM = xxx
|
||||
```
|
||||
|
||||
`LIVE` is false by default, in which case the script only sends an email to `TEST_EMAIL`. Set `LIVE` to true when you're ready to mass-email your group.
|
||||
## Known Issues
|
||||
`send_mail` uses the `requests` module which may or may not be in your system. You'll need to use `pip` if it's not. See [this issue](https://git.handmadecities.com/meetups/send-mail/issues/2) for details.
|
||||
2
app.ini
Normal file
2
app.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
; This file should be populated with the necessary credentials to use send_mail.py
|
||||
; If you're a meetup host talk to Abner to get your own personalized app.ini
|
||||
4
body.html
Normal file
4
body.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<h1>Dear reader,</h1>
|
||||
<p>This is a test email. Handmade people are <strong>BOLD</strong>.</p>
|
||||
<p><em>The software revolution will not be televised.</em></p>
|
||||
<p>Test complete,<br>Abner</p>
|
||||
58
send_mail.py
58
send_mail.py
@@ -5,6 +5,7 @@
|
||||
# ============================ Contributors =========================
|
||||
# Bug & warning fixes
|
||||
# Jacob Bell (@MysteriousJ)
|
||||
# Asaf Gartner
|
||||
#
|
||||
# Emotional Support
|
||||
# Cucui Ganon Rosario
|
||||
@@ -68,30 +69,39 @@ def sendPostmarkMessage(subject, body, to):
|
||||
}
|
||||
|
||||
messages = []
|
||||
for email in to:
|
||||
message = {
|
||||
"From": POSTMARK_SENDER_EMAIL,
|
||||
"To": email,
|
||||
"TemplateId": POSTMARK_TEMPLATE_ID,
|
||||
"TemplateModel": {
|
||||
"name": SENDER_NAME,
|
||||
"email": POSTMARK_SENDER_EMAIL,
|
||||
"body": body,
|
||||
"subject": subject
|
||||
},
|
||||
"MessageStream": POSTMARK_MESSAGE_STREAM
|
||||
|
||||
# Postmark supports 500 messages per API call.
|
||||
# Split the 'to' list into chunks of 500 or fewer email addresses
|
||||
for i in range(0, len(to), 500):
|
||||
to_chunk = to[i:i+500]
|
||||
|
||||
for email in to_chunk:
|
||||
message = {
|
||||
"From": POSTMARK_SENDER_EMAIL,
|
||||
"To": email,
|
||||
"TemplateId": POSTMARK_TEMPLATE_ID,
|
||||
"TemplateModel": {
|
||||
"name": SENDER_NAME,
|
||||
"email": POSTMARK_SENDER_EMAIL,
|
||||
"body": body,
|
||||
"subject": subject
|
||||
},
|
||||
"MessageStream": POSTMARK_MESSAGE_STREAM
|
||||
}
|
||||
messages.append(message)
|
||||
|
||||
payload = {
|
||||
"Messages": messages
|
||||
}
|
||||
messages.append(message)
|
||||
|
||||
payload = {
|
||||
"Messages": messages
|
||||
}
|
||||
# Debug:
|
||||
# print(json.dumps(payload, indent=4))
|
||||
|
||||
# Debug:
|
||||
# print(json.dumps(payload, indent=4))
|
||||
result = requests.post(POSTMARK_API_URL, headers=headers, json=payload)
|
||||
print(result.text)
|
||||
|
||||
result = requests.post(POSTMARK_API_URL, headers=headers, json=payload)
|
||||
print(result.text)
|
||||
# Clear the messages list for the next chunk
|
||||
messages.clear()
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Create a ConfigParser object and read the INI file
|
||||
@@ -127,8 +137,12 @@ if __name__ == '__main__':
|
||||
|
||||
if args.dump:
|
||||
meetup_group = dump_emails()
|
||||
for email in meetup_group:
|
||||
print(email)
|
||||
print(f"Handmade Meetups - \033[1m{HMC_CITY}\033[0m\n")
|
||||
max_index_width = len(str(len(meetup_group)))
|
||||
for index, email in enumerate(meetup_group, start=1):
|
||||
index_str = str(index).rjust(max_index_width) # Right-align the index
|
||||
print(f"{index_str}. \033[1m{email}\033[0m")
|
||||
print("\nMailing list grows automatically as more subscribe at \033[1mhandmadecities.com/meetups\033[0m")
|
||||
elif args.subject and args.body:
|
||||
subject = args.subject
|
||||
body = ''
|
||||
|
||||
Reference in New Issue
Block a user