Sending custom emails via Python - refused by SMTP server - python

I have a solution that distributes approx. 200 - 300 custom emails based on data and distributes personalized content. Based on dataframe, which consolidates and transform data, the unique list of recipients is identified on the top of that and via for loop it builds personalized content via SMTP server send the email. It works fine, however it started to fail lately after couple of iterations. It typically sends out 50% - 70% of emails and it fails then. So I need to restart it, but it keeps failing with the same error message:
SMTPRecipientsRefused Traceback (most recent call last) in 106 s = SMTP('SMTP server') --> 107 s.send_message(msg) 108 s.quit() 109 print('Email has been sent for ' + Recepient_name) d:\program files\alteryx\bin\miniconda3\envs\designerbasetools_venv\lib\smtplib.py in send_message(self, msg, from_addr, to_addrs, mail_options, rcpt_options) 968 g.flatten(msg_copy, linesep='\r\n') 969 flatmsg = bytesmsg.getvalue() --> 970 return self.sendmail(from_addr, to_addrs, flatmsg, mail_options, 971 rcpt_options) 972 d:\program files\alteryx\bin\miniconda3\envs\designerbasetools_venv\lib\smtplib.py in sendmail(self, from_addr, to_addrs, msg, mail_options, rcpt_options) 879 if code == 421: 880 self.close() --> 881 raise SMTPRecipientsRefused(senderrs) 882 if len(senderrs) == len(to_addrs): 883 # the server refused all our recipients SMTPRecipientsRefused: {'': (421, b' Too many bad commands; closing connection')} (Tool Id: 34)
Any ideas, what's wrong there or how to fix it? It started to fail recently and it worked for couple of months without any issues. Can this be caused by some policy or settings on SMTP server? As it says it's been refused by SMTP server. I tried to delay every iteration by 2 seconds, but it did not help really.
Code that distributes the emails:
#for loop that generates and distributes emails to all recipients
for Recepient_name, Recepient_email in Recepients.itertuples(index=False):
df_recepients = df[df['Recepient Email'] == Recepient_email]
df_recepients = df_recepients[['ID','Status', 'Column 1', 'Columns 2']]
df_HTML = df_recepients.to_html(table_id = "table_score", index=False, escape=False, border = 0)
# replace automatically generated style for rows of table, which are aligned to right and cannot be disabled
df_HTML = df_HTML.replace('<tr style="text-align: right;">', "<tr>")
# load HTML template with variables to be replaced later on
with open(HTML_path, 'r') as file:
EmailTemplate = file.read()
EmailTemplate = EmailTemplate.replace("#Name#", Recepient_name)
msg = MIMEText(
Environment().from_string(
EmailTemplate
).render(), "html"
)
subject = "Shipments status of " + Recepient_name + " - " + datetime.datetime.now().strftime("%d %b %Y")
sender = "email#email.com"
recipient = Recepient_email
recipients_cc = sender
msg['Subject'] = subject
msg['From'] = sender
msg['X-Priority'] = '2'
msg['To'] = recipient
msg['Cc'] = recipients_cc
# Send the message via SMTP server.
s = SMTP('SMTP server')
s.send_message(msg)
s.quit()
print('Email has been sent for ' + Recepient_name)

Related

IBM Stats subscription topic always returns Reason 2033: FAILED: MQRC_NO_MSG_AVAILABLE

