// code from goatstore // api: https://store.agi.bd /** * country.js — GoatBot command (Premium canvas card, কোনো API key লাগে না) * Author : Zihad * ডাটা : mledoze/countries (jsDelivr/GitHub) + জনসংখ্যা: World Bank + পতাকা: flagcdn * দরকার : npm i canvas axios fs-extra (GoatBot-এ সাধারণত আগে থেকেই থাকে) */ const axios = require("axios"); const fs = require("fs"); const path = require("path"); let canvasLib = null; try { canvasLib = require("canvas"); } catch (e) { /* canvas না থাকলে টেক্সট মোডে চলবে */ } const AUTHOR = "Zihad"; const DATA_URLS = [ "https://cdn.jsdelivr.net/gh/mledoze/countries@master/dist/countries.json", "https://fastly.jsdelivr.net/gh/mledoze/countries@master/dist/countries.json", "https://raw.githubusercontent.com/mledoze/countries/master/dist/countries.json" ]; // বাংলা নাম -> ইংরেজি (আরও যোগ করতে পারেন) const BN = { "বাংলাদেশ": "Bangladesh", "ভারত": "India", "পাকিস্তান": "Pakistan", "নেপাল": "Nepal", "ভুটান": "Bhutan", "শ্রীলঙ্কা": "Sri Lanka", "মালদ্বীপ": "Maldives", "চীন": "China", "জাপান": "Japan", "আমেরিকা": "United States", "যুক্তরাষ্ট্র": "United States", "যুক্তরাজ্য": "United Kingdom", "ব্রিটেন": "United Kingdom", "সৌদি আরব": "Saudi Arabia", "কুয়েত": "Kuwait", "কাতার": "Qatar", "ওমান": "Oman", "দুবাই": "United Arab Emirates", "আমিরাত": "United Arab Emirates", "মালয়েশিয়া": "Malaysia", "সিঙ্গাপুর": "Singapore", "থাইল্যান্ড": "Thailand", "মিয়ানমার": "Myanmar", "ইন্দোনেশিয়া": "Indonesia", "তুরস্ক": "Turkey", "ইরান": "Iran", "ইরাক": "Iraq", "মিশর": "Egypt", "রাশিয়া": "Russia", "ফ্রান্স": "France", "জার্মানি": "Germany", "ইতালি": "Italy", "স্পেন": "Spain", "কানাডা": "Canada", "অস্ট্রেলিয়া": "Australia", "ব্রাজিল": "Brazil", "আফগানিস্তান": "Afghanistan", "দক্ষিণ কোরিয়া": "South Korea", "উত্তর কোরিয়া": "North Korea", "ফিলিস্তিন": "Palestine" }; /* ───────────────────────── ডাটা ───────────────────────── */ let CACHE = null; async function loadData() { if (CACHE) return CACHE; let lastErr; for (const url of DATA_URLS) { try { const res = await axios.get(url, { timeout: 20000, headers: { "User-Agent": "GoatBot-country" } }); let d = res.data; if (typeof d === "string") d = JSON.parse(d); if (Array.isArray(d) && d.length > 100) return (CACHE = d); lastErr = new Error("Unexpected data format from " + url); } catch (e) { lastErr = e; } } throw lastErr; } const norm = s => String(s || "").toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^\p{L}\p{N} ]/gu, "").trim(); function candidates(c) { const out = [c.name.common, c.name.official, c.cca2, c.cca3, c.cioc, ...(c.altSpellings || [])]; for (const t of Object.values(c.translations || {})) out.push(t.common, t.official); return out.filter(Boolean).map(norm); } function findCountry(list, input) { const q = norm(BN[input.trim()] || input); if (!q) return null; const exact = list.filter(c => candidates(c).includes(q)); if (exact.length) return exact.sort((a, b) => (b.independent === true) - (a.independent === true))[0]; const starts = list.filter(c => candidates(c).some(n => n.startsWith(q))); const pool = starts.length ? starts : list.filter(c => candidates(c).some(n => n.length > 3 && n.includes(q))); if (!pool.length) return null; return pool.sort((a, b) => ((b.independent === true) - (a.independent === true)) || (a.name.common.length - b.name.common.length) )[0]; } async function getPopulation(cca2) { try { const { data } = await axios.get( `https://api.worldbank.org/v2/country/${cca2}/indicator/SP.POP.TOTL?format=json&mrnev=1`, { timeout: 10000 } ); const row = Array.isArray(data) && Array.isArray(data[1]) ? data[1][0] : null; return row && row.value ? { value: Number(row.value), year: row.date } : null; } catch (e) { return null; } } function buildInfo(c, list, pop) { const byCode = {}; list.forEach(x => (byCode[x.cca3] = x.name.common)); const curr = c.currencies ? Object.entries(c.currencies) : []; return { name: c.name.common, official: c.name.official, cca2: c.cca2, cca3: c.cca3, region: c.region || "N/A", subregion: c.subregion || "N/A", capital: c.capital && c.capital.length ? c.capital.join(", ") : "N/A", population: pop ? pop.value.toLocaleString("en-US") : "N/A", popYear: pop ? String(pop.year) : "", area: c.area ? c.area.toLocaleString("en-US") + " km²" : "N/A", currency: curr.length ? curr.map(([k, v]) => `${v.name} (${v.symbol || k})`).join(", ") : "N/A", languages: c.languages ? Object.values(c.languages).join(", ") : "N/A", calling: c.idd && c.idd.root ? c.idd.root + (c.idd.suffixes && c.idd.suffixes.length === 1 ? c.idd.suffixes[0] : "") : "N/A", tld: c.tld && c.tld.length ? c.tld.join(", ") : "N/A", borders: c.borders && c.borders.length ? `${c.borders.length} • ` + c.borders.map(b => byCode[b] || b).join(", ") : "None", independent: c.independent === true, unMember: c.unMember === true, landlocked: c.landlocked === true, flagEmoji: c.flag || "🏳️" }; } /* ───────────────────── ক্যানভাস ডিজাইন ───────────────────── */ const rgba = (c, a) => `rgba(${c[0]},${c[1]},${c[2]},${a})`; const mix = (c, t, k) => c.map((v, i) => Math.round(v + (t[i] - v) * k)); function rr(ctx, x, y, w, h, r) { ctx.beginPath(); ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r); ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.closePath(); } function spaced(ctx, text, x, y, sp) { for (const ch of text) { ctx.fillText(ch, x, y); x += ctx.measureText(ch).width + sp; } return x; } function spacedWidth(ctx, text, sp) { let w = 0; for (const ch of text) w += ctx.measureText(ch).width + sp; return w - sp; } function fitFont(ctx, text, weight, maxSize, minSize, maxW) { let s = maxSize; for (; s > minSize; s -= 2) { ctx.font = `${weight} ${s}px sans-serif`; if (ctx.measureText(text).width <= maxW) return s; } ctx.font = `${weight} ${minSize}px sans-serif`; return minSize; } function wrap(ctx, text, maxW, maxLines) { const words = String(text).split(" "); const lines = []; let cur = ""; for (const w of words) { const t = cur ? cur + " " + w : w; if (ctx.measureText(t).width <= maxW) cur = t; else { if (cur) lines.push(cur); cur = w; } } if (cur) lines.push(cur); if (lines.length > maxLines) { const keep = lines.slice(0, maxLines); let last = keep[maxLines - 1]; while (last.length > 1 && ctx.measureText(last + "…").width > maxW) last = last.slice(0, -1); keep[maxLines - 1] = last + "…"; return keep; } return lines; } // পতাকা থেকে দুটো প্রধান রঙ বের করে function palette(createCanvas, img) { const def = [[245, 197, 66], [124, 92, 255]]; if (!img) return def; try { const s = createCanvas(32, 20), g = s.getContext("2d"); g.drawImage(img, 0, 0, 32, 20); const d = g.getImageData(0, 0, 32, 20).data; const buckets = {}; for (let i = 0; i < d.length; i += 4) { const r = d[i], gg = d[i + 1], b = d[i + 2]; const mx = Math.max(r, gg, b), mn = Math.min(r, gg, b); const lum = (r + gg + b) / 3, sat = (mx - mn) / (mx || 1); if (lum < 35 || lum > 235 || sat < 0.35) continue; const k = (r >> 6) + "," + (gg >> 6) + "," + (b >> 6); const o = buckets[k] || (buckets[k] = { n: 0, r: 0, g: 0, b: 0 }); o.n++; o.r += r; o.g += gg; o.b += b; } const arr = Object.values(buckets).sort((a, b) => b.n - a.n).map(o => [o.r / o.n, o.g / o.n, o.b / o.n].map(Math.round)); const lift = c => (c[0] + c[1] + c[2] < 330 ? mix(c, [255, 255, 255], 0.35) : c); const a1 = arr[0] ? lift(arr[0]) : def[0]; let a2 = arr.find(c => Math.abs(c[0] - a1[0]) + Math.abs(c[1] - a1[1]) + Math.abs(c[2] - a1[2]) > 120); a2 = a2 ? lift(a2) : mix(def[1], a1, 0.25); return [a1, a2]; } catch (e) { return def; } } function drawCard(createCanvas, flagImg, info) { const W = 1280, H = 760; const cv = createCanvas(W, H); const ctx = cv.getContext("2d"); const [A1, A2] = palette(createCanvas, flagImg); const WHITE = [255, 255, 255]; // ── ব্যাকগ্রাউন্ড ── ctx.fillStyle = "#070914"; ctx.fillRect(0, 0, W, H); if (flagImg) { const t1 = createCanvas(16, 10); t1.getContext("2d").drawImage(flagImg, 0, 0, 16, 10); const t2 = createCanvas(160, 95); const g2 = t2.getContext("2d"); g2.imageSmoothingEnabled = true; g2.drawImage(t1, 0, 0, 160, 95); ctx.imageSmoothingEnabled = true; ctx.globalAlpha = 0.6; ctx.drawImage(t2, 0, 0, W, H); ctx.globalAlpha = 1; } let g = ctx.createLinearGradient(0, 0, W, H); g.addColorStop(0, "rgba(7,9,20,0.80)"); g.addColorStop(1, "rgba(7,9,20,0.94)"); ctx.fillStyle = g; ctx.fillRect(0, 0, W, H); g = ctx.createRadialGradient(220, 200, 0, 220, 200, 560); g.addColorStop(0, rgba(A1, 0.30)); g.addColorStop(1, rgba(A1, 0)); ctx.fillStyle = g; ctx.fillRect(0, 0, W, H); g = ctx.createRadialGradient(1100, 650, 0, 1100, 650, 560); g.addColorStop(0, rgba(A2, 0.26)); g.addColorStop(1, rgba(A2, 0)); ctx.fillStyle = g; ctx.fillRect(0, 0, W, H); ctx.fillStyle = "rgba(255,255,255,0.045)"; for (let x = 20; x < W; x += 30) for (let y = 20; y < H; y += 30) ctx.fillRect(x, y, 2, 2); // ── মূল গ্লাস প্যানেল ── rr(ctx, 28, 28, W - 56, H - 56, 36); ctx.fillStyle = "rgba(255,255,255,0.045)"; ctx.fill(); g = ctx.createLinearGradient(28, 28, W - 28, H - 28); g.addColorStop(0, rgba(A1, 0.85)); g.addColorStop(0.5, "rgba(255,255,255,0.12)"); g.addColorStop(1, rgba(A2, 0.85)); ctx.lineWidth = 2.5; ctx.strokeStyle = g; ctx.stroke(); // ── হেডার ট্যাগ ── rr(ctx, 70, 64, 214, 36, 18); ctx.fillStyle = rgba(A1, 0.16); ctx.fill(); ctx.lineWidth = 1.5; ctx.strokeStyle = rgba(A1, 0.8); ctx.stroke(); ctx.fillStyle = rgba(mix(A1, WHITE, 0.55), 1); ctx.font = "bold 14px sans-serif"; spaced(ctx, "COUNTRY INFO", 92, 87, 2.5); // ── পতাকা ── const FX = 70, FY = 140, FW = 400; let fh = 270, fw = FW; if (flagImg) { fh = Math.round(FW * flagImg.height / flagImg.width); if (fh > 290) { fh = 290; fw = Math.round(fh * flagImg.width / flagImg.height); } } const fx = FX + Math.round((FW - fw) / 2); ctx.save(); ctx.shadowColor = rgba(A1, 0.75); ctx.shadowBlur = 55; rr(ctx, fx, FY, fw, fh, 22); ctx.fillStyle = "#101426"; ctx.fill(); ctx.restore(); if (flagImg) { ctx.save(); rr(ctx, fx, FY, fw, fh, 22); ctx.clip(); ctx.drawImage(flagImg, fx, FY, fw, fh); const sh = ctx.createLinearGradient(fx, FY, fx + fw, FY + fh); sh.addColorStop(0, "rgba(255,255,255,0.22)"); sh.addColorStop(0.45, "rgba(255,255,255,0)"); sh.addColorStop(1, "rgba(0,0,0,0.22)"); ctx.fillStyle = sh; ctx.fillRect(fx, FY, fw, fh); ctx.restore(); } else { ctx.fillStyle = "rgba(255,255,255,0.8)"; ctx.font = "bold 90px sans-serif"; ctx.textAlign = "center"; ctx.fillText(info.cca2, fx + fw / 2, FY + fh / 2 + 30); ctx.textAlign = "left"; } rr(ctx, fx, FY, fw, fh, 22); ctx.lineWidth = 3; ctx.strokeStyle = "rgba(255,255,255,0.4)"; ctx.stroke(); // ── ব্যাজ ── const pill = (x, y, text, col) => { ctx.font = "bold 18px sans-serif"; const w = Math.min(ctx.measureText(text).width + 40, 400); rr(ctx, x, y, w, 42, 21); ctx.fillStyle = rgba(col, 0.2); ctx.fill(); ctx.lineWidth = 1.5; ctx.strokeStyle = rgba(col, 0.75); ctx.stroke(); ctx.fillStyle = "#ffffff"; ctx.fillText(text, x + 20, y + 28); return w; }; const py = FY + fh + 34; pill(70, py, `${info.cca2} • ${info.cca3}`, A1); pill(70, py + 54, info.region, A2); // ── স্ট্যাটাস লাইন ── const flags = [ ["Independent", info.independent], ["UN Member", info.unMember], ["Landlocked", info.landlocked] ]; let sy = py + 134; flags.forEach(([label, on]) => { ctx.beginPath(); ctx.arc(84, sy - 6, 7, 0, Math.PI * 2); ctx.fillStyle = on ? "#3ddc97" : "rgba(255,255,255,0.22)"; ctx.fill(); ctx.fillStyle = on ? "#ffffff" : "rgba(255,255,255,0.55)"; ctx.font = "600 19px sans-serif"; ctx.fillText(`${label}: ${on ? "Yes" : "No"}`, 106, sy); sy += 34; }); // ── নাম ── const RX = 520, RW = 700; ctx.fillStyle = "#ffffff"; const ns = fitFont(ctx, info.name, "bold", 84, 36, RW); ctx.shadowColor = rgba(A1, 0.5); ctx.shadowBlur = 24; ctx.fillText(info.name, RX, 128 + ns * 0.75); ctx.shadowBlur = 0; ctx.shadowColor = "transparent"; ctx.fillStyle = "rgba(255,255,255,0.62)"; fitFont(ctx, info.official, "500", 22, 13, RW); ctx.fillText(info.official, RX, 128 + ns * 0.75 + 36); const dy = 128 + ns * 0.75 + 58; g = ctx.createLinearGradient(RX, 0, RX + RW, 0); g.addColorStop(0, rgba(A1, 1)); g.addColorStop(0.6, rgba(A2, 0.7)); g.addColorStop(1, rgba(A2, 0)); ctx.fillStyle = g; rr(ctx, RX, dy, RW, 4, 2); ctx.fill(); // ── স্ট্যাট কার্ড ── const items = [ ["CAPITAL", info.capital], ["POPULATION" + (info.popYear ? " • " + info.popYear : ""), info.population], ["AREA", info.area], ["CURRENCY", info.currency], ["LANGUAGES", info.languages], ["CALLING CODE", info.calling], ["SUBREGION", info.subregion], ["DOMAIN", info.tld], ["BORDERS", info.borders] ]; const CW = 220, CH = 112, GAP = 18; const top = Math.max(dy + 28, 286); const rowStep = Math.min(CH + 16, Math.floor((H - 100 - top) / 3)); const ch = rowStep - 16; items.forEach(([label, value], i) => { const col = i % 3, row = Math.floor(i / 3); const x = RX + col * (CW + GAP); const y = top + row * rowStep; rr(ctx, x, y, CW, ch, 20); ctx.fillStyle = "rgba(255,255,255,0.065)"; ctx.fill(); ctx.lineWidth = 1.5; ctx.strokeStyle = "rgba(255,255,255,0.13)"; ctx.stroke(); const bar = ctx.createLinearGradient(0, y + 16, 0, y + 42); bar.addColorStop(0, rgba(A1, 1)); bar.addColorStop(1, rgba(A2, 1)); ctx.fillStyle = bar; rr(ctx, x + 16, y + 17, 4, 22, 2); ctx.fill(); ctx.fillStyle = "rgba(255,255,255,0.58)"; ctx.font = "bold 12px sans-serif"; spaced(ctx, label, x + 30, y + 33, 1.6); ctx.fillStyle = "#ffffff"; const maxW = CW - 36; ctx.font = "bold 28px sans-serif"; const single = ctx.measureText(value).width; if (single <= maxW) { ctx.fillText(value, x + 18, y + ch - 30); } else { const fs = fitFont(ctx, value, "bold", 26, 20, maxW); if (ctx.measureText(value).width <= maxW) { ctx.fillText(value, x + 18, y + ch - 30); } else { ctx.font = "bold 18px sans-serif"; const lines = wrap(ctx, value, maxW, 2); const base = y + ch - 24 - (lines.length - 1) * 22; lines.forEach((ln, k) => ctx.fillText(ln, x + 18, base + k * 22)); } } }); // ── ফুটার ── const fy = H - 52; ctx.fillStyle = rgba(A1, 1); ctx.beginPath(); ctx.moveTo(70, fy - 6); ctx.lineTo(78, fy - 14); ctx.lineTo(86, fy - 6); ctx.lineTo(78, fy + 2); ctx.closePath(); ctx.fill(); ctx.fillStyle = "rgba(255,255,255,0.8)"; ctx.font = "600 17px sans-serif"; spaced(ctx, "AUTHOR • " + AUTHOR.toUpperCase(), 98, fy, 2); const rt = "GOATBOT • WORLD COUNTRY INFO"; ctx.fillStyle = "rgba(255,255,255,0.5)"; ctx.font = "600 15px sans-serif"; const rw = spacedWidth(ctx, rt, 2); spaced(ctx, rt, W - 70 - rw, fy, 2); return cv; } /* ───────────────────── টেক্সট ফলব্যাক ───────────────────── */ function buildText(i) { return [ `${i.flagEmoji} ${i.name}`, "━━━━━━━━━━━━━━━━━━", `📛 Official: ${i.official}`, `🔤 Code: ${i.cca2} / ${i.cca3}`, `🏙️ Capital: ${i.capital}`, `🌍 Region: ${i.region} / ${i.subregion}`, `👥 Population: ${i.population}${i.popYear ? " (" + i.popYear + ")" : ""}`, `📐 Area: ${i.area}`, `💰 Currency: ${i.currency}`, `🗣️ Languages: ${i.languages}`, `📞 Calling code: ${i.calling}`, `🌐 Domain: ${i.tld}`, `🤝 Borders: ${i.borders}`, `✍️ Author: ${AUTHOR}` ].join("\n"); } /* ───────────────────────── কমান্ড ───────────────────────── */ module.exports = { config: { name: "country", aliases: ["desh", "nation"], version: "4.0", author: AUTHOR, countDown: 5, role: 0, description: { en: "Premium country info card for any country in the world" }, category: "info", guide: { en: "{pn} \nExample: {pn} Bangladesh | {pn} BD | {pn} বাংলাদেশ | {pn} chine" } }, drawCard, // টেস্টের জন্য onStart: async function ({ message, args }) { const query = args.join(" ").trim(); if (!query) return message.reply("❌ একটি দেশের নাম দিন।\nউদাহরণ: country Bangladesh"); let list; try { list = await loadData(); } catch (err) { console.error("[country] data error:", err.message); return message.reply(`❌ ডাটা লোড করা যায়নি।\nকারণ: ${err.code || (err.response && err.response.status) || err.message}`); } const c = findCountry(list, query); if (!c) return message.reply(`❌ "${query}" নামে কোনো দেশ পাওয়া যায়নি।`); const pop = await getPopulation(c.cca2); const info = buildInfo(c, list, pop); const caption = `${info.flagEmoji} ${info.name}\n🗺️ https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(info.name)}`; // ক্যানভাস কার্ড if (canvasLib) { let file; try { let flagImg = null; try { const r = await axios.get(`https://flagcdn.com/w640/${info.cca2.toLowerCase()}.png`, { responseType: "arraybuffer", timeout: 15000 }); flagImg = await canvasLib.loadImage(Buffer.from(r.data)); } catch (e) { console.error("[country] flag error:", e.message); } const cv = drawCard(canvasLib.createCanvas, flagImg, info); const dir = path.join(__dirname, "cache"); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); file = path.join(dir, `country_${Date.now()}.png`); fs.writeFileSync(file, cv.toBuffer("image/png")); await message.reply({ body: caption, attachment: fs.createReadStream(file) }); return fs.unlink(file, () => {}); } catch (err) { console.error("[country] canvas error:", err); if (file) fs.unlink(file, () => {}); } } // ফলব্যাক: সাধারণ টেক্সট return message.reply(buildText(info)); } };