// ๐Ÿ”ฅDiablo๐Ÿ”ฅBot๐Ÿ”ฅ - Anime Complete System + PDF import fetch from 'node-fetch'; import { generateWAMessageFromContent, proto } from 'baileys'; import PDFDocument from 'pdfkit'; import fs from 'fs'; global.animeSessions = global.animeSessions || {}; const CACHE = new Map(); const TTL = { PAST_SEASON: Infinity, CURRENT_SEASON: 30*60*1000, FUTURE_SEASON: 2*60*60*1000, ANIME_FINISHED: Infinity, ANIME_AIRING: 20*60*1000, ANIME_UPCOMING: 3*60*60*1000, GENRE_SEARCH: 60*60*1000, SEARCH_RESULTS: 60*60*1000, CHARACTERS: 6*60*60*1000, STAFF: 6*60*60*1000, RECOMMENDATIONS: 3*60*60*1000 }; function cacheGet(key){const e=CACHE.get(key);if(!e)return null;if(e.ttl===Infinity)return e.data;if(Date.now()-e.time]*>/g,'').trim();if(!clean)return'';if(clean.length>500)clean=clean.slice(0,500);if(/[\u0600-\u06FF]/.test(clean)&&clean.length>10)return clean;if(translationCache.has(clean))return translationCache.get(clean);try{const res=await fetch(`https://api.mymemory.translated.net/get?q=${encodeURIComponent(clean)}&langpair=en|ar`,{timeout:8000});const data=await res.json();let tr=data?.responseData?.translatedText||clean;translationCache.set(clean,tr);return tr;}catch{return clean;}} async function translateBatch(texts){const r=[];for(let t of texts){r.push(await translateToAr(t));await new Promise(x=>setTimeout(x,300));}return r;} function cleanText(text,max=900){if(!text)return'ู„ุง ูŠูˆุฌุฏ ูˆุตู ู…ุชุงุญ.';let c=String(text).replace(//gi,' ').replace(/<\/?[^>]+>/g,'').replace(/\[Written by[^\]]*\]/gi,'').replace(/\[Source:[^\]]*\]/gi,'').replace(/\(Source:[^)]*\)/gi,'').replace(/[#*_~`>|\\]+/g,' ').replace(/\[[^\]]{0,30}\]/g,'').replace(/[\uFFFD\u200B-\u200F\u202A-\u202E]+/g,'').replace(/\s+/g,' ').replace(/\n{3,}/g,'\n\n').trim();if(c.length<15)return'ู„ุง ูŠูˆุฌุฏ ูˆุตู ู…ุชุงุญ.';if(c.length>max)c=c.slice(0,max)+'...';return c;} function shortTitle(t,w=3){if(!t)return'ุจุฏูˆู† ุนู†ูˆุงู†';const p=String(t).trim().split(/\s+/).filter(Boolean);return p.length<=w?t:p.slice(0,w).join(' ')+'โ€ฆ';} const STATUS_MAP_AR={'RELEASING':'๐ŸŸข ู…ุณุชู…ุฑ','FINISHED':'โœ… ู…ูƒุชู…ู„','NOT_YET_RELEASED':'โณ ู‚ุงุฏู…','CANCELLED':'โŒ ู…ู„ุบูŠ','HIATUS':'โธ๏ธ ู…ุชูˆู‚ู'}; const SEASON_AR={WINTER:'ุดุชุงุก',SPRING:'ุฑุจูŠุน',SUMMER:'ุตูŠู',FALL:'ุฎุฑูŠู'}; const SEASON_EMOJI={WINTER:'โ„๏ธ',SPRING:'๐ŸŒธ',SUMMER:'โ˜€๏ธ',FALL:'๐Ÿ‚'}; function currentSeason(){const m=new Date().getMonth()+1;if(m>=1&&m<=3)return'WINTER';if(m>=4&&m<=6)return'SPRING';if(m>=7&&m<=9)return'SUMMER';return'FALL';} function currentYear(){return new Date().getFullYear();} function formatRemaining(s){if(!s||s<=0)return'ุงู„ุขู†';const d=Math.floor(s/86400),h=Math.floor((s%86400)/3600),mn=Math.floor((s%3600)/60);if(d>0)return`${d} ูŠูˆู… ูˆ ${h} ุณุงุนุฉ`;if(h>0)return`${h} ุณุงุนุฉ ูˆ ${mn} ุฏู‚ูŠู‚ุฉ`;return`${mn} ุฏู‚ูŠู‚ุฉ`;} function formatAiringDate(ts){if(!ts)return'ุบูŠุฑ ู…ุนุฑูˆู';const d=new Date(ts*1000);return d.toLocaleString('ar-MA',{timeZone:'Africa/Casablanca',weekday:'long',day:'numeric',month:'long',hour:'2-digit',minute:'2-digit'});} async function sendInteractive(conn,m,body,footer,buttons){const interactiveMessage={body:{text:body},footer:{text:footer||'ุณูŠู„ุงู†ุง ุจูˆุช'},nativeFlowMessage:{buttons}};const msg=generateWAMessageFromContent(m.chat,{viewOnceMessage:{message:{interactiveMessage:proto.Message.InteractiveMessage.fromObject(interactiveMessage)}}},{quoted:m});await conn.relayMessage(m.chat,msg.message,{messageId:msg.key.id,additionalNodes:[{tag:'biz',attrs:{},content:[{tag:'interactive',attrs:{type:'native_flow',v:'1'},content:[{tag:'native_flow',attrs:{v:'9',name:'mixed'}}]}]}]});} // ===== PDF ุนุฑุถ ูƒุงู…ู„ ===== async function performFullView(conn,m,usedPrefix){ const session=global.animeSessions[m.sender]; if(!session)return conn.sendMessage(m.chat,{text:'โŒ ู„ุง ุชูˆุฌุฏ ุฌู„ุณุฉ.'},{quoted:m}); let list=session.results||session.seasonResults||session.genreResults; let source=session.results?'ุจุญุซ':session.seasonResults?`${SEASON_AR[session.seasonName]||''} ${session.seasonYear||''}`:session.genreAr||session.genreMultiNames||'ุชุตู†ูŠู'; if(!list||!list.length)return conn.sendMessage(m.chat,{text:'โŒ ู„ุง ุชูˆุฌุฏ ู†ุชุงุฆุฌ ู„ู„ุนุฑุถ'},{quoted:m}); let wait = await conn.sendMessage(m.chat,{text:`โณ ูƒู†ุตุงูˆุจ PDF ููŠู‡ ${list.length} ุงู†ู…ูŠ ุจุงู„ุตูˆุฑ...`},{quoted:m}); let pdfPath = `./anime_${Date.now()}.pdf` let doc = new PDFDocument({ margin: 20, size: 'A4', autoFirstPage: false }) doc.pipe(fs.createWriteStream(pdfPath)) for(let i=0;i20? await translateToAr(story).catch(()=>story) : story doc.addPage() try{ let imgUrl = full.coverImage?.extraLarge||full.coverImage?.large if(imgUrl){ let r = await fetch(imgUrl) let buf = Buffer.from(await r.arrayBuffer()) doc.image(buf, 30, 30, { width: 150, height: 210 }) } }catch{} doc.fontSize(14).fillColor('black').text(`${i+1}. ${title}`, 200, 30, {width: 350, align:'left'}) doc.fontSize(10).fillColor('gray').text(`ุงู„ุงุณู… ุงู„ุงุฎุฑ: ${title2}`, 200, 55, {width: 350}) doc.fontSize(10).fillColor('black') doc.text(`ุงู„ุณู†ุฉ: ${year} | ุงู„ุญุงู„ุฉ: ${status}`, 200, 80, {width: 350}) doc.text(`ุงู„ุญู„ู‚ุงุช: ${eps} | ุงู„ุชู‚ูŠูŠู…: ${score}/10`, 200, 95, {width: 350}) doc.text(`ุงู„ู†ูˆุน: ${full.format||'?'}`, 200, 110, {width: 350}) doc.fillColor('#e67e22').text(`ุงู„ุชุตู†ูŠู: ${genres}`, 200, 125, {width: 350}) doc.fillColor('black').fontSize(11).text(`ุงู„ู‚ุตุฉ:`, 30, 260) doc.fontSize(9).text(storyAr, 30, 275, {width: 530, align:'right'}) doc.fontSize(8).fillColor('gray').text(`anilist.co/anime/${full.id}`, 30, 780, {align:'center'}) } doc.end() await new Promise(r=>setTimeout(r,2500)) try{await conn.sendMessage(m.chat,{delete:wait.key})}catch{} await conn.sendMessage(m.chat,{ document: fs.readFileSync(pdfPath), mimetype: 'application/pdf', fileName: `${source}_${list.length}anime.pdf`, caption: `๐Ÿ“š *${source}* - ${list.length} ุงู†ู…ูŠ\nPDF ููŠู‡ ุตูˆุฑุฉ + ุงุณู… + ุชุตู†ูŠู + ู‚ุตุฉ` },{quoted:m}) try{fs.unlinkSync(pdfPath)}catch{} return sendInteractive(conn,m,`โœ… *ุชู… ุฅู†ุดุงุก PDF* - ${list.length} ุงู†ู…ูŠ`,'ุณูŠู„ุงู†ุง ุจูˆุช โ€ข PDF',[ {name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'โŒ ุฅุบู„ุงู‚',id:`${usedPrefix}ุงู†ู…ูŠ-ุฅุบู„ุงู‚`})}, {name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'๐Ÿ“š ุงู„ู‚ุงุฆู…ุฉ',id:`${usedPrefix}ุงู†ู…ูŠุงุช`})} ]); } async function fetchFullDetails(animeId){const key=`anime:${animeId}`;const cached=cacheGet(key);if(cached)return cached;const query=`query ($id: Int) { Media(id: $id, type: ANIME) { id title { romaji english native } description episodes duration averageScore popularity genres tags { name description rank isAdult } coverImage { large extraLarge } bannerImage trailer { id site } startDate { year month day } endDate { year month day } status format source season seasonYear studios { nodes { name } } nextAiringEpisode { episode timeUntilAiring airingAt } characters(sort: ROLE, perPage: 25) { edges { role node { id name { full native } image { large } description age gender } voiceActors(language: JAPANESE, sort: RELEVANCE) { id name { full } image { large } description languageV2 } } } staff(perPage: 25) { edges { role node { id name { full native } image { large } description } } } recommendations(perPage: 20, sort: RATING_DESC) { nodes { rating mediaRecommendation { id title { romaji english } coverImage { large } averageScore episodes format status } } } } }`;const res=await fetch('https://graphql.anilist.co/',{method:'POST',headers:{'Content-Type':'application/json','User-Agent':'Mozilla/5.0 (Android 13)','Referer':'https://anilist.co/'},body:JSON.stringify({query,variables:{id:animeId}})});if(!res.ok)throw new Error(`HTTP ${res.status}`);const data=await res.json();const media=data.data?.Media;if(!media)return null;cacheSet(key,media,animeTTL(media.status));return media;} async function sendAnimeDetail(conn,m,anime,usedPrefix){ const sessionKey=m.sender;const rawDesc=cleanText(anime.description,900);const translatedDesc=await translateToAr(rawDesc);const title=anime.title?.romaji||anime.title?.english||anime.title?.native||'ุจุฏูˆู† ุนู†ูˆุงู†';const titleEn=anime.title?.english?` (${anime.title.english})`:'';const titleNative=anime.title?.native?`\n๐Ÿ‡ฏ๐Ÿ‡ต ุงู„ุฃุตู„: ${anime.title.native}`:'';const statusMap={'FINISHED':'ู…ูƒุชู…ู„','RELEASING':'ูŠุนุฑุถ ุญุงู„ูŠุงู‹','NOT_YET_RELEASED':'ู„ู… ูŠุตุฏุฑ ุจุนุฏ','CANCELLED':'ู…ู„ุบูŠ','HIATUS':'ู…ุชูˆู‚ู ู…ุคู‚ุชุงู‹'};const formatMap={'TV':'ุชู„ูุฒูŠูˆู†','MOVIE':'ููŠู„ู…','OVA':'ุฃูˆูุง','ONA':'ุฃูˆู†ุง','SPECIAL':'ุฎุงุต','MUSIC':'ู…ูˆุณูŠู‚ูŠ'};const statusAr=statusMap[anime.status]||anime.status||'ุบูŠุฑ ู…ุนุฑูˆู';const formatAr=formatMap[anime.format]||anime.format||'';let tagsText='';if(anime.tags?.length){const filteredTags=anime.tags.filter(t=>!t.isAdult).slice(0,15);if(filteredTags.length){tagsText='\n๐Ÿท๏ธ *ุงู„ุนู„ุงู…ุงุช:*\n';for(let tag of filteredTags){const percent=tag.rank?`${tag.rank}%`:'';const transName=await translateToAr(tag.name);tagsText+=`โ€ข ${transName} ${percent?`- ${percent}`:''}\n`;await new Promise(r=>setTimeout(r,200));}}} const genresAr=anime.genres?.length?(await translateBatch(anime.genres)).join('ุŒ '):'';let airInfo='';if(anime.status==='RELEASING'){const total=anime.episodes||null;if(anime.nextAiringEpisode){const n=anime.nextAiringEpisode;const currentEp=n.episode-1;const remaining=total?total-currentEp:null;airInfo=`\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n๐ŸŸข *ู…ุณุชู…ุฑ*\n๐Ÿ“บ ุงู„ุญุงู„ูŠุฉ: *${currentEp}*${total?` / ${total}`:''}\n`;if(remaining!==null)airInfo+=`โณ ุงู„ู…ุชุจู‚ูŠุฉ: *${remaining}*\n`;airInfo+=`โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n๐Ÿ“… ุงู„ู‚ุงุฏู…ุฉ: *${n.episode}*\n๐Ÿ• ${formatAiringDate(n.airingAt)}\nโŒ› ู…ุชุจู‚ู: *${formatRemaining(n.timeUntilAiring)}*\n`;}else{airInfo=`\n๐ŸŸข *ู…ุณุชู…ุฑ โ€” ${anime.episodes||'ุŸ'} ุญู„ู‚ุฉ*\n`;}}else if(anime.status==='NOT_YET_RELEASED'){airInfo=`\nโณ *ู„ู… ูŠูุจุซ ุจุนุฏ*${anime.startDate?.year?` โ€” ${anime.startDate.year}`:''}\n`;} let replyText=`๐Ÿ“บ *${title}*${titleEn}${titleNative}\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n๐Ÿ“€ ${formatAr} | ๐Ÿ“Œ ${statusAr}\nโญ ${anime.averageScore||'โ€”'}/100 | ๐Ÿ‘ฅ ${anime.popularity?anime.popularity.toLocaleString():''}\n๐Ÿ“บ ${anime.episodes||'โ€”'} ุญู„ู‚ุฉ | โฑ๏ธ ${anime.duration?anime.duration+' ุฏ':''}\n`;if(anime.seasonYear)replyText+=`๐Ÿ“… ${anime.seasonYear} - ${anime.season?SEASON_AR[anime.season]:''}\n`;if(genresAr)replyText+=`๐ŸŽญ ${genresAr}\n`;if(anime.studios?.nodes?.length)replyText+=`๐Ÿข ${anime.studios.nodes.map(s=>s.name).join(', ')}\n`;replyText+=airInfo;replyText+=`โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n๐Ÿ“ *ุงู„ู‚ุตุฉ:*\n${translatedDesc}\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`;if(tagsText)replyText+=tagsText;replyText+=`\n๐Ÿ”— https://anilist.co/anime/${anime.id}`; const charactersMap=new Map();const staffMap=new Map();const recMap=new Map();(anime.characters?.edges||[]).forEach(e=>{if(e.node?.id)charactersMap.set(e.node.id.toString(),e);});(anime.staff?.edges||[]).forEach(e=>{if(e.node?.id)staffMap.set(e.node.id.toString(),e);});(anime.recommendations?.nodes||[]).forEach(n=>{if(n.mediaRecommendation?.id)recMap.set(n.mediaRecommendation.id.toString(),n);});global.animeSessions[sessionKey]={...global.animeSessions[sessionKey],currentAnime:anime,charactersMap,staffMap,recMap}; const buttons=[];if(charactersMap.size>0){const charRows=[];let idx=0;for(let [id,edge]of charactersMap){if(idx>=20)break;charRows.push({id:`${usedPrefix}ุงู†ู…ูŠ-ุดุฎุตูŠุฉ ${id}`,title:`${edge.node.name.full.slice(0,30)}`,description:`${edge.role||''} - ${edge.voiceActors?.[0]?.name?.full||'ุจุฏูˆู† ู…ู…ุซู„'}`});idx++;}buttons.push({name:'single_select',buttonParamsJson:JSON.stringify({title:'๐Ÿ‘ฅ ุงู„ุดุฎุตูŠุงุช',sections:[{title:`๐Ÿ‘ฅ ุดุฎุตูŠุงุช ${title.slice(0,20)}`,rows:charRows}]})});} if(staffMap.size>0){const staffRows=[];let idx=0;for(let [id,edge]of staffMap){if(idx>=20)break;staffRows.push({id:`${usedPrefix}ุงู†ู…ูŠ-ู…ูˆุธู ${id}`,title:`${edge.node.name.full.slice(0,30)}`,description:`${edge.role||''}`});idx++;}buttons.push({name:'single_select',buttonParamsJson:JSON.stringify({title:'๐ŸŽฌ ุทุงู‚ู… ุงู„ุนู…ู„',sections:[{title:`๐ŸŽฌ ุทุงู‚ู… ${title.slice(0,20)}`,rows:staffRows}]})});} buttons.push({name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'๐Ÿ’ก ุงู„ุชูˆุตูŠุงุช',id:`${usedPrefix}ุงู†ู…ูŠ-ุชูˆุตูŠุงุช`})});buttons.push({name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'โŒ ุฅุบู„ุงู‚',id:`${usedPrefix}ุงู†ู…ูŠ-ุฅุบู„ุงู‚`})}); const interactiveMessage={body:{text:replyText},footer:{text:`ุณูŠู„ุงู†ุง ุจูˆุช โ€ข ${anime.episodes||'?'} ุญู„ู‚ุฉ โ€ข ${formatAr}`},nativeFlowMessage:{buttons}};const msg=generateWAMessageFromContent(m.chat,{viewOnceMessage:{message:{interactiveMessage:proto.Message.InteractiveMessage.fromObject(interactiveMessage)}}},{quoted:m});if(anime.coverImage?.extraLarge||anime.coverImage?.large){await conn.sendMessage(m.chat,{image:{url:anime.coverImage.extraLarge||anime.coverImage.large},caption:`๐ŸŽฌ ${title}`},{quoted:m});}await conn.relayMessage(m.chat,msg.message,{messageId:msg.key.id,additionalNodes:[{tag:'biz',attrs:{},content:[{tag:'interactive',attrs:{type:'native_flow',v:'1'},content:[{tag:'native_flow',attrs:{v:'9',name:'mixed'}}]}]}]});} const ALL_GENRES=[ { code:'Act', ar:'ุฃูƒุดู†', en:'Action', type:'genre', emoji:'โš”๏ธ', group:'genres' },{ code:'Adv', ar:'ู…ุบุงู…ุฑุฉ', en:'Adventure', type:'genre', emoji:'๐Ÿ—บ๏ธ', group:'genres' }, { code:'Com', ar:'ูƒูˆู…ูŠุฏูŠุง', en:'Comedy', type:'genre', emoji:'๐Ÿ˜‚', group:'genres' },{ code:'Dra', ar:'ุฏุฑุงู…ุง', en:'Drama', type:'genre', emoji:'๐ŸŽญ', group:'genres' }, { code:'Ecc', ar:'ุฅูŠุชุดูŠ', en:'Ecchi', type:'genre', emoji:'๐Ÿ’‹', group:'genres' },{ code:'Fan', ar:'ูุงู†ุชุงุฒูŠุง', en:'Fantasy', type:'genre', emoji:'๐Ÿง™', group:'genres' }, { code:'Hor', ar:'ุฑุนุจ', en:'Horror', type:'genre', emoji:'๐Ÿ‘ป', group:'genres' },{ code:'Msh', ar:'ูุชุงุฉ ุณุญุฑูŠุฉ', en:'Mahou Shoujo', type:'genre', emoji:'โœจ', group:'genres' }, { code:'Mec', ar:'ู…ูŠูƒุง', en:'Mecha', type:'genre', emoji:'๐Ÿค–', group:'genres' },{ code:'Mus', ar:'ู…ูˆุณูŠู‚ู‰', en:'Music', type:'genre', emoji:'๐ŸŽต', group:'genres' }, { code:'Mys', ar:'ุบู…ูˆุถ', en:'Mystery', type:'genre', emoji:'๐Ÿ”', group:'genres' },{ code:'Psy', ar:'ู†ูุณูŠ', en:'Psychological', type:'genre', emoji:'๐Ÿง ', group:'genres' }, { code:'Rom', ar:'ุฑูˆู…ุงู†ุณูŠ', en:'Romance', type:'genre', emoji:'โค๏ธ', group:'genres' },{ code:'Sci', ar:'ุฎูŠุงู„ ุนู„ู…ูŠ', en:'Sci-Fi', type:'genre', emoji:'๐Ÿš€', group:'genres' }, { code:'Sol', ar:'ุดุฑูŠุญุฉ ุญูŠุงุฉ', en:'Slice of Life', type:'genre', emoji:'๐ŸŒฑ', group:'genres' },{ code:'Spo', ar:'ุฑูŠุงุถุฉ', en:'Sports', type:'genre', emoji:'๐Ÿ†', group:'genres' }, { code:'Sup', ar:'ุฎุงุฑู‚ ุทุจูŠุนูŠ', en:'Supernatural', type:'genre', emoji:'๐Ÿ‘ฝ', group:'genres' },{ code:'Thr', ar:'ุฅุซุงุฑุฉ', en:'Thriller', type:'genre', emoji:'๐Ÿ”ฅ', group:'genres' }, { code:'Isk', ar:'ุฅูŠุณูŠูƒุงูŠ', en:'Isekai', type:'tag', emoji:'๐ŸŒ', group:'themes' },{ code:'Sch', ar:'ู…ุฏุฑุณุฉ', en:'School', type:'tag', emoji:'๐Ÿซ', group:'themes' }, ]; function genreByCode(code){if(!code)return null;return ALL_GENRES.find(g=>g.code.toLowerCase()===code.toLowerCase().trim());} function parseGenreCodes(input){if(!input)return[];return input.split(/[-+,\s]+/).map(s=>s.trim()).filter(Boolean).map(c=>genreByCode(c)).filter(Boolean);} async function showAnimeMenu(conn,m,usedPrefix){ const body=`๐Ÿ”ฅ *ู†ุธุงู… ุงู„ุฃู†ู…ูŠ ุงู„ุดุงู…ู„ - Diablo Bot* ๐Ÿ”ฅ\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n๐Ÿ“š *ูƒู„ ุงู„ุฃูˆุงู…ุฑ ููŠ ุฃุฒุฑุงุฑ ุชูุงุนู„ูŠุฉ*\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n๐Ÿ”Ž ุงู„ุจุญุซ:.ุงู†ู…ูŠ + ุงุณู…\n๐Ÿ“… ุงู„ู…ูˆุงุณู…: ุชุตูุญ ูƒู„ ุณู†ุฉ ูˆู…ูˆุณู…\n๐ŸŽญ ุงู„ุชุตู†ูŠูุงุช: ุชุตู†ูŠูุงุช\n๐Ÿ–ผ๏ธ ุนุฑุถ ูƒุงู…ู„: PDF ููŠู‡ ูƒู„ ุงู„ู†ุชุงุฆุฌ\n\n๐Ÿ‘‡ *ุงุฎุชุฑ ู…ู† ุงู„ุฃุฒุฑุงุฑ*`; const sections=[ {title:'๐Ÿ”Ž ุงู„ุจุญุซ ุงู„ุฑุฆูŠุณูŠ',rows:[ {header:'ุจุญุซ ุจุงู„ุงุณู…',title:'๐Ÿ”Ž ุจุญุซ ุฃู†ู…ูŠ',description:'ู…ุซุงู„: ุงู†ู…ูŠ ู†ุงุฑูˆุชูˆ',id:`${usedPrefix}ุงู†ู…ูŠ ู†ุงุฑูˆุชูˆ`}, ]}, {title:'๐Ÿ“… ุงู„ู…ูˆุงุณู…',rows:[ {header:'ูƒู„ ุงู„ู…ูˆุงุณู…',title:'๐Ÿ“… ู…ุชุตูุญ ุงู„ู…ูˆุงุณู…',description:'1950-2027',id:`${usedPrefix}ุงู†ู…ูŠ-ู…ูˆุงุณู…`}, ]}, {title:'๐ŸŽญ ุงู„ุชุตู†ูŠูุงุช',rows:[ {header:'ูƒู„ ุงู„ุชุตู†ูŠูุงุช',title:'๐ŸŽญ ู‚ุงุฆู…ุฉ ุงู„ุชุตู†ูŠูุงุช',description:'140 ุชุตู†ูŠู',id:`${usedPrefix}ุชุตู†ูŠูุงุช`}, ]}, ]; const buttons=[ {name:'single_select',buttonParamsJson:JSON.stringify({title:'๐Ÿ“š ู‚ุงุฆู…ุฉ ุงู„ุฃูˆุงู…ุฑ ุงู„ูƒุงู…ู„ุฉ',sections})}, {name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'๐Ÿ”Ž ุจุญุซ',id:`${usedPrefix}ุงู†ู…ูŠ`})}, {name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'๐Ÿ“… ุงู„ู…ูˆุงุณู…',id:`${usedPrefix}ุงู†ู…ูŠ-ู…ูˆุงุณู…`})}, {name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'๐ŸŽญ ุงู„ุชุตู†ูŠูุงุช',id:`${usedPrefix}ุชุตู†ูŠูุงุช`})} ]; return sendInteractive(conn,m,body,'Diablo Bot',buttons); } function buildFilterSections(usedPrefix){ const now=new Date().getFullYear();const maxYear=now+2; const statusRows=[{header:'๐ŸŒ ุฅุฒุงู„ุฉ',title:'ูƒู„ ุงู„ุญุงู„ุงุช',description:'ุจุฏูˆู† ูู„ุชุฑ',id:`${usedPrefix}ุงู†ู…ูŠ-ุญุงู„ุฉ 0`},{header:'๐ŸŸข ูŠูุจุซ',title:'๐ŸŸข ู…ุณุชู…ุฑ',description:'ุงู„ุฌุงุฑูŠุฉ',id:`${usedPrefix}ุงู†ู…ูŠ-ุญุงู„ุฉ RELEASING`},{header:'โœ… ุงู†ุชู‡ู‰',title:'โœ… ู…ูƒุชู…ู„',description:'ุงู„ู…ู†ุชู‡ูŠุฉ',id:`${usedPrefix}ุงู†ู…ูŠ-ุญุงู„ุฉ FINISHED`}]; const decades={};for(let y=maxYear;y>=1950;y--){const dec=Math.floor(y/10)*10;if(!decades[dec])decades[dec]=[];decades[dec].push(y);} const sections=[{title:'๐Ÿ“Œ ุงู„ุญุงู„ุฉ',rows:statusRows}]; for(const [dec,years]of Object.entries(decades).sort((a,b)=>b[0]-a[0])){sections.push({title:`๐Ÿ“… ${dec}s`,rows:years.map(y=>({header:`ุชุตููŠุฉ ุจุณู†ุฉ ${y}`,title:`${y}`,description:`ุฃู†ู…ูŠุงุช ${y}`,id:`${usedPrefix}ุงู†ู…ูŠ-ุณู†ุฉ ${y}`}))});} return sections; } async function performSearch(conn,m,usedPrefix,waitMsg=null){ const sessionKey=m.sender;const session=global.animeSessions[sessionKey];if(!session?.searchQuery)return conn.sendMessage(m.chat,{text:'โŒ ู„ุง ุชูˆุฌุฏ ุฌู„ุณุฉ.'},{quoted:m}); if(waitMsg){try{await conn.sendMessage(m.chat,{delete:waitMsg.key});}catch{}} const page=session.searchPage||1;const cacheKey=`search:${session.searchQuery}:${session.searchYear||'all'}:${session.searchStatus||'all'}:${page}`;let pageObj=cacheGet(cacheKey); if(!pageObj){ const query=`query ($search: String, $page: Int, $status: MediaStatus, $year: Int) { Page(page: $page, perPage: 20) { pageInfo { hasNextPage lastPage total currentPage } media(search: $search, type: ANIME, status: $status, seasonYear: $year, sort: POPULARITY_DESC) { id title { romaji english native } coverImage { large } averageScore episodes status seasonYear format } } }`; const variables={search:session.searchQuery,page};if(session.searchStatus)variables.status=session.searchStatus;if(session.searchYear)variables.year=session.searchYear; try{const res=await fetch('https://graphql.anilist.co/',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({query,variables})});const data=await res.json();pageObj=data.data?.Page;cacheSet(cacheKey,pageObj,TTL.SEARCH_RESULTS);}catch(e){return conn.sendMessage(m.chat,{text:`โŒ ${e.message}`},{quoted:m});} } const mediaList=pageObj?.media||[];const pageInfo=pageObj?.pageInfo||{};session.results=mediaList;session.pageInfo=pageInfo; if(!mediaList.length){return conn.sendMessage(m.chat,{text:`โŒ ู„ุง ุชูˆุฌุฏ ู†ุชุงุฆุฌ`},{quoted:m});} const startIdx=(page-1)*20;const rows=mediaList.map((anime,index)=>{const title=anime.title?.english||anime.title?.romaji||anime.title?.native||'ุจุฏูˆู† ุนู†ูˆุงู†';return{header:`${STATUS_MAP_AR[anime.status]||'โ€”'}`,title:`${startIdx+index+1}. ${shortTitle(title,3)}`,description:`โญ${anime.averageScore?(anime.averageScore/10).toFixed(1):'?'} ยท ๐Ÿ“บ${anime.episodes||'?'}`,id:`${usedPrefix}ุงู†ู…ูŠ-ุงุฎุชูŠุงุฑ ${anime.id}`};}); const sections=[];for(let i=0;i1)buttons.push({name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'โ—€๏ธ ุงู„ุณุงุจู‚',id:`${usedPrefix}ุงู†ู…ูŠ-ุณุงุจู‚`})}); if(pageInfo.hasNextPage)buttons.push({name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'โ–ถ๏ธ ุงู„ุชุงู„ูŠ',id:`${usedPrefix}ุงู†ู…ูŠ-ุชุงู„ูŠ`})}); let body=`๐Ÿ”Ž *ุจุญุซ:* "${session.searchQuery}"\n๐Ÿ“„ ุตูุญุฉ ${pageInfo.currentPage||page} ู…ู† ${pageInfo.lastPage||1}\n๐Ÿ“Š ุงู„ุฅุฌู…ุงู„ูŠ: ${pageInfo.total||mediaList.length}\n\n๐Ÿ“Œ ุงุฎุชุฑ ุฃู†ู…ูŠ ุฃูˆ ุญู…ู„ PDF ู„ูƒู„ ุงู„ู‚ุงุฆู…ุฉ:`;return sendInteractive(conn,m,body,'ุณูŠู„ุงู†ุง ุจูˆุช โ€ข ุฃู†ู…ูŠ',buttons); } function buildSeasonYearSections(usedPrefix){ const nowYear=currentYear();const curSeason=currentSeason();const maxYear=nowYear+2;const minYear=1950;const decades={}; for(let y=maxYear;y>=minYear;y--){const dec=Math.floor(y/10)*10;if(!decades[dec])decades[dec]=[];decades[dec].push(y);} const sections=[];for(const [dec,years]of Object.entries(decades).sort((a,b)=>b[0]-a[0])){sections.push({title:`๐Ÿ“… ${dec}s`,rows:years.map(y=>({header:`ุงุฎุชุฑ ู…ูˆุณู… ${y}`,title:`${y}`,description:'ุดุชุงุก ยท ุฑุจูŠุน ยท ุตูŠู ยท ุฎุฑูŠู',id:`${usedPrefix}ุงู†ู…ูŠ-ู…ูˆุงุณู… ${y}`}))});}return sections; } async function showSeasonYears(conn,m,usedPrefix){ const sections=buildSeasonYearSections(usedPrefix); const buttons=[{name:'single_select',buttonParamsJson:JSON.stringify({title:'๐Ÿ“… ุงุฎุชุฑ ุงู„ุณู†ุฉ',sections})},{name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'โŒ ุฅุบู„ุงู‚',id:`${usedPrefix}ุงู†ู…ูŠ-ุฅุบู„ุงู‚`})}]; return sendInteractive(conn,m,`๐Ÿ“… *ู…ุชุตูุญ ุงู„ู…ูˆุงุณู…*`,'ุณูŠู„ุงู†ุง ุจูˆุช โ€ข ู…ูˆุงุณู…',buttons); } async function showSeasonPickerForYear(conn,m,usedPrefix,year){ const seasons=[{id:'WINTER',ar:'ุดุชุงุก'},{id:'SPRING',ar:'ุฑุจูŠุน'},{id:'SUMMER',ar:'ุตูŠู'},{id:'FALL',ar:'ุฎุฑูŠู'}]; const rows=seasons.map(s=>({header:`ุฃู†ู…ูŠุงุช ${year}`,title:`${SEASON_EMOJI[s.id]} ${s.ar}`,description:s.id,id:`${usedPrefix}ุงู†ู…ูŠ-ู…ูˆุณู… ${year} ${s.id}`})); const buttons=[{name:'single_select',buttonParamsJson:JSON.stringify({title:`๐Ÿ“… ู…ูˆุงุณู… ${year}`,sections:[{title:`๐Ÿ“… ู…ูˆุงุณู… ุณู†ุฉ ${year}`,rows}]})},{name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'โ†ฉ๏ธ ุฑุฌูˆุน',id:`${usedPrefix}ุงู†ู…ูŠ-ู…ูˆุงุณู…`})}]; return sendInteractive(conn,m,`๐Ÿ“… *ุณู†ุฉ ${year}* - ุงุฎุชุฑ ุงู„ู…ูˆุณู…:`, 'ุณูŠู„ุงู†ุง ุจูˆุช', buttons); } async function performSeasonSearch(conn,m,usedPrefix,year,season,waitMsg=null){ const sessionKey=m.sender;const session=global.animeSessions[sessionKey]||{};global.animeSessions[sessionKey]=session; if(waitMsg){try{await conn.sendMessage(m.chat,{delete:waitMsg.key});}catch{}} const page=session.seasonPage||1;const cacheKey=`season:${year}:${season}:${page}`;let pageObj=cacheGet(cacheKey); if(!pageObj){ const query=`query ($year: Int, $season: MediaSeason, $page: Int) { Page(page: $page, perPage: 30) { pageInfo { hasNextPage lastPage total currentPage } media(seasonYear: $year, season: $season, type: ANIME, sort: POPULARITY_DESC, isAdult: false) { id title { romaji english native } coverImage { large } averageScore episodes status format season seasonYear } } }`; try{const res=await fetch('https://graphql.anilist.co/',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({query,variables:{year,season,page}})});const data=await res.json();pageObj=data.data?.Page;cacheSet(cacheKey,pageObj,seasonTTL(year));}catch(e){return conn.sendMessage(m.chat,{text:`โŒ ${e.message}`},{quoted:m});} } const mediaList=pageObj?.media||[];const pageInfo=pageObj?.pageInfo||{};session.seasonYear=year;session.seasonName=season;session.seasonResults=mediaList;session.seasonPageInfo=pageInfo; if(!mediaList.length)return conn.sendMessage(m.chat,{text:'โŒ ู„ุง ุชูˆุฌุฏ ุฃู†ู…ูŠุงุช'},{quoted:m}); const startIdx=(page-1)*30;const rows=mediaList.map((anime,index)=>{const title=anime.title?.english||anime.title?.romaji||'ุจุฏูˆู† ุนู†ูˆุงู†';return{header:`${STATUS_MAP_AR[anime.status]||'โ€”'}`,title:`${startIdx+index+1}. ${shortTitle(title,3)}`,description:`โญ${anime.averageScore?(anime.averageScore/10).toFixed(1):'?'}`,id:`${usedPrefix}ุงู†ู…ูŠ-ุงุฎุชูŠุงุฑ ${anime.id}`};}); const sections=[];for(let i=0;i1)buttons.push({name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'โ—€๏ธ ุงู„ุณุงุจู‚',id:`${usedPrefix}ุงู†ู…ูŠ-ู…ูˆุณู…-ุณุงุจู‚`})}); if(pageInfo.hasNextPage)buttons.push({name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'โ–ถ๏ธ ุงู„ุชุงู„ูŠ',id:`${usedPrefix}ุงู†ู…ูŠ-ู…ูˆุณู…-ุชุงู„ูŠ`})}); return sendInteractive(conn,m,`๐Ÿ“… *${SEASON_AR[season]} ${year}* - ${pageInfo.total} ุงู†ู…ูŠ`, 'ุณูŠู„ุงู†ุง', buttons); } async function showGenresList(conn,m,usedPrefix){ const sections=[{title:'๐Ÿ“š ุชุตู†ูŠูุงุช',rows:ALL_GENRES.map(g=>({header:`ูƒูˆุฏ: ${g.code}`,title:`${g.emoji} ${g.ar}`,description:g.en,id:`${usedPrefix}ุชุตู†ูŠู ${g.code}`}))}] const buttons=[{name:'single_select',buttonParamsJson:JSON.stringify({title:'๐ŸŽญ ุงุฎุชุฑ ุชุตู†ูŠูุงู‹',sections})},{name:'quick_reply',buttonParamsJson:JSON.stringify({display_text:'โŒ ุฅุบู„ุงู‚',id:`${usedPrefix}ุงู†ู…ูŠ-ุฅุบู„ุงู‚`})}]; return sendInteractive(conn,m,`๐ŸŽญ *ูƒู„ ุงู„ุชุตู†ูŠูุงุช* - ${ALL_GENRES.length}`, 'ุณูŠู„ุงู†ุง', buttons); } async function fetchGenrePage(genre,page=1){ const cacheKey=`genre:${genre.code}:${page}`;const cached=cacheGet(cacheKey);if(cached)return cached; const query=`query ($genre: String, $tag: String, $page: Int) { Page(page: $page, perPage: 30) { pageInfo { hasNextPage lastPage total currentPage } media(genre: $genre, tag: $tag, type: ANIME, sort: POPULARITY_DESC, isAdult: false) { id title { romaji english native } coverImage { large } averageScore episodes status format seasonYear startDate { year } } } }`; const variables={page};if(genre.type==='genre')variables.genre=genre.en;else variables.tag=genre.en; const res=await fetch('https://graphql.anilist.co/',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({query,variables})}); const data=await res.json();const pageObj=data.data?.Page;cacheSet(cacheKey,pageObj,TTL.GENRE_SEARCH);return pageObj; } async function performGenreSearch(conn,m,usedPrefix,codesInput,waitMsg=null){ const sessionKey=m.sender;const session=global.animeSessions[sessionKey]||{};global.animeSessions[sessionKey]=session; if(waitMsg){try{await conn.sendMessage(m.chat,{delete:waitMsg.key});}catch{}} const genres=parseGenreCodes(codesInput);if(!genres.length)return conn.sendMessage(m.chat,{text:'โŒ ูƒูˆุฏ ุบูŠุฑ ุตุงู„ุญ'},{quoted:m}); const genre=genres[0];const page=session.genrePage||1;let pageObj;try{pageObj=await fetchGenrePage(genre,page);}catch(e){return conn.sendMessage(m.chat,{text:`โŒ ${e.message}`},{quoted:m});} const mediaList=pageObj?.media||[];const pageInfo=pageObj?.pageInfo||{};session.genreResults=mediaList;session.genrePageInfo=pageInfo;session.genreCode=genre.code; const rows=mediaList.map((anime,index)=>{const title=anime.title?.english||anime.title?.romaji||'ุจุฏูˆู† ุนู†ูˆุงู†';return{header:`${STATUS_MAP_AR[anime.status]||'โ€”'}`,title:`${index+1}. ${shortTitle(title,3)}`,description:`โญ${anime.averageScore?(anime.averageScore/10).toFixed(1):'?'}`,id:`${usedPrefix}ุงู†ู…ูŠ-ุงุฎุชูŠุงุฑ ${anime.id}`};}); const sections=[];for(let i=0;i{ if(command==='ุงู†ู…ูŠุงุช'||command==='animes'){return showAnimeMenu(conn,m,usedPrefix);} if(command==='ุงู†ู…ูŠ-ุนุฑุถ-ูƒุงู…ู„'||command==='anime-fullview'){return performFullView(conn,m,usedPrefix);} if(command==='ุงู†ู…ูŠ-ุงุฎุชูŠุงุฑ'||command==='anime-choice'){const session=global.animeSessions[m.sender];if(!session)return conn.sendMessage(m.chat,{text:`โŒ ู„ุง ุชูˆุฌุฏ ุฌู„ุณุฉ.`},{quoted:m});const match=(m.text||'').match(/\d+/);if(!match)return conn.sendMessage(m.chat,{text:`โŒ ู…ุนุฑู‘ู ุบูŠุฑ ุตุงู„ุญ.`},{quoted:m});const waitMsg=await conn.sendMessage(m.chat,{text:`โณ ุฌุงุฑูŠ ุงู„ุชุญู…ูŠู„...`},{quoted:m});try{const anime=await fetchFullDetails(parseInt(match[0]));if(!anime)throw new Error('ู„ุง ุชูˆุฌุฏ ุชูุงุตูŠู„');await conn.sendMessage(m.chat,{delete:waitMsg.key});await sendAnimeDetail(conn,m,anime,usedPrefix);}catch(e){await conn.sendMessage(m.chat,{text:`โŒ ${e.message}`},{quoted:m});}return;} if(command==='ุงู†ู…ูŠ-ุณู†ุฉ'||command==='anime-year'){const session=global.animeSessions[m.sender];const y=parseInt(args[0]);if(args[0]==='0')session.searchYear=null;else session.searchYear=y;session.searchPage=1;return performSearch(conn,m,usedPrefix);} if(command==='ุงู†ู…ูŠ-ุญุงู„ุฉ'||command==='anime-status'){const session=global.animeSessions[m.sender];if(args[0]==='0')session.searchStatus=null;else session.searchStatus=args[0];session.searchPage=1;return performSearch(conn,m,usedPrefix);} if(command==='ุงู†ู…ูŠ-ุชุงู„ูŠ'||command==='anime-next'){const session=global.animeSessions[m.sender];session.searchPage++;return performSearch(conn,m,usedPrefix);} if(command==='ุงู†ู…ูŠ-ุณุงุจู‚'||command==='anime-prev'){const session=global.animeSessions[m.sender];if(session.searchPage>1)session.searchPage--;return performSearch(conn,m,usedPrefix);} if(command==='ุงู†ู…ูŠ-ู…ูˆุงุณู…'||command==='anime-seasons'){if(!args[0])return showSeasonYears(conn,m,usedPrefix);return showSeasonPickerForYear(conn,m,usedPrefix,parseInt(args[0]));} if(command==='ุงู†ู…ูŠ-ู…ูˆุณู…'||command==='anime-season'){global.animeSessions[m.sender]=global.animeSessions[m.sender]||{};global.animeSessions[m.sender].seasonPage=1;const waitMsg=await conn.sendMessage(m.chat,{text:`โณ ุฌุงุฑูŠ ุงู„ุฌู„ุจ...`},{quoted:m});return performSeasonSearch(conn,m,usedPrefix,parseInt(args[0]),args[1],waitMsg);} if(command==='ุงู†ู…ูŠ-ู…ูˆุณู…-ุชุงู„ูŠ'){const s=global.animeSessions[m.sender];s.seasonPage++;return performSeasonSearch(conn,m,usedPrefix,s.seasonYear,s.seasonName);} if(command==='ุงู†ู…ูŠ-ู…ูˆุณู…-ุณุงุจู‚'){const s=global.animeSessions[m.sender];if(s.seasonPage>1)s.seasonPage--;return performSeasonSearch(conn,m,usedPrefix,s.seasonYear,s.seasonName);} if(command==='ุชุตู†ูŠูุงุช'||command==='genres'){if(!args[0])return showGenresList(conn,m,usedPrefix);global.animeSessions[m.sender]=global.animeSessions[m.sender]||{};global.animeSessions[m.sender].genrePage=1;const waitMsg=await conn.sendMessage(m.chat,{text:`โณ ุฌุงุฑูŠ ุงู„ุฌู„ุจ...`},{quoted:m});return performGenreSearch(conn,m,usedPrefix,args.join(' '),waitMsg);} if(command==='ุชุตู†ูŠู'||command==='genre'){global.animeSessions[m.sender]=global.animeSessions[m.sender]||{};global.animeSessions[m.sender].genrePage=1;const waitMsg=await conn.sendMessage(m.chat,{text:`โณ ุฌุงุฑูŠ ุงู„ุฌู„ุจ...`},{quoted:m});return performGenreSearch(conn,m,usedPrefix,args[0],waitMsg);} if(command==='ุงู†ู…ูŠ'||command==='anime'){if(!args[0])return conn.sendMessage(m.chat,{text:`โœ๏ธ ุงูƒุชุจ ุงุณู… ุงู„ุฃู†ู…ูŠ\nู…ุซุงู„: ${usedPrefix}ุงู†ู…ูŠ ู†ุงุฑูˆุชูˆ`},{quoted:m});global.animeSessions[m.sender]={searchQuery:args.join(' '),searchYear:null,searchStatus:null,searchPage:1};const waitMsg=await conn.sendMessage(m.chat,{text:`โณ ุฌุงุฑูŠ ุงู„ุจุญุซ...`},{quoted:m});return performSearch(conn,m,usedPrefix,waitMsg);} if(command==='ุงู†ู…ูŠ-ุฅุบู„ุงู‚'||command==='anime-close'){delete global.animeSessions[m.sender];return conn.sendMessage(m.chat,{text:`โœ… ุชู… ุงู„ุฅุบู„ุงู‚.`},{quoted:m});} }; handler.help=['ุงู†ู…ูŠ <ุงุณู…>','ุงู†ู…ูŠุงุช','ุชุตู†ูŠูุงุช','ุงู†ู…ูŠ-ุนุฑุถ-ูƒุงู…ู„']; handler.tags=['search']; handler.command=[ 'ุงู†ู…ูŠุงุช','animes','ุงู†ู…ูŠ-ุนุฑุถ-ูƒุงู…ู„','anime-fullview', 'ุงู†ู…ูŠ','anime','ุงู†ู…ูŠ-ุงุฎุชูŠุงุฑ','anime-choice', 'ุงู†ู…ูŠ-ุณู†ุฉ','anime-year','ุงู†ู…ูŠ-ุญุงู„ุฉ','anime-status','ุงู†ู…ูŠ-ุชุงู„ูŠ','anime-next','ุงู†ู…ูŠ-ุณุงุจู‚','anime-prev', 'ุงู†ู…ูŠ-ู…ูˆุงุณู…','anime-seasons','ุงู†ู…ูŠ-ู…ูˆุณู…','anime-season','ุงู†ู…ูŠ-ู…ูˆุณู…-ุชุงู„ูŠ','ุงู†ู…ูŠ-ู…ูˆุณู…-ุณุงุจู‚', 'ุชุตู†ูŠูุงุช','genres','ุชุตู†ูŠู','genre','ุงู†ู…ูŠ-ุฅุบู„ุงู‚','anime-close' ]; export default handler;