Skip to content

Instantly share code, notes, and snippets.

@heyqbnk
Created July 1, 2025 16:34
Show Gist options
  • Select an option

  • Save heyqbnk/3c3ae24b41d9d76c975058636e5a5c67 to your computer and use it in GitHub Desktop.

Select an option

Save heyqbnk/3c3ae24b41d9d76c975058636e5a5c67 to your computer and use it in GitHub Desktop.
A small script to scrape all Telegram gifts using `getAvailableGifts` method and locally save their Lottie animations along with the first frame SVGs.
import puppeteer from 'puppeteer';
export async function exportLottieToSvg(lottieJson) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const frameNumber = 0;
const width = 512;
const height = 512;
// Configure viewport.
await page.setViewport({ width, height });
// Set the page content.
await page.setContent(`
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.13.0/lottie.min.js"></script>
<style>
#animation {
width: ${width}px;
height: ${height}px;
}
</style>
</head>
<body>
<div id="animation"></div>
<script>
const animationData = ${JSON.stringify(lottieJson)};
const anim = lottie.loadAnimation({
container: document.getElementById('animation'),
renderer: 'svg', // Force SVG renderer
loop: false,
autoplay: false,
animationData: animationData
});
// Jump to the target frame when loaded
anim.addEventListener('DOMLoaded', () => {
anim.goToAndStop(${frameNumber}, true);
});
</script>
</body>
</html>
`);
// Wait for the animation to render.
await page.waitForFunction(
(frame) => anim.currentFrame === frame,
{ polling: 100 },
frameNumber
);
// Extract the SVG content.
const svgContent = await page.evaluate(() => {
const svgElement = document.querySelector('#animation svg');
return svgElement?.outerHTML || null;
});
if (!svgContent) {
throw new Error('SVG element not found!');
}
await browser.close();
return svgContent;
}
import zlib from 'node:zlib';
import fs from 'node:fs';
import { resolve } from 'node:path';
import { optimize } from 'svgo';
import { exportLottieToSvg } from './exportLottieToSvg.mjs';
// Specify your Telegram Bot token and target directory here.
const token = process.env.TOKEN;
const targetDir = resolve(import.meta.dirname, './assets');
// Create the target directory.
fs.mkdirSync(targetDir, { recursive: true });
// Calls the Telegram method.
function call(method, body) {
return fetch(`https://api.telegram.org/bot${token}/${method}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
.then(r => r.json())
.then(r => r.result);
}
const { gifts } = await call('getAvailableGifts');
for (const gift of gifts) {
const { file_path: filePath } = await call('getFile', { file_id: gift.sticker.file_id });
const lottieJson = await fetch(`https://api.telegram.org/file/bot${token}/${filePath}`)
.then(r => r.bytes())
.then(b => zlib.gunzipSync(b).toString('utf-8'))
.then(JSON.parse);
const svg = await exportLottieToSvg(lottieJson);
const result = optimize(svg, {
multipass: true,
plugins: [
'preset-default',
'removeDimensions',
'removeViewBox',
'removeXlink',
]
});
fs.mkdirSync(resolve(targetDir, gift.id), { recursive: true });
fs.writeFileSync(resolve(targetDir, gift.id, 'image.svg'), result.data);
fs.writeFileSync(resolve(targetDir, gift.id, 'lottie.json'), JSON.stringify(lottieJson));
console.log(`Processed ${gift.id}`);
await new Promise(r => setTimeout(r, 200));
}
{
"name": "abc",
"version": "1.0.0",
"description": "",
"type": "module",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "[email protected]",
"devDependencies": {
"puppeteer": "^24.11.2",
"svgo": "^4.0.0"
}
}
# Install required dependencies first.
pnpm i puppeteer svgo
# If needed, approve the puppeteer build.
pnpm approve-builds
# Run the script.
TOKEN=YOUR_TELEGRAM_BOT_TOKEN node index.mjs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment