تخطَّ إلى المحتوى
ARAB CODEمجتمع المطورين العرب

روايات-كتب2

javascriptv1MITنُشر ١ أغسطس ٢٠٢٦· ♥ 0 · ⬇ 1 · 💬 0

تحميل كتب وروايات من sunovels

// كود بحث وتحميل روايات من sunovels 
// https://whatsapp.com/channel/0029Vb7Nq294Y9le1aAcTE0D
// تابعو القناة هننشر اكواد تانية "izana,uncel shawaza" 
import axios from 'axios';
import fs from 'fs';
import { generateWAMessageFromContent, prepareWAMessageMedia } from '@whiskeysockets/baileys';

const API_BASE = 'https://engez.a7a.online/api/v1/reading/sunovels';
const FALLBACK_IMAGE = 'https://files.catbox.moe/z5zh28.jpg';

global.sunovelsSessions = global.sunovelsSessions || {};

const getSession = (sender) => {
  if (!global.sunovelsSessions[sender]) {
    global.sunovelsSessions[sender] = {
      results: [],
      novel: null,
      totalPages: 0,
      page: 1,
      chapters: []
    };
  }
  return global.sunovelsSessions[sender];
};

const apiGet = async (params) => {
  const url = `${API_BASE}?${new URLSearchParams(params).toString()}`;
  const { data } = await axios.get(url, { timeout: 30000 });
  return data;
};

const searchNovels = async (q) => {
  try {
    const res = await apiGet({ action: 'search', q });
    if (res?.success && Array.isArray(res?.response)) {
      return res.response;
    }
    return [];
  } catch (error) {
    throw new Error(`فشل البحث: ${error.message}`);
  }
};

const getChaptersPage = async (url, page) => {
  try {
    const res = await apiGet({ action: 'chapters', url, page: String(page) });
    if (res?.success && res?.response) {
      return {
        chapters: Array.isArray(res.response.chapters) ? res.response.chapters : [],
        totalPages: res.response.totalPages || 0
      };
    }
    return { chapters: [], totalPages: 0 };
  } catch (error) {
    throw new Error(`فشل جلب الفصول: ${error.message}`);
  }
};

const getContent = async (url) => {
  try {
    const res = await apiGet({ action: 'content', url });
    if (res?.success && res?.response) {
      return res.response || { text: '', novelTitle: '', chapterTitle: '' };
    }
    return { text: '', novelTitle: '', chapterTitle: '' };
  } catch (error) {
    throw new Error(`فشل جلب المحتوى: ${error.message}`);
  }
};

const makeInteractiveList = async (conn, m, title, body, footer, imageUrl, sections) => {
  try {
    const mediaMessage = await prepareWAMessageMedia(
      { image: { url: imageUrl } },
      { upload: conn.waUploadToServer }
    );

    const msg = generateWAMessageFromContent(
      m.chat,
      {
        viewOnceMessage: {
          message: {
            interactiveMessage: {
              body: { text: body },
              footer: { text: footer },
              header: {
                hasMediaAttachment: true,
                imageMessage: mediaMessage.imageMessage
              },
              nativeFlowMessage: {
                buttons: [
                  {
                    name: 'single_select',
                    buttonParamsJson: JSON.stringify({
                      title,
                      sections
                    })
                  }
                ]
              }
            }
          }
        }
      },
      { userJid: conn.user.jid, quoted: m }
    );

    await conn.relayMessage(m.chat, msg.message, { messageId: msg.key.id });
    return true;
  } catch (error) {
    return false;
  }
};

const sendText = async (conn, m, text) => {
  return conn.sendMessage(m.chat, { text }, { quoted: m });
};

