Posting images to Basecamp Campfire using API with Python

Question:

I’m new with Python and APIs and I’m trying to upload an image into Basecamp campfire by using Basecamp 3 API. I have achieved authentication with my Basecamp account and was able to post a text message to campfire with this script, but I’m having trouble with images/files, and I couldn’t find any examples of how to do this. I posted the image file to ‘/attachments.json’ and gathered its attachable_sgid, but I think I’m not using it correctly.

Here’s a link to Basecamp 3 API documentation.

Here’s the script that I created:

import requests

access_token = 'token here'

account_id = "5437964"
project_id = "29141823"
campfire_id = "5288317671"

base_url = f"https://3.basecampapi.com/{account_id}"

# Post request to acquire attachable_sgid of an image from my filesystem https://github.com/basecamp/bc3-api/blob/master/sections/attachments.md#attachments

attachments_url = f"{base_url}/attachments.json?name=img.png"
img_headers = headers = {
    'Authorization': 'Bearer '+ access_token,
    "Content-Type": "image/png",
    "Content-Length": "123"
    }

with open("img.png", "rb") as img_content:
    image_id = requests.post(attachments_url, headers=img_headers, data=img_content).json()['attachable_sgid']

# Uploading image to Basecamp campfire

headers = {
    "Authorization": "Bearer " + access_token,
    "Content-Type": "application/json",
}


img_data = '{"content": "' + image_id + '"}'

campfire_url = f"{base_url}/buckets/{project_id}/chats/{campfire_id}/lines.json"

requests.post(campfire_url, headers=headers, data=img_data)

I’m getting the following message {'status': 400, 'error': 'Bad Request'}.

Does anyone know how to correctly upload the image in this case?

Asked By: mare011

||

Answers:

I found the answer to this. First, the image sgid needs to be inside "bc-attachment" html tag like this <bc-attachment sgid='sgid here'></bc-attachment>.

Second problem is that at the moment of writing this Basecamp 3 API does not support posting rich text to campfires (even though campfires can have rich text within Basecamp, API is not able to do this), and attachments are considered as rich text.

So instead I have chosen to do this by attaching images to the message board and here is how I did it:

import requests

access_token = 'token here'

account_id = "5437964"
project_id = "29141823"
message_board_id = "5288317664"

base_url = f"https://3.basecampapi.com/{account_id}"

# Post request to acquire attachable_sgid of an image from my filesystem https://github.com/basecamp/bc3-api/blob/master/sections/attachments.md#attachments

attachments_url = f"{base_url}/attachments.json?name=img.png"
img_headers = headers = {
    'Authorization': 'Bearer '+ access_token,
    "Content-Type": "image/png",
    "Content-Length": "123"
    }

with open("img.png", "rb") as img_content:
    image_id = requests.post(attachments_url, headers=img_headers, data=img_content).json()['attachable_sgid']

# Uploading image to Basecamp message board

headers = {
    "Authorization": "Bearer " + access_token,
    "Content-Type": "application/json",
}

data = f"<bc-attachment sgid='{image_id}'></bc-attachment>"

url = f"{base_url}/buckets/{project_id}/message_boards/{message_board_id}/messages.json"

requests.post(url, headers=headers, data='{"subject": "hello", "content": "<div>'+data+'</div>", "status": "active"}')

Edit: Adding code snippet for retrieving a new access token due to request in comments.

def get_access_token():
    client_id = keys['basecamp_client_id']
    client_secret = keys['basecamp_client_secret']
    redirect_url = keys['basecamp_redirect_uri']
    refresh_token = keys['basecamp_refresh_token']

    refresh_token_url = f"https://launchpad.37signals.com/authorization/token?type=refresh&refresh_token={refresh_token}&client_id={client_id}&redirect_uri={redirect_url}&client_secret={client_secret}"
    access_token = requests.post(refresh_token_url).json()['access_token']
    return access_token

You can further optimize this to only request a new access token once the old one has expired.

Edit 2: I created a python package that simplifies the interaction with Basecamp API – basecampapi

Answered By: mare011
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.