I am attempting to port some old java code to python.
I am using pymqi to connect to a queue manager and query for all messageflow statistics topics using the topic string: $SYS/Broker/+/StatisticsAccounting/Archive/#
When using the existing java program messages are read from the topic without issue.
When using the new python code it is able to connect and query the topic without issue but always gives the message
Reason 2033: FAILED: MQRC_NO_MSG_AVAILABLE
Stats messages are published by the broker for each messageflow every 10 minutes, and I have left the new code running for over 30minutes, never having received a message.
I've also tried setting
get_opts['WaitInterval'] = pymqi.CMQC.MQWI_UNLIMITED
and sitting around for 20minutes rather than using a loop, but no luck.
Is there any IIB server config that might be impacting the messages that I am able to see, or are there other options I should be using within the client?
import pymqi
queue_manager = 'MYQM'
channel = 'MYAPP.SVRCONN'
host = 'MYHOST'
port = 'MYPORT'
topic_string = '$SYS/Broker/+/StatisticsAccounting/Archive/#'
conn_info = '%s(%s)' % (host, port)
user = ""
password = ""
qmgr = pymqi.QueueManager(None)
qmgr.connect_tcp_client(queue_manager, pymqi.CD(), channel, conn_info, user, password)
sub_desc = pymqi.SD()
sub_desc['Options'] = pymqi.CMQC.MQSO_CREATE + pymqi.CMQC.MQSO_RESUME + pymqi.CMQC.MQSO_MANAGED
sub_desc.set_vs('SubName', 'apptest')
sub_desc.set_vs('ObjectString', topic_string)
sub = pymqi.Subscription(qmgr)
sub.sub(sub_desc=sub_desc)
get_opts = pymqi.GMO(Options=pymqi.CMQC.MQGMO_WAIT)
get_opts['WaitInterval'] = 10000
md = pymqi.md()
keep_running = True
while keep_running:
try:
# Reset the MsgId, CorrelId & GroupId so that we can reuse
# the same 'md' object again.
md.MsgId = pymqi.CMQC.MQMI_NONE
md.CorrelId = pymqi.CMQC.MQCI_NONE
md.GroupId = pymqi.CMQC.MQGI_NONE
message = sub.get(None, md, get_opts)
print('Have message from Queue')
print(message)
except pymqi.MQMIError as e:
if e.comp == pymqi.CMQC.MQCC_FAILED and e.reason == pymqi.CMQC.MQRC_NO_MSG_AVAILABLE:
print("no message?")
print(e)
pass
else:
# Some other error condition.
raise
except (UnicodeDecodeError, ValueError) as e:
print('Message is not valid json')
print(e)
print(message)
continue
except KeyboardInterrupt:
print('Have received a keyboard interrupt')
keep_running = False
sub.close(sub_close_options=0,close_sub_queue=True)
qmgr.disconnect()

Send an email with pytest

I have method that sends an email and I want to make parametrize pytest test, but when I run my test it doesn't run and in logs it shows no tests ran. What's wrong with my test?
pytest scenario:
import pytest
import new_version
#pytest.mark.parametrize("email", ['my_email#gmail.com'])
def send_email(email):
new_version.send_mail(email)
method that sends email:
# Imports
import smtplib
import time
# Language
error_title = "ERROR"
send_error = "I'm waiting {idle} and I will try again."
send_error_mail = "Mail invalid"
send_error_char = "Invalid character"
send_error_connection_1_2 = "Connection problem..."
send_error_connection_2_2 = "Gmail server is down or internet connection is instabil."
login_browser = "Please log in via your web browser and then try again."
login_browser_info = "That browser and this software shood have same IP connection first time."
# Gmaild ID fro login
fromMail = "myemail#gmail.com"
fromPass = "pass"
# Some configurations
mailDelay = 15
exceptionDelay = 180
# SEND MAILS
def send_mail(email, thisSubject="Just a subject",
thisMessage="This is just a simple message..."):
# To ho to send mails
mailTo = [
email
]
# If still have mails to send
while len(mailTo) != 0:
sendItTo = mailTo[0] # Memorise what mail will be send it (debug purpose)
try:
# Connect to the server
server = smtplib.SMTP("smtp.gmail.com:587")
server.ehlo()
server.starttls()
# Sign In
server.login(fromMail, fromPass)
# Set the message
message = f"Subject: {thisSubject}\n{thisMessage}"
# Send one mail
server.sendmail(fromMail, mailTo.pop(0), message)
# Sign Out
server.quit()
# If is a problem
except Exception as e:
# Convert error in a string for som checks
e = str(e)
# Show me if...
if "The recipient address" in e and "is not a valid" in e:
print(f"\n>>> {send_error_mail} [//> {sendItTo}\n")
elif "'ascii'" in e and "code can't encode characters" in e:
print(f"\n>>> {send_error_char} [//> {sendItTo}\n")
elif "Please" in e and "log in via your web browser" in e:
print(f"\n>>> {login_browser}\n>>> - {login_browser_info}")
break
elif "[WinError 10060]" in e:
if "{idle}" in send_error:
se = send_error.split("{idle}");
seMsg = f"{se[0]}{exceptionDelay} sec.{se[1]}"
else:
seMsg = send_error
print(f"\n>>> {send_error_connection_1_2}\n>>> {send_error_connection_2_2}")
print(f">>> {seMsg}\n")
# Wait 5 minutes
waitTime = exceptionDelay - mailDelay
if waitTime <= 0:
waitTime = exceptionDelay
time.sleep(waitTime)
else:
if "{idle}" in send_error:
se = send_error.split("{idle}");
seMsg = f"{se[0]}{exceptionDelay} sec.{se[1]}"
else:
seMsg = send_error
print(f">>> {error_title} <<<", e)
print(f">>> {seMsg}\n")
# Wait 5 minutes
time.sleep(exceptionDelay)
# If are still mails wait before to send another one
if len(mailTo) != 0:
time.sleep(mailDelay)
logs:
platform darwin -- Python 3.7.5, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- /Users/nikolai/Documents/Python/gmail/venv/bin/python
cachedir: .pytest_cache
rootdir: /Users/nikolai/Documents/Python/gmail
collected 0 items
Unless you change the discovery prefixes for Pytest, test functions must be named test_*:
import pytest
import new_version
#pytest.mark.parametrize("email", ['my_email#gmail.com'])
def test_send_email(email):
new_version.send_mail(email)

