I am trying to create a Telegram bot that allows users to search up recipes that require only ingredients that they have on-hand. It is built with Python and I have a rough code, but before I fine-tune the details, I want to get the basic form of it up and running. Unfortunately, I am facing difficulty in parsing the API for the recipes corresponding to ingredients that the user has listed in his/her message. Specifically, the logged error message is "Error parsing the API". Could someone take a look at my code and help me see what went wrong please?
This is the relevant portion of my code:
def handle_messages(messages):
for message in messages:
mappedIngreds = []
for i in range(len(message.text)):
ingred = message.text[i].lower()
if i == 0:
mappedIngreds.append(ingred)
else:
mappedIngreds.append(f"+ {ingred}")
# get responses from API
try:
response = requests.get(f"{apiURL}{mappedIngreds}{apiId}{apiKey}")
response.raise_for_status() # for debugging
except requests.RequestException:
logging.error("Error connecting to the API")
return None
# format responses into list of recipes
try:
recipes = []
for i in response.json():
recipeInfo = {}
recipeInfo["name"] = i["label"]
recipeInfo["url"] = i["url"]
recipes.append(recipeInfo)
except (KeyError, TypeError, ValueError):
logging.error("Error parsing the API")
return None
# send list of recipes to user
try:
bot.reply_to(message.chat.id, "Try these recipes:", *recipeInfo["name"], *recipeInfo["url"], sep="\n")
except:
logging.error("Error printing recipes")
My full code is here: https://pastebin.com/W0CceAt9
Related
I am trying to use the streaming API from IG Index their documentation is here. The Api requires the light streamer client to be included in the app. So I have used this version and added it to my project.
I have created a function which connects to the server. (I believe)
def connect_light_stream_client():
if cst == None or xt == None:
create_session()
global client
client = lsc.LightstreamerClient(lightstreamer_username=stream_ident,
lightstreamer_password=stream_password,
lightstreamer_url=light_stream_server)
try:
client.connect()
except Exception as e:
print("Unable to connect to Lightstreamer Server")
return
Then I call a second function which should fetch a stream of stock data printing the results after each tick.
def listner(item_info):
print(item_info)
def light_stream_chart_tick():
sub = lsc.LightstreamerSubscription(mode="DISTINCT", items={"CHART:CS.D.XRPUSD.TODAY.IP:TICK"},
fields={"BID"})
sub.addlistener(listner)
sub_key = client.subscribe(sub)
print(sub_key)
The print at the end produces an output of 1. I get nothing from the listener. Any suggestions what I am doing wrong?
There's a few things wrong:
You must wait for the subscription request to respond with any
updates. In your code, execution ends before any ticks are received. I put the code from light_stream_chart_tick() into the connect method, with a request for input as a wait
The items and fields parameters need to be lists not dicts
The Ripple epic is offline (at least when I tried) - I have substituted Bitcoin
def connect_light_stream_client():
if cst == None or xt == None:
create_session()
global client
client = lsc.LightstreamerClient(lightstreamer_username=stream_ident,
lightstreamer_password=stream_password,
lightstreamer_url=light_stream_server)
try:
client.connect()
except Exception as e:
print("Unable to connect to Lightstreamer Server")
return
sub = lsc.LightstreamerSubscription(
mode="DISTINCT",
items=["CHART:CS.D.BITCOIN.TODAY.IP:TICK"],
fields=["BID"]
)
sub.addlistener(listner)
sub_key = client.subscribe(sub)
print(sub_key)
input("{0:-^80}\n".format("Hit CR to unsubscribe and disconnect"))
client.disconnect()
def listner(item_info):
print(item_info)
There's a python project here that makes it a bit easier to interact with the IG APIs, and there's a
streaming sample included. The project is up to date and actively maintained.
Full disclosure: I'm the maintainer of the project
As stated in the question, I would like to check the status of a list of twitter user ids. I had about 20k twitter users. I was able to get the timelines of about half of them. The other have are probably either suspended, deactivated, or have 0 tweets. I found this script online that supposedly allow for checking the status of a twitter user. Here is the script (https://github.com/dbrgn/Twitter-User-Checker/blob/master/checkuser.py):
`
#!/usr/bin/env python2
# Twitter User Checker
# Author: Danilo Bargen
# License: GPLv3
import sys
import tweepy
import urllib2
try:
import json
except ImportError:
import simplejson as json
from datetime import datetime
auth = tweepy.AppAuthHandler("xxx", "xxxx")
api = tweepy.API(auth, wait_on_rate_limit=True, wait_on_rate_limit_notify=True)
if (not api):
print ("Can't Authenticate")
sys.exit(-1)
# Continue with rest of code
try:
user = sys.argv[1]
except IndexError:
print 'Usage: checkuser.py [username]'
sys.exit(-1)
url = 'https://api.twitter.com/1.1/users/show.json?id=%s' % user
try:
request = urllib2.urlopen(url)
status = request.code
data = request.read()
except urllib2.HTTPError, e:
status = e.code
data = e.read()
data = json.loads(data)
print data
if status == 403:
print "helloooooooo"
# if 'suspended' in data['error']:
# print 'User %s has been suspended' % user
# else:
# print 'Unknown response'
elif status == 404:
print 'User %s not found' % user
elif status == 200:
days_active = (datetime.now() - datetime.strptime(data['created_at'],
'%a %b %d %H:%M:%S +0000 %Y')).days
print 'User %s is active and has posted %s tweets in %s days' % \
(user, data['statuses_count'], days_active)
else:
print 'Unknown response'
`
I get the following error:
File "twitter_status_checker.py", line 16, in <module>
auth = tweepy.AppAuthHandler("xxx", "xxxx")
File "/Users/aloush/anaconda/lib/python2.7/site-packages/tweepy/auth.py", line 170, in __init__
'but got %s instead' % data.get('token_type'))
tweepy.error.TweepError: Expected token_type to equal "bearer", but got None instead
Could anyone help me fix the error as well as allow the script to check for a list of users rather than one user for each run.
Here is a list of the HTTP Status Codes I would like to check for: https://dev.twitter.com/overview/api/response-codes
Thank you.
It seems that you failed to authenticate twitter. For the latest version (3.5), tweepy uses OAuthHander to authenticate. Please check how to use Tweepy. And also the linked script you used is to check the account one by one, which could be very slow.
To check the status of a large set of Twitter accounts by Tweepy, particularly if you want to know the reason why it is inactive (e.g., not found, suspended), you need to be aware of the followings:
Which API should be used?
Twitter provides two related APIs, one is user/show and the other is user/lookup. The former one returns the profile of one specified user, while the later one returns profile of a block of up to 100 users. The corresponding tweepy APIs are API.get_user and API.lookup_users (I cannot find it in the documentation, but it does exist in code). Definitely, you should use the second one. However, when there exist some inactive users, the lookup_users API returns only these are active. This means that you have to call get_user API to get the very detail reason for inactive accounts.
How to determine the status of a user?
Of course, you should pay attention to the response code provided by Twitter. However, when using tweepy, instead of the HTTP ERROR CODES, you should pay more attention on the ERROR MESSAGE. Here are some common cases:
If the profile is successfully fetched, it is an active user;
Otherwise, we could check the error code:
50 User not found.
63 User has been suspended.
... maybe more code about the user account
For tweepy, when the profile is failed to fetch, a TweepyError is raised. And the TweepyError.message[0] is the error message from twitter API.
Okay, here are the logic to process
(1) Divide the large block of user into pieces of size of 100;
(2) for each of these pieces, do (3) and (4);
(3) call lookup_users, the returned users will be treated as the active users and the remaining users will be treated as inactive users;
(4) call get_user for each of the inactive users to get the detailed reason.
Here is a sample code for you:
import logging
import tweepy
logger = logging.getLogger(__name__)
def to_bulk(a, size=100):
"""Transform a list into list of list. Each element of the new list is a
list with size=100 (except the last one).
"""
r = []
qt, rm = divmod(len(a), size)
i = -1
for i in range(qt):
r.append(a[i * size:(i + 1) * size])
if rm != 0:
r.append(a[(i + 1) * size:])
return r
def fast_check(api, uids):
""" Fast check the status of specified accounts.
Parameters
---------------
api: tweepy API instance
uids: account ids
Returns
----------
Tuple (active_uids, inactive_uids).
`active_uids` is a list of active users and
`inactive_uids` is a list of inactive uids,
either supended or deactivated.
"""
try:
users = api.lookup_users(user_ids=uids,
include_entities=False)
active_uids = [u.id for u in users]
inactive_uids = list(set(uids) - set(active_uids))
return active_uids, inactive_uids
except tweepy.TweepError as e:
if e[0]['code'] == 50 or e[0]['code'] == 63:
logger.error('None of the users is valid: %s', e)
return [], inactive_uids
else:
# Unexpected error
raise
def check_inactive(api, uids):
""" Check inactive account, one by one.
Parameters
---------------
uids : list
A list of inactive account
Returns
----------
Yield tuple (uid, reason). Where `uid` is the account id,
and `reason` is a string.
"""
for uid in uids:
try:
u = api.get_user(user_id=uid)
logger.warning('This user %r should be inactive', uid)
yield (u, dict(code=-1, message='OK'))
except tweepy.TweepyError as e:
yield (uid, e[0][0])
def check_one_block(api, uids):
"""Check the status of user for one block (<100). """
active_uids, inactive_uids = fast_check(api, uids)
inactive_users_status = list(check_inactive(api, inactive_uids))
return active_uids, inactive_users_status
def check_status(api, large_uids):
"""Check the status of users for any size of users. """
active_uids = []
inactive_users_status = []
for uids in to_bulk(large_uids, size=100):
au, iu = check_one_block(api, uids)
active_uids += au
inactive_users_status += iu
return active_uids, inactive_users_status
def main(twitter_crendient, large_uids):
""" The main function to call check_status. """
# First prepare tweepy API
auth = tweepy.OAuthHandler(twitter_crendient['consumer_key'],
twitter_crendient['consumer_secret'])
auth.set_access_token(twitter_crendient['access_token'],
twitter_crendient['access_token_secret'])
api = tweepy.API(auth, wait_on_rate_limit=True)
# Then, call check_status
active_uids, inactive_user_status = check_status(api, large_uids)
Because of the lack of data, I never test the code. There may be bugs, you should take care of them.
Hope this is helpful.
I'm using YouTube python API v3 to subscribe oauth2 authenticated users to new channels.
def add_subscription(youtube, channel_id):
add_subscription_response = youtube.subscriptions().insert(
part='id,snippet',
body=dict(
snippet=dict(
resourceId=dict(
channelId=channel_id
)
)
)).execute()
return add_subscription_response["id"], add_subscription_response["snippet"]["title"]
youtube = get_authenticated_service(args)
try:
subscription_id,channel_title = add_subscription(youtube, args.channel_id)
except HttpError, e:
print "An HTTP error %d occurred:\n%s" % (e.resp.status, e.content)
else:
print "A subscription to '%s' was added." % channel_title
From https://developers.google.com/youtube/v3/docs/subscriptions/insert it seems that (or maybe, I understood that...) if the user is already subscribed to the channel described by channel_id, the function should raise an HttpError with e.resp.status=400 for a subscriptionDuplicate.
Nevertheless, if I try to subscribe to a channel the user is already subscribed to, the function normally returns a subscription_id and channel_title.
Shouldn't it raise HttpError 400?
And, if I'm not making mistakes and this is exactly the way the function should work, what could I do to check if the authenticated user is already subscribed to channel_id using only subscriptions().insert()?
I could call subscriptions().list() before, to check if user is subscribed:
def is_subscribed(youtube, channel_id):
is_subscription_response = youtube.subscriptions().list(
part='id',
mine='true',
forChannelId=channel_id
).execute()
if len(is_subscription_response["items"]) == 0:
return False
else:
return True
But this would increase quota usage...
I also tried the Subscriptions: insert request and use it in a channel that I already subscribed and I'm not getting the error subscriptionDuplicate. I don't know why the API did not return this error.
So to answer your question about how to check if the authenticated user is already subscribed to channel_id, use the Subscriptions: list to know all the channel that you subscribed.
Here is the sample request, just replace the channelId with your own channelId.
https://www.googleapis.com/youtube/v3/subscriptions?part=snippet&channelId=YOUR_CHANNEL_ID&key={YOUR_API_KEY}
I am using the stripe library in Python for credit card charging. I use the customerID for purposes of charging rather than the token as I want to re-use the card without asking for it each time. The success process works just fine, however, if I create an error condition the "except" is never thrown. I am testing the failure state by using an invalid customer ID.
The server log shows the following error: "InvalidRequestError: Request req_8949iJfEmeX39p: No such customer: 22" but again this is not handled in try/except.
class ChargeCustomerCard(webapp2.RequestHandler):
def post(self):
stripe.api_key = stripeApiKey
customerID = self.request.get("cust")
amount = self.request.get("amt")
try:
charge = stripe.Charge.create(amount=int(amount),currency="usd",customer=customerID,description=customerID)
except stripe.error.CardError, e:
output = {"result":e}
else:
output = {"result":"1"}
self.response.out.write(json.dumps(output))
According to https://stripe.com/docs/api?lang=python#errors you're not handling all the possible errors/exceptions that the stripe library supplies. And dealing with financial data rightfully deserves even more caution so you really need to do at least something like:
class ChargeCustomerCard(webapp2.RequestHandler):
def post(self):
stripe.api_key = stripeApiKey
customerID = self.request.get("cust")
amount = self.request.get("amt")
try:
charge = stripe.Charge.create(amount=int(amount),currency="usd",customer=customerID,description=customerID)
except stripe.error.CardError, e:
output = {"result":e}
except Exception as e:
# handle this e, which could be stripe related, or more generic
pass
else:
output = {"result":"1"}
self.response.out.write(json.dumps(output))
Or even based on the official documentation, a more comprehensive one like:
try:
# Use Stripe's library to make requests...
pass
except stripe.error.CardError, e:
# Since it's a decline, stripe.error.CardError will be caught
body = e.json_body
err = body['error']
print "Status is: %s" % e.http_status
print "Type is: %s" % err['type']
print "Code is: %s" % err['code']
# param is '' in this case
print "Param is: %s" % err['param']
print "Message is: %s" % err['message']
except stripe.error.RateLimitError, e:
# Too many requests made to the API too quickly
pass
except stripe.error.InvalidRequestError, e:
# Invalid parameters were supplied to Stripe's API
pass
except stripe.error.AuthenticationError, e:
# Authentication with Stripe's API failed
# (maybe you changed API keys recently)
pass
except stripe.error.APIConnectionError, e:
# Network communication with Stripe failed
pass
except stripe.error.StripeError, e:
# Display a very generic error to the user, and maybe send
# yourself an email
pass
except Exception, e:
# Something else happened, completely unrelated to Stripe
pass
I think the official documentation could provide a bit more complete boilerplate. This is what I ended up with:
except stripe.error.RateLimitError, e:
# Too many requests made to the API too quickly
err = e.json_body['error']
lg.error("Stripe RateLimitError: %s" % (err))
...
except stripe.error.InvalidRequestError, e:
# Invalid parameters were supplied to Stripe's API
err = e.json_body['error']
lg.error("Stripe InvalidRequestError: %s" % (err))
...
Which makes it a little more clear how you can deal with e to log some useful errors.
I'm trying to subscribe to a tag. It appears that the callback URL is being called correctly with a hub.challenge and hub.mode, and I figured out how to access the challenge using self.request.get('hub.challenge'). I thought I was just supposed to echo the challenge, but that doesn't appear to work since I receive the following errors in the GAE logs:
InstagramAPIError: (400) APISubscriptionError-Challenge verification failed. Sent "647bf6dbed31465093ee970577ce1b72", received "
647bf6dbed31465093ee970577ce1b72
".
Here is the full handler:
class InstagramHandler(BaseHandler):
def get(self):
def process_tag_update(update):
update = update
mode = self.request.get('hub.mode')
challenge = self.request.get('hub.challenge')
verify_token = self.request.get('hub.verify_token')
if challenge:
template_values = {'challenge':challenge}
path = os.path.join(os.path.dirname(__file__), '../templates/instagram.html')
html = template.render(path, template_values)
self.response.out.write(html)
else:
reactor = subscriptions.SubscriptionsReactor()
reactor.register_callback(subscriptions.SubscriptionType.TAG, process_tag_update)
x_hub_signature = self.request.headers.get('X-Hub-Signature')
raw_response = self.request.data
try:
reactor.process('INSTAGRAM_SECRET', raw_response, x_hub_signature)
except subscriptions.SubscriptionVerifyError:
logging.error('Instagram signature mismatch')
So returning it as a string worked. I should have payed closer attention to the error message, but it took a helpful person on the Python IRC to point out the extra line breaks in the message. Once I put the template files on one line, it seemed to work. I can now confirm that my app is authorized via Instagram's list subscription URL.