And then there's this code at the end of each page which imports the Javascript script and then iterates over all the tweets on a page and does the replacement:
Code:
<script sync src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<script>
window.onload = (function(){
var tweets = document.querySelectorAll("div.tweet");
tweets.forEach(
function(tweet) {
var id = tweet.getAttribute("tweetID");
twttr.widgets.createTweet(
id, tweet,
{
conversation : 'none', // or all
cards : 'visible', // or visible
}
);
}
);
});
</script>
__________________ Uncertainty is an uncomfortable position.
But certainty is an absurd one.
Just looked at this, and was wondering if we could do something like:
User enters:
PHP Code:
<bsky>3ldhirk3zcc2c</bsky>
The BBCode transforms that to
PHP Code:
<div class=bsky bskyid="3ldhirk3zcc2c"/>
Then at the bottom (along with the twitter code) you add:
PHP Code:
<script>
var bskys = document.querySelectorAll("div.bsky");
bskys.forEach (
function(bsky) {
var id = bsky.getAttribute("bskyid");
var req = new XMLHttpRequest();
req.onreadystatechange=function() {
if (this.readyState==4&&this.status=200) {
document.querySelectorAll( '[bskyid='+id+']').innerHTML=req.response.html
};
req.open("GET","https://embed.bsky.app/oembed?url=https://bsky.app/profile/aoc.bsky.social/post/3ldhirk3zcc2c",true);
req.send();
}
);
</script>
That javascript code should loop through all div.bsky elements on the page, make the request for the json for each, then replace the contents of the div with the html that is returned in the json. That html includes the blockquote along with the javascript to convert to a bluesky post.
None of this is tested - just read through the thread and thought this might be a possible solution. It is basically the same as the twitter embed, but requires the extra step of first making a call to transforming the tag to the bluesky blockquote, which will then transform itself into the bluesky.
I am curious what the behaviour will be if the same bluesky post shows up multiple times on a page. The embedded javascript that comes with the blockquote will be repeated. Should be ok as long as those scripts don't run in parallel which can cause a race condition. If it causes an issue, we can look at extracting that javascript from the json response, and posting it once at the bottom of the page after all the bsky requests have completed.
The Following User Says Thank You to psyang For This Useful Post:
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();
//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:
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.
The Following User Says Thank You to psyang For This Useful Post:
Cool thanks I'll take a look. I don't think vBulletin has a a simple proxy script, but that's a smart solution creating a separate php script to do that proxying.
__________________ Uncertainty is an uncomfortable position.
But certainty is an absurd one.
Cool thanks I'll take a look. I don't think vBulletin has a a simple proxy script, but that's a smart solution creating a separate php script to do that proxying.
The proxy script may need to be modified. The line
might need to be set to 2 instead of 0. I set it to 0 (and also added the previous line for VERIFYPEER) because I wasn't using https on my local environment, and adding these allowed the proxy to work properly - otherwise it was looking for a cert which I didn't have set up.
Wish I could be more specific, but I'm not a php guy, so was trying to cobble together what I could.