How do I send a string + variable as sms using python?

I need to send a message when my program is done running, but I also want to include variables such as how long the program took to run in the text.
Here is my code for the texting:
import smtplib
carriers = {
'att': '#mms.att.net',
'tmobile': ' #tmomail.net',
'verizon': '#vtext.com',
'sprint': '#page.nextel.com'
}
def send(message):
# Replace the number with your own, or consider using an argument\dict for multiple people.
to_number = 'xxxxxxxxxx{}'.format(carriers['verizon'])
auth = ('xxxxx', 'xxxx')
# Establish a secure session with gmail's outgoing SMTP server using your gmail account
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login(auth[0], auth[1])
# Send text message through SMS gateway of destination number
server.sendmail(auth[0], to_number, message)
Obviously, I replaced my info with the xxx.
Now, to send my text I'm calling the function using:
found = 'The program is done!'
timetook = "Time took: %s (HOUR:MIN:SEC)" % timedelta(seconds=round(elapsed_time_secs))
send(found)
send(timetook)
It just sends blank texts for the timetook, but the program is done message works fine. How do I send the timetook?
The problem is that you aren't following the rules for SMTP. Below is the equivalent solution I wrote for my own use many years ago.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""SMS (short message service) functions."""
import logging
import smtplib
import sys
import rate_limiting
SMTP_GATEWAY = 'localhost'
FROM_ADDR = 'me#domain.us'
PHONE_NUMBER = '4081234567'
# T-Mobile SMS email gateway
# TO_ADDR = PHONE_NUMBER + '#tmomail.net'
# Verizon SMS email gateway
TO_ADDR = PHONE_NUMBER + '#vtext.com'
# Allow only three SMS messages per minute and five per hour.
short_term_rate_limiter = rate_limiting.SimpleRateLimiter(3, 60)
long_term_rate_limiter = rate_limiting.SimpleRateLimiter(5, 60 * 60)
def SendSms(msg, why=''):
"""Send a SMS message."""
short_term_rate_exceeded = short_term_rate_limiter()
long_term_rate_exceeded = long_term_rate_limiter()
if short_term_rate_exceeded or long_term_rate_exceeded:
logging.warning('SMS message rate exceeded, dropping msg: %s', msg)
return
smtp_conn = smtplib.SMTP(SMTP_GATEWAY)
hdrs = 'From: {}\r\nTo: {}\r\n\r\n'.format(FROM_ADDR, TO_ADDR)
if why:
hdrs += 'Subject: {}\r\n'.format(why[:20])
hdrs += "\r\n\r\n"
max_msg_len = 140 - 3 - min(20, len(why))
msg = hdrs + msg[:max_msg_len]
# Make sure the message has only ASCII characters.
msg = msg.encode('ascii', errors='replace').decode('ascii')
smtp_conn.sendmail(FROM_ADDR, [TO_ADDR], msg)
smtp_conn.quit()
if __name__ == '__main__':
SendSms(' '.join(sys.argv[1:]))

