Skip to content

Commit c30a5bb

Browse files
authored
Merge pull request #77 from omkshirsagar442/main
Bulk Email Sender
2 parents 53b1590 + 692522d commit c30a5bb

File tree

3 files changed

+220
-0
lines changed

3 files changed

+220
-0
lines changed

Python/Bulk Email Sender/README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# 📨 Bulk Email Sender
2+
3+
A modern **Streamlit web application** that allows users to send **personalized bulk emails** with optional attachments, Gmail App Password authentication, and live progress tracking.
4+
5+
---
6+
7+
## 🚀 Overview
8+
9+
This application simplifies email marketing and bulk communication by letting you send personalized emails to multiple recipients directly from a simple web interface.
10+
Built with **Python** and **Streamlit**, it supports both **CSV-based** and **manual** recipient entry.
11+
12+
---
13+
14+
## ✨ Key Features
15+
16+
- 📋 **CSV Upload Support** — Upload recipient lists in `.csv` format (`Name`, `Email`)
17+
- ✏️ **Manual Email Entry** — Add extra emails manually in “Name:Email” format
18+
- 🪄 **Personalized Messages** — Use placeholders like `{Name}` in your subject and message
19+
- 📎 **File Attachments** — Attach PDFs, images, or DOCX files to your emails
20+
- 👁️ **Email Preview** — Preview subject and message before sending
21+
- 📊 **Progress Bar + Logs** — Live progress tracker with real-time status updates
22+
- 🔐 **Secure Gmail Login** — Uses **App Passwords** (never your actual Gmail password)
23+
- 🎈 **Clean UI with Streamlit** — User-friendly interface with status updates and balloons on completion
24+
25+
---
26+
27+
## 📂 Example CSV Format
28+
29+
Make sure your CSV has the following structure:
30+
31+
| Name | Email |
32+
|------|--------------------|
33+
| Rahul Pandey | rahul@example.com |
34+
| Riya Sharma | riya@example.com |
35+
36+
---
37+
38+
## 🔧 Gmail App Password Setup
39+
40+
To use your Gmail securely, follow these steps:
41+
42+
1. Go to your [Google Account Security Settings](https://myaccount.google.com/security)
43+
2. Enable **2-Step Verification**
44+
3. Go to **App Passwords**
45+
4. Choose:
46+
- **App:** Mail
47+
- **Device:** Windows (or any)
48+
5. Copy the generated **16-character password**
49+
6. Use it in this app instead of your regular Gmail password
50+
51+
> ⚠️ *Without enabling 2-Step Verification, you cannot use Gmail App Password.
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import smtplib
2+
from email.mime.text import MIMEText
3+
from email.mime.multipart import MIMEMultipart
4+
from email.mime.base import MIMEBase
5+
from email import encoders
6+
import pandas as pd
7+
import streamlit as st
8+
import time
9+
import os
10+
11+
# ---------------- Streamlit Page Config ----------------
12+
st.set_page_config(page_title="📧 Bulk Email Dashboard", page_icon="📨", layout="centered")
13+
st.title("📨 Bulk Email Sender")
14+
st.caption("Send personalized bulk emails with live logs, attachments, and custom names")
15+
16+
st.markdown("---")
17+
18+
# ---------------- Email Login Section ----------------
19+
with st.expander("🔑 Gmail Login Details"):
20+
sender_name = st.text_input("Sender Name (appears in inbox)", placeholder="Your Name")
21+
sender_email = st.text_input("Your Gmail Address", placeholder="youremail@gmail.com")
22+
sender_password = st.text_input("App Password", type="password", placeholder="16-character Gmail App Password")
23+
24+
# ---------------- CSV Upload ----------------
25+
uploaded_file = st.file_uploader("📂 Upload CSV (columns: Name, Email)", type=["csv"])
26+
27+
st.markdown("""
28+
**Example CSV format:**
29+
| Name | Email |
30+
|------|--------------------|
31+
| Rahul Pandey | rahul@example.com |
32+
| Riya Sharma | riya@example.com |
33+
""")
34+
35+
# ---------------- Manual Email Entry ----------------
36+
with st.expander("✏️ Add Optional Emails Manually"):
37+
manual_emails = st.text_area(
38+
"Enter additional emails manually (format: Name:Email, separated by commas)",
39+
placeholder="Rahul Pandey:rahul@gmail.com, Riya Sharma:riya@gmail.com"
40+
)
41+
42+
# ---------------- Email Content ----------------
43+
subject = st.text_input("✉️ Email Subject", placeholder="Hello {Name}, here’s your update!")
44+
message_template = st.text_area(
45+
"📝 Email Body (you can use placeholders like {Name})",
46+
height=150,
47+
placeholder="Dear {Name},\n\nHope you're doing well!\n\nBest regards,\nYour Name"
48+
)
49+
50+
# ---------------- File Attachment ----------------
51+
attachment = st.file_uploader("📎 Optional: Attach a file (PDF, Image, or DOCX)", type=["pdf", "jpg", "jpeg", "png", "docx"])
52+
53+
# ---------------- Email Preview ----------------
54+
if st.button("👁️ Preview Email"):
55+
if uploaded_file:
56+
df_preview = pd.read_csv(uploaded_file)
57+
if "Name" in df_preview.columns and len(df_preview) > 0:
58+
name_sample = df_preview["Name"][0]
59+
st.markdown("### 🧾 Preview for first recipient:")
60+
st.info(f"**Subject:** {subject.format(Name=name_sample)}\n\n{message_template.format(Name=name_sample)}")
61+
else:
62+
st.warning("CSV must have a 'Name' column with at least one entry.")
63+
else:
64+
st.warning("Please upload a CSV first.")
65+
66+
# ---------------- Send Button ----------------
67+
if st.button("🚀 Send Emails"):
68+
if not sender_email or not sender_password:
69+
st.error("Please enter your Gmail and App Password.")
70+
elif uploaded_file is None and not manual_emails:
71+
st.error("Please upload a CSV file or enter emails manually.")
72+
else:
73+
try:
74+
recipients = []
75+
76+
# Load from CSV if available
77+
if uploaded_file is not None:
78+
df = pd.read_csv(uploaded_file)
79+
if "Email" not in df.columns or "Name" not in df.columns:
80+
st.error("CSV must contain columns 'Name' and 'Email'.")
81+
st.stop()
82+
for _, row in df.iterrows():
83+
recipients.append({"Name": row["Name"], "Email": row["Email"]})
84+
85+
# Add manual emails (support Name:Email or just Email)
86+
if manual_emails:
87+
for entry in [e.strip() for e in manual_emails.split(",") if e.strip()]:
88+
if ":" in entry:
89+
name, email = entry.split(":", 1)
90+
recipients.append({"Name": name.strip(), "Email": email.strip()})
91+
else:
92+
recipients.append({"Name": "Friend", "Email": entry.strip()})
93+
94+
# Connect to Gmail
95+
server = smtplib.SMTP("smtp.gmail.com", 587)
96+
server.starttls()
97+
server.login(sender_email, sender_password)
98+
st.success("✅ Logged in successfully!")
99+
100+
# UI Setup: progress bar + live logs
101+
progress = st.progress(0)
102+
status = st.empty()
103+
log_box = st.container()
104+
total = len(recipients)
105+
success_count = 0
106+
107+
# ---------------- Sending Loop ----------------
108+
for i, recipient in enumerate(recipients):
109+
recipient_email = recipient["Email"]
110+
recipient_name = recipient["Name"]
111+
112+
msg = MIMEMultipart()
113+
msg["From"] = f"{sender_name} <{sender_email}>"
114+
msg["To"] = recipient_email
115+
msg["Subject"] = subject.format(Name=recipient_name)
116+
117+
# Message body
118+
body = message_template.format(Name=recipient_name)
119+
msg.attach(MIMEText(body, "plain"))
120+
121+
# Add attachment if uploaded
122+
if attachment is not None:
123+
part = MIMEBase("application", "octet-stream")
124+
part.set_payload(attachment.getvalue())
125+
encoders.encode_base64(part)
126+
part.add_header(
127+
"Content-Disposition",
128+
f"attachment; filename={attachment.name}"
129+
)
130+
msg.attach(part)
131+
132+
# Send email and log live
133+
try:
134+
server.send_message(msg)
135+
success_count += 1
136+
with log_box:
137+
st.markdown(f"✅ **Sent to {recipient_name}** ({recipient_email})")
138+
except Exception as e:
139+
with log_box:
140+
st.markdown(f"⚠️ **Failed to send to {recipient_email}:** {e}")
141+
142+
# Update progress bar
143+
progress.progress((i + 1) / total)
144+
status.text(f"📨 Sending email {i+1}/{total}...")
145+
146+
time.sleep(0.5)
147+
148+
server.quit()
149+
st.success(f"✅ Successfully sent {success_count}/{total} emails!")
150+
st.balloons()
151+
152+
except Exception as e:
153+
st.error(f"❌ Error: {e}")
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
1)Python
2+
Python 3.8+
3+
4+
2)Python Packages
5+
streamlit
6+
pandas
7+
smtplib (built-in)
8+
email (built-in)
9+
time (built-in)
10+
os (built-in)
11+
12+
3)Software / Tools
13+
Gmail account with App Password
14+
Web browser (Chrome, Firefox, Edge)
15+
CSV editor (Excel, Google Sheets) for recipient lists
16+
Optional: PDF, DOCX, or image editor for attachments

0 commit comments

Comments
 (0)