X Archive Website Exchange Tool
PostKõhai!

Abstract
Whether you delete your X account or make it private, you may still want to keep your public conversation data online.
This tool analyzes your archived X data (tweets.js), converts it into a website with a timeline UI, and immediately deploys it to the internet!
How it works
- Data Loading: Loads the formatted data (
fx-yyyy-mm-dd.json,fx-user.json) from the repository. - Conversion & Optimization: Extracts the data, converts it into HTML, and compresses images into the AVIF format for high efficiency.
- Deployment: Finally, deploys your website to Cloudflare's global network.
JSON format tool be this repository. utilize it!
Important Notice
[!WARNING] Please inspect your files carefully before pushing. Never push the entire X archive ZIP file or folder to GitHub, as these files may contain sensitive personal information. Always review the converted JSON files before publishing them.
Features
It is generally a formatting tool that utilizes GitHub Actions. However, this project does not use GitHub Actions. Instead, it utilizes Cloudflare Pages build commands. Therefore, you can build a professional, robust, and rich user interface with just a single command. Also design is very optimized for Japanese🇯🇵,font primary is “ヒラギノ角ゴ ProN” secondary is “Noto Sans JP”!
Getting Started
- Download the X archive data from https://x.com/settings/download_your_data.
- Extract zip and find a “tweets.js”.
- upload to “tweets.js” to “tools/tweets-js_exchanger.html” and download “fx-yyyy-mm-dd.json”.
- make a “fx-user.json”. I suggest read a following Specification.
- Repository connect to Cloudflare Pages and build command type following and set the build output to “dist” and finally push save.
npm i --no-save --no-audit --no-fund sharp html-minifier-terser && node -e 'const fs=require("fs"),sharp=require("sharp"),{minify}=require("html-minifier-terser");(async()=>{const u=JSON.parse(fs.readFileSync("fx-user.json","utf8")).user;const f=fs.readdirSync(".").filter(n=>/^fx-\d{4}-\d{2}-\d{2}\.json$/.test(n)).sort().pop();const p=JSON.parse(fs.readFileSync(f,"utf8")).sort((a,b)=>b.submitted_id-a.submitted_id);fs.mkdirSync("dist",{recursive:true});await sharp(u.path.icon).resize(96,96,{fit:"cover"}).avif({quality:20,effort:9}).toFile("dist/fx-icon.avif");const e=s=>String(s).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/\n/g,"<br>");const d=s=>new Date(s).toLocaleString("ja-JP",{timeZone:"Asia/Tokyo"});const h=`<!DOCTYPE html><html lang="ja"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${e(u.text.name)}のアーカイブ | X | 歴史的資料</title><link rel="stylesheet" href="sttx.css"></head><body><div class="message_container">${p.map(m=>`<div class="message_box"><div class="message_box_header"><img src="fx-icon.avif" alt="" width="96" height="96" class="message_box_header_usericon"><div class="message_box_header_username">${e(u.text.name)}</div></div><div class="message_box_content">${e(m.send_message)}</div><div class="message_box_date">${d(m.send_date)}</div></div>`).join("")}</div></body></html>`;fs.writeFileSync("dist/index.html",await minify(h,{collapseWhitespace:true,removeComments:true,removeAttributeQuotes:true,minifyCSS:true}));if(fs.existsSync("sttx.css"))fs.copyFileSync("sttx.css","dist/sttx.css")})()'
That’s all! Enjoy your X Archive on website!
Specification
fx-user.json Example
{
"user": {
"path": {
"icon": "fx-icon.jpg"
},
"text": {
"name": "はるはる"
}
}
}
path field: Specifies the file path for the user icon. text field: Specifies the display name for the user.