← back to browse
goat command

tiktok

No description

0
views
0
likes
0
installs

Aliases: tt

raw source
const axios = require('axios');
const fs = require('fs-extra');
const path = require('path');

const TIKTOK_SEARCH_API = 'https://lyric-search-neon.vercel.app/kshitiz?keyword=';
const CACHE_DIR = path.join(__dirname, 'tiktok_cache');

async function getStreamFromURL(url) {
  const response = await axios({
    url: url,
    responseType: 'stream',
    timeout: 180000 
  });
  return response.data;
}

module.exports = {
  config: {
    name: "tiktok",
    aliases: ["tt"],
    version: "1.0.0",
    author: "Neoaz ゐ",
    countDown: 5,
    role: 0,
    description: { en: "Search and download TikTok video" },
    category: "media",
    guide: { en: "{pn} <search query>\n{pn} -v <search query>" }
  },

  onStart: async function ({ api, args, event, commandName }) {
    const prefix = args[0];
    const query = args.slice(1).join(" ");

    if (prefix === "-v") {
        if (!query) return api.sendMessage("❌ Provide a search query.", event.threadID, event.messageID);
        await handleSearchAndDownload(query, api, event, commandName);
    } else {
        const fullQuery = args.join(" ");
        if (!fullQuery) return api.sendMessage("❌ Provide a search query.", event.threadID, event.messageID);
        await handleSearchAndDownload(fullQuery, api, event, commandName);
    }
  },

  onReply: async function ({ event, api, Reply }) {
    const { results } = Reply;
    const selection = parseInt(event.body);

    if (isNaN(selection) || selection < 1 || selection > results.length) {
      return api.sendMessage("❌ Invalid selection. Choose 1-" + results.length + ".", event.threadID, event.messageID);
    }

    const selectedVideo = results[selection - 1];
    await api.unsendMessage(Reply.messageID);

    await downloadVideo(selectedVideo, api, event);
  }
};

async function handleSearchAndDownload(query, api, event, commandName) {
    try {
        api.sendMessage("🔎 Searching TikTok for: " + query, event.threadID, event.messageID);
        
        const searchResponse = await axios.get(TIKTOK_SEARCH_API + encodeURIComponent(query), { timeout: 20000 });
        const results = searchResponse.data.slice(0, 6);

        if (!results || results.length === 0) {
            return api.sendMessage("❌ No TikTok videos found for the query.", event.threadID, event.messageID);
        }

        let messageBody = "";
        const thumbnailPromises = [];

        results.forEach((video, index) => {
            messageBody += `${index + 1}. ${video.title.substring(0, 70)}...\n`;
            messageBody += `   • Creator: @${video.author.unique_id}\n`;
            messageBody += `   • Duration: ${video.duration}s\n\n`;
            if (video.cover) {
                thumbnailPromises.push(getStreamFromURL(video.cover));
            }
        });

        const attachments = await Promise.all(thumbnailPromises);
        const validAttachments = attachments.filter(a => a !== null);

        api.sendMessage(
            { 
                body: "Found " + results.length + " videos.\n\n" + messageBody + "Reply with the number (1-" + results.length + ") to download the video.",
                attachment: validAttachments
            },
            event.threadID,
            (err, info) => {
                if (err) {
                    console.error("Error sending search results:", err);
                    return api.sendMessage("❌ Failed to display results.", event.threadID, event.messageID);
                }
                
                global.GoatBot.onReply.set(info.messageID, {
                    commandName: commandName,
                    messageID: info.messageID,
                    author: event.senderID,
                    results: results
                });
            },
            event.messageID
        );
    } catch (error) {
        console.error("TikTok Search Error:", error);
        api.sendMessage("❌ Failed to search TikTok or API error.", event.threadID, event.messageID);
    }
}

async function downloadVideo(videoInfo, api, event) {
    let filePath = null;

    try {
        api.sendMessage("⏳ Downloading video: " + videoInfo.title.substring(0, 50) + "...", event.threadID);

        await fs.ensureDir(CACHE_DIR);

        const safeTitle = videoInfo.title.substring(0, 30).replace(/[^a-z0-9]/gi, '_');
        const filename = `${Date.now()}_${safeTitle}.mp4`;
        filePath = path.join(CACHE_DIR, filename);

        const writer = fs.createWriteStream(filePath);
        const response = await axios({
            url: videoInfo.videoUrl,
            method: 'GET',
            responseType: 'stream',
            timeout: 300000
        });

        response.data.pipe(writer);

        await new Promise((resolve, reject) => {
            writer.on('finish', resolve);
            writer.on('error', reject);
        });

        await api.sendMessage(
            { 
                body: `✅ Downloaded: ${videoInfo.title}\nCreator: @${videoInfo.author.unique_id}\nDuration: ${videoInfo.duration}s`,
                attachment: fs.createReadStream(filePath) 
            },
            event.threadID,
            (err) => {
                if (err) console.error("Error sending file:", err);
                fs.unlink(filePath).catch(console.error);
            },
            event.messageID
        );

    } catch (error) {
        console.error("TikTok Download Error:", error);
        api.sendMessage("❌ Failed to download the video stream.", event.threadID, event.messageID);
    } finally {
        if (filePath && fs.existsSync(filePath)) {
            await fs.unlink(filePath).catch(console.error);
        }
    }
        }
                                            

View raw file