const buildNovelSections = (results, usedPrefix) => {
  return [
    {
      title: 'نتائج البحث',
      highlight_label: 'اختر رواية',
      rows: results.slice(0, 10).map((item, index) => ({
        header: `📖 ${item.title}`,
        title: item.title.length > 40 ? `${item.title.slice(0, 40)}...` : item.title,
        description: item.author ? `✍️ ${item.author}` : 'اختيار الرواية',
        id: `${usedPrefix}رواية-اختيار ${index + 1}`
      }))
    }
  ];
};

const buildPageSections = (totalPages, usedPrefix) => {
  const sections = [];
  const step = 10;
  const pages = Math.min(totalPages, 47);

  for (let start = 1; start <= pages; start += step) {
    const end = Math.min(start + step - 1, pages);
    const rows = [];

    for (let p = start; p <= end; p++) {
      rows.push({
        header: `📄 صفحة ${p}`,
        title: `صفحة ${p}`,
        description: 'عرض فصول هذه الصفحة',
        id: `${usedPrefix}رواية-صفحة ${p}`
      });
    }

    sections.push({
      title: `الصفحات ${start}-${end}`,
      highlight_label: 'اختر صفحة',
      rows
    });
  }

  return sections;
};

const buildChapterSections = (chapters, usedPrefix) => {
  const sections = [];
  const step = 10;
  
  // ترتيب الفصول تصاعدياً (من 1 إلى الأكبر)
  const sortedChapters = [...chapters].sort((a, b) => {
    const numA = parseInt(a.title);
    const numB = parseInt(b.title);
    return numA - numB;
  });

  for (let start = 0; start < sortedChapters.length; start += step) {
    const end = Math.min(start + step, sortedChapters.length);
    const rows = [];

    for (let i = start; i < end; i++) {
      const ch = sortedChapters[i];
      const chapterNum = i + 1;
      rows.push({
        header: `📖 فصل ${chapterNum}`,
        title: ch?.title ? String(ch.title).slice(0, 40) : `فصل ${chapterNum}`,
        description: 'تحميل TXT',
        id: `${usedPrefix}رواية-فصل ${chapterNum}`
      });
    }

    sections.push({
      title: `الفصول ${start + 1}-${end}`,
      highlight_label: 'اختر فصل',
      rows
    });
  }

  return sections;
};

const showNovelResults = async (conn, m, text, results, usedPrefix) => {
  const firstImage = results[0]?.img || FALLBACK_IMAGE;
  const body = `📚 نتائج البحث عن: ${text}\n\nاختر رواية من الأزرار`;
  const ok = await makeInteractiveList(
    conn,
    m,
    'نتائج البحث',
    body,
    'روايات',
    firstImage,
    buildNovelSections(results, usedPrefix)
  );

  if (!ok) {
    let fallback = `📚 نتائج البحث عن: ${text}\n\n`;
    results.slice(0, 10).forEach((r, i) => {
      fallback += `${i + 1}. ${r.title}\n`;
    });
    fallback += `\nاستخدم: ${usedPrefix}رواية-اختيار <رقم>`;
    await sendText(conn, m, fallback);
  }
};

const showPages = async (conn, m, novel, totalPages, usedPrefix) => {
  const image = novel.img || FALLBACK_IMAGE;
  const body = `📖 ${novel.title}\n\nاختر صفحة الفصول من الأزرار`;
  const ok = await makeInteractiveList(
    conn,
    m,
    'صفحات الفصول',
    body,
    'اختيار الصفحة',
    image,
    buildPageSections(totalPages || 1, usedPrefix)
  );

  if (!ok) {
    let fallback = `📖 ${novel.title}\nعدد صفحات الفصول: ${totalPages}\n\n`;
    const pages = Math.min(totalPages, 47);
    for (let i = 1; i <= pages; i++) {
      fallback += `${i}. ${usedPrefix}رواية-صفحة ${i}\n`;
    }
    await sendText(conn, m, fallback);
  }
};

