Posting to BlueSky social with PHP

I can’t help but think of BSkyB whenever I see this social media platform.

Anyway: here’s a very, very simple, non-error-caught set of code. Caution: apparently you can get banned quite quickly if you mess things up with this API, so just be aware of that.

First: in your settings on BlueSky, make an app password. You’ll need it for the top of this script, where you’ll also want to put your username, and the text of the post you wish to make. (I mean, clearly, in production you won’t put it here, right? Right.)

This is raw text only, and URLs in here won’t be clickable. That’s work to do later, I suppose. I don’t really understand how to do that bit, but I’m hoping it’s relatively simple.

Anyway, here’s the code…


$config['bluesky-username']="your.username";
$config['bluesky-password']="your-app-password";
$text="This is a test post";

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://bsky.social/xrpc/com.atproto.server.createSession',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS =>'{
    "identifier":"'.$config['bluesky-username'].'",
    "password":"'.$config['bluesky-password'].'"
}',
  CURLOPT_HTTPHEADER => array(
    'Content-Type: application/json'
  ),
));

$response = curl_exec($curl);
curl_close($curl);
$session=json_decode($response,TRUE);

// That's got the auth bearer, and other bits of session data
// So now we need to post the message

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://bsky.social/xrpc/com.atproto.repo.createRecord',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS =>'{
    "repo":"'.$session['did'].'",
    "collection":"app.bsky.feed.post",
    "record":{
        "$type":"app.bsky.feed.post",
        "createdAt":"'.date("c").'",
        "text":"'.$text.'"
    }
}',
  CURLOPT_HTTPHEADER => array(
    'Content-Type: application/json',
    'Authorization: Bearer '.$session['accessJwt']
  ),
));

$response = curl_exec($curl);

curl_close($curl);

echo "SKEET: Sent: ".$text.' skeet';

… later, someone asks how you post links. I hope this vaguely points you in the right direction. With BlueSky, you tell it where the links are (start and end), and put a link in the text …

//Get the URL from the text
preg_match_all('#\bhttps?://[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/))#', $text, $matches);

if (!empty($matches) AND strpos($text," #")) {
  //We have a URL, and a hashtag. Likely that this is press release or another thing

  $url=$matches[0][0];
  $text = preg_replace('/#\w+\s*/', '', $text);

  $postfields='{
    "repo":"'.$session['did'].'",
    "collection":"app.bsky.feed.post",
    "record":{
        "$type":"app.bsky.feed.post",
        "createdAt":"'.date("c").'",
        "text":"'.$text.'",
        "facets": [
          {
            "index": {
              "byteStart": '.strpos($text,'https:').',
              "byteEnd": '.(strpos($text,'https:')+strlen($url)).'
            },
            "features": [
              {
                "uri": "'.$url.'",
                "$type": "app.bsky.richtext.facet#link"
              }
            ]
          }
        ]
    }
}';

} else {
  // We won't try to do anything clever with this
  $postfields='{
    "repo":"'.$session['did'].'",
    "collection":"app.bsky.feed.post",
    "record":{
        "$type":"app.bsky.feed.post",
        "createdAt":"'.date("c").'",
        "text":"'.$text.'"
    }
}';

}