Quote:
Originally Posted by photon
Yeah that's basically what I had in mind just didn't really know how to do the Javascript well.
I did also find this: https://github.com/itteco/iframely
Which looks like it might work plus support a lot of different things all at once, was going to take a look at it.
|
Ok, did some testing today. A couple errors in the code I posted above (I'll post fixed code below), however, there was a more fundamental issue, which is that bluesky's oembed does not support client-side integration. A call to their oembed endpoint using javascript/xmlhttprequest will return a cors error. The request needs to be made using a server-side call.
For some oembeds, you can use jsonp to get around the cors error. But that requires the oembed implementation to support jsonp, and bluesky's does not.
So there were only two alternatives:
1) Implement the oembed call server side. I'm unfamiliar with what vbulletin supports regarding customizing the output of posts server-side, so not sure what would be involved here.
2) Use a local server-side proxy that the page javascript will call. The proxy will take the bluesky oembed url, and return the json result. Since it's server-side, it won't run into the cors issue.
I tried to implement #2, and I think I have it working.
I slightly modified (simplified) a php proxy file I got from
here and renamed it proxy.php
Spoiler!
PHP Code:
<?php
/*
* Author - Rob Thomson <rob@marotori.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
session_start();
ob_start();
/* config settings */
$base = "https://embed.bsky.app/oembed?url="; //set this to the url you want to scrape
// Get the passed in url to proxy
$url = $base . $_GET['url'];
// Open the cURL session
$curlSession = curl_init();
curl_setopt ($curlSession, CURLOPT_URL, $url);
curl_setopt ($curlSession, CURLOPT_HEADER, 1);
curl_setopt($curlSession, CURLOPT_RETURNTRANSFER,1);
curl_setopt($curlSession, CURLOPT_TIMEOUT,30);
curl_setopt($curlSession, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($curlSession, CURLOPT_SSL_VERIFYHOST, 0);
//Send the request and store the result in an array
$response = curl_exec ($curlSession);
// Check that a connection was made
if (curl_error($curlSession)){
// If it wasn't...
print curl_error($curlSession);
} else {
//clean duplicate header that seems to appear on fastcgi with output buffer on some servers!!
$response = str_replace("HTTP/1.1 100 Continue\r\n\r\n","",$response);
$ar = explode("\r\n\r\n", $response, 2);
$header = $ar[0];
$body = $ar[1];
// return the body
print $body;
}
curl_close ($curlSession);
?>
I then created a test page that adds the following div:
PHP Code:
<div class=bsky url="https://bsky.app/profile/aoc.bsky.social/post/3lapo7x44ws2k"/>
I'm placing the entire url instead of just the id since the profile also has to be passed in. It's probably easier to pass the entire url instead of cutting out the profile and id.
I then add 2 scripts at the end of the page. One is the script that calls oembed:
Spoiler!
PHP Code:
<script>
var bskys = document.querySelectorAll("div.bsky");
bskys.forEach (
function(bsky) {
var url = bsky.getAttribute("url");
var req = new XMLHttpRequest();
req.onreadystatechange=function() {
if (this.readyState==4&&this.status==200) {
var matches = document.querySelectorAll( 'div[url="'+url+'"].bsky')
matches.forEach ( function(el) { el.innerHTML = JSON.parse(req.response).html + "<a href="+url+">bluesky link</a>"; window.bluesky.scan(el);} );
};
}
req.open("GET","http://localhost/proxy.php?url="+url,true);
req.send();
}
);
</script>
The other is the script that is returned by bluesky's oembed. I'm adding the blockquote+script using innerHTML above, but using innerHTML does not execute the script. So I took the script and placed it in its own script tags, and made a call to the script directly after the innerHTML replacement.
Spoiler!
PHP Code:
<script>
"use strict";
var EMBED_URL = 'https://embed.bsky.app';
/**
* Listen for messages from the Bluesky embed iframe and adjust the height of
* the iframe accordingly.
*/
window.addEventListener('message', function (event) {
if (event.origin !== EMBED_URL) {
return;
}
var id = event.data.id;
if (!id) {
return;
}
var embed = document.querySelector("[data-bluesky-id=\"".concat(id, "\"]"));
if (!embed) {
return;
}
var height = event.data.height;
if (height) {
embed.style.height = "".concat(height, "px");
}
});
/**
* Scan the document for all elements with the data-bluesky-aturi attribute,
* and initialize them as Bluesky embeds.
*
* @param element Only scan this specific element @default document @optional
* @returns
*/
window.bluesky = {
scan : function(node) {
if (node === void 0) { node = document; }
var embeds = node.querySelectorAll('[data-bluesky-uri]');
for (var i = 0; i < embeds.length; i++) {
var id = String(Math.random()).slice(2);
var embed = embeds[i];
var aturi = embed.getAttribute('data-bluesky-uri');
if (!aturi) {
continue;
}
var ref_url = location.origin + location.pathname;
var searchParams = new URLSearchParams();
searchParams.set('id', id);
if (ref_url.startsWith('http')) {
searchParams.set('ref_url', encodeURIComponent(ref_url));
}
var iframe = document.createElement('iframe');
iframe.setAttribute('data-bluesky-id', id);
iframe.src = "".concat(EMBED_URL, "/embed/").concat(aturi.slice('at://'.length), "?").concat(searchParams.toString());
iframe.width = '100%';
iframe.style.border = 'none';
iframe.style.display = 'block';
iframe.style.flexGrow = '1';
iframe.frameBorder = '0';
iframe.scrolling = 'no';
var container = document.createElement('div');
container.style.maxWidth = '600px';
container.style.width = '100%';
container.style.marginTop = '10px';
container.style.marginBottom = '10px';
container.style.display = 'flex';
container.className = 'bluesky-embed';
container.appendChild(iframe);
embed.replaceWith(container);
}
}
}
</script>
Finally, here is the full context of my test page.
Spoiler!
PHP Code:
<HTML>
<HEAD>Calgary Puck Bluesky Test</HEAD>
<BODY>
<div class=bsky url="https://bsky.app/profile/aoc.bsky.social/post/3lapo7x44ws2k"/>
<script>
var bskys = document.querySelectorAll("div.bsky");
bskys.forEach (
function(bsky) {
var url = bsky.getAttribute("url");
var req = new XMLHttpRequest();
req.onreadystatechange=function() {
if (this.readyState==4&&this.status==200) {
var matches = document.querySelectorAll( 'div[url="'+url+'"].bsky')
matches.forEach ( function(el) { el.innerHTML = JSON.parse(req.response).html + "<a href="+url+">bluesky link</a>"; window.bluesky.scan(el);} );
};
}
req.open("GET","http://localhost/proxy.php?url="+url,true);
req.send();
}
);
</script>
<script>
"use strict";
var EMBED_URL = 'https://embed.bsky.app';
/**
* Listen for messages from the Bluesky embed iframe and adjust the height of
* the iframe accordingly.
*/
window.addEventListener('message', function (event) {
if (event.origin !== EMBED_URL) {
return;
}
var id = event.data.id;
if (!id) {
return;
}
var embed = document.querySelector("[data-bluesky-id=\"".concat(id, "\"]"));
if (!embed) {
return;
}
var height = event.data.height;
if (height) {
embed.style.height = "".concat(height, "px");
}
});
/**
* Scan the document for all elements with the data-bluesky-aturi attribute,
* and initialize them as Bluesky embeds.
*
* @param element Only scan this specific element @default document @optional
* @returns
*/
window.bluesky = {
scan : function(node) {
if (node === void 0) { node = document; }
var embeds = node.querySelectorAll('[data-bluesky-uri]');
for (var i = 0; i < embeds.length; i++) {
var id = String(Math.random()).slice(2);
var embed = embeds[i];
var aturi = embed.getAttribute('data-bluesky-uri');
if (!aturi) {
continue;
}
var ref_url = location.origin + location.pathname;
var searchParams = new URLSearchParams();
searchParams.set('id', id);
if (ref_url.startsWith('http')) {
searchParams.set('ref_url', encodeURIComponent(ref_url));
}
var iframe = document.createElement('iframe');
iframe.setAttribute('data-bluesky-id', id);
iframe.src = "".concat(EMBED_URL, "/embed/").concat(aturi.slice('at://'.length), "?").concat(searchParams.toString());
iframe.width = '100%';
iframe.style.border = 'none';
iframe.style.display = 'block';
iframe.style.flexGrow = '1';
iframe.frameBorder = '0';
iframe.scrolling = 'no';
var container = document.createElement('div');
container.style.maxWidth = '600px';
container.style.width = '100%';
container.style.marginTop = '10px';
container.style.marginBottom = '10px';
container.style.display = 'flex';
container.className = 'bluesky-embed';
container.appendChild(iframe);
embed.replaceWith(container);
}
}
}
</script>
</BODY>
</HTML>
I was able to "embed" the bluesky post directly into my page using this method.
To add this to CP, you will have to:
1) add the proxy.php (maybe vbulletin already has its own method to do a proxy, in which case this isn't needed, and a small modification of the first script has to be done to call the built-in proxy properly).
2) Add the two scripts at the bottom of each page. First one converts the one-line bsky div into a blockquote. Second one converts the blockquote into a properly formatted bluesky iframe.