Skip to content

Ngawa tafe/issue18 #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea/
User_Credentials/
__pycache__
6 changes: 4 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
FROM python:3.10.12-alpine
RUN apk update
WORKDIR /emailbomber
COPY emailbomber.py emailbomber.py

COPY src/ ./src/
COPY main.py .
COPY ./Welcome/welcome.txt ./Welcome/welcome.txt
CMD "python" "emailbomber.py"
CMD ["python", "main.py"]
89 changes: 0 additions & 89 deletions emailbomber.py

This file was deleted.

9 changes: 9 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from src.emailbomber import email_bomber


def main():
email_bomber()


if __name__ == "__main__":
main()
14 changes: 14 additions & 0 deletions src/authentication.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import smtplib


def login_to_smtp(server, sender, app_password):
"""
logs into the SMTP server.
Returns:
smtplib.SMTP: authenticated SMTP server object
"""
try:
server.login(sender, app_password)
except smtplib.SMTPAuthenticationError as e:
raise smtplib.SMTPAuthenticationError(e.smtp_code,"Login failed: Check your email or app password.") from e
return server
50 changes: 50 additions & 0 deletions src/crendentials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import getpass
import os.path
from os import path, mkdir

CREDENTIALS_DIR = "User_Credentials"


def get_credentials(dir_name=CREDENTIALS_DIR):
"""
Loads sender email and app password from file if exists,
otherwise prompts the user to input and saves them.
Returns:
tuple: sender_email, app_password
"""
parent_dir = os.path.abspath(os.getcwd())
file_path = os.path.join(parent_dir, dir_name)
if not path.exists(file_path):
sender = input("Enter your Gmail address ([email protected]): ")
app_password = getpass.getpass("Enter your app password (xxxx xxxx xxxx xxxx): ")
else:
with open(f"{file_path}/sender.txt", "r") as sender_file:
sender = sender_file.read()
with open(f"{file_path}/app_password.txt", "r") as password_file:
app_password = password_file.read()
print("\nCredentials Loaded successfully.")

return sender, app_password


def save_credentials(sender, app_password, dir_name=CREDENTIALS_DIR):
"""
Saves sender email and app password into file if exists.
Create a file if it doesn't exist.
:param sender: email address
:param app_password: app password
:param dir_name: directory to save credentials
:return: None
"""
parent_dir = os.path.abspath(os.getcwd())
file_path = os.path.join(parent_dir, dir_name)
try:
if not path.exists(file_path):
mkdir(file_path)
with open(f"{file_path}/sender.txt", "w") as sender_file:
sender_file.write(sender)
with open(f"{file_path}/app_password.txt", "w") as password_file:
password_file.write(app_password)
Copy link
Preview

Copilot AI Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing application passwords in plaintext poses a security risk; consider using an OS keyring or encrypting the credentials on disk.

Copilot uses AI. Check for mistakes.

print("\nCredentials saved successfully.")
except OSError as e:
raise OSError(f"Error saving credentials: {e}")
55 changes: 55 additions & 0 deletions src/e_mail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import smtplib
from email.message import EmailMessage


def get_email_details():
"""
Prompts the user for recipients, subject, message, and count.
Returns:
tuple: recipients, subject, message_body, email_count
"""
print(
"If you would like to spam more than one email, separate the emails by commas ([email protected], [email protected], [email protected])\n")
recipients = input(
"Specify the email(s) you would like to email-bomb (comma-separated) -> "
).replace(" ", "").split(",")
subject = input("Enter the email subject: ")
msg = input("Enter the email message: ")

while True:
try:
count = int(input("Enter the number of times to send each email: "))
if count <= 0:
raise ValueError("Count must be positive.")
break
except ValueError as e:
raise ValueError(f"Invalid input: {e}")
Copy link
Preview

Copilot AI Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This except block re-raises a ValueError on invalid count input, exiting instead of re-prompting; consider printing the error and using 'continue' to allow the user to retry.

Suggested change
raise ValueError(f"Invalid input: {e}")
print(f"Error: {e}. Please try again.")
continue

Copilot uses AI. Check for mistakes.


return recipients, subject, msg, count


def create_email(subject, body):
"""
Creates an EmailMessage object.
Returns:
EmailMessage: the constructed message
"""
msg = EmailMessage()
msg["Subject"] = subject
msg.set_content(body, subtype="plain", charset="us-ascii")
return msg


def send_bulk_emails(server, sender, recipients, msg, count):
"""
Sends emails using the authenticated SMTP server.
"""
print("\nSending emails...\n")
for _ in range(count):
for recipient in recipients:
try:
print(f"Sending to {recipient}...")
server.sendmail(sender, recipient, msg.as_string())
print("Sent successfully!")
except smtplib.SMTPException as e:
raise smtplib.SMTPException(f"Failed to send to {recipient}: {e}")
39 changes: 39 additions & 0 deletions src/emailbomber.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import smtplib
from src.server import get_server
from src.load_welcome import display_welcome
from src.authentication import login_to_smtp
from src.crendentials import get_credentials, save_credentials
from src.e_mail import get_email_details, create_email, send_bulk_emails


def email_bomber():
display_welcome()
sender, app_password = get_credentials()

server = None
try:
server = get_server()
if server:
server = login_to_smtp(server, sender, app_password)
save_credentials(sender, app_password)
recipients, subject, body, count = get_email_details()
msg = create_email(subject, body)
send_bulk_emails(server, sender, recipients, msg, count)
print("Email Bombing process completed successfully.")
except smtplib.SMTPConnectError as e:
print(f"SMTP Connect error: {e}")
except smtplib.SMTPAuthenticationError as e:
print(f"SMTP authentication error: {e}")
except ValueError as e:
print(f"ValueError: {e}")
except smtplib.SMTPException as e:
print(f"SMTP error: {e}")
except Exception as e:
print(f"Unknown error: {e}")
finally:
if server:
server.close()
print("\nDisconnected from SMTP server.")
print("Email Bomber closing...")
else:
print("\nEmail process aborted due to connection/login failure.")
14 changes: 14 additions & 0 deletions src/load_welcome.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import os

WELCOME_FILE = r"Welcome/welcome.txt"


def display_welcome(filepath=WELCOME_FILE):
"""Displays the welcome message from a file."""
parent_dir = os.path.abspath(os.getcwd())
Copy link
Preview

Copilot AI Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolving the welcome file relative to the current working directory may break if run elsewhere; consider using 'os.path.dirname(file)' or pathlib to reference the module's directory.

Suggested change
parent_dir = os.path.abspath(os.getcwd())
parent_dir = os.path.abspath(os.path.dirname(__file__))

Copilot uses AI. Check for mistakes.

filepath = os.path.join(parent_dir, filepath)
try:
with open(filepath, encoding="utf-8") as file:
print(f"{file.read()}\n\n")
except FileNotFoundError:
raise FileNotFoundError("Welcome file not found.")
13 changes: 13 additions & 0 deletions src/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import smtplib

SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587


def get_server(smtp_server=SMTP_SERVER, smtp_port=SMTP_PORT):
try:
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
Comment on lines +9 to +10
Copy link
Preview

Copilot AI Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] It’s recommended to call 'server.ehlo()' before and after 'starttls()' to ensure proper SMTP handshake and compliance with server requirements.

Suggested change
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
server = smtplib.SMTP(smtp_server, smtp_port)
server.ehlo()
server.starttls()
server.ehlo()

Copilot uses AI. Check for mistakes.

except smtplib.SMTPConnectError as e:
raise smtplib.SMTPConnectError(e.smtp_code, f"Connection failed: {e}")
return server