How to get http server response on a different device in LAN?

I am new to python and my networking logics are at the beginner level. I have an HTTP server running in a VM and when I curl it from a different terminal on the same machine, I get the expected response. I am looking for a functionality where I can get the same response on my mobile device when I type the ip and port in the browser. My mobile device is connected to the same WiFi. Here's the server code:
import socket
MAX_PACKET = 32768
def recv_all(sock):
r'''Receive everything from `sock`, until timeout occurs, meaning sender
is exhausted, return result as string.'''
# dirty hack to simplify this stuff - you should really use zero timeout,
# deal with async socket and implement finite automata to handle incoming data
prev_timeout = sock.gettimeout()
try:
sock.settimeout(0.01)
rdata = []
while True:
try:
rdata.append(sock.recv(MAX_PACKET))
except socket.timeout:
return ''.join(rdata)
# unreachable
finally:
sock.settimeout(prev_timeout)
def normalize_line_endings(s):
r'''Convert string containing various line endings like \n, \r or \r\n,
to uniform \n.'''
return ''.join((line + '\n') for line in s.splitlines())
def run():
r'''Main loop'''
# Create TCP socket listening on 10000 port for all connections,
# with connection queue of length 1
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, \
socket.IPPROTO_TCP)
server_sock.bind(('10.0.2.15',80))
server_sock.listen(1)
while True:
# accept connection
client_sock, client_addr = server_sock.accept()
# headers and body are divided with \n\n (or \r\n\r\n - that's why we
# normalize endings). In real application usage, you should handle
# all variations of line endings not to screw request body
request = normalize_line_endings(recv_all(client_sock)) # hack again
request_head, request_body = request.split('\n\n', 1)
# first line is request headline, and others are headers
request_head = request_head.splitlines()
request_headline = request_head[0]
# headers have their name up to first ': '. In real world uses, they
# could duplicate, and dict drops duplicates by default, so
# be aware of this.
request_headers = dict(x.split(': ', 1) for x in request_head[1:])
# headline has form of "POST /can/i/haz/requests HTTP/1.0"
request_method, request_uri, request_proto = request_headline.split(' ', 3)
response_body = [
'<html><body><h1>Hello, world!</h1>',
'<p>This page is in location %(request_uri)r, was requested ' % locals(),
'using %(request_method)r, and with %(request_proto)r.</p>' % locals(),
'<p>Request body is %(request_body)r</p>' % locals(),
'<p>Actual set of headers received:</p>',
'<ul>',
]
for request_header_name, request_header_value in request_headers.iteritems():
response_body.append('<li><b>%r</b> == %r</li>' % (request_header_name, \
request_header_value))
response_body.append('</ul></body></html>')
response_body_raw = ''.join(response_body)
# Clearly state that connection will be closed after this response,
# and specify length of response body
response_headers = {
'Content-Type': 'text/html; encoding=utf8',
'Content-Length': len(response_body_raw),
'Connection': 'close',
}
response_headers_raw = ''.join('%s: %s\n' % (k, v) for k, v in \
response_headers.iteritems())
# Reply as HTTP/1.1 server, saying "HTTP OK" (code 200).
response_proto = 'HTTP/1.1'
response_status = '200'
response_status_text = 'OK' # this can be random
# sending all this stuff
client_sock.send('%s %s %s' % (response_proto, response_status, \
response_status_text))
client_sock.send(response_headers_raw)
client_sock.send('\n') # to separate headers from body
client_sock.send(response_body_raw)
# and closing connection, as we stated before
client_sock.close()
run()
Here's the response when I run curl from a different terminal on the same VM.
I want to ping it from my mobile device connected to the same WiFi. Thank you!

Twitch bot a bytes like object is required, not 'str'

Hello Guys i am trying to learn how to make a twitch bot for my friends' channel they streams regulary and sometimes i join them.
Now i was able to make the bot join to the chat room but i couldn't figure out administration part so the bot suppoused to timeout any body uses the word "swear"
instead i get this error:
tmi: :tmi.twitch.tv 001 wasddabulyuasede_bot :Welcome, GLHF!
:tmi.twitch.tv 002 wasddabulyuasede_bot :Your host is tmi.twitch.tv
:tmi.twitch.tv 003 wasddabulyuasede_bot :This server is rather new
:tmi.twitch.tv 004 wasddabulyuasede_bot :-
:tmi.twitch.tv 375 wasddabulyuasede_bot :-
:tmi.twitch.tv 372 wasddabulyuasede_bot :You are in a maze of twisty passages, all alike.
:tmi.twitch.tv 376 wasddabulyuasede_bot :>
wasddabulyuasede_bot: :wasddabulyuasede_bot!wasddabulyuasede_bot#wasddabulyuasede_bot.tmi.twitch.tv JOIN #wlrs
_
:wasddabulyuasede_bot.tmi.twitch.tv 353 wasddabulyuasede_bot = #wlrs_ :wasddabulyuasede_bot
:wasddabulyuasede_bot.tmi.twitch.tv 366 wasddabulyuasede_bot #wlrs_ :End of /NAMES list
wlrs_: swear
Traceback (most recent call last):
File "Bot.py", line 57, in <module>
timeout(s,username,10)
File "Bot.py", line 33, in timeout
chat(sock, ".timeout {}".format(user, secs))
File "Bot.py", line 14, in chat
sock.send("PRIVMSG #{} :{}".format(cfg.CHAN, msg_encoded))
TypeError: a bytes-like object is required, not 'str'
CODE
#cfg.py
#oauth key has been removed
HOST = "irc.chat.twitch.tv"
PORT = 6667
PASS = "oauth:xxxxxxxxxxxxxxxxxxxx"
NICK = "wasddabulyuasede_bot"
CHAN = "#wlrs_"
RATE = (20/30)
PATT = [
r"swear"
]
Bot.py
from cfg import HOST, PORT, PASS, NICK, CHAN, RATE, PATT
import cfg
import socket
import re
import time
def chat(sock, msg):
"""
Send a chat message to the server.
Keyword arguments:
sock -- the socket over which to send the message
msg -- the message to be sent
"""
msg_encoded = msg.encode("utf-8")
sock.send("PRIVMSG #{} :{}".format(cfg.CHAN, msg_encoded))
# def ban(sock, user):
# """
# Ban a user from the current channel.
# Keyword arguments:
# sock -- the socket over which to send the ban command
# user -- the user to be banned
# """
# chat(sock, ".ban {}".format(user))
#
def timeout(sock, user, secs=600):
"""
Time out a user for a set period of time.
Keyword arguments:
sock -- the socket over which to send the timeout command
user -- the user to be timed out
secs -- the length of the timeout in seconds (default 600)
"""
chat(sock, ".timeout {}".format(user, secs))
# ----- network functions -----
s = socket.socket()
s.connect((HOST, PORT))
s.send("PASS {} \r\n".format(PASS).encode("utf-8"))
s.send("NICK {} \r\n".format(NICK).encode("utf-8"))
s.send("JOIN {} \r\n".format(CHAN).encode("utf-8"))
# pattern we are looking for
CHAT_MSG=re.compile(r"^:\w+!\w+#\w+\.tmi\.twitch\.tv PRIVMSG #\w+ :")
while True:
response = s.recv(1024).decode("utf-8")
if response == "PING :tmi.twitch.tv\r\n":
s.send("PONG :tmi.twitch.tv\r\n".encode("utf-8"))
else:
username = re.search(r"\w+", response).group(0) # return the entire match
message = CHAT_MSG.sub("", response)
print(username + ": " + message)
for pattern in cfg.PATT:
if re.match(pattern,message):
timeout(s,username,10)
break
time.sleep(1/cfg.RATE)
A string is an abstraction, representing a sequence of unicode codepoints.
To turn a string into a sequence of bytes, you need to encode your string, aka decide on how you want to represent your text on the wire. For Twitch, use UTF-8:
full_msg = "PRIVMSG #{} :{}".format(cfg.CHAN, msg)
msg_encoded = full_msg.encode("utf-8")
sock.send(msg_encoded)

Categories