pythonpython-3.xpastebin

Python 3.5 / Pastebin "Bad API request, invalid api_option"


I'm working on a twitch irc bot and one of the components I wanted to have available was the ability for the bot to save quotes to a pastebin paste on close, and then retrieve the same quotes on start up.

I've started with the saving part, and have hit a road block where I can't seem to get a valid post, and I can't figure out a method.

#!/usr/bin/env python3

import urllib.parse
import urllib.request

# --------------------------------------------- Pastebin Requisites --------------------------------------------------

pastebin_key = 'my pastebin key'  # developer api key, required. GET: http://pastebin.com/api
pastebin_password = 'password'  # password for pastebin_username
pastebin_postexp = 'N'  # N = never expire
pastebin_private = 0  # 0 = Public 1 = unlisted 2 = Private
pastebin_url = 'http://pastebin.com/api/api_post.php'
pastebin_username = 'username'  # user corresponding with key


# --------------------------------------------- Value clean up --------------------------------------------------

pastebin_password = urllib.parse.quote(pastebin_password, safe='/')
pastebin_username = urllib.parse.quote(pastebin_username, safe='/')

# --------------------------------------------- Pastebin Functions --------------------------------------------------

def post(title, content):  # used for posting a new paste
    pastebin_vars = {'api_option': 'paste', 'api_user_key':     pastebin_username, 'api_paste_private': pastebin_private,
                 'api_paste_name': title, 'api_paste_expire_date': pastebin_postexp,  'api_dev_key': pastebin_key,
                 'api_user_password': pastebin_password, 'api_paste_code': content}
    try:
        str_to_paste = ', '.join("{!s}={!r}".format(key, val) for (key, val) in pastebin_vars.items())  # dict to str :D
        str_to_paste = str_to_paste.replace(":", "")  # remove :
        str_to_paste = str_to_paste.replace("'", "")  # remove '
        str_to_paste = str_to_paste.replace(")", "")  # remove )
        str_to_paste = str_to_paste.replace(", ", "&")  # replace dividers with &
        urllib.request.urlopen(pastebin_url, urllib.parse.urlencode(pastebin_vars)).read()
        print('did that work?')
    except:
        print("post submit failed :(")
        print(pastebin_url + "?" + str_to_paste)  # print the output for test

 post("test", "stuff")

I'm open to importing more libraries and stuff, not really sure what I'm doing wrong after working on this for two days straight :S


Solution

  • First, your try/except block is throwing away the actual error. You should almost never use a "bare" except clause without capturing or re-raising the original exception. See this article for a full explanation.

    Once you remove the try/except, and you will see the underlying error:

    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "paste.py", line 42, in post
        urllib.request.urlopen(pastebin_url, urllib.parse.urlencode(pastebin_vars)).read()
      File "/usr/lib/python3.4/urllib/request.py", line 161, in urlopen
        return opener.open(url, data, timeout)
      File "/usr/lib/python3.4/urllib/request.py", line 461, in open
        req = meth(req)
      File "/usr/lib/python3.4/urllib/request.py", line 1112, in do_request_
        raise TypeError(msg)
    TypeError: POST data should be bytes or an iterable of bytes. It cannot be of type str.
    

    This means you're trying to pass a unicode string into a function that's expecting bytes. When you do I/O (like reading/writing files on disk, or sending/receiving data over HTTP) you typically need to encode any unicode strings as bytes. See this presentation for a good explanation of unicode vs. bytes and when you need to encode and decode.

    Next, this line:

    urllib.request.urlopen(pastebin_url, urllib.parse.urlencode(pastebin_vars)).read()
    

    Is throwing away the response, so you have no way of knowing the result of your API call. Assign this to a variable or return it from your function so you can then inspect the value. It will either be a URL to the paste, or an error message from the API.

    Next, I think your code is sending a lot of unnecessary parameters to the API and your str_to_paste statements aren't necessary.

    I was able to make a paste using the following, much simpler, code:

    import urllib.parse
    import urllib.request
    
    PASTEBIN_KEY = 'my-api-key' # developer api key, required. GET: http://pastebin.com/api
    PASTEBIN_URL = 'http://pastebin.com/api/api_post.php'
    
    def post(title, content):  # used for posting a new paste
        pastebin_vars = dict(
            api_option='paste',
            api_dev_key=PASTEBIN_KEY,
            api_paste_name=title,
            api_paste_code=content,
        )
        return urllib.request.urlopen(PASTEBIN_URL, urllib.parse.urlencode(pastebin_vars).encode('utf8')).read()
    

    Here it is in use:

    >>> post("test", "hello\nworld.")
    b'http://pastebin.com/v8jCkHDB'