Added support for Friendica exports.

main
Stefan Bohacek 2023-08-30 00:51:42 -04:00
rodzic e427f19b1d
commit a2e6b839cd
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: FC75CD588A42BC10
6 zmienionych plików z 87 dodań i 33 usunięć

Wyświetl plik

@ -1,21 +1,23 @@
import sortArrayOfObjects from"/js/modules/sortArrayOfObjects.min.js";import loadEmbedScript from"/js/modules/loadEmbedScript.min.js";import Cookie from"/js/cookies/main.min.js";const cookieManager=new Cookie,fileInput=document.getElementById("file-input"),introElement=document.getElementById("intro"),loadingAnimation=document.getElementById("loading-animation"),loadingText=document.getElementById("loading-text"),resultsElement=document.getElementById("results"),userInfo=document.getElementById("user-info"),userAvatar=document.getElementById("user-avatar"),userDescription=document.getElementById("user-description"),userDataBreakdown=document.getElementById("user-data-breakdown"),chartElement=document.getElementById("chart"),handleUpload=()=>{fileInput&&fileInput.addEventListener("change",async e=>{loadingAnimation.classList.remove("d-none"),loadingText.classList.remove("d-none"),fileInput.disabled=!0;var r=new FormData;r.set("archive",fileInput.files[0]);r=await(await fetch("/extract-data",{method:"POST",body:r})).json();if(console.log(r),r&&r.data){introElement.classList.add("d-none"),resultsElement.classList.remove("d-none");r=r.data;let e="",t="",o=(r.actor?(e+=`
import sortArrayOfObjects from"/js/modules/sortArrayOfObjects.min.js";import loadEmbedScript from"/js/modules/loadEmbedScript.min.js";import Cookie from"/js/cookies/main.min.js";const cookieManager=new Cookie,fileInput=document.getElementById("file-input"),introElement=document.getElementById("intro"),loadingAnimation=document.getElementById("loading-animation"),loadingText=document.getElementById("loading-text"),resultsElement=document.getElementById("results"),userInfo=document.getElementById("user-info"),userAvatar=document.getElementById("user-avatar"),userDescription=document.getElementById("user-description"),userDataBreakdown=document.getElementById("user-data-breakdown"),chartElement=document.getElementById("chart"),handleUpload=()=>{fileInput&&fileInput.addEventListener("change",async e=>{loadingAnimation.classList.remove("d-none"),loadingText.classList.remove("d-none"),fileInput.disabled=!0;var s=new FormData;s.set("archive",fileInput.files[0]);s=await(await fetch("/extract-data",{method:"POST",body:s})).json();if(console.log(s),s&&s.data){introElement.classList.add("d-none"),resultsElement.classList.remove("d-none");s=s.data;let e="",t="",a=(s.actor?(e+=`
<p class="mb-0">
<strong>${r.actor.name||r.actor.preferredUsername}</strong>
<strong>${s.actor.name||s.actor.preferredUsername}</strong>
</p>
${r.actor.summary.replaceAll('class="invisible"',"")}
`,r.actor.attachment&&(e+=`
${s.actor.summary.replaceAll('class="invisible"',"")}
`,s.actor.attachment&&(e+=`
<ul class="list-group">
${r.actor.attachment.map(e=>`
${s.actor.attachment.map(e=>`
<li class="list-group-item">${e.name}: ${e.value.replace('class="invisible"',"")}</li>
`).join("")}
</ul>
`),userDescription.innerHTML=`<div>${e}</div>`,userAvatar.innerHTML=`
<img class="img-thumbnail" width="64" height="64" src="data:image/jpg;base64,${r.avatar}">
`):(userInfo.remove(),userDescription.remove()),[]),a,n=0;r?.outbox?.orderedItems?o=r.outbox.orderedItems:r?.outbox&&(o=r.outbox),(n=o.length)&&(a=o[0]);var i={weekday:"long",year:"numeric",month:"long",day:"numeric"};r.actor&&(l=moment(r.actor.published),l=moment().diff(l,"days"),t+=`
`),userDescription.innerHTML=`<div>${e}</div>`,s.avatar_url?userAvatar.innerHTML=`
<img class="img-thumbnail" width="64" height="64" src="${s.avatar_url}">
`:s.avatar&&(userAvatar.innerHTML=`
<img class="img-thumbnail" width="64" height="64" src="data:image/jpg;base64,${s.avatar}">
`)):(userInfo.remove(),userDescription.remove()),[]),o,n=0;s?.outbox?.orderedItems?a=s.outbox.orderedItems:s?.outbox&&(a=s.outbox),(n=a.length)&&(o=a[0]);var i={weekday:"long",year:"numeric",month:"long",day:"numeric"};s.actor&&(l=moment(s.actor.published),l=moment().diff(l,"days"),t+=`
<p>
You created your account on <strong>${new Date(r.actor.published).toLocaleDateString(void 0,i)}</strong>, which is <strong>${l.toLocaleString()} day(s) ago</strong>. Since then, you posted <strong>${n.toLocaleString()} times</strong>, or about ${Math.round(n/l).toLocaleString()} time(s) a day on average.
You created your account on <strong>${new Date(s.actor.published).toLocaleDateString(void 0,i)}</strong>, which is <strong>${l.toLocaleString()} day(s) ago</strong>. Since then, you posted <strong>${n.toLocaleString()} times</strong>, or about ${Math.round(n/l).toLocaleString()} time(s) a day on average.
</p>
`);let s;"mastodon"===r.format?(a&&(i=a?.object?.id||a?.id,l=new URL(i),s=l.protocol+"//"+l.hostname,t=t+`
`);let r;"mastodon"===s.format?(o&&(i=o?.object?.id||o?.id,l=new URL(i),r=l.protocol+"//"+l.hostname,t=t+`
<p>
Here's your <a href="${i}" target="_blank">first post</a>!
</p>
@ -33,5 +35,5 @@ import sortArrayOfObjects from"/js/modules/sortArrayOfObjects.min.js";import loa
<p>
Here's what your posting history looks like.
</p>
`,userDataBreakdown.innerHTML=t,"mastodon"===r.format&&loadEmbedScript(s);var l={labels:o.map(e=>moment(e.published||e.createdAt)),datasets:[{label:"Your posts in time",data:o.map((e,t)=>({x:moment(e.published||e.createdAt),y:new Date(e.published||e.createdAt).getHours()})),backgroundColor:["#ff6384"]}]};new Chart(chartElement,{type:"scatter",data:l,options:{scales:{x:{type:"time",position:"bottom",ticks:{beginAtZero:!1,stepSize:10}},y:{ticks:{beginAtZero:!1,display:!1},scaleLabel:{display:!1},minorTickInterval:null}}}})}else loadingAnimation.classList.add("d-none"),loadingText.classList.add("d-none"),fileInput.disabled=!1})};export default handleUpload;
`,userDataBreakdown.innerHTML=t,"mastodon"===s.format&&loadEmbedScript(r);var l={labels:a.map(e=>moment(e.published||e.createdAt||e.created)),datasets:[{label:"Your posts in time",data:a.map((e,t)=>({x:moment(e.published||e.createdAt||e.created),y:new Date(e.published||e.createdAt||e.created).getHours()})),backgroundColor:["#ff6384"]}]};new Chart(chartElement,{type:"scatter",data:l,options:{scales:{x:{type:"time",position:"bottom",ticks:{beginAtZero:!1,stepSize:10}},y:{ticks:{beginAtZero:!1,display:!1},scaleLabel:{display:!1},minorTickInterval:null}},plugins:{tooltip:{callbacks:{label:e=>e.label}}}}})}else loadingAnimation.classList.add("d-none"),loadingText.classList.add("d-none"),fileInput.disabled=!1})};export default handleUpload;
//# sourceMappingURL=handleUpload.min.js.map

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -15,7 +15,7 @@ const router = express.Router();
router.post("/", upload, async (req, res) => {
const {file} = req;
console.log({file});
// console.log({file});
let data = {};
if (file) {
@ -29,8 +29,6 @@ router.post("/", upload, async (req, res) => {
file.path.endsWith('outbox.json') ||
file.path.includes('avatar'));
console.log(JSONfiles);
JSONfiles.forEach(f => {
if (f.path.endsWith('.json')){
data[f.path.replace('.json', '').replace('.jpg', '')] = JSON.parse(f.data.toString('utf8'));
@ -46,16 +44,30 @@ router.post("/", upload, async (req, res) => {
const jsonData = JSON.parse(req.file.buffer.toString());
data.outbox = jsonData;
data.format = 'firefish';
}
break;
case 'application/octet-stream':
if (file.originalname.endsWith('.backup')){
let userData = req.file.buffer.toString().split('\n').filter(d => d.trim().length > 0);
userData = userData.map(data => JSON.parse(data));
const username = userData[0].user.nickname;
console.log(jsonData);
data.actor = {
name: userData[0].user.username,
summary: userData[0].contact[0].about,
published: userData[0].user.register_date
};
data.avatar_url = userData[0].contact[0].avatar;
data.outbox = userData[1].item.filter(data => data['author-link'].endsWith(`profile/${username}`));
data.format = 'friendica';
}
break;
default:
break;
}
console.log({data});
// console.log({data});
res.json({data});
} else {
res.json({error: 'no_data'});

Wyświetl plik

@ -66,9 +66,16 @@ const handleUpload = () => {
}
userDescription.innerHTML = `<div>${userDescriptionHTML}</div>`;
userAvatar.innerHTML = `
<img class="img-thumbnail" width="64" height="64" src="data:image/jpg;base64,${userData.avatar}">
`;
if (userData.avatar_url){
userAvatar.innerHTML = `
<img class="img-thumbnail" width="64" height="64" src="${userData.avatar_url}">
`;
} else if (userData.avatar){
userAvatar.innerHTML = `
<img class="img-thumbnail" width="64" height="64" src="data:image/jpg;base64,${userData.avatar}">
`;
}
} else {
userInfo.remove();
userDescription.remove();
@ -118,7 +125,7 @@ const handleUpload = () => {
if (firstPost) {
const postURL = firstPost?.object?.id || firstPost?.id;
const url = new URL(postURL);
instanceURL = `${url.protocol}//${url.hostname}`
instanceURL = `${url.protocol}//${url.hostname}`;
userDataBreakdownHTML += `
@ -160,15 +167,15 @@ const handleUpload = () => {
// };
const data = {
labels: posts.map((post) => moment(post.published || post.createdAt)),
labels: posts.map((post) => moment(post.published || post.createdAt || post.created)),
datasets: [
{
label: "Your posts in time",
data: posts.map((post, index) => {
return {
x: moment(post.published || post.createdAt),
x: moment(post.published || post.createdAt || post.created),
// y: (new Date(post.published || post.createdAt)).getHour() + 1,
y: (new Date(post.published || post.createdAt)).getHours(),
y: (new Date(post.published || post.createdAt || post.created)).getHours(),
};
}),
backgroundColor: ['#ff6384']
@ -205,13 +212,18 @@ const handleUpload = () => {
minorTickInterval: null,
},
},
// scales: {
// x: {
// type: 'linear',
// position: 'bottom'
// }
// }
plugins: {
tooltip: {
callbacks: {
// label: (ctx) => ctx.label
label: (ctx) => {
// console.log(ctx);
// console.log(posts[ctx.dataIndex].object.content);
return ctx.label
}
}
}
}
},
});
} else {

Wyświetl plik

@ -14,7 +14,7 @@
<div class="col-sm-12 col-md-6">
<h1 class="text-start">Explore your fediverse data</h1>
<p class="lead mb-4">
Currently supports Mastodon and Firefish data exports. More to come!
Currently supports Mastodon, Firefish, Friendica, and Pixelfed data exports, with more to come!
</p>
<p class="mt-5 mt-md-2 text-start">
<ol>

Wyświetl plik

@ -37,6 +37,34 @@
</li>
</ol>
</details>
<details>
<summary><strong>Friendica</strong></summary>
<ol class="mt-3">
<li>
Click your profile picture in the top right corner, then click "Settings".
</li>
<li>
Look for "Export personal data" in the sidebar.
</li>
<li>
Click "Export all".
</li>
</ol>
</details>
<details>
<summary><strong>Pixelfed</strong></summary>
<ol class="mt-3">
<li>
Visit the profile edit page.
</li>
<li>
Look for "Export" in the sidebar.
</li>
<li>
Click the "Download" button next to "Statuses".
</li>
</ol>
</details>
</div>
<div class="col-sm-12 text-start">
More coming soon!