Djrill throwing 'TypeError' when send_mail method is used - django

I am currently trying to integrate mandrill into this Django-based website for emails. Djrill is the recommended package for Django and sits in place of the default SMTP/email backend, passing emails through to a Mandrill account.
When I try to test that this new backend is working by running this command:
send_mail('Test email', body, 'noreply#*********.com', [user.email], fail_silently=False)
It throws the following error: http://pastebin.ca/2239978
Can anybody point me to my mistake?

Update:
As #DavidRobinson mentions in a comment, you are not getting a successful response from the mandrill API authentication call. You should double check your API key.
If that is correct, try using curl to post {"key": <your api key>, "email": <your from email>} to MANDRILL_API_URL + "/users/verify-sender.json" and see if you get a 200.
Something like this:
curl -d key=1234567890 -d email=noreply#mydomain.com http://mandrill.whatever.com/user/verify-sender.json
Original answer:
There is also an issue in Djrill that prevents a useful error message from propagating up. That last line of the stack trace is the problem.
This is the entire open method taken from the source:
def open(self, sender):
"""
"""
self.connection = None
valid_sender = requests.post(
self.api_verify, data={"key": self.api_key, "email": sender})
if valid_sender.status_code == 200:
data = json.loads(valid_sender.content)
if data["is_enabled"]:
self.connection = True
return True
else:
if not self.fail_silently:
raise
See how it just says raise without an exception argument? That syntax is only allowed inside an except block, and raises the exception currently being handled. It doesn't work outside an except block.
An open issue in Djrill mentions a send failure and links a fork that supposedly fixes it. I suspect Djrill isn't well supported and you might try that fork or another solution entirely.

Related

Django easypost buy returns malformed syntax when using test api key but insufficient funds with production

Using the easypost python library I call the buy function passing in the rate like the documentation says but it returns an error.
Can you use your test api key with buy for easypost or not? I didn't see anything in the documentation with it. It might seem to work with production but I am not able to test that yet so I was wondering if I could test it with the test api key?
The code is:
import easypost
def get_shipment(shipment_id):
return easypost.Shipment.retrieve(shipment_id)
......
shipment = get_shipment(shipment_id)
try:
shipment.buy(rate=shipment.lowest_rate())
except Exception as e:
raise ValidationError({'detail': e.message})
The error message I get with test key
Traceback (most recent call last):
File "/app/returns/serializers.py", line 237, in handle_shipment_purchase
shipment.buy(rate=shipment.lowest_rate())
File "/usr/local/lib/python3.6/dist-packages/easypost/__init__.py", line 725, in buy
response, api_key = requestor.request('post', url, params)
File "/usr/local/lib/python3.6/dist-packages/easypost/__init__.py", line 260, in request
response = self.interpret_response(http_body, http_status)
File "/usr/local/lib/python3.6/dist-packages/easypost/__init__.py", line 321, in interpret_response
self.handle_api_error(http_status, http_body, response)
File "/usr/local/lib/python3.6/dist-packages/easypost/__init__.py", line 383, in handle_api_error
raise Error(error.get('message', ''), http_status, http_body)
easypost.Error: The request could not be understood by the server due to malformed syntax.
I got the same issue with Python though my shipment id and API_KEY are correct. with the EasyPost python exception message, it will not show the root cause of the exception. Try to do request with curl or inside exception check e.json_body and raise ValidationError accordingly.
try:
shipment.buy(rate=shipment.lowest_rate())
except Exception as e:
# Put debugger here and Check exception e.json_body
e.json_body
raise ValidationError({'detail': e.http_body})
Yes, you can buy shipments with your TEST API key. From the code you shared, I don't see any obvious problems, but you'll obviously want to double check that your shipment_id is being set correctly and that your API key is as well. Beyond that, write to us as support#easypost.com and we can actually look into our system logs to see what may be coming in "malformed".

Flask-Mail not Authenticating with Gmail and App password

So I am getting an SMTPSenderRefused error when I try to use the flask app I built to send an email. Let me start buy saying this worked previously but now it has stopped. I am at a loss and I have spent many hours testing and tweaking and reading online and NOTHING has given me an answer.
I keep getting the following error.
[2019-01-21 03:07:51,954] ERROR in app: Exception on /register/ [POST]
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "MyFlaskApp.py", line 131, in register
mail.send(msg)
File "/usr/local/lib/python2.7/site-packages/flask_mail.py", line 492, in send
message.send(connection)
File "/usr/local/lib/python2.7/site-packages/flask_mail.py", line 427, in send
connection.send(self)
File "/usr/local/lib/python2.7/site-packages/flask_mail.py", line 192, in send
message.rcpt_options)
File "/usr/lib64/python2.7/smtplib.py", line 737, in sendmail
raise SMTPSenderRefused(code, resp, from_addr)
SMTPSenderRefused: (530, '5.5.1 Authentication Required. Learn more at\n5.5.1 https://support.google.com/mail/?p=WantAuthError u186sm20332024pfu.51 - gsmtp', u'myemail#gmail.com')
IP.XX.XXX.XXX - - [21/Jan/2019 03:07:51] "POST /register/ HTTP/1.1" 500 -
My gmail account is configured with 2 step verification and then an application password that this Flask App uses. I have tried every combination and it still will not work. This is my code that I am using.
app = Flask(__name__, static_url_path='/static')
app.config.update(
DEBUG = False,
MAIL_SERVER = 'smtp.gmail.com',
MAIL_PORT = 465,
MAIL_USE_SSL = True,
MAIL_USERNAME = 'myemail#gmail.com',
MAIL_DEFAULT_SENDER = 'myemail#gmail.com',
MAIL_PASSWORD = 'GmailApplicationPassword',
)
mail = Mail(app)
The lines to actually send the message (inside the POST):
msg = Message("Welcome",
sender = 'myemail#gmail.com',
recipients = [request.form["email"]])
msg.body = "Welcome! \n\n Congratulations on your successful registration. \n\n Cheers!"
mail.send(msg)
Some additional information that might be helpful. This exact code used to work when I was running my application locally. Then I deployed my code to my AWS EC2 instance and had it working on there. It was working until I did the following.
I associated a Elastic IP address with the site and then pointed my DNS to that IPv4. I have also added AWS Public Certificate.
Can someone please help me understand why I am unable to send emails. I have tried everything I can find as solutions online.
Additional Details:
$ pip freeze
Flask-Mail==0.9.1
Thanks in advance and I will help provide any answers if more clarification is needed so please ask.
This is the solution that I found to work best. (I am not saying this is the best method but it works) It involves the use of a few different key concepts that I would Highly Recommend using. It involves the use of secrets, which you can follow this guide here for more information on this (as it is not the heart of this question I will only leave a link here). It also required the use of Boto3 to be able to read the variables. The AWS documentation will be helpful here so again I am not including it.
My method was to set up AWS Simple Email Service (SES). This makes it such that the application using Flask-Mail accesses AWS SES to send the email which then talks to gmail to actually send out the email. A few important notes here are that the SES is not in every region but presently (June 2019) it is not required that your EC2 instance be hosted in the same region that you are using SES in. Additionally, if you want to be able to send emails to outside addresses you MUST ensure that you have the email inbox configured in accordance with and not violate AWS terms of use. And Finally if you are on the free tier, you must submit a support ticket to get a rate increase and it will also allow you to send emails externally. Now here is the bit of code that I use:
app = Flask(__name__, static_url_path='/static')
app.config.update(
DEBUG = False,
MAIL_SERVER = params.getParameter("SES_SERVER"),
MAIL_PORT = 587,
MAIL_USE_TLS = True,
MAIL_USERNAME = params.getParameter("SES_USERNAME"),
MAIL_PASSWORD = params.getParameter("SES_PASSWORD"),
)
mail = Mail(app)
The sending code remains unchanged.
I did need to add the an import for Boto3 and I created a new file that is called params.py and is where I built my getParameter method to access the variables. If you want you could hard code the values above but I would not recommend it, especially if you are storing your code in a cloud repository.
I am not 100% sure but I suspect that gmail prohibits a IP address or domain from AWS accessing its servers and thus you cannot send emails. This is my suspicion but I could not find a way to prove it.
For gmail accounts for which 2-Step verification is activated, allowing less secure app is disabled. In that case to access App Password need to be generated.
This is app config code written in flask app.
`app = Flask(__name__)
app.config.update(
MAIL_SERVER = "smtp.gmail.com",
MAIL_PORT="465",
MAIL_USE_SSL=True,
MAIL_USERNAME=params['gmail-user'],
MAIL_PASSWORD=params['gmail-pass']
)
mail=Mail(app)`
Created "config.json" to define the parameter, which will be used above. For username and password check "gmail-user" and "gmail-pass"
{"params":
{ "local_server":"True",
"local_uri":"mysql+pymysql://root:#localhost/jbh_datasc_blog",
"prod_uri": "mysql+pymysql://root:#localhost/jbh_datasc_blog",
"fb_url":"https://facebook.com/<give the name of extension>",
"tw_url":"https://twitter.com/<give the name of extension>",
"gh_url":"https://github.com/<give the name of extension>",
"blog_head":"Data Science Digital",
"sub_head":"Innovating Future",
"gmail-user":"xxxxxxxx#gmail.com",
"gmail-pass":"xxxxxxxxxxxxxxxx"
}
}
Go the your gmail account and in the search bar type App Password.
It will ask about the name of app and computer from where r u accessing, provide it.
Google will generate a 16 digit password.
Use this password to fill in the 'gmail-pass'
Save and execute the code.

Why does the Python script to send data to Slack web hook not work when variable is pulled from a line?

Language: Python 2.7
Hello all. I found a really helpful script here: Python to Slack Web Hook
that shows how to send messages to a Slack web hook.
import json
import requests
# Set the webhook_url to the one provided by Slack when you create the webhook at https://my.slack.com/services/new/incoming-webhook/
webhook_url = 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX'
slack_data = {"text": "<https://alert-system.com/alerts/1234|Click here> for details!"}
response = requests.post(
webhook_url, data=json.dumps(slack_data),
headers={'Content-Type': 'application/json'}
)
if response.status_code != 200:
raise ValueError(
'Request to slack returned an error %s, the response is:\n%s'
% (response.status_code, response.text)
)
It works flawlessly when I run .py file.
Now, I have a file that has many lines of messages that I want to send to Slack. I have it formatted correctly already in the file, no spaces etc.. It's just a matter of grabbing it and passing it so slack_data = line1 etc..
So, I modify the file with something like this:
with open('export.txt', 'r') as e:
for line in e:
slack_data = line
Now if I do a print slack_data right after that, the information returns on the screen exactly as it should be, so I'm thinking it's good. I haven't began to get it working for each line yet, because it's not even working on the first line.
I get an invalid payload 400 when I run it.
EDIT: Slack support said the what they were receiving has escape characters inserted into for some reason.
"{\"text\": \"<https://alert-system.com/alerts/1234|Click here> for details!"}\n"
Any direction or assistance is appreciated.
Thanks!!
Just posting as it might help somebody. For me the below snippet worked:
data = json.dumps(slack_data)
response = requests.post(
URL, json={"text": data},
headers={'Content-Type': 'application/json'}
)
As #Geo pointed out the final payload that we are going to send should have keyword "text", else it will fail.
Moreover, in post method I have to replace data= with json= else it kept throwing error for invalid payload with 400
Since I already had the data preformatted in the file as JSON already, it was just a matter of removing json.dumps out of the code.
OLD:
#response = requests.post(webhook_url, data=json.dumps(slack_data), headers={'Content-Type': 'application/json'})
NEW:
response = requests.post(webhook_url, data=slack_data, headers={'Content-Type': 'application/json'})
Once I did that, everything worked like a charm.
If you change the code to this:
with open('export.txt', 'r') as e:
slack_data = e.read()
do you still get the 400?

Setting Spotify credentials using Spotipy

