Last active
April 7, 2025 03:15
-
-
Save weskerty/5311f55462e5b84736bde5b1ac7e2ae3 to your computer and use it in GitHub Desktop.
Telegram Sticker Downloader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs').promises; | |
const path = require('path'); | |
const os = require('os'); | |
const { promisify } = require('util'); | |
const { exec: execCallback } = require('child_process'); | |
const { bot, isUrl, sticker, addExif } = require('../lib'); | |
require('dotenv').config(); | |
const exec = promisify(execCallback); | |
class DownloadQueue { | |
constructor(maxConcurrent = 2) { | |
this.queue = []; | |
this.activeDownloads = 0; | |
this.maxConcurrent = maxConcurrent; | |
} | |
async add(task) { | |
return new Promise((resolve, reject) => { | |
this.queue.push({ task, resolve, reject }); | |
this.processNext(); | |
}); | |
} | |
async processNext() { | |
if (this.activeDownloads >= this.maxConcurrent || this.queue.length === 0) { | |
return; | |
} | |
this.activeDownloads++; | |
const { task, resolve, reject } = this.queue.shift(); | |
try { | |
const result = await task(); | |
resolve(result); | |
} catch (error) { | |
reject(error); | |
} finally { | |
this.activeDownloads--; | |
this.processNext(); | |
} | |
} | |
} | |
class StickerDownloader { | |
constructor() { | |
this.config = { | |
apiKey: process.env.TGBOTAPIKEY || '', | |
converterPath: path.join(process.cwd(), 'media', 'bin'), | |
tgsdPath: path.join(process.cwd(), 'media', 'bin', 'TGSD'), | |
maxConcurrent: parseInt(process.env.MAXSOLICITUD, 10) || 1, | |
}; | |
this.downloadQueue = new DownloadQueue(this.config.maxConcurrent); | |
this.converterBinaries = new Map([ | |
['win32-x64', 'lottie-converter.windows.amd64.exe.zip'], | |
['linux-x64', 'lottie-converter.linux.amd64.zip'], | |
['linux-arm64', 'lottie-converter.linux.arm64.zip'], | |
['default', 'lottie-converter.linux.amd64.zip'], | |
]); | |
} | |
async safeExecute(command, silentError = false) { | |
try { | |
const result = await exec(command); | |
return result; | |
} catch (error) { | |
if (!silentError) { | |
console.error(`Execution failed: ${error.message}`); | |
} | |
throw new Error('Execution failed'); | |
} | |
} | |
detectConverterBinaryName() { | |
const platform = os.platform(); | |
const arch = os.arch(); | |
const key = `${platform}-${arch}`; | |
return this.converterBinaries.get(key) || this.converterBinaries.get('default'); | |
} | |
async ensureDirectories() { | |
await Promise.all([ | |
fs.mkdir(this.config.converterPath, { recursive: true }), | |
fs.mkdir(this.config.tgsdPath, { recursive: true }), | |
fs.mkdir(path.join(this.config.tgsdPath, 'TGSs'), { recursive: true }), | |
fs.mkdir(path.join(this.config.tgsdPath, 'converted'), { recursive: true }), | |
]); | |
} | |
async isScriptAvailable() { | |
const scriptPath = path.join(this.config.tgsdPath, 'DownloaderFlags.sh'); | |
try { | |
await fs.access(scriptPath); | |
return true; | |
} catch { | |
return false; | |
} | |
} | |
async downloadConverterAndScript(message) { | |
await this.ensureDirectories(); | |
const fileName = this.detectConverterBinaryName(); | |
const platform = os.platform(); | |
const arch = os.arch(); | |
const key = `${platform}-${arch}`; | |
let downloadUrl; | |
if (key === 'win32-x64') { | |
downloadUrl = 'https://github.com/weskerty/TGStickerDownloader/releases/download/1.1.2/lottie-converter.windows.amd64.exe.zip'; | |
} else if (key === 'linux-x64') { | |
downloadUrl = 'https://github.com/weskerty/TGStickerDownloader/releases/download/1.1.2/lottie-converter.linux.amd64.zip'; | |
} else if (key === 'linux-arm64' || platform === 'android') { | |
downloadUrl = 'https://github.com/weskerty/TGStickerDownloader/releases/download/1.1.2/lottie-converter.linux.arm64.zip'; | |
} else { | |
downloadUrl = 'https://github.com/weskerty/TGStickerDownloader/releases/download/1.1.2/lottie-converter.linux.amd64.zip'; | |
} | |
const filePath = path.join(this.config.converterPath, fileName); | |
const fetch = (await import('node-fetch')).default; | |
const response = await fetch(downloadUrl); | |
if (!response.ok) throw new Error(`Download failed: ${response.statusText}`); | |
const buffer = Buffer.from(await response.arrayBuffer()); | |
await fs.writeFile(filePath, buffer); | |
await this.safeExecute(`unzip -o "${filePath}" -d "${this.config.converterPath}"`); | |
if (platform !== 'win32') { | |
const extractedPath = path.join(this.config.converterPath, 'lottie-converter'); | |
await fs.chmod(extractedPath, '755').catch(() => {}); | |
} | |
if (platform !== 'win32') { | |
await this.safeExecute(`chmod -R 755 "${this.config.tgsdPath}"`).catch(() => {}); | |
} | |
const scriptPath = path.join(this.config.tgsdPath, 'DownloaderFlags.sh'); | |
const scriptUrl = 'https://raw.githubusercontent.com/weskerty/TGStickerDownloader/main/DownloaderFlags.sh'; | |
const scriptResponse = await fetch(scriptUrl); | |
if (!scriptResponse.ok) throw new Error(`Script download failed: ${scriptResponse.statusText}`); | |
const scriptBuffer = Buffer.from(await scriptResponse.arrayBuffer()); | |
await fs.writeFile(scriptPath, scriptBuffer); | |
if (platform !== 'win32') { | |
await fs.chmod(scriptPath, '755').catch(() => {}); | |
} | |
return true; | |
} | |
async downloadStickerPack(message, packName) { | |
return this.downloadQueue.add(async () => { | |
try { | |
await this.ensureDirectories(); | |
if (!(await this.isScriptAvailable())) { | |
await this.downloadConverterAndScript(message); | |
} | |
let processedPackName = packName; | |
if (packName.startsWith('https://t.me/addstickers/')) { | |
processedPackName = packName.replace('https://t.me/addstickers/', ''); | |
} | |
const scriptPath = path.join(this.config.tgsdPath, 'DownloaderFlags.sh'); | |
try { | |
await this.safeExecute( | |
`cd "${this.config.tgsdPath}" && bash "${scriptPath}" --key "${this.config.apiKey}" --s "${processedPackName}"` | |
); | |
} catch (execError) { | |
console.error(`Script execution error: ${execError.message}`); | |
await message.send(`Error executing download script.`, { quoted: message.data }); | |
await this.downloadConverterAndScript(message); | |
await this.safeExecute( | |
`cd "${this.config.tgsdPath}" && bash "${scriptPath}" --key "${this.config.apiKey}" --s "${processedPackName}"` | |
); | |
} | |
const convertedDir = path.join(this.config.tgsdPath, 'converted'); | |
const files = await fs.readdir(convertedDir); | |
if (files.length === 0) { | |
await message.send('No stickers found or conversion failed', { quoted: message.data }); | |
return; | |
} | |
await message.send(`Sending ${files.length} stickers!...`, { quoted: message.data }); | |
for (const file of files) { | |
try { | |
const filePath = path.join(convertedDir, file); | |
const fileData = await fs.readFile(filePath); | |
await message.send(fileData, { isAnimated: true, }, 'sticker'); | |
} catch (error) { | |
console.error(`Error sending sticker ${file}: ${error.message}`); | |
} | |
} | |
await fs.rm(path.join(this.config.tgsdPath, 'TGSs'), { recursive: true, force: true }).catch(() => {}); | |
await fs.rm(path.join(this.config.tgsdPath, 'converted'), { recursive: true, force: true }).catch(() => {}); | |
await fs.mkdir(path.join(this.config.tgsdPath, 'TGSs'), { recursive: true }); | |
await fs.mkdir(path.join(this.config.tgsdPath, 'converted'), { recursive: true }); | |
} catch (error) { | |
console.error(`Error downloading sticker pack: ${error.message}`); | |
await message.send(`Error ${error.message}`, { quoted: message.data }); | |
} | |
}); | |
} | |
} | |
const stickerDownloader = new StickerDownloader(); | |
bot( | |
{ | |
pattern: 'dtg ?(.*)', | |
fromMe: true, | |
desc: 'Telegram Sticker Pack Downloader.', | |
type: 'download', | |
}, | |
async (message, match) => { | |
const input = match.trim() || message.reply_message?.text || ''; | |
if (!input) { | |
await message.send( | |
'> 🎭 Download Telegram Sticker Pack:\n`dtg` <pack_name>\n\n' + | |
'> 🔗 Or with URL:\n`dtg` <https://t.me/addstickers/pack_name>\n\n' + | |
'> ℹ️ Create a Bot with @BotFather in Telegram and add the APIKEY with `.setvar TGBOTAPIKEY=`', | |
{ quoted: message.data } | |
); | |
return; | |
} | |
try { | |
if (!stickerDownloader.config.apiKey) { | |
await message.send( | |
'Error: TGBOTAPIKEY not set. \n Create a Bot with @BotFather in Telegram and add the APIKEY with `.setvar TGBOTAPIKEY=`', | |
{ quoted: message.data } | |
); | |
return; | |
} | |
await stickerDownloader.downloadStickerPack(message, input); | |
} catch (error) { | |
console.error(`Error in dtg command: ${error.message}`); | |
await message.send(`Error ${error.message}`, { quoted: message.data }); | |
} | |
} | |
); | |
module.exports = { stickerDownloader }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment