Skip to content

Commit 8274796

Browse files
Add initial implementation of Email Sender
WESMUN 2026 email sender with CSV processing and email template sender
1 parent 747a21a commit 8274796

5 files changed

Lines changed: 543 additions & 0 deletions

File tree

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# WESMUN 2026 Delegate Email Sender
2+
3+
A Python script to send personalized committee allocation emails to Model United Nations (MUN) delegates for WESMUN 2026.
4+
5+
## 📋 Overview
6+
7+
This tool automates the process of sending allocation emails to delegates, including:
8+
- Personalized email content with delegate name and country/portfolio allocation
9+
- Committee-specific information (agendas, group chat links)
10+
- PDF attachments (Code of Conduct, Delegate Handbook, Schedule, Waiver Form)
11+
- Committee-specific Background Guide PDFs
12+
13+
## 📁 Project Structure
14+
15+
```
16+
wesmun_del_email/
17+
├── sendMail.py # Main email sending script
18+
├── body.txt # Email body template with placeholders
19+
├── bodyReplacements.csv # Committee-specific data (agendas, GC links)
20+
├── chairsEmailLogin.csv # Committee chair email credentials
21+
├── countryMatrixCSV/ # Delegate allocation CSVs (one per committee)
22+
│ ├── AL.csv
23+
│ ├── ECOSOC.csv
24+
│ ├── GA1.csv
25+
│ └── ... (other committees)
26+
├── allAttachments/ # PDF attachments sent to all delegates
27+
│ ├── Code Of Conduct.pdf
28+
│ ├── Delegate Handbook.pdf
29+
│ ├── Schedule.pdf
30+
│ ├── Waiver Consent Form.pdf
31+
│ └── BackGroundGuide/ # Committee-specific background guides
32+
│ ├── AL.pdf
33+
│ ├── ECOSOC.pdf
34+
│ └── ... (other committees)
35+
├── backup/ # Backup of original country matrix files
36+
└── scripts/ # Utility scripts
37+
└── csvStripper.py
38+
```
39+
40+
## 📝 File Formats
41+
42+
### `countryMatrixCSV/*.csv` (Delegate Data)
43+
Each committee has a CSV with delegate allocations:
44+
```csv
45+
Allocation,Delegate Name,Email
46+
Bahrain,John doe,email@example.com
47+
Jordan,Doe John,example@email.com
48+
```
49+
50+
### `chairsEmailLogin.csv` (Chair Credentials)
51+
Email credentials for sending from committee chairs:
52+
```csv
53+
name,email,password
54+
AL,al@wesmun.com,password123
55+
ECOSOC,ecosoc@wesmun.com,password456
56+
```
57+
> ⚠️ **Important**: The `name` column must match the CSV filename in `countryMatrixCSV/` (e.g., `AL` for `AL.csv`)
58+
59+
### `bodyReplacements.csv` (Committee Info)
60+
Committee-specific details for email body:
61+
```csv
62+
name,Agenda1,Agenda2,GCLink
63+
AL,Strengthening Regional Peacekeeping,Enhancing Humanitarian Aid,https://gc.link/al
64+
ECOSOC,Global Economic Recovery,Sustainable Development Goals Review,https://gc.link/ecosoc
65+
```
66+
67+
### `body.txt` (Email Template)
68+
Template with placeholders that get replaced:
69+
- `[[name]]` - Committee name
70+
- `[[delegateName]]` - Delegate's name
71+
- `[[allocation]]` - Country/Portfolio assignment
72+
- `[[Agenda1]]` - First agenda topic
73+
- `[[Agenda2]]` - Second agenda topic
74+
- `[[GCLink]]` - Group chat link
75+
- `[[name.lower]]` - Committee name in lowercase
76+
77+
## Usage
78+
79+
### Prerequisites
80+
- Python 3.6+
81+
- PurelyMail accounts for `@wesmun.com` email addresses
82+
- **⚠️ IMPORTANT**: All sender emails MUST be `@wesmun.com` addresses
83+
- You need the PurelyMail passwords for each committee email account
84+
- The script validates that all emails are from the `@wesmun.com` domain
85+
86+
### Run Modes
87+
88+
```bash
89+
# Test run - validates everything without sending emails
90+
python sendMail.py --testRun
91+
92+
# Fake run - sends all emails to it@wesmun.com (for testing)
93+
python sendMail.py --fakeRun
94+
95+
# Real run - sends emails to actual delegates with CC to staff
96+
python sendMail.py --run
97+
```
98+
99+
### Command Line Options
100+
101+
| Flag | Description |
102+
|-------------|--------------------------------------------------------------|
103+
| `--testRun` | Dry run - logs what would be sent without actually sending |
104+
| `--fakeRun` | Sends emails to `it@wesmun.com` instead of actual recipients |
105+
| `--run` | Production mode - sends to real recipients with CC to staff |
106+
107+
### CC Recipients (Production Mode Only)
108+
When using `--run`, emails are CC'd to:
109+
- it@wesmun.com
110+
- secretariat@wesmun.com
111+
- chiefofstaff@wesmun.com
112+
113+
## Email Details
114+
115+
- **From**: Committee chair email (e.g., `ecosoc@wesmun.com`)
116+
- **Subject**: "WESMUN 2026 - Committee Allocation"
117+
- **Attachments**:
118+
- Code Of Conduct.pdf
119+
- Delegate Handbook.pdf
120+
- Schedule.pdf
121+
- Waiver Consent Form.pdf
122+
- Committee Background Guide (e.g., `ECOSOC.pdf`)
123+
124+
## Logging
125+
126+
All activity is logged to:
127+
- **Console**: Real-time status messages
128+
- **File**: `email_sender.log` (persistent log)
129+
130+
## ⚠️ Important Notes
131+
132+
1. **PurelyMail Setup**: All committee email addresses must be `@wesmun.com` addresses hosted on PurelyMail. The script validates this before sending.
133+
134+
2. **Committee Name Matching**: The committee name in `chairsEmailLogin.csv` and `bodyReplacements.csv` must **exactly match** the CSV filename in `countryMatrixCSV/` (without `.csv` extension).
135+
136+
3. **Email Validation**: The script automatically validates that all sender emails are `@wesmun.com` before processing. If any invalid emails are found, it will exit with an error.
137+
138+
4. **Test First**: Always run with `--testRun` or `--fakeRun` before using `--run` to verify everything is configured correctly.
139+
140+
## 🔧 Troubleshooting
141+
142+
| Issue | Solution |
143+
|----------------------------------------|--------------------------------------------------------------------------|
144+
| "Chair info not found for committee X" | Ensure committee name in `chairsEmailLogin.csv` matches the CSV filename |
145+
| "Replacement info not found" | Ensure committee name in `bodyReplacements.csv` matches the CSV filename |
146+
| "Invalid sender email" | Ensure all emails in `chairsEmailLogin.csv` end with `@wesmun.com` |
147+
| Authentication failed | Verify the PurelyMail password is correct for that email address |
148+
| Connection timeout | Check that PurelyMail SMTP (smtp.purelymail.com:587) is accessible |
149+
150+
## 📜 License
151+
152+
Internal tool for WESMUN 2026 conference organization.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
Dear [[delegateName]],
2+
3+
We are pleased to inform you of your allocation for WESMUN 2026. You have been assigned to the following committee:
4+
5+
Committee: [[name]]
6+
Country/Portfolio: [[allocation]]
7+
- Agenda 1: [[Agenda1]]
8+
- Agenda 2: [[Agenda2]]
9+
10+
You can check more info at https://wesmun.com/committees/[[name.lowercase]]
11+
12+
Conference Dates:
13+
- 31st January to 1st Feb 2026
14+
15+
Venue Location:
16+
- Google Maps link: https://google.com/maps?ll=25.294047,55.450106&z=15&t=m&hl=en-GB&gl=US&mapclient=embed&cid=439979476831574513
17+
18+
19+
Before you agree to attending the conference, please sign the waiver form below and submit it in the following link (*NOTE: You will not be allowed into the conference without submitting this form*):
20+
- https://forms.gle/ERfAH7E15HFynUgV6
21+
22+
23+
Please find attached the following important documents:
24+
- Code of Conduct
25+
- Delegate Handbook
26+
- Conference Schedule
27+
- Background Guide for your committee
28+
29+
These resources will help you prepare thoroughly for the event. Should you have any questions, feel free to reach out.
30+
31+
Additionally, a position paper is a document required to be sent to *this email* as per the deadline given with the following guidelines
32+
1. Font - Times New Roman size 12
33+
2. Line Spacing 1
34+
3. Content - 700 word maximum excluding bibliography
35+
4. Bibliography is mandatory
36+
37+
We recommend you include the following
38+
- A brief introduction to the agenda and its issues (15% of your paper)
39+
- Past actions taken by the UN and your nation (20%)
40+
- The stance of your nation, issues, initiatives taken (40%)
41+
- Solutions of your own that align with your nation (15%)
42+
- Conclusion (10%)
43+
Please note this is a suggestion.
44+
45+
Finally, please be sure to click the link below to join your committee group-chat:
46+
- [[GCLink]]
47+
48+
*NOTE: YOU WILL NOT BE ALLOWED IN ANY COMMITTEE OTHER THAN YOUR OWN. Joining other group-chats may lead to termination of your delegation*
49+
50+
We look forward to welcoming you to WESMUN 2026 and seeing your participation in a meaningful and engaging debate.
51+
52+
Regards,
53+
DIAS Of [[name]]
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import csv
2+
import os
3+
4+
ALLOCATION_ALIASES = ["Allocation", "Delegation", "Country"]
5+
INTEL_COL = "Intelligence Agency"
6+
OUTPUT_COLS = ["Allocation", "Delegate Name", "Email"]
7+
8+
for filename in os.listdir("."):
9+
if not filename.lower().endswith(".csv"):
10+
continue
11+
12+
with open(filename, newline="", encoding="utf-8") as f:
13+
reader = csv.DictReader(f)
14+
headers = reader.fieldnames
15+
16+
if not headers:
17+
continue
18+
19+
allocation_key = None
20+
for key in ALLOCATION_ALIASES:
21+
if key in headers:
22+
allocation_key = key
23+
break
24+
25+
if allocation_key is None:
26+
raise ValueError(
27+
f"{filename} missing Allocation / Delegation / Country column"
28+
)
29+
30+
for col in ["Delegate Name", "Email"]:
31+
if col not in headers:
32+
raise ValueError(f"{filename} missing required column: {col}")
33+
34+
has_intel = INTEL_COL in headers
35+
rows = []
36+
37+
for row in reader:
38+
allocation = row[allocation_key].strip()
39+
40+
if has_intel:
41+
intel = row[INTEL_COL].strip()
42+
if intel:
43+
allocation = f"{allocation} ({intel})"
44+
45+
rows.append({
46+
"Allocation": allocation,
47+
"Delegate Name": row["Delegate Name"].strip(),
48+
"Email": row["Email"].strip()
49+
})
50+
51+
with open(filename, "w", newline="", encoding="utf-8") as f:
52+
writer = csv.DictWriter(f, fieldnames=OUTPUT_COLS)
53+
writer.writeheader()
54+
writer.writerows(rows)
55+
56+
print("All CSV files processed.")
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import smtplib
2+
3+
email = "email here"
4+
password = "app password here - not login password" # app password
5+
6+
try:
7+
with smtplib.SMTP('smtp.purelymail.com', 587) as smtp:
8+
smtp.ehlo()
9+
smtp.starttls()
10+
smtp.ehlo()
11+
smtp.login(email, password)
12+
print("Login successful")
13+
except smtplib.SMTPAuthenticationError as e:
14+
print("Authentication failed:", e)
15+
except Exception as e:
16+
print("Other error:", e)

0 commit comments

Comments
 (0)