I am trying out spotipy with python 2.7.10 preinstalled on my mac 10.10, specifically [add_a_saved_track.py][1] Here is the code as copied from github:
# Add tracks to 'Your Collection' of saved tracks
import pprint
import sys
import spotipy
import spotipy.util as util
scope = 'user-library-modify'
if len(sys.argv) > 2:
username = sys.argv[1]
tids = sys.argv[2:]
else:
print("Usage: %s username track-id ..." % (sys.argv[0],))
sys.exit()
token = util.prompt_for_user_token(username, scope)
if token:
sp = spotipy.Spotify(auth=token)
sp.trace = False
results = sp.current_user_saved_tracks_add(tracks=tids)
pprint.pprint(results)
else:
print("Can't get token for", username)
I registered the application with developer.spotify.com/my-applications and received client_id and client_secret. I am a bit unclear about selection of redirect_uri so I set that to 'https://play.spotify.com/collection/songs'
Running this from terminal I get an error that says:
You need to set your Spotify API credentials. You can do this by
setting environment variables like so:
export SPOTIPY_CLIENT_ID='your-spotify-client-id'
export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret'
export SPOTIPY_REDIRECT_URI='your-app-redirect-url'
I put that into my code with the id, secret, and url as strings, just following the imports but above the util.prompt_for_user_token method.
That caused a traceback:
File "add-track.py", line 8
export SPOTIPY_CLIENT_ID='4f...6'
^
SyntaxError: invalid syntax
I noticed that Text Wrangler does not recognize 'export' as a special word. And I searched docs.python.org for 'export' and came up with nothing helpful. What is export? How am I using it incorrectly?
I next tried passing the client_id, client_secret, and redirect_uri as arguments in the util.prompt_for_user_token method like so:
util.prompt_for_user_token(username,scope,client_id='4f...6',client_secret='xxx...123',redirect_uri='https://play.spotify.com/collection/songs')
When I tried that, this is what happens in terminal:
User authentication requires interaction with your
web browser. Once you enter your credentials and
give authorization, you will be redirected to
a url. Paste that url you were directed to to
complete the authorization.
Opening https://accounts.spotify.com/authorize?scope=user-library-modify&redirect_uri=https%3A%2F%2Fplay.spotify.com%2Fcollection%2Fsongs&response_type=code&client_id=4f...6 in your browser
Enter the URL you were redirected to:
I entered https://play.spotify.com/collection/songs and then got this traceback:
Traceback (most recent call last):
File "add-track.py", line 21, in <module>
token = util.prompt_for_user_token(username, scope, client_id='4f...6', client_secret='xxx...123', redirect_uri='https://play.spotify.com/collection/songs')
File "/Library/Python/2.7/site-packages/spotipy/util.py", line 86, in prompt_for_user_token
token_info = sp_oauth.get_access_token(code)
File "/Library/Python/2.7/site-packages/spotipy/oauth2.py", line 210, in get_access_token
raise SpotifyOauthError(response.reason)
spotipy.oauth2.SpotifyOauthError: Bad Request
It seems like I am missing something, perhaps another part of Spotipy needs to be imported, or some other python module. It seems I am missing the piece that sets client credentials. How do I do that? I am fairly new at this (if that wasn't obvious). Please help.
UPDATE: I changed redirect_uri to localhost:8888/callback. That causes a Firefox tab to open with an error -- "unable to connect to server." (Since I do not have a server running. I thought about installing node.js as in the Spotify Web API tutorial, but I have not yet). The python script then asks me to copy and paste the URL I was redirected to. Even though FF could not open a page, I got this to work by copying the entire URL including the "code=BG..." that follows localhost:8888/callback? I am not sure this is an ideal setup, but at least it works.
Does it matter if I set up node.js or not?
The process you've followed (including your update) is exactly as the example intends and you are not missing anything! Obviously, it is a fairly simple tutorial, but it sets you up with a token and you should be able to get the information you need.
For the credentials, you can set these directly in your Terminal by running each of the export commands. Read more about EXPORT here: https://www.cyberciti.biz/faq/linux-unix-shell-export-command/

Python : urllib2 put request returns 301 error

I'm trying to make a put request via the urllib2 module of Python 2.7. When I perform a GET it works just fine but when I try to turn it into a PUT it returns me a 301 http error.
My code is above :
opener = urllib2.build_opener(urllib2.HTTPHandler)
req = urllib2.Request(reqUrl)
base64string = base64.encodestring('%s:%s' % (v_username, v_password)).replace('\n', '')
req.add_header("Authorization", "Basic %s" % base64string)
req.add_header("Content-Type", "application/rdf+xml")
req.add_header("Accept", "application/rdf+xml")
req.add_header("OSLC-Core-Version", "2.0")
req.get_method = lambda: 'PUT'
req.allow_redirects=True
url = opener.open(req)
If I suppress the line
req.get_method = lambda: 'PUT'
it works but it's a get request (or a post if I pass some data) but it has to be a PUT and I don't how to do it differently with this module.
The error is
urllib2.HTTPError: HTTP Error 301: Moved Permanently.
Does anyone understand this more than I do? I'm quite a newbie with REST request and there are some specificity that remains obscure to me.
I'm not certain, but could it be that urllib is handling the 301 automatically for the GET but not for the PUT? According to the RFC, user agents can redirect GETs automatically, but not PUTs.
This page seems to suggest that urllib does indeed handle the 301 redirection automatically, and it seems plausible it wouldn't automatically handle the PUT redirect given the RFC. Guess you should find out what the redirect is to and redirect there.
Thanks Ken F, you helped me understand the problem. I changed the handler directly in the urllib2.py file (not sure if it's very clean but whatever) so it can handle PUT requests:
if (code in (301, 302, 303, 307) and m in ("GET", "HEAD")
or code in (301, 302, 303) and m in ("POST", "PUT")):
Indeed, when the request was neither GET nor POST, it automatically raised an error. I'm surprised I couldn't find anyone else with the same issue.