Close Encounter with Vendor Lock-in, Python Triumphs

By | July 25, 2017

mailchimp logoVendor lock-in is a term that refers to the practice of a company making a customer dependent (but not entirely) on their goods or services. In IT circles, I hear Cisco’s name thrown around a lot with references to “building a moat“. Some other examples of this are: the patented K-Cup brewing system of Kuerig, printer ink cartridges in general, and Nvidia’s proprietary G-sync. My experience wasn’t nearly as dramatic, but I quickly felt the power that was being held over me when I went to follow up with readers who entered a giveaway that I coordinated through the use of a third party, MailChimp. I had a list of people who entered and wanted to follow up with them to let them know how the contest ended. When I went to send that email, a message popped up saying my account was temporarily disabled until further notice. There was a button I could click to send a message, but that was all. No response.

mailchimp

Sure, I probably could’ve imported the contacts list into gmail and sent it to everyone in BCC instead, but what fun would that be? Bonus tidbit: though adding HTML to emails in Gmail isn’t supported, you can still achieve it through Gmail’s browser interface (2nd answer).

I wanted to send that email over the weekend when people would have time to kill and might actually open it. But I couldn’t do that, and I’m not exactly a big spender with them so I did not expect this to be resolved quickly. Thankfully, they let you download your lists and email templates, so you aren’t entirely dependent. And I had a Python emailing script from a year ago, so I put 2 and 2 together and modified the script to send the email I designed with MailChimp to all the people on the mailing list I gathered with MailChimp. Here’s the code if you’re interested:


import smtplib
from email.mime.text import MIMEText
from time import sleep
import re
import io
import getpass
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import csv
import sys

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# created by transposed messenger on 7.24.2017 							       						   #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# You must turn ON less secure apps for Gmail (https://www.google.com/settings/security/lesssecureapps)#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# usage: subscriber_mailinglist.py /path/to/list.csv /path/to/htmltemplate.html 					   #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# http://adamantine.me/index.php/2016/06/11/how-to-create-a-bulk-emailing-script-with-python/          #
# http://adamantine.me/index.php/2017/07/25/close-encounter-with-vendor-lock-in-python-triumphs/       #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

def main():

	while 1:
	
		sender = raw_input('Enter your sending email address: ')
		subjectline = raw_input('Enter your email subject line: ')
		passwd = getpass.getpass('Your password: ')
		answer = raw_input('"' + str(subjectline) + '"' + ' is your subject for this email.\n' + sender + ' is the sender email address.\n O.K.? Enter 1 to confirm. ')
		if answer == "1":
			break
			
	subscriberlist = sys.argv[1]
	html_template = sys.argv[2]

	try:
		s = smtplib.SMTP_SSL('smtp.gmail.com', 465)
		# identify ourselves to gmail client
		# use port 587 for TLS with the basic smtplib.SMTP('domain.com', 587) method.
		# re-identify ourselves as an encrypted connection
		s.ehlo()
		# If using TLS, uncomment the line below.
		# s.starttls()
		# login with user input if you are not comfortable storing your password in plaintext
		s.login(sender, passwd)
		s.set_debuglevel(1)
		
	except IOError:

		print IOError

	# initialize your sending address and create empty lists
	recipients = []

	# As it stands, the script takes a .csv file with emails in each cell of the first column
	with open(subscriberlist,'r') as csvfile:
	
		next(csvfile, None) # skip headers
		reader = csv.reader(csvfile)
		
		for row in reader:
		
			recipients.append(row[0])
	
	with open(html_template) as file:
			html = file.read()

	# write the message to a log file for later analysis/debugging
	# send an email every 75 seconds to avoid getting flagged as spam
	# print out "sending..." and the message for debugging purposes

	for k in range(len(recipients)):

		msg = MIMEMultipart('alternative')
		msg['Subject'] = subjectline
		msg['From'] = sender
		msg['To'] = recipients[k]
		html_body = MIMEText(html, 'html')
		msg.attach(html_body)
		
		print "Sending..."
		#print msg
		
		try:
			s.sendmail(sender, recipients[k], msg.as_string())
		
		# Basic error handling: sometimes the SSL/TLS connection is interrupted which will stop the script. When this happens, it will try to reconnect, send the message, and continue the loop.
		except Exception, e:
			
			print str(e) + "error: logging in and continuing loop with next address..."
			s = smtplib.SMTP_SSL('smtp.domain.com',465)
			s.ehlo()
			s.login(sender, passwd)
			s.set_debuglevel(1)
			continue	
		
		with io.open('log.txt', 'a', encoding='utf-8') as f:
		  
		  try:
			f.write(unicode(msg))
		  
		  # As the code stands, it has trouble with accented characters. You might want to figure out a way to remove them or change the encoding of the script.
		  except:
			f.write("Error handling ASCII encoded names: "+ unicode(recipients[k]))
			
		print "Sleeping for 75 seconds..."
		sleep(75)

	print "Messages have been sent."
	s.quit()

if __name__ == "__main__":
	main()

And that got my email across when the service I was depending on failed me (I even cleaned it up a little bit and made it more command line tool-ish). I am not denouncing MailChimp – they offer free and paid services that typically work extremely well. It’s not viable to hire a large enough support staff to handle every single account, paid and free alike. The hybrid free/paid model is the current best solution to the “things should be free-money motivation” conundrum. However, it is good to have something to fall back on if your current solution – paid or free – falls through. It is rarely good to be completely dependent on a thing, since it reduces competition and stifles creativity. Here’s to a healthy dose of self-reliance and Python.

Facebooktwittergoogle_plusredditpinterestlinkedintumblr

3 thoughts on “Close Encounter with Vendor Lock-in, Python Triumphs

  1. Pingback: How to create a bulk emailing script with Python – Adamantine.me

  2. Daniel

    You do actually want a third-party that knows how to do this right do your mass emailing for you. Mass-emailing is covered under the US CAN-SPAM act and relevant international laws. Google Mail, Outlook, and basically every email provider out there will block your domain and IP address from sending any of their users emails if you don’t follow a strict set of technical guidelines. One of them is that your email gateway must must delist bouncing email addresses (undeliverables) and implement other technical requirements for handling authentication (DKIM, SPF, etc.).

    Every outgoing email must also contain a unsubscription link and your physical mailing address (!).

    Although it might be tempting to roll-your-own mailing list, it’s actually quite a daunting task. You can use a ready-made solution like phpList, but that is still quite difficult to setup and maintain.

    This all being said, MailChimp are indeed a*holes.

    Reply
    1. transposedmessenger Post author

      I agree, emails are far more complicated than I thought they would be. This isn’t even including what it takes to create an actual mail server (there’s an Ars post about this, it’s like 7+ pages long). I would rather pay and have it done right. BUT my mailing list isn’t that large currently and all the emails were recently validated so this worked well enough.

      Reply

Leave a Reply

Your email address will not be published.