const showChapters = async (conn, m, novel, pageNumber, totalPages, chapters, usedPrefix) => {
  const image = novel.img || FALLBACK_IMAGE;
  const body = `📖 ${novel.title}\n📄 الصفحة ${pageNumber} / ${totalPages}\n\nاختر فصل من الأزرار`;
  
  const ok = await makeInteractiveList(
    conn,
    m,
    'الفصول',
    body,
    'اختيار الفصل',
    image,
    buildChapterSections(chapters, usedPrefix)
  );

  if (!ok) {
    // ترتيب الفصول تصاعدياً للعرض النصي
    const sortedChapters = [...chapters].sort((a, b) => {
      const numA = parseInt(a.title);
      const numB = parseInt(b.title);
      return numA - numB;
    });
    
    let fallback = `📖 ${novel.title}\n📄 الصفحة ${pageNumber} / ${totalPages}\n\n`;
    sortedChapters.slice(0, 50).forEach((ch, i) => {
      const num = i + 1;
      fallback += `${num}. ${ch.title}\n`;
    });
    fallback += `\nاستخدم: ${usedPrefix}رواية-فصل <رقم>`;
    await sendText(conn, m, fallback);
  }
};

const handler = async (m, { conn, text, command, usedPrefix }) => {
  const session = getSession(m.sender);

  try {
    if (command === 'رواية' || command === 'روايه' || command === 'anovel') {
      if (!text) {
        return await sendText(conn, m, "📚 اكتب اسم الرواية\nمثال: رواية القس المجنون");
      }

      await sendText(conn, m, `⏳ جاري البحث عن: ${text}...`);
      
      const results = await searchNovels(text);
      if (!results.length) {
        return await sendText(conn, m, `❌ لم أجد روايات باسم: ${text}`);
      }

      session.results = results;
      session.novel = null;
      session.totalPages = 0;
      session.page = 1;
      session.chapters = [];

      await showNovelResults(conn, m, text, results, usedPrefix);
      return;
    }

    if (command === 'رواية-اختيار' || command === 'روايه-اختيار') {
      const idx = parseInt(text);
      if (!idx || idx < 1 || idx > session.results.length) {
        return await sendText(conn, m, `❌ اكتب رقم صحيح من 1 إلى ${session.results.length}`);
      }

      const novel = session.results[idx - 1];
      if (!novel?.url) {
        return await sendText(conn, m, `❌ تعذر تحديد الرواية`);
      }

      await sendText(conn, m, `⏳ جاري تحميل فصول: ${novel.title}...`);

      const firstPage = await getChaptersPage(novel.url, 0);

      session.novel = novel;
      session.totalPages = Number(firstPage.totalPages || 1);
      session.page = 1;
      session.chapters = Array.isArray(firstPage.chapters) ? firstPage.chapters : [];

      await showPages(conn, m, novel, session.totalPages, usedPrefix);
      return;
    }

    if (command === 'رواية-صفحة' || command === 'روايه-صفحة') {
      if (!session.novel) {
        return await sendText(conn, m, "❌ اختر رواية أولاً باستخدام: رواية-اختيار <رقم>");
      }

      const pageNumber = parseInt(text);
      const maxPages = Math.min(session.totalPages, 47);
      if (!pageNumber || pageNumber < 1 || pageNumber > maxPages) {
        return await sendText(conn, m, `❌ رقم الصفحة غير صحيح (1 - ${maxPages})`);
      }

      await sendText(conn, m, `⏳ جاري تحميل الصفحة ${pageNumber}...`);

      const pageData = await getChaptersPage(session.novel.url, pageNumber - 1);
      const chapters = Array.isArray(pageData.chapters) ? pageData.chapters : [];

      if (!chapters.length) {
        return await sendText(conn, m, `❌ لا توجد فصول في هذه الصفحة`);
      }

      session.page = pageNumber;
      session.chapters = chapters;

      await showChapters(conn, m, session.novel, pageNumber, session.totalPages, chapters, usedPrefix);
      return;
    }

    if (command === 'رواية-فصل' || command === 'روايه-فصل') {
      if (!session.novel) {
        return await sendText(conn, m, "❌ اختر رواية أولاً باستخدام: رواية-اختيار <رقم>");
      }

      // ترتيب الفصول تصاعدياً للبحث
      const sortedChapters = [...session.chapters].sort((a, b) => {
        const numA = parseInt(a.title);
        const numB = parseInt(b.title);
        return numA - numB;
      });
      
      const num = parseInt(text);
      if (!num || num < 1 || num > sortedChapters.length) {
        return await sendText(conn, m, `❌ رقم الفصل غير صحيح (1 - ${sortedChapters.length})`);
      }

      const chapter = sortedChapters[num - 1];
      if (!chapter?.url) {
        return await sendText(conn, m, `❌ لم أجد هذا الفصل`);
      }

      await sendText(conn, m, `⏳ جاري تحميل الفصل ${num}...`);

      const content = await getContent(chapter.url);
      if (!content?.text) {
        return await sendText(conn, m, `❌ تعذر جلب نص الفصل`);
      }

      const fileName = `${(content.novelTitle || session.novel.title || 'novel')
        .replace(/[\/\\:*?"<>|]/g, '_')
        .slice(0, 60)}_${(content.chapterTitle || chapter.title || `chapter_${num}`)
        .replace(/[\/\\:*?"<>|]/g, '_')
        .slice(0, 60)}.txt`;

      const filePath = `/tmp/${Date.now()}.txt`;
      fs.writeFileSync(filePath, content.text, 'utf8');

      await conn.sendMessage(
        m.chat,
        {
          document: fs.readFileSync(filePath),
          mimetype: 'text/plain',
          fileName
        },
        { quoted: m }
      );

      fs.unlinkSync(filePath);
      return;
    }

    if (command === 'رواية-الصفحات' || command === 'روايه-الصفحات') {
      if (!session.novel) {
        return await sendText(conn, m, "❌ اختر رواية أولاً باستخدام: رواية-اختيار <رقم>");
      }

      await showPages(conn, m, session.novel, session.totalPages || 1, usedPrefix);
      return;
    }

    if (command === 'رواية-تحديث' || command === 'روايه-تحديث') {
      if (!session.novel) {
        return await sendText(conn, m, "❌ اختر رواية أولاً باستخدام: رواية-اختيار <رقم>");
      }

      await sendText(conn, m, `⏳ جاري تحديث الفصول...`);

      const pageData = await getChaptersPage(session.novel.url, Math.max(0, (session.page || 1) - 1));
      const chapters = Array.isArray(pageData.chapters) ? pageData.chapters : [];
      session.chapters = chapters;
      await showChapters(conn, m, session.novel, session.page || 1, session.totalPages || 1, chapters, usedPrefix);
      return;
    }
  } catch (error) {
    console.error('❌ Handler error:', error);
    await sendText(conn, m, `❌ حدث خطأ: ${error.message || 'خطأ غير معروف'}`);
  }
};

handler.help = [
  'رواية <اسم> - البحث عن رواية',
  'رواية-اختيار <رقم> - اختيار رواية من النتائج',
  'رواية-صفحة <رقم> - عرض فصول صفحة معينة',
  'رواية-فصل <رقم> - تحميل فصل معين',
  'رواية-الصفحات - عرض صفحات الفصول',
  'رواية-تحديث - تحديث قائمة الفصول'
];

handler.tags = ['internet'];
handler.command = [
  'رواية',
  'روايه',
  'anovel',
  'رواية-اختيار',
  'روايه-اختيار',
  'رواية-صفحة',
  'روايه-صفحة',
  'رواية-فصل',
  'روايه-فصل',
  'رواية-الصفحات',
  'روايه-الصفحات',
  'رواية-تحديث',
  'روايه-تحديث'
];

export default handler;

التعليقات (0)

سجّل الدخول للتعليق.