Updated many downloaders

This commit is contained in:
Divam 2024-06-28 11:29:36 +03:00
parent 963ac9afed
commit 717d74f742
20 changed files with 1055 additions and 3909 deletions

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2023 Divam Copyright (c) 2024 Divam
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

11
config.json Normal file
View File

@ -0,0 +1,11 @@
{
"general": {
"delayTimeLimit": 5000,
"localServer": "",
"fileSize": "",
"about": {
"name": "@AYNT_Bot",
"support": "@DivamYT"
}
}
}

4060
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "aynt", "name": "aynt",
"version": "1.1.0", "version": "2.0.0",
"description": "AYNT Bot is a Telegram bot designed to help users easily download videos from various social media platforms such as YouTube, TikTok, Instagram, and Twitter.", "description": "AYNT Bot is a Telegram bot designed to help users easily download videos from various social media platforms such as YouTube, TikTok, Instagram, and Twitter.",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
@ -25,10 +25,13 @@
"ffmpeg-static": "^5.1.0", "ffmpeg-static": "^5.1.0",
"fluent-ffmpeg": "^2.1.2", "fluent-ffmpeg": "^2.1.2",
"fs": "^0.0.1-security", "fs": "^0.0.1-security",
"node-cron": "^3.0.2", "mongodb": "^6.3.0",
"nodemon": "^2.0.22", "nodemon": "^2.0.22",
"request": "^2.88.2",
"soundcloud-scraper": "^5.0.3",
"telegraf": "^4.12.2", "telegraf": "^4.12.2",
"telegraf-session-local": "^2.1.1", "telegraf-session-local": "^2.1.1",
"yt-search": "^2.10.4",
"ytdl-core": "^4.11.5" "ytdl-core": "^4.11.5"
} }
} }

60
src/commands/botStatus.js Normal file
View File

@ -0,0 +1,60 @@
const os = require('os');
module.exports = async function statusCommand(ctx) {
const statusMessage = generateStatusMessage();
await ctx.replyWithMarkdown(statusMessage);
};
function fetchDataFromDatabase() {
const users = 1000; // Example: number of users
const messages = 5000; // Example: number of messages
const downloadedGB = 10; // Example: GB downloaded
const downloadedFiles = 20; // Example: number of files downloaded
return { users, messages, downloadedGB, downloadedFiles };
}
function generateStatusMessage() {
const { users, messages, downloadedGB, downloadedFiles } = fetchDataFromDatabase();
// Remaining code stays the same
const uptime = process.uptime();
const memoryUsage = process.memoryUsage().heapUsed / 1024 / 1024;
const nodeVersion = process.version;
const osName = () => {
switch (os.platform()) {
case 'aix': return 'AIX';
case 'darwin': return 'macOS';
case 'freebsd': return 'FreeBSD';
case 'linux': return 'Linux';
case 'openbsd': return 'OpenBSD';
case 'sunos': return 'SunOS';
case 'win32': return 'Windows';
case 'android': return 'Android';
default: return os.platform();
}
};
const osVersion = os.release();
const statusMessage = `
🤖 Bot Status:
🕒 Uptime: ${formatTime(uptime)}
👥 Users: ${users}
💬 Messages: ${messages}
📁 Downloaded Files: ${downloadedFiles}
💾 Downloaded GB: ${downloadedGB}
💻 OS: ${osName()} ${osVersion}
🚀 Node.js Version: ${nodeVersion}
`;
return statusMessage;
}
function formatTime(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = Math.floor(seconds % 60);
return `${hours}h ${minutes}m ${remainingSeconds}s`;
}

View File

@ -10,9 +10,15 @@
"", "",
"Currently you can download from:", "Currently you can download from:",
" - Youtube(in developing)", " - Youtube(in developing)",
" - Youtube Music",
" - Spotify",
" - SoundCloud",
" - Tiktok", " - Tiktok",
" - Instagram", " - Instagram",
" - Twitter", " - Twitter (X)",
" - Facebook",
" - Pinterest",
" - Reddit",
"", "",
"Feedback - @DivamYT" "Feedback - @DivamYT"
] ]

43
src/database/mongodb.js Normal file
View File

@ -0,0 +1,43 @@
const { MongoClient } = require('mongodb');
const uri = 'mongodb://localhost:27017'; // Your MongoDB URI
const client = new MongoClient(uri);
async function connectToDatabase() {
try {
await client.connect();
console.log('Connected to MongoDB');
} catch (error) {
console.error('Error connecting to MongoDB:', error);
}
}
async function insertUserData(userData) {
const db = client.db('your_database_name');
const usersCollection = db.collection('users');
try {
await usersCollection.insertOne(userData);
console.log('User data inserted into the database');
} catch (error) {
console.error('Error inserting user data:', error);
}
}
async function updateLinkCount(userId) {
const db = client.db('your_database_name');
const usersCollection = db.collection('users');
try {
await usersCollection.updateOne({ userId }, { $inc: { linksSent: 1 } });
console.log('Links count updated for user:', userId);
} catch (error) {
console.error('Error updating link count:', error);
}
}
module.exports = {
connectToDatabase,
insertUserData,
updateLinkCount
};

