Jul-17-2018, 07:20 AM
Hi all. I just finished writing a web crawler which sends emails through gmail. It's one of my first GUI based programs that almost resembles a finished product, so I thought it would be worth posting on here to get some feedback. It is quite buggy at the moment, but it's functional and that's good enough for me.
Here's the code:
This program also requires a text file which is formatted like this:
You will need to change line 407 to the path of this file on your computer for it to run properly.
Other than that, everything should work as intended and you'll be able to send bulk emails with ease.
Thanks for your time if you decide to try this program out.
Note: The annoy function waits until the minute value of current time changes to send each repeat, therefore if you have a repeat value of 5 the program will become unresponsive for 5 minutes.
Here's the code:
#Import necessary modules
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from tkinter import *
import time
#Centers tkinter window (https://stackoverflow.com/a/10018670)
def center(win):
win.update_idletasks()
width = win.winfo_width()
height = win.winfo_height()
x = (win.winfo_screenwidth() // 2) - (width // 2)
y = (win.winfo_screenheight() // 2) - (height // 2)
win.geometry('{}x{}+{}+{}'.format(width, height, x, y))
#Create GUI
def createGUILogin(oldRoot=False):
global sendingEmail, sendingPassword, GUIText
if oldRoot != False:
oldRoot.destroy()
readFile()
root = Tk()
root.title('Emailer GUI')
root.geometry('320x250')
center(root)
#Login frame
entry = Frame(root)
entry.pack(anchor='center', padx=10, pady=10)
emailText = Label(entry, text='Email:')
emailText.grid(row=0, column=0)
emailEntry = Entry(entry)
emailEntry.grid(row=0, column=1)
passwordText = Label(entry, text='Password:')
passwordText.grid(row=1, column=0)
passwordEntry = Entry(entry)
passwordEntry.grid(row=1, column=1)
defaultButton = Button(entry, text='Use default account')
defaultButton['command'] = lambda:login(sendingEmail, sendingPassword)
defaultButton.grid(row=0, column=2)
loginButton = Button(entry, text='Login')
loginButton['command'] = lambda:login(emailEntry.get(), passwordEntry.get())
loginButton.grid(row=1, column=2)
#Update frame
update = Frame(root)
update.pack(anchor='center', padx=10, pady=10)
GUILabel = Label(update, text='Update box:')
GUILabel.grid(row=0, column=0)
GUIText = Text(update, width=35, height=5, wrap=WORD)
GUIText.grid(row=1, column=0, columnspan=3)
#Buttons frame
buttons = Frame(root)
buttons.pack(anchor='center', padx=10, pady=10)
logoutButton = Button(buttons, text='Logout')
logoutButton['command'] = logout
logoutButton.grid(row=2, column=0)
exitButton = Button(buttons, text='Exit program')
exitButton['command'] = root.destroy
exitButton.grid(row=2, column=1)
mainMenu = Button(buttons, text='Main menu')
mainMenu['command'] = lambda:createGUIMenu(root)
mainMenu.grid(row=2, column=2)
root.mainloop()
def createGUIMenu(oldRoot=False):
global GUIText
if oldRoot != False:
oldRoot.destroy()
readFile()
root = Tk()
root.title('Emailer GUI')
root.geometry('200x130')
center(root)
#Options frame
options = Frame(root)
options.pack(anchor='center', padx=10, pady=10)
annoyButton = Button(options, text='Annoy')
annoyButton['command'] = lambda:createGUIAnnoy(root)
annoyButton.pack()
annoyButton = Button(options, text='Send')
annoyButton['command'] = lambda:createGUISend(root)
annoyButton.pack()
#Buttons frame
buttons = Frame(root)
buttons.pack(anchor='center', padx=10, pady=10)
exitButton = Button(buttons, text='Exit program')
exitButton['command'] = root.destroy
exitButton.grid(row=0, column=0)
loginMenu = Button(buttons, text='Login screen')
loginMenu['command'] = lambda:createGUILogin(root)
loginMenu.grid(row=0, column=1)
root.mainloop()
def createGUIAnnoy(oldRoot=False):
global GUIText, emailsList, message
if oldRoot != False:
oldRoot.destroy()
readFile()
root = Tk()
root.title('Emailer GUI')
root.geometry('350x400')
center(root)
#Emails frame
emails = Frame(root)
emails.pack(anchor='center', padx=10, pady=10)
emailsString = ''
for x in emailsList:
emailsString += x + '\n'
GUILabel = Label(emails, text='Emails list:')
GUILabel.grid(row=0, column=0)
emailsText = Text(emails, width=35, height=5, wrap=WORD)
emailsText.grid(row=1, column=0, columnspan=3)
emailsText.insert(0.0, emailsString)
#Entry frame
entry = Frame(root)
entry.pack(anchor='center', padx=10, pady=10)
messageLabel = Label(entry, text='Message:')
messageLabel.grid(row=0, column=0)
messageEntry = Entry(entry)
messageEntry.grid(row=0, column=1)
defaultMessageButton = Button(entry, text='Use default message')
defaultMessageButton['command'] = lambda:annoyingRepeats(emailsText.get(0.0, END), subjectEntry.get(), message, repeatsEntry.get())
defaultMessageButton.grid(row=0, column=2)
subjectLabel = Label(entry, text='Subject:')
subjectLabel.grid(row=1, column=0)
subjectEntry = Entry(entry)
subjectEntry.grid(row=1, column=1)
repeatsLabel = Label(entry, text='Repeats:')
repeatsLabel.grid(row=2, column=0)
repeatsEntry = Entry(entry)
repeatsEntry.grid(row=2, column=1)
annoyButton = Button(entry, text='Annoy')
annoyButton['command'] = lambda:annoyingRepeats(emailsText.get(0.0, END), subjectEntry.get(), messageEntry.get(), repeatsEntry.get())
annoyButton.grid(row=2, column=2)
#Buttons frame
buttons = Frame(root)
buttons.pack(anchor='center', padx=10, pady=10)
mainMenu = Button(buttons, text='Main menu')
mainMenu['command'] = lambda:createGUIMenu(root)
mainMenu.grid(row=2, column=0)
onTopButton = Button(buttons, text='Always on top')
onTopButton['command'] = lambda:root.wm_attributes("-topmost", 1)
onTopButton.grid(row=2, column=1)
#Update frame
update = Frame(root)
update.pack(anchor='center', padx=10, pady=10)
GUILabel = Label(update, text='Update box:')
GUILabel.grid(row=0, column=0)
GUIText = Text(update, width=35, height=5, wrap=WORD)
GUIText.grid(row=1, column=0, columnspan=3)
root.mainloop()
def createGUISend(oldRoot=False):
global GUIText, emailsList, message
if oldRoot != False:
oldRoot.destroy()
readFile()
root = Tk()
root.title('Emailer GUI')
root.geometry('320x290')
center(root)
#Entry frame
entry = Frame(root)
entry.pack(anchor='center', padx=10, pady=10)
emailLabel = Label(entry, text='Email:')
emailLabel.grid(row=0, column=0)
emailEntry = Entry(entry)
emailEntry.grid(row=0, column=1)
subjectLabel = Label(entry, text='Subject:')
subjectLabel.grid(row=1, column=0)
subjectEntry = Entry(entry)
subjectEntry.grid(row=1, column=1)
messageLabel = Label(entry, text='Message:')
messageLabel.grid(row=2, column=0)
messageEntry = Entry(entry)
messageEntry.grid(row=2, column=1)
repeatsLabel = Label(entry, text='Repeats:')
repeatsLabel.grid(row=3, column=0)
repeatsEntry = Entry(entry)
repeatsEntry.grid(row=3, column=1)
sendButton = Button(entry, text='Send')
sendButton['command'] = lambda:send(emailEntry.get(), subjectEntry.get(), messageEntry.get(), repeatsEntry.get())
sendButton.grid(row=3, column=2)
#Buttons frame
buttons = Frame(root)
buttons.pack(anchor='center', padx=10, pady=10)
mainMenu = Button(buttons, text='Main menu')
mainMenu['command'] = lambda:createGUIMenu(root)
mainMenu.grid(row=0, column=1)
onTopButton = Button(buttons, text='Always on top')
onTopButton['command'] = lambda:root.wm_attributes("-topmost", 1)
onTopButton.grid(row=0, column=2)
#Update frame
update = Frame(root)
update.pack(anchor='center', padx=10, pady=10)
GUILabel = Label(update, text='Update box:')
GUILabel.grid(row=0, column=0)
GUIText = Text(update, width=35, height=5, wrap=WORD)
GUIText.grid(row=1, column=0, columnspan=3)
root.mainloop()
#Find element function | http://isaacviel.name/make-web-driver-wait-element-become-visiable/
def findElement(xpath):
global browser
var = WebDriverWait(browser, 20).until(
expected_conditions.visibility_of_element_located((By.XPATH, xpath)))
return var
#Login function
def login(sendingEmail, sendingPassword):
global browser, loggedIn
if loggedIn == False:
browser.get('https://mail.google.com')
try:
emailEntry = findElement('//input[@id="identifierId"]')
emailEntry.send_keys(sendingEmail)
nextButton = findElement('//div[@id="identifierNext"]')
nextButton.click()
passwordEntry = findElement('//input[@name="password"]')
passwordEntry.send_keys(sendingPassword)
nextButton = findElement('//span[text()="Next"]')
nextButton.click()
error('UPDATE: Logged in as ' + sendingEmail)
loggedIn = True
except:
error('ERROR: Could not login as ' + sendingEmail)
else:
error('ERROR: Already logged in, please logout first')
#Logout function
def logout():
global browser, loggedIn
if loggedIn == True:
try:
userPicture = findElement('//a[@href="https://accounts.google.com/SignOutOptions?hl=en&continue=https://mail.google.com/mail&service=mail"]')
userPicture.click()
signOutButton = findElement('//a[text()="Sign out"]')
signOutButton.click()
switchAccountsButton = findElement('//div[@aria-label="Switch account"]')
switchAccountsButton.click()
removeAccountButton = findElement('//button[text()="Remove an account"]')
removeAccountButton.click()
currentAccountButton = findElement('//div[@data-profileindex="0"]')
currentAccountButton.click()
removeConfirmButton = findElement('//div[text()="Yes, remove"]')
removeConfirmButton.click()
error('UPDATE: Logged out')
loggedIn = False
except:
error('ERROR: Could not logout')
else:
error('ERROR: Already logged out, please login first')
#Send mail function
def send(email, subject, message, repeats):
global browser, loggedIn
if loggedIn == True:
try:
repeats = int(repeats)
if repeats <= 0:
error('ERROR: Please enter a positive integer as repeats')
passed = False
else:
passed = True
except:
error('ERROR: Please enter an integer as repeats')
passed = False
if passed == True:
try:
for x in range(0, repeats):
composeButton = findElement('//div[@gh="cm"]')
composeButton.click()
recipientEntry = findElement('//textarea[@aria-label="To"]')
recipientEntry.send_keys(email)
subjectEntry = findElement('//input[@name="subjectbox"]')
subjectEntry.send_keys(subject)
textEntry = findElement('//div[@aria-label="Message Body"]')
textEntry.send_keys(message)
sendButton = findElement('//div[@data-tooltip-delay="800"]')
sendButton.click()
error('UPDATE: ' + subject + ' sent to: ' + email)
except:
error('ERROR: Could not send ' + subject + ' to ' + email)
else:
error('ERROR: You must be logged in to send mail')
#Sends emails in list a message every time clock changes
def annoyingRepeats(emailsList, subject, message, repeats):
global browser, finishedEmail, loggedIn
if loggedIn == True:
#Print emails and message to be sent
if type(emailsList).__name__ != 'list':
emailsList = [emailsList]
#Check that repeats is valid
try:
repeats = int(repeats)
if repeats <= 0:
error('ERROR: Please enter a positive integer as repeats')
passed = False
else:
error('UPDATE: You will be notified at {} when the annoying is finished'.format(finishedEmail))
passed = True
except:
error('ERROR: Please enter an integer as repeats')
passed = False
if passed == True:
#Send all emails in list the message every time the clock changes
prevMin = time.strftime("%M")
for x in range(0, repeats):
while True:
minute = time.strftime("%M")
if minute != prevMin:
prevMin = minute
for y in emailsList:
try:
send(y, subject + ' ' + str(x + 1), message, 1)
except:
break
break
#Send finishing email
finishedText = 'UPDATE: You annoyed the following emails for {:0.2f} hours:'.format(repeats / 60)
for x in emailsList:
finishedText += '\n' + x
send(finishedEmail, 'Annoying is finished', finishedText, 1)
error(finishedText)
else:
error('ERROR: You must be logged in to annoy mail')
#Read Email list.txt and get a list of emails and variables
def readFile():
global emailsList, finishedEmail, sendingEmail, sendingPassword, message
emailsList = []
file = open('Your file location')
for x in file.readlines():
if len(x.strip()) == 0:
continue
elif len(x.split(' = ')) == 1:
emailsList.append(list(x.split('\n'))[0])
elif len(x.split(' = ')) == 2:
var, data = x.split(' = ')
if var == 'finishedEmail':
finishedEmail = data.strip()
elif var == 'sendingEmail':
sendingEmail = data.strip()
elif var == 'sendingPassword':
sendingPassword = data.strip()
elif var == 'message':
message = data.strip()
else:
error('ERROR: Unknown variable in file')
else:
error('ERROR: Unknown error in file')
def error(text):
global GUIText
print(text)
try:
GUIText.delete(0.0, END)
GUIText.insert(0.0, text)
except:
pass
#Run functions
loggedIn = False
readFile()
browser = webdriver.Chrome('Your driver location')
createGUILogin()
#Quit browser if GUI is closed
try:
browser.quit()
except:
passFor selenium to work you must first install the module, then change the code on line 442 to the location of your browser driver. The web driver for google chrome can be found at: https://sites.google.com/a/chromium.org/.../downloadsThis program also requires a text file which is formatted like this:
Quote:finishedEmail = Email to send updates to
sendingEmail = Default login email
sendingPassword = Default login password
message = Default message
[email protected]
[email protected]
You will need to change line 407 to the path of this file on your computer for it to run properly.
Other than that, everything should work as intended and you'll be able to send bulk emails with ease.
Thanks for your time if you decide to try this program out.
Note: The annoy function waits until the minute value of current time changes to send each repeat, therefore if you have a repeat value of 5 the program will become unresponsive for 5 minutes.