Binary file not shown.

View File

@ -0,0 +1,85 @@
const axios = require('axios');
const fs = require('fs');
require('util');
const { promisify } = require('util');
const { maxVideoSize } = require('../handlers/links_handler');``
const writeFileAsync = promisify(fs.writeFile);
const unlinkAsync = promisify(fs.unlink);
const parseString = (string) => {
try {
return JSON.parse(`{"text": "${string}"}`).text;
} catch (error) {
throw new Error("Error parsing string");
}
};
const getFBInfo = async (videoUrl, cookie, useragent) => {
const headers = {
"sec-fetch-mode": "navigate",
"sec-fetch-user": "?1",
"sec-ch-ua": '"Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"',
"sec-ch-ua-mobile": "?0",
"sec-fetch-site": "none",
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-language": "en-GB,en;q=0.9,tr-TR;q=0.8,tr;q=0.7,en-US;q=0.6",
authority: "www.facebook.com",
"cache-control": "max-age=0",
cookie: cookie || "sb=Rn8BYQvCEb2fpMQZjsd6L382; datr=Rn8BYbyhXgw9RlOvmsosmVNT; c_user=100003164630629; _fbp=fb.1.1629876126997.444699739; wd=1920x939; spin.r.1004812505_b.trunk_t.1638730393_s.1_v.2_; xs=28%3A8ROnP0aeVF8XcQ%3A2%3A1627488145%3A-1%3A4916%3A%3AAcWIuSjPy2mlTPuZAeA2wWzHzEDuumXI89jH8a_QIV8; fr=0jQw7hcrFdas2ZeyT.AWVpRNl_4noCEs_hb8kaZahs-jA.BhrQqa.3E.AAA.0.0.BhrQqa.AWUu879ZtCw",
"upgrade-insecure-requests": "1",
"user-agent": useragent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36"
};
if (!videoUrl || !videoUrl.trim() || !["facebook.com", "fb.watch"].some(domain => videoUrl.includes(domain))) {
throw new Error("Please specify a valid Facebook URL");
}
try {
const { data } = await axios.get(videoUrl, { headers });
const sdMatch = data.match(/(?:"browser_native_sd_url"|"playable_url"|sd_src)\s*:\s*"([^"]*)"/);
if (sdMatch && sdMatch[1]) {
return {
sd: parseString(sdMatch[1]),
};
} else {
throw new Error("Unable to fetch video information at this time. Please try again");
}
} catch (error) {
throw new Error("Unable to fetch video information at this time. Please try again");
}
};
const downloadFacebookVideo = async (ctx, facebookVideoUrl, cookie, useragent) => {
try {
const videoInfo = await getFBInfo(facebookVideoUrl, cookie, useragent);
const response = await axios({
url: videoInfo.sd,
method: 'GET',
responseType: 'arraybuffer',
});
const videoFileName = `facebook_video_${Date.now()}.mp4`;
await writeFileAsync(videoFileName, response.data);
const videoFileSize = fs.statSync(videoFileName).size;
if (videoFileSize > maxVideoSize) {
await ctx.reply('Error: The video file is too large to send.');
await unlinkAsync(videoFileName);
return;
}
await ctx.replyWithVideo({ source: videoFileName });
await unlinkAsync(videoFileName);
} catch (error) {
console.error('Error downloading Facebook video:', error);
await ctx.reply('Error downloading Facebook video');
}
};
// Export the functions for use in other modules
module.exports = downloadFacebookVideo;

View File

@ -0,0 +1,110 @@
const axios = require('axios');
const fs = require('fs');
const util = require('util');
util.promisify(require('child_process').exec);
const { promisify } = require('util');
const { maxVideoSize } = require('../handlers/links_handler');
const writeFileAsync = promisify(fs.writeFile);
const unlinkAsync = promisify(fs.unlink);
async function downloadPinterestPost(ctx, url) {
try {
// Transform Pinterest link
url = url.replace('https://www.pinterest.co.uk' || `https://www.ru.pinterest.com`, 'https://www.pinterest.com');
// Fetch Pinterest post details from the downloader API
const response = await axios.get(`https://pinterestdownloader.io/frontendService/DownloaderService?url=${encodeURIComponent(url)}`);
const postData = response.data;
if (!postData || !postData.source || postData.source !== 'pinterest') {
throw new Error('Invalid Pinterest post data');
}
if (postData.medias && postData.medias.length > 0) {
// Check if it's a video post
const videoMedia = postData.medias.find(media => media.videoAvailable && media.extension === 'mp4');
if (videoMedia) {
const videoUrl = videoMedia.url;
const videoFileName = `pinterest_video_${Date.now()}.mp4`;
// Download the video
const videoResponse = await axios.get(videoUrl, { responseType: 'arraybuffer' });
await writeFileAsync(videoFileName, videoResponse.data);
// Check video file size
const videoFileSize = fs.statSync(videoFileName).size;
if (videoFileSize > maxVideoSize) {
await ctx.reply('Error: The video file is too large to send.');
await unlinkAsync(videoFileName);
return;
}
// Send the video to the user
await ctx.replyWithVideo({ source: videoFileName });
// Remove the downloaded file
await unlinkAsync(videoFileName);
} else {
// Check if it's a GIF post
const gifMedia = postData.medias.find(media => media.videoAvailable && media.extension === 'gif');
if (gifMedia) {
const gifUrl = gifMedia.url;
const gifFileName = `pinterest_gif_${Date.now()}.gif`;
// Download the GIF
const gifResponse = await axios.get(gifUrl, { responseType: 'arraybuffer' });
await writeFileAsync(gifFileName, gifResponse.data);
// Check GIF file size
const gifFileSize = fs.statSync(gifFileName).size;
if (gifFileSize > maxVideoSize) {
await ctx.reply('Error: The GIF file is too large to send.');
await unlinkAsync(gifFileName);
return;
}
// Send the GIF to the user
await ctx.replyWithDocument({ source: gifFileName });
// Remove the downloaded file
await unlinkAsync(gifFileName);
} else if (postData.medias[0].url) {
// Photo post
const photoUrl = postData.medias[0].url; // Assuming the first media is the photo
const photoFileName = `pinterest_photo_${Date.now()}.jpg`;
// Download the photo
const photoResponse = await axios.get(photoUrl, { responseType: 'arraybuffer' });
await writeFileAsync(photoFileName, photoResponse.data);
// Check photo file size
const photoFileSize = fs.statSync(photoFileName).size;
if (photoFileSize > maxVideoSize) {
await ctx.reply('Error: The photo file is too large to send.');
await unlinkAsync(photoFileName);
return;
}
// Send the photo to the user
await ctx.replyWithPhoto({ source: photoFileName });
// Remove the downloaded file
await unlinkAsync(photoFileName);
} else {
throw new Error('Invalid media format in Pinterest post');
}
}
} else {
throw new Error('No media available in the Pinterest post');
}
} catch (error) {
console.error('Error downloading Pinterest post:', error);
await ctx.reply('Error downloading Pinterest post');
}
}
// Export the function for use in other modules
module.exports = downloadPinterestPost;

View File

@ -0,0 +1,90 @@
const axios = require('axios');
const fs = require('fs');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const { promisify } = require('util');
const { maxVideoSize } = require('../handlers/links_handler');
const writeFileAsync = promisify(fs.writeFile);
const unlinkAsync = promisify(fs.unlink);
// let maxVideoSize;
// if (process.env.LOCAL_SERVER) {
// // 2GB in bytes (2 * 1024 * 1024 * 1024)
// maxVideoSize = 2 * 1024 * 1024 * 1024;
// } else {
// // 50MB in bytes (50 * 1024 * 1024)
// maxVideoSize = 50 * 1024 * 1024;
// }
async function downloadRedditPost(ctx, url) {
try {
// Extract the Reddit post ID from the URL
const match = url.match(/reddit\.com\/(?:r\/[^/]+\/comments\/|[^/]+\/comments\/)([\w-]+)/i);
if (!match) {
throw new Error('Invalid Reddit post URL');
}
const postId = match[1];
// Fetch post details from Reddit API
const response = await axios.get(`https://api.reddit.com/api/info.json?id=t3_${postId}`);
const postData = response.data.data.children[0].data;
// Check if it's a video post
if (postData.media && postData.media.reddit_video && postData.media.reddit_video.hls_url) {
// Video post
const hlsPlaylistUrl = postData.media.reddit_video.hls_url;
const cleanHlsPlaylistUrl = hlsPlaylistUrl.split('?')[0];
const videoFileName = `reddit_video_${postId}.mp4`;
// Use ffmpeg to download and convert the video
await exec(`ffmpeg -i ${cleanHlsPlaylistUrl} -c copy ${videoFileName}`);
// Check video file size
const videoFileSize = fs.statSync(videoFileName).size;
if (videoFileSize > maxVideoSize) {
await ctx.reply('Error: The video file is too large to send.');
await unlinkAsync(videoFileName);
return;
}
// Send the video to the user
await ctx.replyWithVideo({ source: videoFileName });
// Remove the downloaded file
await unlinkAsync(videoFileName);
} else if (postData.url_overridden_by_dest) {
// Photo post
const photoUrl = postData.url_overridden_by_dest;
const photoFileName = `reddit_photo_${postId}.jpg`;
const photoResponse = await axios.get(photoUrl, { responseType: 'arraybuffer' });
await writeFileAsync(photoFileName, photoResponse.data);
// Check photo file size
const photoFileSize = fs.statSync(photoFileName).size;
if (photoFileSize > maxVideoSize) {
await ctx.reply('Error: The photo file is too large to send.');
await unlinkAsync(photoFileName);
return;
}
// Send the photo to the user
await ctx.replyWithPhoto({ source: photoFileName });
// Remove the downloaded file
await unlinkAsync(photoFileName);
} else {
throw new Error('Unsupported Reddit post type');
}
} catch (error) {
console.error('Error downloading Reddit post:', error);
await ctx.reply('Error downloading Reddit post');
}
}
// Export the function for use in other modules
module.exports = downloadRedditPost;

View File

@ -0,0 +1,42 @@
const SoundCloud = require("soundcloud-scraper");
const fs = require("fs");
const { promisify } = require('util');
const { maxVideoSize } = require('../handlers/links_handler');
const unlinkAsync = promisify(fs.unlink);
async function downloadSoundCloudMusic(ctx, url) {
try {
const client = new SoundCloud.Client();
const song = await client.getSongInfo(url);
const stream = await song.downloadProgressive();
const fileName = `./${song.title.replace(/[^\w\s]/g, '')}.mp3`;
const writeStream = fs.createWriteStream(fileName);
stream.pipe(writeStream);
await new Promise((resolve, reject) => {
writeStream.on('finish', resolve);
writeStream.on('error', reject);
});
const fileSize = fs.statSync(fileName).size;
if (fileSize > maxVideoSize) {
await ctx.reply('Error: The audio file is too large to send.');
await unlinkAsync(fileName);
return;
}
await ctx.replyWithAudio({ source: fileName });
// Remove the downloaded file
await unlinkAsync(fileName);
} catch (error) {
console.error('Error downloading SoundCloud music:', error);
await ctx.reply('Error downloading SoundCloud music. Please try again later.');
}
}
module.exports = downloadSoundCloudMusic;

View File

@ -0,0 +1,227 @@
const ytdl = require('ytdl-core');
const ytSearch = require('yt-search');
const axios = require('axios');
const ffmpeg = require('fluent-ffmpeg');
const fs = require('fs');
const ffmpegPath = require('ffmpeg-static');
const { spawn } = require('child_process');
const { maxVideoSize } = require('../handlers/links_handler');
// Spotify API credentials
const clientId = 'acc6302297e040aeb6e4ac1fbdfd62c3';
const clientSecret = '0e8439a1280a43aba9a5bc0a16f3f009';
const authString = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
const tokenUrl = 'https://accounts.spotify.com/api/token';
const findYouTubeVideo = async (songName, artistName) => {
const searchTerms = `${songName} ${artistName}`;
const searchResults = await ytSearch(searchTerms);
if (searchResults && searchResults.videos && searchResults.videos.length > 0) {
return searchResults.videos[0].url;
} else {
return null;
}
};
const downloadYouTubeVideo = async (videoUrl, outputFileName, artworkFileName) => {
try {
const videoInfo = await ytdl.getBasicInfo(videoUrl);
if (!videoInfo.formats || videoInfo.formats.length === 0) {
console.error('No video formats found.');
return null;
}
const ffmpegCommand = ffmpeg({ timeout: 10 * 60 })
.input(ytdl(videoUrl, { quality: 'highestaudio' }))
.audioBitrate(256)
.audioFilter('volume=0.3');
if (artworkFileName) {
ffmpegCommand.input(artworkFileName);
}
ffmpegCommand
.save(outputFileName)
.format('mp3');
await new Promise((resolve, reject) => {
ffmpegCommand
.on('end', resolve)
.on('error', reject);
});
const stats = fs.statSync(outputFileName);
const fileSizeInBytes = stats.size;
if (fileSizeInBytes > maxVideoSize) {
console.error('Downloaded file size exceeds the maximum limit.');
// Delete the downloaded file
await fs.promises.unlink(outputFileName);
// Delete the album artwork file if it exists
if (artworkFileName && fs.existsSync(artworkFileName)) {
try {
await fs.promises.unlink(artworkFileName);
console.log('Deleted album artwork file.');
} catch (unlinkError) {
console.error('Error deleting album artwork file:', unlinkError);
}
}
return null;
}
return outputFileName;
} catch (error) {
console.error('Error in downloadYouTubeVideo:', error);
return null;
}
};
// Spotify API function to get data
const getSpotifyTrackInfo = async (spotifyTrackUrl) => {
const trackId = extractTrackIdFromUrl(spotifyTrackUrl);
const accessToken = await getToken();
const trackUrl = `https://api.spotify.com/v1/tracks/${trackId}`;
const trackResponse = await axios.get(trackUrl, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const albumId = trackResponse.data.album.id;
const albumInfo = await getSpotifyAlbumInfo(albumId, accessToken);
return {
songName: trackResponse.data.name,
artistName: trackResponse.data.artists[0].name,
albumName: albumInfo.name,
albumArtworkUrl: albumInfo.images.length > 0 ? albumInfo.images[0].url : null,
};
};
const getSpotifyAlbumInfo = async (albumId, accessToken) => {
const albumUrl = `https://api.spotify.com/v1/albums/${albumId}`;
const albumResponse = await axios.get(albumUrl, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return albumResponse.data;
};
const extractTrackIdFromUrl = (spotifyTrackUrl) => {
const match = spotifyTrackUrl.match(/\/track\/(\w+)/);
return match ? match[1] : null;
};
const getToken = async () => {
const response = await axios.post(
tokenUrl,
'grant_type=client_credentials',
{
headers: {
Authorization: `Basic ${authString}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
return response.data.access_token;
};
const downloadSpotifyMusic = async (ctx, url) => {
try {
const { songName, artistName, albumArtworkUrl } = await getSpotifyTrackInfo(url);
const videoUrl = await findYouTubeVideo(songName, artistName);
if (videoUrl) {
const downloadedFileName = 'song.mp3';
const artworkFileName = 'album_artwork.jpg';
if (albumArtworkUrl) {
const artworkResponse = await axios.get(albumArtworkUrl, { responseType: 'arraybuffer' });
fs.writeFileSync(artworkFileName, Buffer.from(artworkResponse.data));
}
const downloadedFilePath = await downloadYouTubeVideo(videoUrl, downloadedFileName, artworkFileName);
if (!downloadedFilePath) {
await ctx.reply('Error: Video size exceeds the maximum limit.');
return;
}
console.log('Download completed.');
const outputFileName = `${songName} (${artistName}).mp3`;
const mp3FilePath = downloadedFilePath;
const imageFilePath = 'album_artwork.jpg';
const outputFilePath = outputFileName;
const ffmpegCommand = [
'-i', mp3FilePath,
'-i', imageFilePath,
'-map', '0:0',
'-map', '1:0',
'-c', 'copy',
'-id3v2_version', '3',
'-metadata:s:v', 'title=Album cover',
outputFilePath
];
await new Promise((resolve, reject) => {
console.log('Starting FFmpeg process...');
const ffmpegProcess = spawn(ffmpegPath, ffmpegCommand, { stdio: 'ignore' });
ffmpegProcess.on('close', (code) => {
if (code === 0) {
console.log('FFmpeg process completed.');
resolve();
} else {
console.error(`FFmpeg process exited with code ${code}`);
reject(`FFmpeg process exited with code ${code}`);
}
});
ffmpegProcess.on('error', (err) => {
console.error('FFmpeg process error:', err);
reject(err);
});
});
console.log(`MP3 file merged with image. Output file: ${outputFilePath}`);
await ctx.replyWithAudio({ source: outputFilePath });
console.log('Sent the merged file to the user.');
try {
await fs.promises.unlink(mp3FilePath);
await fs.promises.unlink(imageFilePath);
await fs.promises.unlink(outputFilePath);
console.log('Removed the downloaded files.');
} catch (unlinkError) {
console.error('Error removing downloaded files:', unlinkError);
}
} else {
console.log('No video found.');
await ctx.reply('No video found.');
}
} catch (error) {
console.error('Error:', error.message);
await ctx.reply(`Error: ${error.message}`);
}
};
module.exports = downloadSpotifyMusic;

View File

@ -1,6 +1,4 @@
const axios = require('axios'); const axios = require('axios');
const { Markup } = require('telegraf');
// Adding useragent to avoid IP bans // Adding useragent to avoid IP bans
const headers = { const headers = {
'User-Agent': 'TikTok 26.2.0 rv:262018 (iPhone; iOS 14.4.2; en_US) Cronet' 'User-Agent': 'TikTok 26.2.0 rv:262018 (iPhone; iOS 14.4.2; en_US) Cronet'
@ -9,8 +7,7 @@ const headers = {
const getVideo = async (url) => { const getVideo = async (url) => {
const API_URL = `https://aemt.me/download/tiktokslide?url=${encodeURIComponent(url)}`; const API_URL = `https://aemt.me/download/tiktokslide?url=${encodeURIComponent(url)}`;
const response = await axios.get(API_URL, { headers }); const response = await axios.get(API_URL, { headers });
const data = response.data; return response.data;
return data;
}; };
module.exports = function(bot) { module.exports = function(bot) {
@ -25,10 +22,12 @@ module.exports = function(bot) {
} }
}); });
return async function downloadTikTokVideo(ctx, videoUrl) { async function downloadTikTokVideo(ctx, videoUrl) {
try { try {
const fullVideoUrl = `https://${videoUrl}`; // const fullVideoUrl = `https://${videoUrl}`;
const data = await getVideo(fullVideoUrl); // const data = await getVideo(fullVideoUrl);
const data = await getVideo(videoUrl);
const mediaUrl = data.result.data.play; const mediaUrl = data.result.data.play;
const isAudio = mediaUrl.endsWith('.mp3'); const isAudio = mediaUrl.endsWith('.mp3');
@ -60,5 +59,7 @@ module.exports = function(bot) {
console.error(err); console.error(err);
ctx.reply('Error downloading media'); ctx.reply('Error downloading media');
} }
}; }
return downloadTikTokVideo;
}; };

View File

@ -1,6 +1,6 @@
const axios = require('axios'); const axios = require('axios');
const fs = require('fs'); const fs = require('fs');
const { maxVideoSize } = require('../handlers/links_handler');
// Function to get the media information from a Twitter URL. // Function to get the media information from a Twitter URL.
async function getTwitterMedia(url, options = {}) { async function getTwitterMedia(url, options = {}) {
let input = {}; let input = {};
@ -65,15 +65,6 @@ async function convertXToTwitterURL(url) {
return url; return url;
} }
let maxVideoSize;
if (process.env.LOCAL_SERVER) {
// 2GB in bytes (2 * 1024 * 1024 * 1024)
maxVideoSize = 2 * 1024 * 1024 * 1024;
} else {
// 50MB in bytes (50 * 1024 * 1024)
maxVideoSize = 50 * 1024 * 1024;
}
// Function to download Twitter media and send it through Telegram. // Function to download Twitter media and send it through Telegram.
async function downloadTwitterMedia(ctx, url) { async function downloadTwitterMedia(ctx, url) {
try { try {

View File

@ -3,6 +3,7 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const ffmpeg = require('fluent-ffmpeg'); const ffmpeg = require('fluent-ffmpeg');
const ffmpegPath = require('ffmpeg-static'); const ffmpegPath = require('ffmpeg-static');
const { maxVideoSize } = require('../handlers/links_handler');
ffmpeg.setFfmpegPath(ffmpegPath); ffmpeg.setFfmpegPath(ffmpegPath);
@ -18,16 +19,6 @@ async function downloadYoutubeVideo(ctx, url) {
return; return;
} }
// Calculate the size of the video in bytes
let maxVideoSize;
if (process.env.LOCAL_SERVER) {
// 2GB in bytes (2 * 1024 * 1024 * 1024)
maxVideoSize = 2 * 1024 * 1024 * 1024;
} else {
// 50MB in bytes (50 * 1024 * 1024)
maxVideoSize = 50 * 1024 * 1024;
}
// Max video size // Max video size
const videoSize = parseInt(format.contentLength); const videoSize = parseInt(format.contentLength);
if (videoSize > maxVideoSize) { if (videoSize > maxVideoSize) {

View File

@ -0,0 +1,56 @@
const ytdl = require('ytdl-core');
const fs = require('fs');
const { promisify } = require('util');
const { maxVideoSize } = require('../handlers/links_handler');
promisify(fs.writeFile);
const unlinkAsync = promisify(fs.unlink);
async function downloadYoutubeMusic(ctx, url) {
try {
const videoIdMatch = url.match(/(?:watch\?v=|\/)([\w-]{11})/);
if (!videoIdMatch) {
throw new Error('Invalid YouTube Music link');
}
const videoId = videoIdMatch[1];
const info = await ytdl.getInfo(videoId);
const audioFormat = ytdl.chooseFormat(info.formats, { filter: 'audioonly' });
if (!audioFormat) {
await ctx.reply('Error: No audio format found for the given YouTube Music video.');
return;
}
const audioStream = ytdl(videoId, { format: audioFormat });
const fileName = `${info.videoDetails.title.replace(/[^\w\s]/g, '')}.mp3`;
const writeStream = fs.createWriteStream(fileName);
audioStream.pipe(writeStream);
await new Promise((resolve, reject) => {
writeStream.on('finish', resolve);
writeStream.on('error', reject);
});
const fileSize = fs.statSync(fileName).size;
if (fileSize > maxVideoSize) {
await ctx.reply('Error: The audio file is too large to send.');
await unlinkAsync(fileName);
return;
}
await ctx.replyWithAudio({ source: fileName });
//remove the downloaded file
await unlinkAsync(fileName);
} catch (error) {
console.error('Error downloading YouTube Music:', error);
await ctx.reply('Error downloading YouTube Music. Please try again later.');
}
}
module.exports = downloadYoutubeMusic;

View File

@ -0,0 +1,21 @@
const config = require("../../config.json");
let maxVideoSize;
if (!config.general.localServer) {
// If localServer is empty, set maxVideoSize to 50MB
maxVideoSize = 50 * 1024 * 1024;
} else {
// If localServer is not empty, set maxVideoSize to 2GB
maxVideoSize = 2 * 1024 * 1024 * 1024;
}
// If fileSize is defined and smaller than maxVideoSize, override maxVideoSize
if (config.general.fileSize && config.general.fileSize < maxVideoSize) {
maxVideoSize = config.general.fileSize;
}
module.exports = {
maxVideoSize,
};

View File

@ -1,22 +1,25 @@
const {Telegraf} = require('telegraf'); const {Telegraf} = require('telegraf');
require('dotenv').config(); require('dotenv').config();
const cron = require('node-cron'); const statusCommand = require('./commands/botStatus');
const { MongoClient } = require('mongodb');
const config = require('../config.json');
let bot; const bot = config.general.localServer
if (process.env.LOCAL_SERVER) { ? new Telegraf(process.env.BOT_TOKEN, { telegram: { apiRoot: config.general.localServer } })
bot = new Telegraf(process.env.BOT_TOKEN, { telegram: { apiRoot: process.env.LOCAL_SERVER } }); : new Telegraf(process.env.BOT_TOKEN);
} else {
bot = new Telegraf(process.env.BOT_TOKEN);
}
const downloadYoutubeVideo = require('./downloaders/youtube_dl'); const downloadYoutubeVideo = require('./downloaders/youtube_dl');
const downloadTikTokVideo = require('./downloaders/tiktok_dl')(bot); const downloadTikTokVideo = require('./downloaders/tiktok_dl')(bot);
const downloadInstagram = require('./downloaders/instagram_dl'); const downloadInstagram = require('./downloaders/instagram_dl');
const downloadTwitterVideo = require('./downloaders/twitter_dl'); const downloadTwitterVideo = require('./downloaders/twitter_dl');
const helpMessage = require('./helpMessage.json'); const downloadYoutubeMusic = require('./downloaders/youtube_music_dl');
const downloadRedditPost = require('./downloaders/reddit_dl');
const SECONDS_PER_MINUTE = 60; const downloadPinterestPost = require('./downloaders/pinterest_dl');
const downloadSoundCloudMusic = require('./downloaders/soundcloud_dl');
const downloadFacebookVideo = require('./downloaders/facebook_dl');
const downloadSpotifyMusic = require('./downloaders/spotify_dl');
const helpMessage = require('./commands/helpMessage.json');
let isBotRunning = false; let isBotRunning = false;
const userLastLinkTime = {}; const userLastLinkTime = {};
@ -34,56 +37,66 @@ bot.help(async (ctx) => {
} }
}); });
bot.command('status', statusCommand);
bot.on('text', async (ctx) => { bot.on('text', async (ctx) => {
const {id: userId} = ctx.from; const {id: userId} = ctx.from;
const {text, date: messageTime} = ctx.message; const {text, date: messageTime} = ctx.message;
const currentTime = Math.floor(Date.now() / 1000); const currentTime = Math.floor(Date.now() / 1000);
if (currentTime - messageTime > SECONDS_PER_MINUTE) { if (currentTime - messageTime > 60) {
return; return;
} }
if (userLastLinkTime[userId] && Date.now() - userLastLinkTime[userId] < 5000) { let delayTime = config.general.delayTimeLimit;
await ctx.reply('You can send a link once in 5 seconds');
if (delayTime !== 0 && userLastLinkTime[userId] && Date.now() - userLastLinkTime[userId] < delayTime) {
await ctx.reply(`You can send a link once in ${delayTime/1000} seconds`);
return; return;
} }
const youtubeUrlRegex = /(https?:\/\/(?:www\.)?youtube\.com\/watch\?v=|https?:\/\/youtu\.be\/|https?:\/\/(?:www\.)?youtube\.com\/shorts\/)([\w-]{11})/gi; const youtubeUrls = text.match (/(https?:\/\/(?:www\.)?youtube\.com\/watch\?v=|https?:\/\/youtu\.be\/|https?:\/\/(?:www\.)?youtube\.com\/shorts\/)([\w-]{11})/gi);
const tiktokUrlRegex = /(https?:\/\/(?:www\.)?tiktok\.com\/(?:@[\w.-]+\/video\/[\w-]+|@[\w.-]+)|vm\.tiktok\.com\/[\w.-]+|vt\.tiktok\.com\/[\w.-]+)/gi; const tiktokUrls = text.match (/(https?:\/\/(?:www\.)?tiktok\.com\/(?:@[\w.-]+\/video\/[\w-]+|@[\w.-]+)|vm\.tiktok\.com\/[\w.-]+|vt\.tiktok\.com\/[\w.-]+)/gi);
const instagramUrlRegex = /https?:\/\/(?:www\.)?instagram\.com\/(?:([^\/]+)\/)?(?:p|tv|reel|reels|stories)\/([\w.-]+)/gi; const instagramUrls = text.match (/https?:\/\/(?:www\.)?instagram\.com\/(?:([^\/]+)\/)?(?:p|tv|reel|reels|stories)\/([\w.-]+)/gi);
const twitterUrlRegex = /(https?:\/\/(?:www\.)?(?:twitter|x)\.com\/(?:[\w.-]+)\/status\/[\d]+|https?:\/\/t\.co\/[\w.-]+)/gi; const twitterUrls = text.match (/(https?:\/\/(?:www\.)?(?:twitter|x)\.com\/(?:[\w.-]+)\/status\/.+|https?:\/\/t\.co\/.+)/gi);
const youtubeMusicUrls = text.match (/https?:\/\/music\.youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)(?:&(?:list|si)=([a-zA-Z0-9_-]+))?/gi);
const redditUrls = text.match(/(https?:\/\/(?:www\.)?reddit\.com\/(?:r\/[^/]+\/comments\/|[^/]+\/comments\/)[\w-]+)/gi);
const pinterestUrls= text.match (/(https?:\/\/(?:www\.)?(?:ru\.)?pinterest\.(com|co.uk)\/pin\/[\w.-]+)/gi);
const soundcloudUrls = text.match(/https?:\/\/soundcloud\.com\/[\w.-]+\/[\w.-]+/gi);
const facebookUrls = text.match(/https?:\/\/(?:www\.|)facebook\.com\/(?:watch\?v=\d+|[^\/]+\/videos\/\d+\/?|permalink\.php\?story_fbid=\d+&id=\d+|fb\.watch\/[a-zA-Z0-9_-]+\/?)\b/gi);
const spotifyUrls = text.match(/https?:\/\/open\.spotify\.com\/track\/[a-zA-Z0-9]{22}/gi);
if (redditUrls && redditUrls.length > 0) {
const youtubeUrls = text.match(youtubeUrlRegex); await handleDownload(ctx, redditUrls, downloadRedditPost);
const tiktokUrls = text.match(tiktokUrlRegex); } else if (youtubeMusicUrls && youtubeMusicUrls.length > 0) {
const instagramUrls = text.match(instagramUrlRegex); await handleDownload(ctx, youtubeMusicUrls, downloadYoutubeMusic);
} else if (youtubeUrls && youtubeUrls.length > 0) {
const twitterUrls = []; await handleDownload(ctx, youtubeUrls, downloadYoutubeVideo);
let match;
while ((match = twitterUrlRegex.exec(text)) !== null) {
twitterUrls.push(match[0]);
}
if (youtubeUrls && youtubeUrls.length > 0) {
await handleVideoDownload(ctx, youtubeUrls, downloadYoutubeVideo);
} else if (tiktokUrls && tiktokUrls.length > 0) { } else if (tiktokUrls && tiktokUrls.length > 0) {
await handleVideoDownload(ctx, tiktokUrls, downloadTikTokVideo); await handleDownload(ctx, tiktokUrls, downloadTikTokVideo);
} else if (instagramUrls && instagramUrls.length > 0) { } else if (instagramUrls && instagramUrls.length > 0) {
await handleVideoDownload(ctx, instagramUrls, downloadInstagram); await handleDownload(ctx, instagramUrls, downloadInstagram);
} else if (twitterUrls.length > 0) { } else if (twitterUrls && twitterUrls.length > 0) {
await handleVideoDownload(ctx, twitterUrls, downloadTwitterVideo); await handleDownload(ctx, twitterUrls, downloadTwitterVideo);
} else if (pinterestUrls && pinterestUrls.length > 0) {
await handleDownload(ctx, pinterestUrls, downloadPinterestPost);
} else if (soundcloudUrls && soundcloudUrls.length > 0) {
await handleDownload(ctx, soundcloudUrls, downloadSoundCloudMusic);
} else if (facebookUrls && facebookUrls.length > 0) {
await handleDownload(ctx, facebookUrls, downloadFacebookVideo);
} else if (spotifyUrls && spotifyUrls.length > 0) {
await handleDownload(ctx, spotifyUrls, downloadSpotifyMusic);
} else { } else {
await ctx.reply('Unknown command'); await ctx.reply('Unknown command');
} }
}); });
async function handleVideoDownload(ctx, urls, downloaderFn) { async function handleDownload(ctx, urls, downloaderFn) {
for (const url of urls) { for (const url of urls) {
await downloaderFn(ctx, url); await downloaderFn(ctx, url);
} }
userLastLinkTime[ctx.from.id] = Date.now(); userLastLinkTime[ctx.from.id] = Date.now();
} }
bot.launch(); bot.launch();
process.once('SIGINT', () => bot.stop('SIGINT')); process.once('SIGINT', () => bot.stop('SIGINT'));

0
tests/regexTest.js Normal file
View File