diff --git a/scripts/cmds/4k.js b/scripts/cmds/4k.js deleted file mode 100644 index bbfb2251..00000000 --- a/scripts/cmds/4k.js +++ /dev/null @@ -1,75 +0,0 @@ -const axios = require("axios"); - -const mahmud = async () => { - const base = await axios.get("https://raw.githubusercontent.com/mahmudx7/HINATA/main/baseApiUrl.json"); - return base.data.mahmud; -}; - -/** -* @author MahMUD -* @author: do not delete it -*/ - -module.exports = { - config: { - name: "4k", - version: "1.7", - author: "MahMUD", - countDown: 10, - role: 0, - category: "AI", - description: "Enhance or restore image quality using 4k AI.", - guide: { - en: "{pn} [url] or reply with image" - } - }, - - onStart: async function ({ message, event, args }) { - - const obfuscatedAuthor = String.fromCharCode(77, 97, 104, 77, 85, 68); - if (module.exports.config.author !== obfuscatedAuthor) { - return api.sendMessage("You are not authorized to change the author name.", event.threadID, event.messageID); - } - const startTime = Date.now(); - let imgUrl; - - if (event.messageReply?.attachments?.[0]?.type === "photo") { - imgUrl = event.messageReply.attachments[0].url; - } - - else if (args[0]) { - imgUrl = args.join(" "); - } - - if (!imgUrl) { - return message.reply("Baby, Please reply to an image or provide an image URL"); - } - - const waitMsg = await message.reply("𝐋𝐨𝐚𝐝𝐢𝐧𝐠 𝟒𝐤 𝐢𝐦𝐚𝐠𝐞...𝐰𝐚𝐢𝐭 𝐛𝐚𝐛𝐲 <😘"); - message.reaction("😘", event.messageID); - - try { - - const apiUrl = `${await mahmud()}/api/hd?imgUrl=${encodeURIComponent(imgUrl)}`; - - const res = await axios.get(apiUrl, { responseType: "stream" }); - if (waitMsg?.messageID) message.unsend(waitMsg.messageID); - - message.reaction("✅", event.messageID); - - const processTime = ((Date.now() - startTime) / 1000).toFixed(2); - - message.reply({ - body: `✅ | 𝐇𝐞𝐫𝐞'𝐬 𝐲𝐨𝐮𝐫 𝟒𝐤 𝐢𝐦𝐚𝐠𝐞 𝐛𝐚𝐛𝐲`, - attachment: res.data - }); - - } catch (error) { - - if (waitMsg?.messageID) message.unsend(waitMsg.messageID); - - message.reaction("❎", event.messageID); - message.reply(`🥹error baby, contact MahMUD.`); - } - } -}; diff --git a/scripts/cmds/acp.js b/scripts/cmds/acp.js deleted file mode 100644 index 7dbac9c7..00000000 --- a/scripts/cmds/acp.js +++ /dev/null @@ -1,149 +0,0 @@ -const moment = require("moment-timezone"); - -module.exports = { - config: { - name: "accept", - aliases: ['acp'], - version: "1.0", - author: "Loid Butter", - countDown: 8, - role: 2, - shortDescription: "accept users", - longDescription: "accept users", - category: "Utility", - }, - - onReply: async function ({ message, Reply, event, api, commandName }) { - const { author, listRequest, messageID } = Reply; - if (author !== event.senderID) return; - - const args = event.body.trim().toLowerCase().split(" "); - clearTimeout(Reply.unsendTimeout); - - const form = { - av: api.getCurrentUserID(), - fb_api_caller_class: "RelayModern", - variables: { - input: { - source: "friends_tab", - actor_id: api.getCurrentUserID(), - client_mutation_id: Math.random().toString(36).substring(2, 15) - }, - scale: 3, - refresh_num: 0 - } - }; - - const success = []; - const failed = []; - - if (args[0] === "add") { - form.fb_api_req_friendly_name = "FriendingCometFriendRequestConfirmMutation"; - form.doc_id = "3147613905362928"; - } else if (args[0] === "del") { - form.fb_api_req_friendly_name = "FriendingCometFriendRequestDeleteMutation"; - form.doc_id = "4108254489275063"; - } else { - return api.sendMessage("Please select ", event.threadID, event.messageID); - } - - let targetIDs = args[1] === "all" ? listRequest.map((_, idx) => idx + 1) : args.slice(1); - const promiseFriends = []; - - for (const stt of targetIDs) { - const index = parseInt(stt) - 1; - const user = listRequest[index]; - - if (!user) { - failed.push(`Can't find target ${stt}`); - continue; - } - - form.variables.input.friend_requester_id = user.node.id; - promiseFriends.push(api.httpPost("https://www.facebook.com/api/graphql/", { - ...form, - variables: JSON.stringify(form.variables) - })); - - success.push({ name: user.node.name, id: user.node.id }); - } - - const finalSuccess = []; - const finalFailed = []; - - for (let i = 0; i < promiseFriends.length; i++) { - try { - const res = await promiseFriends[i]; - const data = JSON.parse(res); - if (data.errors) { - finalFailed.push(success[i].name); - } else { - finalSuccess.push(success[i].name); - } - } catch { - finalFailed.push(success[i].name); - } - } - - let resultMsg = ""; - if (finalSuccess.length) { - resultMsg += `✅ ${args[0] === "add" ? "Accepted" : "Deleted"}: ${finalSuccess.length} user(s)\n${finalSuccess.join("\n")}`; - } - if (finalFailed.length) { - resultMsg += `\n❌ Failed: ${finalFailed.length} user(s)\n${finalFailed.join("\n")}`; - } - if (!resultMsg) { - resultMsg = "No users were processed."; - } - - api.unsendMessage(messageID); - return api.sendMessage(resultMsg, event.threadID); - }, - - onStart: async function ({ event, api, commandName }) { - const form = { - av: api.getCurrentUserID(), - fb_api_req_friendly_name: "FriendingCometFriendRequestsRootQueryRelayPreloader", - fb_api_caller_class: "RelayModern", - doc_id: "4499164963466303", - variables: JSON.stringify({ input: { scale: 3 } }) - }; - - try { - const response = await api.httpPost("https://www.facebook.com/api/graphql/", form); - const listRequest = JSON.parse(response)?.data?.viewer?.friending_possibilities?.edges || []; - - if (listRequest.length === 0) { - return api.sendMessage("No pending friend requests found.", event.threadID); - } - - let msg = ""; - listRequest.forEach((user, i) => { - msg += `\n${i + 1}. Name: ${user.node.name}` - + `\nID: ${user.node.id}` - + `\nURL: ${user.node.url.replace("www.facebook", "fb")}` - + `\nTime: ${moment().tz("Asia/Manila").format("DD/MM/YYYY HH:mm:ss")}\n`; - }); - - api.sendMessage( - `${msg}\nReply to this message with: `, - event.threadID, - (e, info) => { - global.GoatBot.onReply.set(info.messageID, { - commandName, - messageID: info.messageID, - listRequest, - author: event.senderID, - unsendTimeout: setTimeout(() => { - api.unsendMessage(info.messageID); - }, this.config.countDown * 1000) - }); - }, - event.messageID - ); - } catch (error) { - console.error(error); - api.sendMessage("Error retrieving friend request list.", event.threadID); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/activemember.js b/scripts/cmds/activemember.js deleted file mode 100644 index 44cf1097..00000000 --- a/scripts/cmds/activemember.js +++ /dev/null @@ -1,64 +0,0 @@ -const axios = require('axios'); - -module.exports = { - config: { - name: "activemember", - aliases: ["am"], - version: "1.0", - author: "nexo_here", - countDown: 5, - role: 0, - shortDescription: "Get the top 15 users by message count in the current chat", - longDescription: "Get the top 15 users by message count in the current chat", - category: "box chat", - guide: "{p}{n}", - }, - onStart: async function ({ api, event }) { - const threadId = event.threadID; - const senderId = event.senderID; - - try { - - const participants = await api.getThreadInfo(threadId, { participantIDs: true }); - - - const messageCounts = {}; - - - participants.participantIDs.forEach(participantId => { - messageCounts[participantId] = 0; - }); - - - const messages = await api.getThreadHistory(threadId, 1000); // Adjust the limit as needed if you want if you wanna get all message - - - messages.forEach(message => { - const messageSender = message.senderID; - if (messageCounts[messageSender] !== undefined) { - messageCounts[messageSender]++; - } - }); - - - const topUsers = Object.entries(messageCounts) - .sort((a, b) => b[1] - a[1]) - .slice(0, 15); - - - const userList = []; - for (const [userId, messageCount] of topUsers) { - const userInfo = await api.getUserInfo(userId); - const userName = userInfo[userId].name; - userList.push(`\n『${userName}』 \nSent ${messageCount} messages \n`); - } - - - const messageText = `Active members are 💁‍♀️:\n${userList.join('\n')}`; - api.sendMessage({ body: messageText, mentions: [{ tag: senderId, id: senderId, type: "user" }] }, threadId); - - } catch (error) { - console.error(error); - } - }, -}; \ No newline at end of file diff --git a/scripts/cmds/adboxonly.js b/scripts/cmds/adboxonly.js deleted file mode 100644 index 5912db6e..00000000 --- a/scripts/cmds/adboxonly.js +++ /dev/null @@ -1,65 +0,0 @@ -module.exports = { - config: { - name: "onlyadminbox", - aliases: ["onlyadbox", "adboxonly", "adminboxonly"], - version: "1.3", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "bật/tắt chế độ chỉ quản trị của viên nhóm mới có thể sử dụng bot", - en: "turn on/off only admin box can use bot" - }, - category: "box chat", - guide: { - vi: " {pn} [on | off]: bật/tắt chế độ chỉ quản trị viên nhóm mới có thể sử dụng bot" - + "\n {pn} noti [on | off]: bật/tắt thông báo khi người dùng không phải là quản trị viên nhóm sử dụng bot", - en: " {pn} [on | off]: turn on/off the mode only admin of group can use bot" - + "\n {pn} noti [on | off]: turn on/off the notification when user is not admin of group use bot" - } - }, - - langs: { - vi: { - turnedOn: "Đã bật chế độ chỉ quản trị viên nhóm mới có thể sử dụng bot", - turnedOff: "Đã tắt chế độ chỉ quản trị viên nhóm mới có thể sử dụng bot", - turnedOnNoti: "Đã bật thông báo khi người dùng không phải là quản trị viên nhóm sử dụng bot", - turnedOffNoti: "Đã tắt thông báo khi người dùng không phải là quản trị viên nhóm sử dụng bot", - syntaxError: "Sai cú pháp, chỉ có thể dùng {pn} on hoặc {pn} off" - }, - en: { - turnedOn: "Turned on the mode only admin of group can use bot", - turnedOff: "Turned off the mode only admin of group can use bot", - turnedOnNoti: "Turned on the notification when user is not admin of group use bot", - turnedOffNoti: "Turned off the notification when user is not admin of group use bot", - syntaxError: "Syntax error, only use {pn} on or {pn} off" - } - }, - - onStart: async function ({ args, message, event, threadsData, getLang }) { - let isSetNoti = false; - let value; - let keySetData = "data.onlyAdminBox"; - let indexGetVal = 0; - - if (args[0] == "noti") { - isSetNoti = true; - indexGetVal = 1; - keySetData = "data.hideNotiMessageOnlyAdminBox"; - } - - if (args[indexGetVal] == "on") - value = true; - else if (args[indexGetVal] == "off") - value = false; - else - return message.reply(getLang("syntaxError")); - - await threadsData.set(event.threadID, isSetNoti ? !value : value, keySetData); - - if (isSetNoti) - return message.reply(value ? getLang("turnedOnNoti") : getLang("turnedOffNoti")); - else - return message.reply(value ? getLang("turnedOn") : getLang("turnedOff")); - } -}; \ No newline at end of file diff --git a/scripts/cmds/adduser.js b/scripts/cmds/adduser.js deleted file mode 100644 index d74fcea4..00000000 --- a/scripts/cmds/adduser.js +++ /dev/null @@ -1,180 +0,0 @@ -const { findUid } = global.utils; -const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); - -module.exports = { - config: { - name: "adduser", - version: "1.5", - author: "NTKhang", - countDown: 5, - role: 1, - description: { - vi: "Thêm thành viên vào box chat của bạn", - en: "Add user to box chat of you" - }, - category: "box chat", - guide: { - en: " {pn} [link profile | uid]" - } - }, - - langs: { - vi: { - alreadyInGroup: "Đã có trong nhóm", - successAdd: "- Đã thêm thành công %1 thành viên vào nhóm", - failedAdd: "- Không thể thêm %1 thành viên vào nhóm", - approve: "- Đã thêm %1 thành viên vào danh sách phê duyệt", - invalidLink: "Vui lòng nhập link facebook hợp lệ", - cannotGetUid: "Không thể lấy được uid của người dùng này", - linkNotExist: "Profile url này không tồn tại", - cannotAddUser: "Bot bị chặn tính năng hoặc người dùng này chặn người lạ thêm vào nhóm" - }, - en: { - alreadyInGroup: "Already in group", - successAdd: "- Successfully added %1 members to the group", - failedAdd: "- Failed to add %1 members to the group", - approve: "- Added %1 members to the approval list", - invalidLink: "Please enter a valid facebook link", - cannotGetUid: "Cannot get uid of this user", - linkNotExist: "This profile url does not exist", - cannotAddUser: "Bot is blocked or this user blocked strangers from adding to the group" - }, - tl: { - alreadyInGroup: "Nasa grupo na", - successAdd: "- Matagumpay na naidagdag ang %1 miyembro sa grupo", - failedAdd: "- Nabigo ang pagdaragdag ng %1 miyembro sa grupo", - approve: "- Naidagdag ang %1 miyembro sa listahan ng pag-apruba", - invalidLink: "Mangyaring maglagay ng wastong facebook link", - cannotGetUid: "Hindi makuha ang uid ng user na ito", - linkNotExist: "Ang profile url na ito ay hindi umiiral", - cannotAddUser: "Naka-block ang bot o naka-block ng user na ito ang mga estranyo mula sa pagdaragdag sa grupo" - }, - hi: { - alreadyInGroup: "Pehle se group mein hai", - successAdd: "- %1 members ko group mein successfully add kar diya gaya", - failedAdd: "- %1 members ko group mein add karne mein fail", - approve: "- %1 members ko approval list mein add kar diya gaya", - invalidLink: "Kripya valid facebook link dalein", - cannotGetUid: "Is user ka uid nahi mil sakta", - linkNotExist: "Ye profile url exist nahi karta", - cannotAddUser: "Bot blocked hai ya is user ne strangers ko group mein add karne se block kar rakha hai" - }, - ar: { - alreadyInGroup: "موجود بالفعل في المجموعة", - successAdd: "- تمت إضافة %1 عضو بنجاح إلى المجموعة", - failedAdd: "- فشل إضافة %1 عضو إلى المجموعة", - approve: "- تمت إضافة %1 عضو إلى قائمة الموافقة", - invalidLink: "الرجاء إدخال رابط فيسبوك صحيح", - cannotGetUid: "لا يمكن الحصول على uid لهذا المستخدم", - linkNotExist: "عنوان url للملف الشخصي هذا غير موجود", - cannotAddUser: "البوت محظور أو هذا المستخدم منع الغرباء من إضافته للمجموعة" - }, - bn: { - alreadyInGroup: "ইতিমধ্যে গ্রুপে আছে", - successAdd: "- %1 জন সদস্যকে গ্রুপে সফলভাবে যোগ করা হয়েছে", - failedAdd: "- %1 জন সদস্যকে গ্রুপে যোগ করতে ব্যর্থ", - approve: "- %1 জন সদস্যকে approval তালিকায় যোগ করা হয়েছে", - invalidLink: "অনুগ্রহ করে সঠিক facebook link দিন", - cannotGetUid: "এই user এর uid পাওয়া যাচ্ছে না", - linkNotExist: "এই profile url টি বিদ্যমান নেই", - cannotAddUser: "Bot blocked আছে বা এই user অপরিচিতদের গ্রুপে add করা block করে রেখেছে" - } - }, - - onStart: async function ({ message, api, event, args, threadsData, getLang }) { - const { members, adminIDs, approvalMode } = await threadsData.get(event.threadID); - const botID = api.getCurrentUserID(); - - const success = [ - { - type: "success", - uids: [] - }, - { - type: "waitApproval", - uids: [] - } - ]; - const failed = []; - - function checkErrorAndPush(messageError, item) { - item = item.replace(/(?:https?:\/\/)?(?:www\.)?(?:facebook|fb|m\.facebook)\.(?:com|me)/i, ''); - const findType = failed.find(error => error.type == messageError); - if (findType) - findType.uids.push(item); - else - failed.push({ - type: messageError, - uids: [item] - }); - } - - const regExMatchFB = /(?:https?:\/\/)?(?:www\.)?(?:facebook|fb|m\.facebook)\.(?:com|me)\/(?:(?:\w)*#!\/)?(?:pages\/)?(?:[\w\-]*\/)*([\w\-\.]+)(?:\/)?/i; - for (const item of args) { - let uid; - let continueLoop = false; - - if (isNaN(item) && regExMatchFB.test(item)) { - for (let i = 0; i < 10; i++) { - try { - uid = await findUid(item); - break; - } - catch (err) { - if (err.name == "SlowDown" || err.name == "CannotGetData") { - await sleep(1000); - continue; - } - else if (i == 9 || (err.name != "SlowDown" && err.name != "CannotGetData")) { - checkErrorAndPush( - err.name == "InvalidLink" ? getLang('invalidLink') : - err.name == "CannotGetData" ? getLang('cannotGetUid') : - err.name == "LinkNotExist" ? getLang('linkNotExist') : - err.message, - item - ); - continueLoop = true; - break; - } - } - } - } - else if (!isNaN(item)) - uid = item; - else - continue; - - if (continueLoop == true) - continue; - - if (members.some(m => m.userID == uid && m.inGroup)) { - checkErrorAndPush(getLang("alreadyInGroup"), item); - } - else { - try { - await api.addUserToGroup(uid, event.threadID); - if (approvalMode === true && !adminIDs.includes(botID)) - success[1].uids.push(uid); - else - success[0].uids.push(uid); - } - catch (err) { - checkErrorAndPush(getLang("cannotAddUser"), item); - } - } - } - - const lengthUserSuccess = success[0].uids.length; - const lengthUserWaitApproval = success[1].uids.length; - const lengthUserError = failed.length; - - let msg = ""; - if (lengthUserSuccess) - msg += `${getLang("successAdd", lengthUserSuccess)}\n`; - if (lengthUserWaitApproval) - msg += `${getLang("approve", lengthUserWaitApproval)}\n`; - if (lengthUserError) - msg += `${getLang("failedAdd", failed.reduce((a, b) => a + b.uids.length, 0))} ${failed.reduce((a, b) => a += `\n + ${b.uids.join('\n ')}: ${b.type}`, "")}`; - await message.reply(msg); - } -}; \ No newline at end of file diff --git a/scripts/cmds/admin.js b/scripts/cmds/admin.js deleted file mode 100644 index ad2a3db5..00000000 --- a/scripts/cmds/admin.js +++ /dev/null @@ -1,152 +0,0 @@ -const { config } = global.GoatBot; -const { writeFileSync } = require("fs-extra"); - -module.exports = { - config: { - name: "admin", - version: "1.6", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Thêm, xóa, sửa quyền admin", - en: "Add, remove, edit admin role" - }, - category: "box chat", - guide: { - vi: ' {pn} [add | -a] : Thêm quyền admin cho người dùng' - + '\n {pn} [remove | -r] : Xóa quyền admin của người dùng' - + '\n {pn} [list | -l]: Liệt kê danh sách admin', - en: ' {pn} [add | -a] : Add admin role for user' - + '\n {pn} [remove | -r] : Remove admin role of user' - + '\n {pn} [list | -l]: List all admins' - } - }, - - langs: { - vi: { - added: "✅ | Đã thêm quyền admin cho %1 người dùng:\n%2", - alreadyAdmin: "\n⚠️ | %1 người dùng đã có quyền admin từ trước rồi:\n%2", - missingIdAdd: "⚠️ | Vui lòng nhập ID hoặc tag người dùng muốn thêm quyền admin", - removed: "✅ | Đã xóa quyền admin của %1 người dùng:\n%2", - notAdmin: "⚠️ | %1 người dùng không có quyền admin:\n%2", - missingIdRemove: "⚠️ | Vui lòng nhập ID hoặc tag người dùng muốn xóa quyền admin", - listAdmin: "👑 | Danh sách admin:\n%1" - }, - en: { - added: "✅ | Added admin role for %1 users:\n%2", - alreadyAdmin: "\n⚠️ | %1 users already have admin role:\n%2", - missingIdAdd: "⚠️ | Please enter ID or tag user to add admin role", - removed: "✅ | Removed admin role of %1 users:\n%2", - notAdmin: "⚠️ | %1 users don't have admin role:\n%2", - missingIdRemove: "⚠️ | Please enter ID or tag user to remove admin role", - listAdmin: "👑 | List of admins:\n%1" - }, - tl: { - added: "✅ | Naidagdag ang admin role para sa %1 user:\n%2", - alreadyAdmin: "\n⚠️ | %1 user ay mayroon nang admin role:\n%2", - missingIdAdd: "⚠️ | Mangyaring maglagay ng ID o mag-tag ng user para idagdag ang admin role", - removed: "✅ | Naalis ang admin role ng %1 user:\n%2", - notAdmin: "⚠️ | %1 user ay walang admin role:\n%2", - missingIdRemove: "⚠️ | Mangyaring maglagay ng ID o mag-tag ng user para alisin ang admin role", - listAdmin: "👑 | Listahan ng mga admin:\n%1" - }, - hi: { - added: "✅ | %1 users ko admin role de diya gaya:\n%2", - alreadyAdmin: "\n⚠️ | %1 users ke paas pehle se admin role hai:\n%2", - missingIdAdd: "⚠️ | Admin role dene ke liye ID dalein ya user ko tag karein", - removed: "✅ | %1 users ka admin role hata diya gaya:\n%2", - notAdmin: "⚠️ | %1 users ke paas admin role nahi hai:\n%2", - missingIdRemove: "⚠️ | Admin role hatane ke liye ID dalein ya user ko tag karein", - listAdmin: "👑 | Admins ki list:\n%1" - }, - ar: { - added: "✅ | تمت إضافة دور المسؤول لـ %1 مستخدم:\n%2", - alreadyAdmin: "\n⚠️ | %1 مستخدم لديهم بالفعل دور المسؤول:\n%2", - missingIdAdd: "⚠️ | الرجاء إدخال ID أو وضع علامة على المستخدم لإضافة دور المسؤول", - removed: "✅ | تمت إزالة دور المسؤول من %1 مستخدم:\n%2", - notAdmin: "⚠️ | %1 مستخدم ليس لديهم دور المسؤول:\n%2", - missingIdRemove: "⚠️ | الرجاء إدخال ID أو وضع علامة على المستخدم لإزالة دور المسؤول", - listAdmin: "👑 | قائمة المسؤولين:\n%1" - }, - bn: { - added: "✅ | %1 জন user কে admin role দেওয়া হয়েছে:\n%2", - alreadyAdmin: "\n⚠️ | %1 জন user এর আগে থেকেই admin role আছে:\n%2", - missingIdAdd: "⚠️ | Admin role দিতে ID দিন বা user কে tag করুন", - removed: "✅ | %1 জন user এর admin role সরানো হয়েছে:\n%2", - notAdmin: "⚠️ | %1 জন user এর admin role নেই:\n%2", - missingIdRemove: "⚠️ | Admin role সরাতে ID দিন বা user কে tag করুন", - listAdmin: "👑 | Admin তালিকা:\n%1" - } - }, - - onStart: async function ({ message, args, usersData, event, getLang }) { - switch (args[0]) { - case "add": - case "-a": { - if (args[1]) { - let uids = []; - if (Object.keys(event.mentions).length > 0) - uids = Object.keys(event.mentions); - else if (event.messageReply) - uids.push(event.messageReply.senderID); - else - uids = args.filter(arg => !isNaN(arg)); - const notAdminIds = []; - const adminIds = []; - for (const uid of uids) { - if (config.adminBot.includes(uid)) - adminIds.push(uid); - else - notAdminIds.push(uid); - } - - config.adminBot.push(...notAdminIds); - const getNames = await Promise.all(uids.map(uid => usersData.getName(uid).then(name => ({ uid, name })))); - writeFileSync(global.client.dirConfig, JSON.stringify(config, null, 2)); - return message.reply( - (notAdminIds.length > 0 ? getLang("added", notAdminIds.length, getNames.map(({ uid, name }) => `• ${name} (${uid})`).join("\n")) : "") - + (adminIds.length > 0 ? getLang("alreadyAdmin", adminIds.length, adminIds.map(uid => `• ${uid}`).join("\n")) : "") - ); - } - else - return message.reply(getLang("missingIdAdd")); - } - case "remove": - case "-r": { - if (args[1]) { - let uids = []; - if (Object.keys(event.mentions).length > 0) - uids = Object.keys(event.mentions)[0]; - else - uids = args.filter(arg => !isNaN(arg)); - const notAdminIds = []; - const adminIds = []; - for (const uid of uids) { - if (config.adminBot.includes(uid)) - adminIds.push(uid); - else - notAdminIds.push(uid); - } - for (const uid of adminIds) - config.adminBot.splice(config.adminBot.indexOf(uid), 1); - const getNames = await Promise.all(adminIds.map(uid => usersData.getName(uid).then(name => ({ uid, name })))); - writeFileSync(global.client.dirConfig, JSON.stringify(config, null, 2)); - return message.reply( - (adminIds.length > 0 ? getLang("removed", adminIds.length, getNames.map(({ uid, name }) => `• ${name} (${uid})`).join("\n")) : "") - + (notAdminIds.length > 0 ? getLang("notAdmin", notAdminIds.length, notAdminIds.map(uid => `• ${uid}`).join("\n")) : "") - ); - } - else - return message.reply(getLang("missingIdRemove")); - } - case "list": - case "-l": { - const getNames = await Promise.all(config.adminBot.map(uid => usersData.getName(uid).then(name => ({ uid, name })))); - return message.reply(getLang("listAdmin", getNames.map(({ uid, name }) => `• ${name} (${uid})`).join("\n"))); - } - default: - return message.SyntaxError(); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/adminmention.js b/scripts/cmds/adminmention.js deleted file mode 100644 index 40b20956..00000000 --- a/scripts/cmds/adminmention.js +++ /dev/null @@ -1,39 +0,0 @@ -module.exports = { - config: { - name: "adminmention", - version: "1.3.2", - author: "MOHAMMAD AKASH", - countDown: 0, - role: 0, - shortDescription: "Replies angrily when someone tags admins", - longDescription: "If anyone mentions an admin, bot will angrily reply with random messages.", - category: "system" - }, - - onStart: async function () {}, - - onChat: async function ({ event, message }) { - const adminIDs = ["100078049308655", "100090071683807", "100092480994957"].map(String); - - // Skip if sender is admin - if (adminIDs.includes(String(event.senderID))) return; - - // যদি কেউ মেনশন দেয় - const mentionedIDs = event.mentions ? Object.keys(event.mentions).map(String) : []; - const isMentioningAdmin = adminIDs.some(id => mentionedIDs.includes(id)); - - if (!isMentioningAdmin) return; - - // র‍্যান্ডম রাগী রিপ্লাই - const REPLIES = [ - " ওরে মেনশন দিস না বউ নিয়া চিপায় গেছে 😩🐸", - "বস এক আবাল তুমারে ডাকতেছে 😂😏", - " বুকাচুদা তুই মেনশন দিবি না আমার বস রে 🥹", - "মেনশন দিছস আর বেচে যাবি? দারা বলতাছি 😠", - "Boss এখন বিজি আছে 😌🥱" - ]; - - const randomReply = REPLIES[Math.floor(Math.random() * REPLIES.length)]; - return message.reply(randomReply); - } -}; diff --git a/scripts/cmds/adminonly.js b/scripts/cmds/adminonly.js deleted file mode 100644 index a34ab27f..00000000 --- a/scripts/cmds/adminonly.js +++ /dev/null @@ -1,93 +0,0 @@ -const fs = require("fs-extra"); -const { config } = global.GoatBot; -const { client } = global; - -module.exports = { - config: { - name: "adminonly", - aliases: ["adonly", "onlyad", "onlyadmin"], - version: "1.5", - author: "NTKhang", - countDown: 5, - role: 1, - description: { - vi: "bật/tắt chế độ chỉ admin mới có thể sử dụng bot", - en: "turn on/off only admin can use bot" - }, - category: "owner", - guide: { - vi: " {pn} [on | off]: bật/tắt chế độ chỉ admin mới có thể sử dụng bot" - + "\n {pn} noti [on | off]: bật/tắt thông báo khi người dùng không phải là admin sử dụng bot", - en: " {pn} [on | off]: turn on/off the mode only admin can use bot" - + "\n {pn} noti [on | off]: turn on/off the notification when user is not admin use bot" - } - }, - - langs: { - vi: { - turnedOn: "Đã bật chế độ chỉ admin mới có thể sử dụng bot", - turnedOff: "Đã tắt chế độ chỉ admin mới có thể sử dụng bot", - turnedOnNoti: "Đã bật thông báo khi người dùng không phải là admin sử dụng bot", - turnedOffNoti: "Đã tắt thông báo khi người dùng không phải là admin sử dụng bot" - }, - en: { - turnedOn: "Turned on the mode only admin can use bot", - turnedOff: "Turned off the mode only admin can use bot", - turnedOnNoti: "Turned on the notification when user is not admin use bot", - turnedOffNoti: "Turned off the notification when user is not admin use bot" - }, - tl: { - turnedOn: "Na-on ang mode na admin lamang ang makakagamit ng bot", - turnedOff: "Na-off ang mode na admin lamang ang makakagamit ng bot", - turnedOnNoti: "Na-on ang abiso kapag ang user ay hindi admin at gumagamit ng bot", - turnedOffNoti: "Na-off ang abiso kapag ang user ay hindi admin at gumagamit ng bot" - }, - hi: { - turnedOn: "Sirf admin bot use kar sakta hai wala mode on ho gaya", - turnedOff: "Sirf admin bot use kar sakta hai wala mode off ho gaya", - turnedOnNoti: "Non-admin user ke bot use karne par notification on ho gaya", - turnedOffNoti: "Non-admin user ke bot use karne par notification off ho gaya" - }, - ar: { - turnedOn: "تم تفعيل وضع الاستخدام للمسؤولين فقط", - turnedOff: "تم إيقاف وضع الاستخدام للمسؤولين فقط", - turnedOnNoti: "تم تفعيل الإشعار عند استخدام غير المسؤولين للبوت", - turnedOffNoti: "تم إيقاف الإشعار عند استخدام غير المسؤولين للبوت" - }, - bn: { - turnedOn: "শুধুমাত্র admin bot ব্যবহার করতে পারবে এই মোড চালু হয়েছে", - turnedOff: "শুধুমাত্র admin bot ব্যবহার করতে পারবে এই মোড বন্ধ হয়েছে", - turnedOnNoti: "Non-admin user bot ব্যবহার করলে notification চালু হয়েছে", - turnedOffNoti: "Non-admin user bot ব্যবহার করলে notification বন্ধ হয়েছে" - } - }, - - onStart: function ({ args, message, getLang }) { - let isSetNoti = false; - let value; - let indexGetVal = 0; - - if (args[0] == "noti") { - isSetNoti = true; - indexGetVal = 1; - } - - if (args[indexGetVal] == "on") - value = true; - else if (args[indexGetVal] == "off") - value = false; - else - return message.SyntaxError(); - - if (isSetNoti) { - config.hideNotiMessage.adminOnly = !value; - message.reply(getLang(value ? "turnedOnNoti" : "turnedOffNoti")); - } - else { - config.adminOnly.enable = value; - message.reply(getLang(value ? "turnedOn" : "turnedOff")); - } - - fs.writeFileSync(client.dirConfig, JSON.stringify(config, null, 2)); - } -}; diff --git a/scripts/cmds/age.js b/scripts/cmds/age.js deleted file mode 100644 index 69b93f12..00000000 --- a/scripts/cmds/age.js +++ /dev/null @@ -1,117 +0,0 @@ -const moment = require("moment-timezone"); - -module.exports = { - config: { - name: "age", - aliases: ["myage"], - version: "6.0", - author: "𝐌𝐨𝐡𝐚ᴍᴍᴀᴅ 𝐀ᴋᴀsʜ", - role: 0, - category: "AI", - guide: "age ", - countDown: 5 - }, - - onStart: async function ({ api, event, args }) { - try { - if (!args.length) { - return api.sendMessage( - "⚠️ Uꜱᴇ:\n• age 2007\n• age 01/05/2007\n• age 3 May 2007\n• age 3/may/2007", - event.threadID - ); - } - - let input = args.join(" ").trim(); - let day, month, year; - - const monthMap = { - jan:1,january:1,feb:2,february:2,mar:3,march:3, - apr:4,april:4,may:5,jun:6,june:6, - jul:7,july:7,aug:8,august:8, - sep:9,september:9,oct:10,october:10, - nov:11,november:11,dec:12,december:12 - }; - - // YYYY - if (/^\d{4}$/.test(input)) { - day = 1; month = 1; year = Number(input); - } - - // DD/MM/YYYY - else if (/^\d{1,2}\/\d{1,2}\/\d{2,4}$/.test(input)) { - const p = input.split("/"); - day = +p[0]; - month = +p[1]; - year = +p[2]; - if (year < 100) year += 2000; - } - - // 3 May 2007 - else if (/^\d{1,2}\s+[a-zA-Z]{3,9}\s+\d{4}$/.test(input)) { - const p = input.split(" "); - day = +p[0]; - month = monthMap[p[1].toLowerCase()]; - year = +p[2]; - } - - // 3/May/2007 - else if (/^\d{1,2}\/[a-zA-Z]{3,9}\/\d{4}$/.test(input)) { - const p = input.split("/"); - day = +p[0]; - month = monthMap[p[1].toLowerCase()]; - year = +p[2]; - } - - else { - return api.sendMessage( - "❌ Fᴏʀᴍᴀᴛ ভুল\n✔ age 2007\n✔ age 01/05/2007\n✔ age 3 May 2007\n✔ age 3/may/2007", - event.threadID - ); - } - - if (!day || !month || !year) { - return api.sendMessage("❌ Dᴀᴛᴇ পাʀsᴇ হʏ নɪ", event.threadID); - } - - const birth = moment.tz( - `${year}-${month}-${day}`, - "YYYY-MM-DD", - "Asia/Dhaka" - ); - - if (!birth.isValid()) { - return api.sendMessage("❌ Iɴᴠᴀʟɪᴅ Dᴀᴛᴇ", event.threadID); - } - - const now = moment.tz("Asia/Dhaka"); - const d = moment.duration(now.diff(birth)); - - const y = d.years(); - const m = d.months(); - const dy = d.days(); - - const totalMonths = y * 12 + m; - const totalDays = Math.floor(d.asDays()); - const totalHours = Math.floor(d.asHours()); - - const msg = `━━━━━━━━━━━━━━ -🎂 Sᴍᴀʀᴛ Aɢᴇ Cᴏᴜɴᴛ🎂 -━━━━━━━━━━━━━━ - -📅 Bɪʀᴛʜᴅᴀʏ: ${String(day).padStart(2,"0")}/${String(month).padStart(2,"0")}/${year} -🕒 Aɢᴇ: ${y} Yᴇᴀʀs ${m} Mᴏɴᴛʜs ${dy} Dᴀʏs - -📌 Tᴏᴛᴀʟ: -➤ ${totalMonths} Mᴏɴᴛʜs -➤ ${totalDays} Dᴀʏs -➤ ${totalHours} Hᴏᴜʀs -━━━━━━━━━━━━━━`; - - return api.sendMessage(msg, event.threadID); - - } catch (e) { - console.error(e); - return api.sendMessage("❌ Eʀʀᴏʀ", event.threadID); - } - } -}; diff --git a/scripts/cmds/all.js b/scripts/cmds/all.js deleted file mode 100644 index daa965df..00000000 --- a/scripts/cmds/all.js +++ /dev/null @@ -1,42 +0,0 @@ -module.exports = { - config: { - name: "all", - version: "1.2", - author: "NTKhang", - countDown: 5, - role: 1, - description: { - vi: "Tag tất cả thành viên trong nhóm chat của bạn", - en: "Tag all members in your group chat" - }, - category: "box chat", - guide: { - vi: " {pn} [nội dung | để trống]", - en: " {pn} [content | empty]" - } - }, - - onStart: async function ({ message, event, args }) { - const { participantIDs } = event; - const lengthAllUser = participantIDs.length; - const mentions = []; - let body = args.join(" ") || "@all"; - let bodyLength = body.length; - let i = 0; - for (const uid of participantIDs) { - let fromIndex = 0; - if (bodyLength < lengthAllUser) { - body += body[bodyLength - 1]; - bodyLength++; - } - if (body.slice(0, i).lastIndexOf(body[i]) != -1) - fromIndex = i; - mentions.push({ - tag: body[i], - id: uid, fromIndex - }); - i++; - } - message.reply({ body, mentions }); - } -}; \ No newline at end of file diff --git a/scripts/cmds/allbox.js b/scripts/cmds/allbox.js deleted file mode 100644 index 891e0561..00000000 --- a/scripts/cmds/allbox.js +++ /dev/null @@ -1,117 +0,0 @@ -const moment = require("moment-timezone"); - -module.exports = { - config: { - name: "allbox", - version: "1.0.0", - author: "MOHAMMAD AKASH", - countDown: 60, - role: 2, - shortDescription: "Manage all joined groups", - longDescription: "List all groups and reply to Ban, Unban, Delete data, or remove the bot", - category: "box chat", - usages: "[page number/all]", - }, - - onStart: async function ({ event, api, commandName }) { - const { threadID, messageID } = event; - - try { - const dataThreads = await api.getThreadList(100, null, ["INBOX"]); - const groups = dataThreads.filter(thread => thread.isGroup); - if (!groups.length) return api.sendMessage("There are currently no groups!", threadID); - - // Sort groups by messageCount descending - groups.sort((a, b) => b.messageCount - a.messageCount); - - let msg = "🎭 GROUP LIST 🎭\n\n"; - const groupid = []; - const groupName = []; - - groups.forEach((g, i) => { - msg += `${i + 1}. ${g.name}\n🔰TID: ${g.threadID}\n💌MessageCount: ${g.messageCount}\n\n`; - groupid.push(g.threadID); - groupName.push(g.name); - }); - - msg += "Reply to this message with: + number or 'all'"; - - api.sendMessage(msg, threadID, (err, info) => { - global.GoatBot.onReply.set(info.messageID, { - commandName, - messageID: info.messageID, - author: event.senderID, - groupid, - groupName, - unsendTimeout: setTimeout(() => api.unsendMessage(info.messageID), this.config.countDown * 1000) - }); - }, messageID); - - } catch (error) { - console.error(error); - api.sendMessage("Error fetching group list.", threadID); - } - }, - - onReply: async function ({ event, Reply, api }) { - const { author, groupid, groupName, messageID } = Reply; - if (event.senderID !== author) return; - - const args = event.body.trim().toLowerCase().split(" "); - clearTimeout(Reply.unsendTimeout); - - const action = args[0]; - const index = parseInt(args[1]) - 1; - - if (!["ban", "unban", "del", "out"].includes(action)) { - return api.sendMessage("Invalid action. Use: ban, unban, del, out", event.threadID); - } - - if (args[1] === "all") { - for (let i = 0; i < groupid.length; i++) { - await processGroup(action, i); - } - return api.sendMessage(`✅ ${action.toUpperCase()} executed on all groups.`, event.threadID); - } else { - if (index < 0 || index >= groupid.length) return api.sendMessage("Invalid number!", event.threadID); - await processGroup(action, index); - } - - async function processGroup(act, i) { - const idgr = groupid[i]; - const gName = groupName[i]; - const Threads = global.GoatBot.Threads; - - if (act === "ban") { - const data = (await Threads.getData(idgr)).data || {}; - data.banned = 1; - data.dateAdded = moment.tz("Asia/Dhaka").format("HH:mm:ss L"); - await Threads.setData(idgr, { data }); - global.data.threadBanned.set(idgr, { dateAdded: data.dateAdded }); - api.sendMessage(`✅ Banned: ${gName}`, event.threadID); - } - - if (act === "unban") { - const data = (await Threads.getData(idgr)).data || {}; - data.banned = 0; - data.dateAdded = null; - await Threads.setData(idgr, { data }); - global.data.threadBanned.delete(idgr); - api.sendMessage(`✅ Unbanned: ${gName}`, event.threadID); - } - - if (act === "del") { - const data = (await Threads.getData(idgr)).data || {}; - await Threads.delData(idgr, { data }); - api.sendMessage(`✅ Data deleted: ${gName}`, event.threadID); - } - - if (act === "out") { - api.removeUserFromGroup(api.getCurrentUserID(), idgr); - api.sendMessage(`✅ Bot removed from: ${gName}`, event.threadID); - } - } - - api.unsendMessage(messageID); - } -}; diff --git a/scripts/cmds/aniinfo.js b/scripts/cmds/aniinfo.js deleted file mode 100644 index a893cc0b..00000000 --- a/scripts/cmds/aniinfo.js +++ /dev/null @@ -1,79 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -module.exports = { - config: { - name: "aniinfo", - aliases: ["animeinfo", "a-info"], - version: "1.0", - author: "nexo_here", - countDown: 0, - role: 0, - description: "Get anime information using Jikan API", - category: "anime", - guide: { - en: "{pn} [anime name] — shows anime details using Jikan API" - } - }, - - onStart: async function ({ api, event, args }) { - const query = args.join(" "); - if (!query) { - return api.sendMessage("❗ Anime name missing. Try: aniinfo demon slayer", event.threadID); - } - - try { - const res = await axios.get(`https://api.jikan.moe/v4/anime?q=${encodeURIComponent(query)}&limit=1`); - const anime = res.data.data[0]; - - if (!anime) return api.sendMessage("❌ No results found.", event.threadID); - - const { - title, - title_english, - type, - episodes, - status, - score, - aired, - synopsis, - images, - genres, - url - } = anime; - - const msg = `🎬 Title: ${title_english || title} -📺 Type: ${type} -📊 Score: ${score || "?"}/10 -📡 Status: ${status} -🎞 Episodes: ${episodes || "?"} -📅 Aired: ${aired.string || "?"} -🎭 Genres: ${genres.map(g => g.name).join(", ")} - -📝 Description: -${synopsis?.substring(0, 400) || "No synopsis found."}... - -🔗 ${url}`; - - const imageURL = images.jpg.large_image_url; - const imgData = (await axios.get(imageURL, { responseType: "arraybuffer" })).data; - const filePath = path.join(__dirname, "aniinfo.jpg"); - fs.writeFileSync(filePath, imgData); - - api.sendMessage( - { - body: msg, - attachment: fs.createReadStream(filePath) - }, - event.threadID, - () => fs.unlinkSync(filePath), - event.messageID - ); - - } catch (err) { - console.error(err); - api.sendMessage("🚫 Error fetching anime data. Please try again.", event.threadID); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/anisearch.js b/scripts/cmds/anisearch.js deleted file mode 100644 index e834f118..00000000 --- a/scripts/cmds/anisearch.js +++ /dev/null @@ -1,70 +0,0 @@ -const axios = require('axios'); -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -async function getStreamFromURL(url) { - const response = await axios.get(url, { responseType: 'stream' }); - return response.data; -} - -async function fetchTikTokVideos(query) { - try { - const response = await axios.get(`https://lyric-search-neon.vercel.app/kshitiz?keyword=${query}`); - return response.data; - } catch (error) { - console.error(error); - return null; - } -} - -module.exports = { - config: { - name: "anisearch", - aliases: [], - author: "Vex_kshitiz", - version: "1.0", - shortDescription: { - en: "get anime edit", - }, - longDescription: { - en: "search for anime edits video", - }, - category: "media", - guide: { - en: "{p}{n} [query]", - }, - }, - onStart: async function ({ api, event, args }) { - api.setMessageReaction("✨", event.messageID, (err) => {}, true); - const query = args.join(' '); - const modifiedQuery = `${query} anime edit`; - - const videos = await fetchTikTokVideos(modifiedQuery); - - if (!videos || videos.length === 0) { - api.sendMessage({ body: `${query} not found.` }, event.threadID, event.messageID); - return; - } - - const selectedVideo = videos[Math.floor(Math.random() * videos.length)]; - const videoUrl = selectedVideo.videoUrl; - - if (!videoUrl) { - api.sendMessage({ body: 'Error: Video not found.' }, event.threadID, event.messageID); - return; - } - - try { - const videoStream = await getStreamFromURL(videoUrl); - - await api.sendMessage({ - body: ``, - attachment: videoStream, - }, event.threadID, event.messageID); - } catch (error) { - console.error(error); - api.sendMessage({ body: 'An error occurred while processing the video.\nPlease try again later.' }, event.threadID, event.messageID); - } - }, -}; \ No newline at end of file diff --git a/scripts/cmds/anti_isis_leave.js b/scripts/cmds/anti_isis_leave.js deleted file mode 100644 index c77e8dd6..00000000 --- a/scripts/cmds/anti_isis_leave.js +++ /dev/null @@ -1,79 +0,0 @@ -module.exports = { - config: { - name: "anti_isis_leave", - author: "MOHAMMAD AKASH", - version: "7.0", - shortDescription: "ISIS সংশ্লিষ্ট শব্দ পেলেই স্বয়ংক্রিয় লিভ", - category: "system" - }, - - onStart: async function () {}, - - // ========================== - // 🔥 All trigger list - // ========================== - triggers: [ - "我是 ISIS☝", - "我是杀人犯☝", - "☝️😭‼️‼️我是一名恐怖分子,我是一名 ISIS 恐怖分子,我是一名☝️😭‼️‼️" - ], - - // Universal checker - checkTrigger(text, triggers) { - if (!text) return false; - return triggers.some(trigger => text.includes(trigger)); - }, - - // ========================== - // 🔥 On chat event (message + bot add) - // ========================== - onChat: async function ({ event, api }) { - try { - const botID = api.getCurrentUserID(); - const triggers = this.triggers; - - // === ✔ MESSAGE CHECK === - if (event.body && this.checkTrigger(event.body, triggers)) { - await api.removeUserFromGroup(botID, event.threadID); - return; - } - - // === ✔ BOT ADDED CHECK === - if (event.logMessageType === "log:subscribe") { - const added = event.logMessageData?.addedParticipants?.find(p => p.userFbId == botID); - - if (added) { - api.getThreadInfo(event.threadID, async (err, info) => { - if (err) return; - - const groupName = info.threadName || ""; - if (this.checkTrigger(groupName, triggers)) { - await api.removeUserFromGroup(botID, event.threadID); - } - }); - } - } - - } catch (err) { - console.log("auto leave error:", err); - } - }, - - // ========================== - // 🔥 On group rename event - // ========================== - onEvent: async function ({ event, api }) { - try { - if (event.logMessageType === "log:thread-name") { - const botID = api.getCurrentUserID(); - const newName = event.logMessageData?.name || ""; - - if (this.checkTrigger(newName, this.triggers)) { - await api.removeUserFromGroup(botID, event.threadID); - } - } - } catch (err) { - console.log("rename auto leave error:", err); - } - } -}; diff --git a/scripts/cmds/antichangeinfobox.js b/scripts/cmds/antichangeinfobox.js deleted file mode 100644 index 64fc9e0d..00000000 --- a/scripts/cmds/antichangeinfobox.js +++ /dev/null @@ -1,222 +0,0 @@ -const { getStreamFromURL, uploadImgbb } = global.utils; - -module.exports = { - config: { - name: "antichangeinfobox", - version: "1.9", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Bật tắt chức năng chống thành viên đổi thông tin box chat của bạn", - en: "Turn on/off anti change info box" - }, - category: "box chat", - guide: { - vi: " {pn} avt [on | off]: chống đổi avatar box chat" - + "\n {pn} name [on | off]: chống đổi tên box chat" - + "\n {pn} nickname [on | off]: chống đổi nickname trong box chat" - + "\n {pn} theme [on | off]: chống đổi theme (chủ đề) box chat" - + "\n {pn} emoji [on | off]: chống đổi trạng emoji box chat", - en: " {pn} avt [on | off]: anti change avatar box chat" - + "\n {pn} name [on | off]: anti change name box chat" - + "\n {pn} nickname [on | off]: anti change nickname in box chat" - + "\n {pn} theme [on | off]: anti change theme (chủ đề) box chat" - + "\n {pn} emoji [on | off]: anti change emoji box chat" - } - }, - - langs: { - vi: { - antiChangeAvatarOn: "Đã bật chức năng chống đổi avatar box chat", - antiChangeAvatarOff: "Đã tắt chức năng chống đổi avatar box chat", - missingAvt: "Bạn chưa đặt avatar cho box chat", - antiChangeNameOn: "Đã bật chức năng chống đổi tên box chat", - antiChangeNameOff: "Đã tắt chức năng chống đổi tên box chat", - antiChangeNicknameOn: "Đã bật chức năng chống đổi nickname box chat", - antiChangeNicknameOff: "Đã tắt chức năng chống đổi nickname box chat", - antiChangeThemeOn: "Đã bật chức năng chống đổi theme (chủ đề) box chat", - antiChangeThemeOff: "Đã tắt chức năng chống đổi theme (chủ đề) box chat", - antiChangeEmojiOn: "Đã bật chức năng chống đổi emoji box chat", - antiChangeEmojiOff: "Đã tắt chức năng chống đổi emoji box chat", - antiChangeAvatarAlreadyOn: "Hiện tại box chat của bạn đang bật chức năng cấm thành viên đổi avatar", - antiChangeAvatarAlreadyOnButMissingAvt: "Hiện tại box chat của bạn đang bật chức năng cấm thành viên đổi avatar box chat chưa được đặt avatar", - antiChangeNameAlreadyOn: "Hiện tại box chat của bạn đang bật chức năng cấm thành viên đổi tên", - antiChangeNicknameAlreadyOn: "Hiện tại box chat của bạn đang bật chức năng cấm thành viên đổi nickname", - antiChangeThemeAlreadyOn: "Hiện tại box chat của bạn đang bật chức năng cấm thành viên đổi theme (chủ đề)", - antiChangeEmojiAlreadyOn: "Hiện tại box chat của bạn đang bật chức năng cấm thành viên đổi emoji" - }, - en: { - antiChangeAvatarOn: "Turn on anti change avatar box chat", - antiChangeAvatarOff: "Turn off anti change avatar box chat", - missingAvt: "You have not set avatar for box chat", - antiChangeNameOn: "Turn on anti change name box chat", - antiChangeNameOff: "Turn off anti change name box chat", - antiChangeNicknameOn: "Turn on anti change nickname box chat", - antiChangeNicknameOff: "Turn off anti change nickname box chat", - antiChangeThemeOn: "Turn on anti change theme box chat", - antiChangeThemeOff: "Turn off anti change theme box chat", - antiChangeEmojiOn: "Turn on anti change emoji box chat", - antiChangeEmojiOff: "Turn off anti change emoji box chat", - antiChangeAvatarAlreadyOn: "Your box chat is currently on anti change avatar", - antiChangeAvatarAlreadyOnButMissingAvt: "Your box chat is currently on anti change avatar but your box chat has not set avatar", - antiChangeNameAlreadyOn: "Your box chat is currently on anti change name", - antiChangeNicknameAlreadyOn: "Your box chat is currently on anti change nickname", - antiChangeThemeAlreadyOn: "Your box chat is currently on anti change theme", - antiChangeEmojiAlreadyOn: "Your box chat is currently on anti change emoji" - } - }, - - onStart: async function ({ message, event, args, threadsData, getLang }) { - if (!["on", "off"].includes(args[1])) - return message.SyntaxError(); - const { threadID } = event; - const dataAntiChangeInfoBox = await threadsData.get(threadID, "data.antiChangeInfoBox", {}); - async function checkAndSaveData(key, data) { - // dataAntiChangeInfoBox[key] = args[1] === "on" ? data : false; - if (args[1] === "off") - delete dataAntiChangeInfoBox[key]; - else - dataAntiChangeInfoBox[key] = data; - - await threadsData.set(threadID, dataAntiChangeInfoBox, "data.antiChangeInfoBox"); - message.reply(getLang(`antiChange${key.slice(0, 1).toUpperCase()}${key.slice(1)}${args[1].slice(0, 1).toUpperCase()}${args[1].slice(1)}`)); - } - switch (args[0]) { - case "avt": - case "avatar": - case "image": { - const { imageSrc } = await threadsData.get(threadID); - if (!imageSrc) - return message.reply(getLang("missingAvt")); - const newImageSrc = await uploadImgbb(imageSrc); - await checkAndSaveData("avatar", newImageSrc.image.url); - break; - } - case "name": { - const { threadName } = await threadsData.get(threadID); - await checkAndSaveData("name", threadName); - break; - } - case "nickname": { - const { members } = await threadsData.get(threadID); - await checkAndSaveData("nickname", members.map(user => ({ [user.userID]: user.nickname })).reduce((a, b) => ({ ...a, ...b }), {})); - break; - } - case "theme": { - const { threadThemeID } = await threadsData.get(threadID); - await checkAndSaveData("theme", threadThemeID); - break; - } - case "emoji": { - const { emoji } = await threadsData.get(threadID); - await checkAndSaveData("emoji", emoji); - break; - } - default: { - return message.SyntaxError(); - } - } - }, - - onEvent: async function ({ message, event, threadsData, role, api, getLang }) { - const { threadID, logMessageType, logMessageData, author } = event; - switch (logMessageType) { - case "log:thread-image": { - const dataAntiChange = await threadsData.get(threadID, "data.antiChangeInfoBox", {}); - if (!dataAntiChange.avatar && role < 1) - return; - return async function () { - // check if user not is admin or bot then change avatar back - if (role < 1 && api.getCurrentUserID() !== author) { - if (dataAntiChange.avatar != "REMOVE") { - message.reply(getLang("antiChangeAvatarAlreadyOn")); - api.changeGroupImage(await getStreamFromURL(dataAntiChange.avatar), threadID); - } - else { - message.reply(getLang("antiChangeAvatarAlreadyOnButMissingAvt")); - } - } - // else save new avatar - else { - const imageSrc = logMessageData.url; - if (!imageSrc) - return await threadsData.set(threadID, "REMOVE", "data.antiChangeInfoBox.avatar"); - - const newImageSrc = await uploadImgbb(imageSrc); - await threadsData.set(threadID, newImageSrc.image.url, "data.antiChangeInfoBox.avatar"); - } - }; - } - case "log:thread-name": { - const dataAntiChange = await threadsData.get(threadID, "data.antiChangeInfoBox", {}); - // const name = await threadsData.get(threadID, "data.antiChangeInfoBox.name"); - // if (name == false) - if (!dataAntiChange.hasOwnProperty("name")) - return; - return async function () { - if (role < 1 && api.getCurrentUserID() !== author) { - message.reply(getLang("antiChangeNameAlreadyOn")); - api.setTitle(dataAntiChange.name, threadID); - } - else { - const threadName = logMessageData.name; - await threadsData.set(threadID, threadName, "data.antiChangeInfoBox.name"); - } - }; - } - case "log:user-nickname": { - const dataAntiChange = await threadsData.get(threadID, "data.antiChangeInfoBox", {}); - // const nickname = await threadsData.get(threadID, "data.antiChangeInfoBox.nickname"); - // if (nickname == false) - if (!dataAntiChange.hasOwnProperty("nickname")) - return; - return async function () { - const { nickname, participant_id } = logMessageData; - - if (role < 1 && api.getCurrentUserID() !== author) { - message.reply(getLang("antiChangeNicknameAlreadyOn")); - api.changeNickname(dataAntiChange.nickname[participant_id], threadID, participant_id); - } - else { - await threadsData.set(threadID, nickname, `data.antiChangeInfoBox.nickname.${participant_id}`); - } - }; - } - case "log:thread-color": { - const dataAntiChange = await threadsData.get(threadID, "data.antiChangeInfoBox", {}); - // const themeID = await threadsData.get(threadID, "data.antiChangeInfoBox.theme"); - // if (themeID == false) - if (!dataAntiChange.hasOwnProperty("theme")) - return; - return async function () { - if (role < 1 && api.getCurrentUserID() !== author) { - message.reply(getLang("antiChangeThemeAlreadyOn")); - api.changeThreadColor(dataAntiChange.theme || "196241301102133", threadID); // 196241301102133 is default color - } - else { - const threadThemeID = logMessageData.theme_id; - await threadsData.set(threadID, threadThemeID, "data.antiChangeInfoBox.theme"); - } - }; - } - case "log:thread-icon": { - const dataAntiChange = await threadsData.get(threadID, "data.antiChangeInfoBox", {}); - // const emoji = await threadsData.get(threadID, "data.antiChangeInfoBox.emoji"); - // if (emoji == false) - if (!dataAntiChange.hasOwnProperty("emoji")) - return; - return async function () { - if (role < 1 && api.getCurrentUserID() !== author) { - message.reply(getLang("antiChangeEmojiAlreadyOn")); - api.changeThreadEmoji(dataAntiChange.emoji, threadID); - } - else { - const threadEmoji = logMessageData.thread_icon; - await threadsData.set(threadID, threadEmoji, "data.antiChangeInfoBox.emoji"); - } - }; - } - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/appstore.js b/scripts/cmds/appstore.js deleted file mode 100644 index 393306a1..00000000 --- a/scripts/cmds/appstore.js +++ /dev/null @@ -1,68 +0,0 @@ -const itunes = require("searchitunes"); -const { getStreamFromURL } = global.utils; - -module.exports = { - config: { - name: "appstore", - version: "1.2", - author: "NTKhang", - countDown: 5, - role: 0, - description: { - vi: "Tìm app trên appstore", - en: "Search app on appstore" - }, - category: "software", - guide: " {pn}: " - + "\n - Example:" - + "\n {pn} PUBG", - envConfig: { - limitResult: 3 - } - }, - - langs: { - vi: { - missingKeyword: "Bạn chưa nhập từ khóa", - noResult: "Không tìm thấy kết quả nào cho từ khóa %1" - }, - en: { - missingKeyword: "You haven't entered any keyword", - noResult: "No result found for keyword %1" - } - }, - - onStart: async function ({ message, args, commandName, envCommands, getLang }) { - if (!args[0]) - return message.reply(getLang("missingKeyword")); - let results = []; - try { - results = (await itunes({ - entity: "software", - country: "VN", - term: args.join(" "), - limit: envCommands[commandName].limitResult - })).results; - } - catch (err) { - return message.reply(getLang("noResult", args.join(" "))); - } - - if (results.length > 0) { - let msg = ""; - const pedningImages = []; - for (const result of results) { - msg += `\n\n- ${result.trackCensoredName} by ${result.artistName}, ${result.formattedPrice} and rated ${"🌟".repeat(result.averageUserRating)} (${result.averageUserRating.toFixed(1)}/5)` - + `\n- ${result.trackViewUrl}`; - pedningImages.push(await getStreamFromURL(result.artworkUrl512 || result.artworkUrl100 || result.artworkUrl60)); - } - message.reply({ - body: msg, - attachment: await Promise.all(pedningImages) - }); - } - else { - message.reply(getLang("noResult", args.join(" "))); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/assets/font/BeVietnamPro-Bold.ttf b/scripts/cmds/assets/font/BeVietnamPro-Bold.ttf deleted file mode 100644 index 68c7a502..00000000 Binary files a/scripts/cmds/assets/font/BeVietnamPro-Bold.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/BeVietnamPro-Regular.ttf b/scripts/cmds/assets/font/BeVietnamPro-Regular.ttf deleted file mode 100644 index 2d57ee50..00000000 Binary files a/scripts/cmds/assets/font/BeVietnamPro-Regular.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/BeVietnamPro-SemiBold.ttf b/scripts/cmds/assets/font/BeVietnamPro-SemiBold.ttf deleted file mode 100644 index 4b9bf7b6..00000000 Binary files a/scripts/cmds/assets/font/BeVietnamPro-SemiBold.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/CourierPrime-Bold.ttf b/scripts/cmds/assets/font/CourierPrime-Bold.ttf deleted file mode 100644 index 7e6b2228..00000000 Binary files a/scripts/cmds/assets/font/CourierPrime-Bold.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/CourierPrime-Regular.ttf b/scripts/cmds/assets/font/CourierPrime-Regular.ttf deleted file mode 100644 index 4af1ff54..00000000 Binary files a/scripts/cmds/assets/font/CourierPrime-Regular.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/Kanit-SemiBoldItalic.ttf b/scripts/cmds/assets/font/Kanit-SemiBoldItalic.ttf deleted file mode 100644 index 01b86eb0..00000000 Binary files a/scripts/cmds/assets/font/Kanit-SemiBoldItalic.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/Noto Emoji Bold 700.ttf b/scripts/cmds/assets/font/Noto Emoji Bold 700.ttf deleted file mode 100644 index 37bc8404..00000000 Binary files a/scripts/cmds/assets/font/Noto Emoji Bold 700.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/Noto Emoji Light 300.ttf b/scripts/cmds/assets/font/Noto Emoji Light 300.ttf deleted file mode 100644 index a6133b56..00000000 Binary files a/scripts/cmds/assets/font/Noto Emoji Light 300.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/Noto Emoji Medium 500.ttf b/scripts/cmds/assets/font/Noto Emoji Medium 500.ttf deleted file mode 100644 index 007e7abd..00000000 Binary files a/scripts/cmds/assets/font/Noto Emoji Medium 500.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/Noto Emoji Regular 400.ttf b/scripts/cmds/assets/font/Noto Emoji Regular 400.ttf deleted file mode 100644 index 69f38117..00000000 Binary files a/scripts/cmds/assets/font/Noto Emoji Regular 400.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/Noto Emoji SemiBold 600.ttf b/scripts/cmds/assets/font/Noto Emoji SemiBold 600.ttf deleted file mode 100644 index 85b656b8..00000000 Binary files a/scripts/cmds/assets/font/Noto Emoji SemiBold 600.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/NotoSansBengali-Bold.ttf b/scripts/cmds/assets/font/NotoSansBengali-Bold.ttf deleted file mode 100644 index 267c839f..00000000 Binary files a/scripts/cmds/assets/font/NotoSansBengali-Bold.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/NotoSansBengali-Regular.ttf b/scripts/cmds/assets/font/NotoSansBengali-Regular.ttf deleted file mode 100644 index 3e66c1a9..00000000 Binary files a/scripts/cmds/assets/font/NotoSansBengali-Regular.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/README.txt b/scripts/cmds/assets/font/README.txt deleted file mode 100644 index 641464da..00000000 --- a/scripts/cmds/assets/font/README.txt +++ /dev/null @@ -1 +0,0 @@ -Ekhane Font Download kore Rakhle Canvas e seta Support korbe.... \ No newline at end of file diff --git a/scripts/cmds/assets/font/kalpurush ANSI.ttf b/scripts/cmds/assets/font/kalpurush ANSI.ttf deleted file mode 100644 index fc760f0b..00000000 Binary files a/scripts/cmds/assets/font/kalpurush ANSI.ttf and /dev/null differ diff --git a/scripts/cmds/assets/font/kalpurush.ttf b/scripts/cmds/assets/font/kalpurush.ttf deleted file mode 100644 index 537cf8da..00000000 Binary files a/scripts/cmds/assets/font/kalpurush.ttf and /dev/null differ diff --git a/scripts/cmds/assets/hubble/nasa.json b/scripts/cmds/assets/hubble/nasa.json deleted file mode 100644 index 85aeef91..00000000 --- a/scripts/cmds/assets/hubble/nasa.json +++ /dev/null @@ -1,2930 +0,0 @@ -[ - { - "date": "January 1 2019", - "image": "january-1-2019-galaxy-leo-iv.jpg", - "name": "Galaxy Leo IV", - "caption": "Leo IV is one of more than a dozen ultra-faint dwarf galaxies near the Milky Way. These galaxies are dominated by dark matter, an invisible substance that makes up most of the universe's mass.", - "url": "https://hubblesite.org/contents/media/images/2012/26/3054-Image.html", - "year": 2012 - }, - { - "date": "January 2 2019", - "image": "january-2-2019-galaxy-cluster-sdss-j1004-4112.jpg", - "name": "Galaxy Cluster SDSS J1004+4112", - "caption": "This picture captures a galaxy cluster called SDSS J1004+4112 that's so massive that its gravity bends light from galaxies behind it. The light of a distant quasar (the brilliant core of an active galaxy) has been bent around the cluster, appearing in five places in this image.", - "url": "https://hubblesite.org/contents/media/images/2006/23/1929-Image.html", - "year": 2005 - }, - { - "date": "January 3 2019", - "image": "january-3-2019-ngc-4302-and-ngc-4298.png", - "name": "NGC 4302 and NGC 4298", - "caption": "This image captures two spiral galaxies. They look quite different because we see them from different angles. The edge-on galaxy (on the left) is called NGC 4302, and the other is NGC 4298.", - "url": "https://hubblesite.org/contents/media/images/2017/14/4019-Image.html", - "year": 2017 - }, - { - "date": "January 4 2019", - "image": "january-4-2019-saturn-in-infrared.jpg", - "name": "Saturn in Infrared", - "caption": "This false-color image of Saturn captures infrared light reflecting off the planet. The image also captures two of Saturn's moons, Dione in the lower left and Tethys in the upper right.", - "url": "https://hubblesite.org/contents/media/images/1998/18/659-Image.html", - "year": 1998 - }, - { - "date": "January 5 2019", - "image": "january-5-2019-galaxy-ngc-2841.jpg", - "name": "Galaxy NGC 2841", - "caption": "Young, blue stars and dark lanes of dust trace the winding arms of NGC 2841. Winds from the young stars may have cleared out the gas needed for additional star birth and halted star formation in the spiral galaxy.", - "url": "https://hubblesite.org/contents/media/images/2011/06/2821-Image.html", - "year": 2010 - }, - { - "date": "January 6 2019", - "image": "january-6-2019-interacting-galaxies-arp-220.jpg", - "name": "Interacting Galaxies Arp 220", - "caption": "Arp 220 is the result of a collision between two spiral galaxies that began 700 millions years ago. Located about 250 million light-years from Earth, it is one of the nearest galaxy mergers to our planet.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2314-Image.html", - "year": 2006 - }, - { - "date": "January 7 2019", - "image": "january-7-2019-galaxy-ngc-2841.jpg", - "name": "Galaxy NGC 2841", - "caption": "Young, blue stars and dark lanes of dust trace the winding arms of NGC 2841. Winds from the young, super-hot stars may have cleared out the gas needed for additional star birth and halted star formation in the spiral galaxy.", - "url": "https://hubblesite.org/contents/media/images/2011/06/2821-Image.html", - "year": 2010 - }, - { - "date": "January 8 2019", - "image": "january-8-2019-galaxy-ngc-2976.jpg", - "name": "Galaxy NGC 2976", - "caption": "This picture shows the inner region of NGC 2976, located roughly 11 million light-years away in the constellation Ursa Major. Despite the lack of well-defined arms visible in this image, NGC 2976 is a spiral galaxy.", - "url": "https://hubblesite.org/contents/media/images/2010/05/2682-Image.html", - "year": 2007 - }, - { - "date": "January 9 2019", - "image": "january-9-2019-galaxy-ngc-1427a.jpg", - "name": "Galaxy NGC 1427A", - "caption": "This image captures NGC 1427A, an irregular dwarf galaxy that is warped by the gravitational influence of its larger galactic neighbors in the Fornax galaxy cluster.", - "url": "https://hubblesite.org/contents/media/images/2005/09/1662-Image.html", - "year": 2003 - }, - { - "date": "January 10 2019", - "image": "january-10-2019-galaxy-centaurus-a.jpg", - "name": "Galaxy Centaurus A", - "caption": "This image captures a turbulent firestorm of star birth along a nearly edge-on dust disk girdling nearby galaxy Centaurus A. Brilliant clusters of young, blue stars lie along the edge of the dark dust lane.", - "url": "https://hubblesite.org/contents/media/images/1998/14/637-Image.html", - "year": 1998 - }, - { - "date": "January 11 2019", - "image": "january-11-2019-ngc-2392.jpg", - "name": "NGC 2392", - "caption": "NGC 2392 contains the glowing remains of a dying Sun-like star. The bright, central region is material being blown away by the nebula's central star.", - "url": "https://hubblesite.org/contents/media/images/2000/07/940-Image.html", - "year": 2000 - }, - { - "date": "January 12 2019", - "image": "january-12-2019-andromeda-galaxy.jpg", - "name": "Andromeda Galaxy", - "caption": "Over 100 million stars are on display in this portion of the Andromeda galaxy, located over 2 million light-years away. This portrait of our galactic neighbor is the largest image yet assembled by Hubble.", - "url": "https://hubblesite.org/contents/news-releases/2015/news-2015-02.html", - "year": 2011 - }, - { - "date": "January 13 2019", - "image": "january-13-2019-andromeda-galaxy.jpg", - "name": "Andromeda Galaxy", - "caption": "Over 100 million stars are on display in this portion of the Andromeda galaxy, located over 2 million light-years away. This portrait of our galactic neighbor is the largest image yet assembled by Hubble.", - "url": "https://hubblesite.org/contents/news-releases/2015/news-2015-02.html", - "year": 2011 - }, - { - "date": "January 14 2019", - "image": "january-14-2019-galaxy-ngc-2768.jpg", - "name": "Galaxy NGC 2768", - "caption": "NGC 2768 is an elliptical galaxy located 65 million light-years away in the constellation Ursa Major. The galaxy hosts a supermassive black hole, fueling jets of material in its active center.", - "url": "https://hubblesite.org/contents/news-releases/2015/news-2015-28.html", - "year": 2003 - }, - { - "date": "January 15 2019", - "image": "january-15-2019-galaxy-ngc-4710.jpg", - "name": "Galaxy NGC 4710", - "caption": "The magnificent galaxy NGC 4710 is tilted nearly edge-on to our view from Earth. This perspective allows the central bulge of stars to be easily distinguished from the galaxy's pancake-flat disk of stars, dust and gas.", - "url": "https://hubblesite.org/contents/media/images/2009/30/2643-Image.html", - "year": 2006 - }, - { - "date": "January 16 2019", - "image": "january-16-2019-lindsay-shapley-ring-galaxy.jpg", - "name": "Lindsay-Shapley Ring Galaxy", - "caption": "The striking blue ring of the Lindsay-Shapely Ring Galaxy (AM 0644-741) is comprised of brilliant star clusters. About 150,000 light-years across, the ring structure is larger than our galaxy, the Milky Way.", - "url": "https://hubblesite.org/contents/media/images/2004/15/1520-Image.html", - "year": 2004 - }, - { - "date": "January 17 2019", - "image": "january-17-2019-supernova-1987a.png", - "name": "Supernova 1987A", - "caption": "The remnant of Supernova 1987A, located in a neighboring galaxy called the Large Magellanic Cloud, appears at the center of this image. The red, gaseous clouds that surround it fuel a firestorm of new star formation.", - "url": "https://hubblesite.org/contents/media/images/2017/08/3987-Image.html", - "year": 2017 - }, - { - "date": "January 18 2019", - "image": "january-18-2019-galaxy-ngc-4163.jpg", - "name": "Galaxy NGC 4163", - "caption": "This swarm of stars is the dwarf galaxy NGC 4163, located 10 million light-years from Earth in the constellation Canes Venatici. Irregularly shaped red blobs are regions of active star formation.", - "url": "https://hubblesite.org/contents/media/images/2009/19/2556-Image.html", - "year": 2004 - }, - { - "date": "January 19 2019", - "image": "january-19-2019-whirlpool-galaxy.jpg", - "name": "Whirlpool Galaxy", - "caption": "This image provides a close-up of some of the winding arms in the Whirlpool galaxy. Tracing the arms of the spiral galaxy are red-colored clouds of hydrogen gas, which are giving birth to new stars.", - "url": "https://hubblesite.org/contents/media/images/2005/21/1731-Image.html", - "year": 2005 - }, - { - "date": "January 20 2019", - "image": "january-20-2019-whirlpool-galaxy.jpg", - "name": "Whirlpool Galaxy", - "caption": "This image provides a close-up of some of the winding arms in the Whirlpool galaxy. Tracing the arms of the spiral galaxy are red-colored clouds of hydrogen gas, which are giving birth to new stars.", - "url": "https://hubblesite.org/contents/media/images/2005/21/1731-Image.html", - "year": 2005 - }, - { - "date": "January 21 2019", - "image": "january-21-2019-reflection-nebula-ngc-1999.jpg", - "name": "Reflection Nebula NGC 1999", - "caption": "NGC 1999 is a reflection nebula. It does not emit any visible light of its own but shines only because the light from the star just to the left of the center illuminates the nebula's dust.", - "url": "https://hubblesite.org/contents/media/images/2000/10/952-Image.html", - "year": 2000 - }, - { - "date": "January 22 2019", - "image": "january-22-2019-whirlpool-galaxy.jpg", - "name": "Whirlpool Galaxy", - "caption": "This image captures the winding arms of the Whirlpool galaxy. It highlights the galaxy's graceful, curving arms, pink star-forming regions and brilliant blue strands of star clusters.", - "url": "https://hubblesite.org/contents/media/images/2011/03/2809-Image.html", - "year": 2005 - }, - { - "date": "January 23 2019", - "image": "january-23-2019-asteroid-ceres.jpg", - "name": "Asteroid Ceres", - "caption": "The largerst known asteriod, Ceres, is approximately 590 miles across, about the size of Texas. It resides with tens of thousands of other asteroids in the main asteroid belt.", - "url": "https://hubblesite.org/contents/media/images/2005/27/1763-Image.html", - "year": 2004 - }, - { - "date": "January 24 2019", - "image": "january-24-2019-jupiter-and-moons.jpg", - "name": "Jupiter and Moons", - "caption": "Three of Jupiter's moons cast their shadows on the planet. Callisto and Io are visible in the lower left and upper right, respectively, but Europa (whose shadow is on Jupiter's left edge) is out of the frame.", - "url": "https://hubblesite.org/contents/media/images/2015/05/3488-Image.html", - "year": 2015 - }, - { - "date": "January 25 2019", - "image": "january-25-2019-galaxy-ngc-4013.jpg", - "name": "Galaxy NGC 4013", - "caption": "A dark band of dust bisects the spiral galaxy NGC 4013. This edge-on galaxy is located 55 million light-years away in the constellation Ursa Major.", - "url": "https://hubblesite.org/contents/media/images/2001/07/1022-Image.html", - "year": 2000 - }, - { - "date": "January 26 2019", - "image": "january-26-2019-comet-332p-ikeya-murakami.jpg", - "name": "Comet 332P/Ikeya-Murakami", - "caption": "This image reveals the ancient comet 332P/Ikeya-Murakami disintegrating as it approaches the Sun. It is one of the sharpest views ever captured of an icy comet breaking apart.", - "url": "https://hubblesite.org/contents/media/images/2016/35/3785-Image.html", - "year": 2016 - }, - { - "date": "January 27 2019", - "image": "january-27-2019-comet-332p-ikeya-murakami.jpg", - "name": "Comet 332P/Ikeya-Murakami", - "caption": "This image reveals the ancient comet 332P/Ikeya-Murakami disintegrating as it approaches the Sun. It is one of the sharpest views ever captured of an icy comet breaking apart.", - "url": "https://hubblesite.org/contents/media/images/2016/35/3784-Image.html", - "year": 2016 - }, - { - "date": "January 28 2019", - "image": "january-28-2019-comet-ikeya-murakami.jpg", - "name": "Comet Ikeya-Murakami", - "caption": "This image reveals the ancient comet 332P/Ikeya-Murakami disintegrating as it approached the Sun in 2016. The comet debris consists of building-size chunks near the center of the image. The main nucleus of the comet is the bright object at lower left.", - "url": "https://hubblesite.org/contents/media/images/2016/35/3784-Image.html", - "year": 2016 - }, - { - "date": "January 29 2019", - "image": "january-29-2019-galaxy-ngc-2787.jpg", - "name": "Galaxy NGC 2787", - "caption": "Galaxy NGC 2787 is located 24 million light-years from Earth in the constellation Ursa Major. Arms of dark dust encircle the galaxy's bright center. The points of light scattered around the galaxy are huge collections of old stars known as globuar clusters.", - "url": "https://hubblesite.org/contents/media/images/2002/07/1164-Image.html", - "year": 1999 - }, - { - "date": "January 30 2019", - "image": "january-30-2019-galaxy-ngc-5584.jpg", - "name": "Galaxy NGC 5584", - "caption": "The brilliant, blue glow of young stars traces the graceful spiral arms of galaxy NGC 5584. Thin, dark dust lanes appear to be flowing from the yellowish core, where older stars reside.", - "url": "https://hubblesite.org/contents/media/images/2011/08/2824-Image.html?news=true", - "year": 2010 - }, - { - "date": "January 31 2019", - "image": "january-31-2019-starfield-in-the-large-magellanic-cloud.jpg", - "name": "Starfield in the Large Magellanic Cloud", - "caption": "Over 10,000 stars appear in this image, which covers a region about 130 light-years wide in a nearby galaxy called the Large Magellanic Cloud. The faintest stars in the picture are some 100 million times dimmer than the human eye can see.", - "url": "https://hubblesite.org/contents/media/images/1999/44/922-Image.html", - "year": 1996 - }, - { - "date": "February 1 2019", - "image": "february-1-2019-carina-nebula-pillars.jpg", - "name": "Carina Nebula Pillars", - "caption": "These cosmic pinnacles lie within a tempestuous stellar nursery called the Carina Nebula. Infant stars buried inside the pillars fire off jets of gas that stream away from the towering peaks.", - "url": "https://hubblesite.org/contents/media/images/2010/13/2707-Image.html", - "year": 2010 - }, - { - "date": "February 2 2019", - "image": "february-2-2019-carina-nebula-pillars.jpg", - "name": "Carina Nebula Pillars", - "caption": "These cosmic pinnacles lie within a tempestuous stellar nursery called the Carina Nebula. Infant stars buried inside the pillars fire off jets of gas that stream away from the towering peaks.", - "url": "https://hubblesite.org/contents/media/images/2010/13/2707-Image.html", - "year": 2010 - }, - { - "date": "February 3 2019", - "image": "february-3-2019-bow-shock-around-ll-orionis.jpg", - "name": "Bow Shock Around LL Orionis", - "caption": "Named for the crescent-shaped wave made by a ship as it moves through water, a bow shock can be created in space when streams of gas collide. This image captures the bow shock around the star LL Orionis.", - "url": "https://hubblesite.org/contents/media/images/2002/05/1149-Image.html", - "year": 1995 - }, - { - "date": "February 4 2019", - "image": "february-4-2019-galaxy-cluster-macs-j0717-5-3745.jpg", - "name": "Galaxy Cluster MACS J0717.5+3745", - "caption": "Nearly every object in this image is a distant galaxy in the cluster MACS J0717.5+3745. Some faint arcs and streaks in the image are even farther galaxies whose light has been bent by the powerful gravity of the massive cluster.", - "url": "https://hubblesite.org/contents/media/images/2013/44/3251-Image.html", - "year": 2005 - }, - { - "date": "February 5 2019", - "image": "february-5-2019-asteroid-6478-gault.png", - "name": "Asteroid (6478) Gault", - "caption": "Hubble viewed the gradual self-destruction of the asteroid (6478) Gault caused by the long-term effects of sunlight. Dusty material ejected from the asteroid formed two comet-like tails 500,000 and 3,000 miles long.", - "url": "https://hubblesite.org/contents/media/images/2019/22/4379-Image.html", - "year": 2019 - }, - { - "date": "February 6 2019", - "image": "february-6-2019-planetary-nebula-ngc-2440.jpg", - "name": "Planetary Nebula NGC 2440", - "caption": "Planetary nebula NGC 2440 is a relic of a star once like our Sun that has cast off its outer layers of gas, forming a colorful cocoon around the star's remaining core.", - "url": "https://hubblesite.org/contents/media/images/2007/09/2058-Image.html", - "year": 2007 - }, - { - "date": "February 7 2019", - "image": "february-7-2019-thackeray-s-globules.jpg", - "name": "Thackeray's Globules", - "caption": "These dense, dark dust clouds, named \"Thackeray's globules\" after astronomer A.D. Thackeray, are silhouetted against stars and bright gas clouds of the star-forming region IC 2944. The largest globule is actually two separate, overlapping clouds.", - "url": "https://hubblesite.org/contents/media/images/2002/01/1127-Image.html", - "year": 1999 - }, - { - "date": "February 8 2019", - "image": "february-8-2019-monkey-head-nebula.jpg", - "name": "Monkey Head Nebula", - "caption": "This image reveals carved knots of gas and dust in a small portion of the Monkey Head Nebula. The nebula is a star-forming region that hosts dusky dust clouds silhouetted against glowing gas.", - "url": "https://hubblesite.org/contents/media/images/2014/18/3336-Image.html", - "year": 2014 - }, - { - "date": "February 9 2019", - "image": "february-9-2019-monkey-head-nebula.jpg", - "name": "Monkey Head Nebula", - "caption": "This image reveals carved knots of gas and dust in a small portion of the Monkey Head Nebula. The nebula is a star-forming region that hosts dusky dust clouds silhouetted against glowing gas.", - "url": "https://hubblesite.org/contents/media/images/2014/18/3336-Image.html", - "year": 2014 - }, - { - "date": "February 10 2019", - "image": "february-10-2019-brown-dwarf-candidate-chxr-73-b.jpg", - "name": "Brown Dwarf Candidate CHXR 73 B", - "caption": "The bright spot at lower right is a suspected brown dwarf, an object bigger than a planet but smaller than a star. Named CHXR 73 B, it orbits a red dwarf star dubbed CHXR 73, which is much less massive than the Sun.", - "url": "https://hubblesite.org/contents/media/images/2006/31/1946-Image.html", - "year": 2005 - }, - { - "date": "February 11 2019", - "image": "february-11-2019-thackeray-s-globules.jpg", - "name": "Thackeray's Globules", - "caption": "These dense, dark dust clouds, named \"Thackeray's globules\" after astronomer A.D. Thackeray, are silhouetted against stars and bright gas clouds of the star-forming region IC 2944. The largest globule is actually two separate, overlapping clouds.", - "url": "https://hubblesite.org/contents/media/images/2002/01/1127-Image.html", - "year": 2001 - }, - { - "date": "February 12 2019", - "image": "february-12-2019-lagoon-nebula.png", - "name": "Lagoon Nebula", - "caption": "This image zooms into the heart of a vast star-forming region called the Lagoon Nebula. A massive young star at the center of the image is blasting radiation and stellar winds, carving shapes into the surrounding gas and dust.", - "url": "https://hubblesite.org/contents/media/images/2018/21/4150-Image.html", - "year": 2018 - }, - { - "date": "February 13 2019", - "image": "february-13-2019-nebula-sharpless-2-106.jpg", - "name": "Nebula Sharpless 2-106", - "caption": "This star-forming region, called Sharpless 2-106, looks like a celestial angel. The \"wings\" of the nebula are twin lobes of hot gas that stretch outward from a massive, young star near the center of the image.", - "url": "https://hubblesite.org/contents/media/images/2011/38/2932-Image.html", - "year": 2011 - }, - { - "date": "February 14 2019", - "image": "february-14-2019-colliding-galaxies-arp-272.jpg", - "name": "Colliding Galaxies Arp 272", - "caption": "Arp 272 is a collision between two spiral galaxies, linked by their swirling arms. The galaxies are members of the Hercules Galaxy cluster and are located roughly 450 million light-years from Earth.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2334-Image.html", - "year": 2007 - }, - { - "date": "February 15 2019", - "image": "february-15-2019-galaxy-i-zwicky-18.jpg", - "name": "Galaxy I Zwicky 18", - "caption": "This image captures the irregular dwarf galaxy I Zwicky 18 and a companion galaxy to its upper right. The two galaxies are interacting, triggering star formation in I Zwicky 18.", - "url": "https://hubblesite.org/contents/media/images/2004/35/1621-Image.html", - "year": 1998 - }, - { - "date": "February 16 2019", - "image": "february-16-2019-antennae-galaxies.jpg", - "name": "Antennae Galaxies", - "caption": "The two merging spiral galaxies that comprise the Antennae galaxies began their interaction only a few hundred million years ago. Over the course of the merger, billions of stars will be formed.", - "url": "https://hubblesite.org/contents/media/images/2006/46/1995-Image.html", - "year": 2005 - }, - { - "date": "February 17 2019", - "image": "february-17-2019-dwarf-galaxy-kiso-5639.jpg", - "name": "Dwarf Galaxy Kiso 5639", - "caption": "Hubble captured a firestorm of star birth lighting up one end of this dwarf galaxy. Called Kiso 5639, it is a member of a class of \"tadpole\" galaxies so named because of their bright heads and elongated tails.", - "url": "https://hubblesite.org/contents/media/images/2016/23/3754-Image.html", - "year": 2015 - }, - { - "date": "February 18 2019", - "image": "february-18-2019-herbig-haro-24.jpg", - "name": "Herbig-Haro 24", - "caption": "A partially obscured, newborn star near the center of this image is shooting twin jets into the surrounding gas and dust. The shocks from the collision light up patches of nebulosity collectively called Herbig-Haro 24.", - "url": "https://hubblesite.org/contents/media/images/2015/42/3656-Image.html", - "year": 2014 - }, - { - "date": "February 19 2019", - "image": "february-19-2019-monkey-head-nebula.jpg", - "name": "Monkey Head Nebula", - "caption": "This image reveals carved knots of gas and dust in a small portion of the Monkey Head Nebula. The nebula is a star-forming region that hosts dusky dust clouds silhouetted against glowing gas.", - "url": "https://hubblesite.org/contents/media/images/2014/18/3336-Image.html", - "year": 2014 - }, - { - "date": "February 20 2019", - "image": "february-20-2019-monkey-head-nebula.jpg", - "name": "Monkey Head Nebula", - "caption": "This image reveals carved knots of gas and dust in a small portion of the Monkey Head Nebula. The nebula is a star-forming region that hosts dusky dust clouds silhouetted against glowing gas.", - "url": "https://hubblesite.org/contents/media/images/2014/18/3336-Image.html", - "year": 2014 - }, - { - "date": "February 21 2019", - "image": "february-21-2019-monkey-head-nebula.jpg", - "name": "Monkey Head Nebula", - "caption": "This image reveals carved knots of gas and dust in a small portion of the Monkey Head Nebula. The nebula is a star-forming region that hosts dusky dust clouds silhouetted against glowing gas.", - "url": "https://hubblesite.org/contents/media/images/2014/18/3336-Image.html", - "year": 2014 - }, - { - "date": "February 22 2019", - "image": "february-22-2019-spiral-galaxy-ngc-1313.jpg", - "name": "Spiral Galaxy NGC 1313", - "caption": "This image resolves stars in the center of the barred spiral galaxy NGC 1313. The galaxy is roughly 14 million light-years away in the constellation Reticulum.", - "url": "https://hubblesite.org/contents/media/images/2007/05/2044-Image.html", - "year": 2004 - }, - { - "date": "February 23 2019", - "image": "february-23-2019-galaxies-in-the-goods-north-field.png", - "name": "Galaxies in the GOODS-North Field", - "caption": "This image captures about 15,000 galaxies stretching back through 11 billion years of cosmic history. Hubble examined this part of the sky, located near the Big Dipper and called the GOODS-North field, as part of the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2018/35/4219-Image.html", - "year": 2013 - }, - { - "date": "February 24 2019", - "image": "february-24-2019-saturn-and-moons.jpg", - "name": "Saturn and Moons", - "caption": "In this image, four moons of Saturn are passing in front of the giant planet. The large, orange moon Titan casts a large shadow on the northern pole. Smaller moons Mimas, Dione and Enceladus appear as white dots.", - "url": "https://hubblesite.org/contents/media/images/2009/12/2508-Image.html", - "year": 2009 - }, - { - "date": "February 25 2019", - "image": "february-25-2019-mars.jpg", - "name": "Mars", - "caption": "This image captures springtime in the northern hemisphere of Mars. The northern polar ice cap has receded to its core of solid water-ice several hundred miles across. Morning clouds appear along the planet's western (left) limb.", - "url": "https://hubblesite.org/contents/media/images/1995/16/280-Image.html?news=true", - "year": 1995 - }, - { - "date": "February 26 2019", - "image": "february-26-2019-bubble-nebula.jpg", - "name": "Bubble Nebula", - "caption": "An enormous bubble is being blown into space by a super-hot, massive star. The Bubble Nebula is roughly seven light-years across and is located 7,100 light-years away in the constellation Cassiopeia.", - "url": "https://hubblesite.org/contents/media/images/2016/13/3725-Image.html", - "year": 2016 - }, - { - "date": "February 27 2019", - "image": "february-27-2019-little-ghost-nebula.jpg", - "name": "Little Ghost Nebula", - "caption": "The Little Ghost Nebula appears as a small, ghostly cloud surrounding a dying star. It is found in the constellation Ophiuchus between 2,000 and 5,000 light-years away.", - "url": "https://hubblesite.org/contents/media/images/2002/25/1251-Image.html", - "year": 2002 - }, - { - "date": "February 28 2019", - "image": "february-28-2019-galactic-center.jpg", - "name": "Galactic Center", - "caption": "This infrared image of the center of our Milky Way galaxy reveals a population of massive stars and complex structures in the hot ionized gas that swirls around the galactic core.", - "url": "https://hubblesite.org/contents/media/images/2009/02/2453-Image.html", - "year": 2008 - }, - { - "date": "February 29 2019", - "image": "february-29-2019-sweeps-star-field.jpg", - "name": "SWEEPS Star Field", - "caption": "Hubble peered into the crowded central bulge of our galaxy 26,000 light-years away and collected information for 180,000 stars as part of a survey called the Sagittarius Window Eclipsing Extrasolar Planet Search (SWEEPS).", - "url": "https://hubblesite.org/contents/news-releases/2011/news-2011-16.html", - "year": 2004 - }, - { - "date": "March 1 2019", - "image": "march-1-2019-galaxy-cluster-rcs2-032727-132623.jpg", - "name": "Galaxy Cluster RCS2 032727-132623", - "caption": "The light from a distant galaxy, nearly 10 billion light-years away, has been warped into arcs and streaks by the gravity of galaxy cluster RCS2 032727-132623. The cluster acts as a gravitational lens, bending and amplifying light from the background galaxy.", - "url": "https://hubblesite.org/contents/media/images/2012/08/2977-Image.html", - "year": 2011 - }, - { - "date": "March 2 2019", - "image": "march-2-2019-pluto-system.jpg", - "name": "Pluto System", - "caption": "This image, taken through a red filter, captures Pluto and three of its satellites. The largest object in the image is Pluto and the second largest is Charon. Two smaller moons appear below them.", - "url": "https://hubblesite.org/contents/media/images/2006/15/1893-Image.html", - "year": 2006 - }, - { - "date": "March 3 2019", - "image": "march-3-2019-globular-cluster-ngc-6397.jpg", - "name": "Globular Cluster NGC 6397", - "caption": "This image captures about 200 stars in the globular cluster NGC 6397. The density of this star cluster is so low that Hubble can see right through the cluster and resolve far more-distant background galaxies behind it.", - "url": "https://hubblesite.org/contents/media/images/1994/41/198-Image.html", - "year": 1994 - }, - { - "date": "March 4 2019", - "image": "march-4-2019-galaxy-fornax-a.jpg", - "name": "Galaxy Fornax A", - "caption": "The dust lanes and star clusters of this giant elliptical galaxy, known as Fornax A, give evidence that the galaxy formed from a past merger of two gas-rich galaxies. It is also one of the strongest sources of radio emission in the sky.", - "url": "https://hubblesite.org/contents/media/images/2005/11/1671-Image.html", - "year": 2003 - }, - { - "date": "March 5 2019", - "image": "march-5-2019-galaxy-ngc-1512.jpg", - "name": "Galaxy NGC 1512", - "caption": "The core of the barred spiral galaxy NGC 1512 is unique for its stunning 2,400-light-year-wide circle of infant star clusters, called a \"circumnuclear\" starburst ring. Starbursts are episodes of vigorous star formation.", - "url": "https://hubblesite.org/contents/media/images/2001/16/1059-Image.html", - "year": 1999 - }, - { - "date": "March 6 2019", - "image": "march-6-2019-beta-pictoris-disk.jpg", - "name": "Beta Pictoris Disk", - "caption": "In 1984, Beta Pictoris was the very first star discovered to be surrounded by a bright disk of light-scattering dust and debris. Planets are thought to form in such disks, and astronomers have discovered two planets orbiting Beta Pictoris.", - "url": "https://hubblesite.org/contents/media/images/2015/06/3492-Image.html", - "year": 2012 - }, - { - "date": "March 7 2019", - "image": "march-7-2019-saturn-in-ultraviolet.jpg", - "name": "Saturn in Ultraviolet", - "caption": "This false-color image of Saturn, taken in ultraviolet light, reveals details in the hazes and clouds of the planet's atmosphere that are not easy or possible to see in visible light.", - "url": "https://hubblesite.org/contents/media/images/2003/23/1391-Image.html", - "year": 2003 - }, - { - "date": "March 8 2019", - "image": "march-8-2019-einstein-ring-sdss-j120540.jpg", - "name": "Einstein Ring SDSS J120540", - "caption": "Einstein rings like this form when two galaxies are almost perfectly aligned, one behind the other, and the gravitational field of the closer galaxy bends the light from the more-distant galaxy into bright arcs around itself.", - "url": "https://hubblesite.org/contents/media/images/2005/32/1788-Image.html", - "year": 2005 - }, - { - "date": "March 9 2019", - "image": "march-9-2019-galaxy-cluster-abell-2261.jpg", - "name": "Galaxy Cluster Abell 2261", - "caption": "The giant elliptical galaxy in the center of this image is the most massive and brightest member of galaxy cluster Abell 2261. More than a million light-years wide, the galaxy is about 10 times bigger than our Milky Way galaxy.", - "url": "https://hubblesite.org/contents/media/images/2012/24/3049-Image.html", - "year": 2011 - }, - { - "date": "March 10 2019", - "image": "march-10-2019-mars.jpg", - "name": "Mars", - "caption": "This stunning portrait of Mars was taken just before the planet made one of its closest approaches to Earth (passing about 60 million miles from us). This view was taken on the last day of spring in the planet's northern hemisphere.", - "url": "https://hubblesite.org/contents/media/images/1997/09/471-Image.html", - "year": 1997 - }, - { - "date": "March 11 2019", - "image": "march-11-2019-southern-crab-nebula.png", - "name": "Southern Crab Nebula", - "caption": "An aging red giant star is shedding its outer layers to produce the Southern Crab Nebula. The \"legs\" are likely to be the places where the outflowing material slams into surrounding gas and dust.", - "url": "https://hubblesite.org/contents/media/images/2019/15/4384-Image.html", - "year": 2019 - }, - { - "date": "March 12 2019", - "image": "march-12-2019-einstein-ring-sdss-j125028-25-052349.jpg", - "name": "Einstein Ring SDSS J125028.25+052349", - "caption": "Einstein rings like this form when two galaxies are almost perfectly aligned, one behind the other, and the gravitational field of the closer galaxy bends the light from the more-distant galaxy into bright arcs around itself.", - "url": "https://hubblesite.org/contents/media/images/2005/32/1788-Image.html", - "year": 2005 - }, - { - "date": "March 13 2019", - "image": "march-13-2019-galaxy-ngc-3310.jpg", - "name": "Galaxy NGC 3310", - "caption": "There are several hundred star clusters in the starburst galaxy NGC 3310. They appear in this image as the bright, blue clumps that trace the galaxy's spiral arms.", - "url": "https://hubblesite.org/contents/media/images/2001/26/1094-Image.html", - "year": 1997 - }, - { - "date": "March 14 2019", - "image": "march-14-2019-interacting-galaxies-arp-297.jpg", - "name": "Interacting Galaxies Arp 297", - "caption": "Arp 297 is a pair of interacting galaxies that consists of NGC 5754, the large spiral at the top, and NGC 5752, the smaller companion at the bottom left. NGC 5754's internal structure has hardly been disturbed, but it does have some kinked arms just beyond its inner ring.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2308-Image.html", - "year": 1999 - }, - { - "date": "March 15 2019", - "image": "march-15-2019-interacting-galaxies-arp-81.jpg", - "name": "Interacting Galaxies Arp 81", - "caption": "Arp 81 is a pair of interacting galaxies consisting of NGC 6621 (center) and NGC 6622 (left). The encounter has pulled a long tail out of NGC 6621 that has now wrapped behind the pair.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2328-Image.html", - "year": 1999 - }, - { - "date": "March 16 2019", - "image": "march-16-2019-supernova-didius.jpg", - "name": "Supernova Didius", - "caption": "Supernova Didius, named after a Roman emperor, is the white dot in the center of this image. The bright blob at upper left is the core of the supernova's host galaxy. The supernova is so far away, we see it as it appeared 7 billion years ago.", - "url": "https://hubblesite.org/contents/media/images/2014/21/3346-Image.html", - "year": 2012 - }, - { - "date": "March 17 2019", - "image": "march-17-2019-red-rectangle-nebula.jpg", - "name": "Red Rectangle Nebula", - "caption": "This image reveals details of one of the most unusual nebulas known in our Milky Way. Cataloged as HD 44179, this nebula is more commonly called the \"Red Rectangle\" because of its unique shape and color.", - "url": "https://hubblesite.org/contents/media/images/2004/11/1497-Image.html", - "year": 1999 - }, - { - "date": "March 18 2019", - "image": "march-18-2019-pinwheel-galaxy.jpg", - "name": "Pinwheel Galaxy", - "caption": "The Pinwheel galaxy has a pancake-like shape that we view face-on. This perspective shows off the spiral structure that gives the galaxy its nickname.", - "url": "https://hubblesite.org/contents/media/images/2009/07/2477-Image.html", - "year": 1994 - }, - { - "date": "March 19 2019", - "image": "march-19-2019-galaxy-m83.jpg", - "name": "Galaxy M83", - "caption": "This image of spiral galaxy M83 captures thousands of star clusters, hundreds of thousands of individual stars, and \"ghosts\" of dead stars called supernova remnants.", - "url": "https://hubblesite.org/contents/media/images/2014/04/3293-Image.html", - "year": 2009 - }, - { - "date": "March 20 2019", - "image": "march-20-2019-colliding-galaxies-ngc-6745.jpg", - "name": "Colliding Galaxies NGC 6745", - "caption": "This image captures the collision of two galaxies. The larger spiral galaxy, NGC 6745, boasts an intact nucleus as it interacts with the smaller, passing galaxy that is nearly out of the frame to the lower right.", - "url": "https://hubblesite.org/contents/media/images/2000/34/1007-Image.html", - "year": 1996 - }, - { - "date": "March 21 2019", - "image": "march-21-2019-colliding-galaxies-ngc-6745.jpg", - "name": "Colliding Galaxies NGC 6745", - "caption": "This image captures the collision of two galaxies. The larger spiral galaxy, NGC 6745, boasts an intact nucleus as it interacts with the smaller, passing galaxy that is nearly out of the frame to the lower right.", - "url": "https://hubblesite.org/contents/media/images/2000/34/1007-Image.html", - "year": 1996 - }, - { - "date": "March 22 2019", - "image": "march-22-2019-saturn.jpg", - "name": "Saturn", - "caption": "This image of Saturn captures details in the hazes and clouds of the planet's atmosphere. The view is so sharp that it also reveals individual ringlets in Saturn's ring system.", - "url": "https://hubblesite.org/contents/media/images/2004/18/1545-Image.html", - "year": 2004 - }, - { - "date": "March 23 2019", - "image": "march-23-2019-dwarf-galaxy-holmberg-ix.jpg", - "name": "Dwarf Galaxy Holmberg IX", - "caption": "This loose collection of stars is actually a dwarf irregular galaxy, called Holmberg IX. Of the more than 20,000 stars that can be resolved in this image, only about 10 percent are considered to be old stars.", - "url": "https://hubblesite.org/contents/media/images/2008/02/2236-Image.html", - "year": 2006 - }, - { - "date": "March 24 2019", - "image": "march-24-2019-galactic-center.jpg", - "name": "Galactic Center", - "caption": "This infrared image of the center of our Milky Way galaxy reveals a population of massive stars and complex structures in the hot ionized gas that swirls around the galactic core.", - "url": "https://hubblesite.org/contents/media/images/2009/02/2453-Image.html", - "year": 2008 - }, - { - "date": "March 25 2019", - "image": "march-25-2019-interstellar-bubble-n44f.jpg", - "name": "Interstellar Bubble N44F", - "caption": "This circular feature on the left side of this image is an interstellar bubble called N44F. It is being inflated by a torrent of fast-moving particles from an exceptionally hot star once buried inside this cold, dense cloud.", - "url": "https://hubblesite.org/contents/media/images/2004/26/1577-Image.html", - "year": 2000 - }, - { - "date": "March 26 2019", - "image": "march-26-2019-star-rs-puppis.jpg", - "name": "Star RS Puppis", - "caption": "The bright star RS Puppis is swaddled in a cocoon of reflective dust illuminated by the glittering star. The star is 10 times more massive than our Sun and is 200 times larger.", - "url": "https://hubblesite.org/contents/media/images/2013/51/3263-Image.html", - "year": 2010 - }, - { - "date": "March 27 2019", - "image": "march-27-2019-galaxy-m81.jpg", - "name": "Galaxy M81", - "caption": "The arms of the \"grand design\" spiral galaxy M81 are filled with young, bluish, hot stars. The greenish regions in the image are bright, gaseous clouds where new stars are forming.", - "url": "https://hubblesite.org/contents/media/images/2007/19/2127-Image.html", - "year": 2005 - }, - { - "date": "March 28 2019", - "image": "march-28-2019-ghost-head-nebula.jpg", - "name": "Ghost Head Nebula", - "caption": "The Ghost Head Nebula is a star-forming region in a nearby galaxy called the Large Magellanic Cloud. The two bright areas (the \"eyes of the ghost\") are very hot, glowing blobs of hydrogen and oxygen.", - "url": "https://hubblesite.org/contents/media/images/2001/34/1118-Image.html", - "year": 2000 - }, - { - "date": "March 29 2019", - "image": "march-29-2019-galaxy-m82.jpg", - "name": "Galaxy M82", - "caption": "Galaxy M82 is remarkable for its bright blue disk, webs of shredded clouds, and fiery-looking plumes of glowing hydrogen blasting out of its central region. In M82, stars are being born 10 times faster than they are inside our Milky Way galaxy.", - "url": "https://hubblesite.org/contents/media/images/2006/14/1876-Image.html", - "year": 2006 - }, - { - "date": "March 30 2019", - "image": "march-30-2019-four-faces-of-mars.jpg", - "name": "Four Faces of Mars", - "caption": "Four sides of Mars are captured in these Hubble images taken over the course of a day. Mars has rotated about ninety degrees between each view.", - "url": "https://hubblesite.org/contents/media/images/1997/15/481-Image.html", - "year": 1997 - }, - { - "date": "March 31 2019", - "image": "march-31-2019-interacting-galaxies-am-0500-620.jpg", - "name": "Interacting Galaxies AM 0500-620", - "caption": "AM 0500-620 includes a pair of galaxies, with one spiral galaxy seen nearly face-on that is partially backlit by a background galaxy. These interacting galaxies are located 350 million light-years away in the constellation Dorado.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2326-Image.html", - "year": 1997 - }, - { - "date": "April 1 2019", - "image": "april-1-2019-eagle-nebula-pillars.jpg", - "name": "Eagle Nebula Pillars", - "caption": "Hubble's view of the \"Pillars of Creation\" in the Eagle Nebula displays three giant columns of cold gas giving birth to new stars. The pillars are bathed in the scorching ultraviolet light from a cluster of young, massive stars beyond the top of the image.", - "url": "https://hubblesite.org/contents/media/images/1995/44/351-Image.html", - "year": 1995 - }, - { - "date": "April 2 2019", - "image": "april-2-2019-interacting-galaxies-arp-274.jpg", - "name": "Interacting Galaxies Arp 274", - "caption": "Arp 274 is a system of three galaxies that appear to be partially overlapping. Two of the galaxies are rapidly forming new stars, evident in the bright blue knots strung along the arms of the galaxy on the right and along the small galaxy on the left.", - "url": "https://hubblesite.org/contents/media/images/2009/14/2523-Image.html", - "year": 2009 - }, - { - "date": "April 3 2019", - "image": "april-3-2019-jupiter.png", - "name": "Jupiter", - "caption": "This image of Jupiter was taken when the planet was closest to Earth in 2017. The Great Red Spot appears on the left side, along with a smaller, reddish storm in the lower right dubbed \"Red Spot Jr.", - "url": "https://hubblesite.org/contents/media/images/2017/15/4012-Image.html", - "year": 2017 - }, - { - "date": "April 4 2019", - "image": "april-4-2019-galaxy-pair-ngc-3314.jpg", - "name": "Galaxy Pair NGC 3314", - "caption": "This image shows a pair of galaxies called NGC 3314. Through a chance alignment, a face-on spiral galaxy lies precisely in front of another, larger spiral. This provides a view of dark material within the front galaxy, seen because it is silhouetted against the galaxy behind it.", - "url": "https://hubblesite.org/contents/media/images/2000/14/958-Image.html", - "year": 1999 - }, - { - "date": "April 5 2019", - "image": "april-5-2019-galactic-center.jpg", - "name": "Galactic Center", - "caption": "This infrared image of the center of our Milky Way galaxy reveals a population of massive stars and complex structures in the hot ionized gas that swirls around the galactic core.", - "url": "https://hubblesite.org/contents/media/images/2009/02/2453-Image.html", - "year": 2008 - }, - { - "date": "April 6 2019", - "image": "april-6-2019-galaxy-eso-510-g13.jpg", - "name": "Galaxy ESO 510-G13", - "caption": "Usually, when a spiral galaxy appears edge-on, its dust and spiral arms appear flat. The warping of the disk in ESO 510-G13 suggests this galaxy has recently undergone a collision with a nearby galaxy and is in the process of swallowing it.", - "url": "https://hubblesite.org/contents/media/images/2001/23/1089-Image.html", - "year": 2001 - }, - { - "date": "April 7 2019", - "image": "april-7-2019-30-doradus-nebula.jpg", - "name": "30 Doradus Nebula", - "caption": "This is a close-up view of a star-birth region called the 30 Doradus Nebula. The giant stellar factory lies 170,000 light-years away inside a nearby galaxy known as the Large Magellanic Cloud. The image reveals glowing clouds of hydrogen and dark filamentary structures of dust.", - "url": "https://hubblesite.org/contents/media/images/2014/02/3286-Image.html", - "year": 2013 - }, - { - "date": "April 8 2019", - "image": "april-8-2019-galaxy-ngc-4650a.jpg", - "name": "Galaxy NGC 4650A", - "caption": "About 130 million light-years away, NGC 4650A is one of only 100 known polar-ring galaxies, which feature a ring of stars encircling a disk. Polar rings might form when two galaxies collide, with one galaxy becoming the inner disk and the other forming the ring.", - "url": "https://hubblesite.org/contents/media/images/1999/16/800-Image.html", - "year": 1999 - }, - { - "date": "April 9 2019", - "image": "april-9-2019-jupiter-and-ganymede.jpg", - "name": "Jupiter and Ganymede", - "caption": "This image shows Jupiter and its large moon Ganymede as the moon peeks out from behind the planet. Composed of rock and ice, Ganymede is the largest moon in our solar system.", - "url": "https://hubblesite.org/contents/media/images/2008/42/2440-Image.html", - "year": 2007 - }, - { - "date": "April 10 2019", - "image": "april-10-2019-circinus-galaxy.jpg", - "name": "Circinus Galaxy", - "caption": "Resembling a swirling witch's cauldron of glowing vapors, the black-hole-powered core of the Circinus galaxy appears in this image. Much of the gas in the spiral galaxy's disk is concentrated in two rings.", - "url": "https://hubblesite.org/contents/media/images/2000/37/1010-Image.html", - "year": 1999 - }, - { - "date": "April 11 2019", - "image": "april-11-2019-galactic-center.jpg", - "name": "Galactic Center", - "caption": "This infrared image of the center of our Milky Way galaxy reveals a population of massive stars and complex structures in the hot ionized gas that swirls around the galactic core.", - "url": "https://hubblesite.org/contents/media/images/2009/02/2453-Image.html", - "year": 2008 - }, - { - "date": "April 12 2019", - "image": "april-12-2019-hanny-s-voorwerp.jpg", - "name": "Hanny's Voorwerp", - "caption": "This image shows a ghostly green blob of gas that appears to float near a normal-looking spiral galaxy. The bizarre object, dubbed Hanny's Voorwerp, is the visible part of a 300,000-light-year-long streamer of gas stretching around the galaxy, called IC 2497.", - "url": "https://hubblesite.org/contents/media/images/2011/01/2803-Image.html", - "year": 2010 - }, - { - "date": "April 13 2019", - "image": "april-13-2019-galaxy-cluster-abell-2261.jpg", - "name": "Galaxy Cluster Abell 2261", - "caption": "The giant elliptical galaxy in the center of this image is the most massive and brightest member of galaxy cluster Abell 2261. More than a million light-years wide, the galaxy is about 10 times bigger than our Milky Way galaxy.", - "url": "https://hubblesite.org/contents/media/images/2012/24/3049-Image.html", - "year": 2011 - }, - { - "date": "April 14 2019", - "image": "april-14-2019-galaxy-eso-99-4.jpg", - "name": "Galaxy ESO 99-4", - "caption": "ESO 99-4 is a galaxy with a highly peculiar shape. It is probably the remnant of an earlier merger process that has deformed it, leaving the main body largely obscured by dark bands of dust.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2322-Image.html", - "year": 2006 - }, - { - "date": "April 15 2019", - "image": "april-15-2019-veil-nebula.jpg", - "name": "Veil Nebula", - "caption": "In this small piece of the Veil Nebula, wisps of gas are part of what remains of a star that was once 20 times more massive than our Sun. A fast-moving blast wave from the star's explosion is plowing into a wall of interstellar gas, causing it to glow.", - "url": "https://hubblesite.org/contents/media/images/2015/29/3620-Image.html", - "year": 2015 - }, - { - "date": "April 16 2019", - "image": "april-16-2019-veil-nebula.jpg", - "name": "Veil Nebula", - "caption": "In this small piece of the Veil Nebula, wisps of gas are part of what remains of a star that was once 20 times more massive than our Sun. A fast-moving blast wave from the star's explosion is plowing into a wall of interstellar gas, causing it to glow.", - "url": "https://hubblesite.org/contents/media/images/2015/29/3620-Image.html?itemsPerPage=100&page=2&filterUUID=8a87f02e-e18b-4126-8133-2576f4fdc5e2&news=true", - "year": 2015 - }, - { - "date": "April 17 2019", - "image": "april-17-2019-galaxy-cluster-abell-2261.jpg", - "name": "Galaxy Cluster Abell 2261", - "caption": "The giant elliptical galaxy in the center of this image is the most massive and brightest member of galaxy cluster Abell 2261. More than a million light-years wide, the galaxy is about 10 times bigger than our Milky Way galaxy.", - "url": "https://hubblesite.org/contents/media/images/2012/24/3049-Image.html", - "year": 2011 - }, - { - "date": "April 18 2019", - "image": "april-18-2019-globular-cluster-m79.png", - "name": "Globular Cluster M79", - "caption": "The globular star cluster M79 is located 41,000 light-years from Earth. It contains about 150,000 stars packed into an area measuring only 118 light-years across. Its stars are some of the oldest in our galaxy.", - "url": "https://hubblesite.org/contents/media/images/2017/37/4096-Image.html", - "year": 1997 - }, - { - "date": "April 19 2019", - "image": "april-19-2019-asteroid-p-2010-a2.jpg", - "name": "Asteroid P/2010 A2", - "caption": "Hubble imaged a tail flowing from this peculiar asteroid, dubbed P/2010 A2. Scientists suspect the debris was produced by a collision with another asteroid.", - "url": "https://hubblesite.org/contents/media/images/2010/34/2780-Image.html", - "year": 2010 - }, - { - "date": "April 20 2019", - "image": "april-20-2019-galactic-center.jpg", - "name": "Galactic Center", - "caption": "This infrared image of the center of our Milky Way galaxy reveals a population of massive stars and complex structures in the hot ionized gas that swirls around the galactic core.", - "url": "https://hubblesite.org/contents/media/images/2009/02/2453-Image.html", - "year": 2008 - }, - { - "date": "April 21 2019", - "image": "april-21-2019-jupiter.jpg", - "name": "Jupiter", - "caption": "This image of Jupiter was taken by the Outer Planet Atmospheres Legacy (OPAL) program, a long-term project that uses Hubble to capture global maps of the outer planets every year. The Great Red Spot appears in the lower right.", - "url": "https://hubblesite.org/contents/media/images/2016/24/3758-Image.html", - "year": 2014 - }, - { - "date": "April 22 2019", - "image": "april-22-2019-30-doradus-nebula.jpg", - "name": "30 Doradus Nebula", - "caption": "This is the inner part of the 30 Doradus Nebula, a turbulent star-birth region in the Large Magellanic Cloud, a satellite galaxy of our Milky Way. The bright cluster of stars at left is known as R136.", - "url": "https://hubblesite.org/contents/media/images/2001/21/1080-Image.html", - "year": 2000 - }, - { - "date": "April 23 2019", - "image": "april-23-2019-galactic-center.jpg", - "name": "Galactic Center", - "caption": "This infrared image of the center of our Milky Way galaxy reveals a population of massive stars and complex structures in the hot ionized gas that swirls around the galactic core.", - "url": "https://hubblesite.org/contents/media/images/2009/02/2453-Image.html", - "year": 2008 - }, - { - "date": "April 24 2019", - "image": "april-24-2019-cygnus-loop-supernova-remnant.jpg", - "name": "Cygnus Loop Supernova Remnant", - "caption": "This image captures a small portion of the Cygnus Loop supernova remnant. The Cygnus Loop marks the edge of a bubble-like, expanding blast wave from a colossal stellar explosion that occurred about 15,000 years ago.", - "url": "https://hubblesite.org/contents/media/images/1993/01/90-Image.html?news=true", - "year": 1991 - }, - { - "date": "April 25 2019", - "image": "april-25-2019-two-red-spots-on-jupiter.jpg", - "name": "Two Red Spots on Jupiter", - "caption": "This image captures a second \"red spot\" (lower left) that emerged alongside the bigger and more famous Great Red Spot (right) on Jupiter. The new storm is roughly one-half the size of the Great Red Spot.", - "url": "https://hubblesite.org/contents/media/images/2006/19/1913-Image.html", - "year": 2006 - }, - { - "date": "April 26 2019", - "image": "april-26-2019-galactic-center.jpg", - "name": "Galactic Center", - "caption": "This infrared image of the center of our Milky Way galaxy reveals a population of massive stars and complex structures in the hot ionized gas that swirls around the galactic core.", - "url": "https://hubblesite.org/contents/media/images/2009/02/2453-Image.html", - "year": 2008 - }, - { - "date": "April 27 2019", - "image": "april-27-2019-supernova-remnant-n-49.jpg", - "name": "Supernova Remnant N 49", - "caption": "N 49 is a supernova remnant in a neighboring galaxy called the Large Magellanic Cloud. The delicate filaments are sheets of debris from a stellar explosion whose light would have reached Earth thousands of years ago.", - "url": "https://hubblesite.org/contents/media/images/2003/20/1379-Image.html", - "year": 1999 - }, - { - "date": "April 28 2019", - "image": "april-28-2019-galaxy-cluster-sdss-j1004-4112.jpg", - "name": "Galaxy Cluster SDSS J1004+4112", - "caption": "This picture captures a galaxy cluster called SDSS J1004+4112 that's so massive that its gravity bends light from galaxies behind it. The light of a distant quasar (the brilliant core of an active galaxy) has been bent around the cluster, appearing in five places in this image.", - "url": "https://hubblesite.org/contents/media/images/2006/23/1929-Image.html", - "year": 2004 - }, - { - "date": "April 29 2019", - "image": "april-29-2019-nebula-n-180b.jpg", - "name": "Nebula N 180B", - "caption": "N 180B is an active region of star formation in the Large Magellanic Cloud, a dwarf galaxy orbiting our Milky Way. This particular region contains some of the brightest known star clusters.", - "url": "https://hubblesite.org/contents/media/images/2006/41/1983-Image.html", - "year": 1998 - }, - { - "date": "April 30 2019", - "image": "april-30-2019-star-v838-monocerotis.jpg", - "name": "Star V838 Monocerotis", - "caption": "In 2002, a dim star suddenly became 600,000 times more luminous than our Sun, temporarily making it the brightest star in our Milky Way galaxy. This image of V838 Monocerotis captures its \"light echo.", - "url": "https://hubblesite.org/contents/media/images/2003/10/1306-Image.html", - "year": 2002 - }, - { - "date": "May 1 2019", - "image": "may-1-2019-galaxy-ngc-3982.jpg", - "name": "Galaxy NGC 3982", - "caption": "This image captures the face-on spiral galaxy NGC 3982. Its arms are lined with pink star-forming regions of glowing hydrogen, blue newborn star clusters, and dark dust lanes that provide the raw material for future generations of stars.", - "url": "https://hubblesite.org/contents/media/images/2010/36/2795-Image.html", - "year": 2000 - }, - { - "date": "May 2 2019", - "image": "may-2-2019-star-cluster-m15.jpg", - "name": "Star Cluster M15", - "caption": "This dense cluster of stars is known as Messier 15 (or M15) and is located about 35,000 light-years away. The fuzzy, blue area to the left of the cluster's core is a planetary nebula, a cloud of gas that has been cast off by a dying, medium-sized star.", - "url": "https://www.nasa.gov/feature/goddard/2017/messier-15", - "year": 2006 - }, - { - "date": "May 3 2019", - "image": "may-3-2019-galaxy-eso-239-2.jpg", - "name": "Galaxy ESO 239-2", - "caption": "ESO 239-2 is the result of a cosmic collision between galaxies that will eventually result in a larger \"elliptical\" galaxy. The intermediate stage captured here shows a galaxy with long tails of dust and gas that envelope the galaxy's core.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2329-Image.html", - "year": 2006 - }, - { - "date": "May 4 2019", - "image": "may-4-2019-cat-s-eye-nebula.jpg", - "name": "Cat's Eye Nebula", - "caption": "Produced by a dying star, the Cat's Eye Nebula is one of the most complex planetary nebulas known. This image reveals a pattern of concentric rings around the central star. Each \"ring\" is actually the edge of a spherical bubble of material ejected by the star.", - "url": "https://hubblesite.org/contents/media/images/2004/27/1578-Image.html", - "year": 2002 - }, - { - "date": "May 5 2019", - "image": "may-5-2019-galactic-center.jpg", - "name": "Galactic Center", - "caption": "This infrared image of the center of our Milky Way galaxy reveals a population of massive stars and complex structures in the hot ionized gas that swirls around the galactic core.", - "url": "https://hubblesite.org/contents/media/images/2009/02/2453-Image.html", - "year": 2008 - }, - { - "date": "May 6 2019", - "image": "may-6-2019-stars-in-the-galactic-core.png", - "name": "Stars in the Galactic Core", - "caption": "These colorful stars reside at the heart of our Milky Way galaxy, about 26,000 light-years from Earth. Aging red-giant stars coexist with their more plentiful younger cousins, the smaller, white, Sun-like stars, in this crowded region of our galaxy’s central hub.", - "url": "https://hubblesite.org/contents/media/images/2018/01/4101-Image.html", - "year": 2012 - }, - { - "date": "May 7 2019", - "image": "may-7-2019-galaxy-ngc-3982.jpg", - "name": "Galaxy NGC 3982", - "caption": "This image captures the face-on spiral galaxy NGC 3982. Its arms are lined with pink star-forming regions of glowing hydrogen, blue newborn star clusters, and dark dust lanes that provide the raw material for future generations of stars.", - "url": "https://hubblesite.org/contents/media/images/2010/36/2795-Image.html", - "year": 2000 - }, - { - "date": "May 8 2019", - "image": "may-8-2019-comet-ison.jpg", - "name": "Comet ISON", - "caption": "At the time Hubble took this image, comet ISON (C/2012 S1) was hurtling toward the Sun at a whopping 48,000 miles per hour. The comet was 403 million miles from Earth, between the orbits of Mars and Jupiter.", - "url": "https://hubblesite.org/contents/media/images/2013/24/3197-Image.html", - "year": 2013 - }, - { - "date": "May 9 2019", - "image": "may-9-2019-red-spots-on-jupiter.jpg", - "name": "Red Spots on Jupiter", - "caption": "This image captures three red spots in Jupiter's atmosphere. The famous Great Red Spot appears on the right, while \"Red Spot Jr.\" is to the lower left and an even smaller \"baby red spot\" appears at left.", - "url": "https://hubblesite.org/contents/media/images/2008/23/2354-Image.html?news=true", - "year": 2008 - }, - { - "date": "May 10 2019", - "image": "may-10-2019-red-spots-on-jupiter.jpg", - "name": "Red Spots on Jupiter", - "caption": "This image captures three red spots in Jupiter's atmosphere. The famous Great Red Spot appears on the right, while \"Red Spot Jr.\" is to the lower left and an even smaller \"baby red spot\" appears at left.", - "url": "https://hubblesite.org/contents/media/images/2008/23/2354-Image.html", - "year": 2008 - }, - { - "date": "May 11 2019", - "image": "may-11-2019-cone-nebula.jpg", - "name": "Cone Nebula", - "caption": "This image shows the tip of the Cone Nebula, a star-forming region in the constellation Monoceros. This conical pillar stretches over seven light-years and is just a small portion of a much larger star-formation complex.", - "url": "https://hubblesite.org/contents/media/images/2002/13/1200-Image.html", - "year": 2002 - }, - { - "date": "May 12 2019", - "image": "may-12-2019-star-forming-region-n11b.jpg", - "name": "Star-Forming Region N11B", - "caption": "This panoramic view captures an iridescent tapestry of star birth, filled with glowing gas, dark dust clouds, and young, hot stars. The star-forming region, cataloged as N11B, lies in a nearby galaxy, the Large Magellanic Cloud.", - "url": "https://hubblesite.org/contents/media/images/2004/22/1565-Image.html", - "year": 1999 - }, - { - "date": "May 13 2019", - "image": "may-13-2019-interacting-galaxies-iras-19297-0406.jpg", - "name": "Interacting Galaxies IRAS 19297-0406", - "caption": "This image shows a tumultuous collision between four galaxies located 1 billion light-years from Earth. The tangled-up galaxies, called IRAS 19297-0406, are crammed together in the center of the picture.", - "url": "https://hubblesite.org/contents/media/images/2002/13/1199-Image.html", - "year": 2002 - }, - { - "date": "May 14 2019", - "image": "may-14-2019-galaxy-cluster-abell-2744.jpg", - "name": "Galaxy Cluster Abell 2744", - "caption": "Located 3.5 billion light-years away, Abell 2744 contains several hundred galaxies and might be a pile-up of at least four smaller galaxy clusters. Abell 2744’s strong gravitational field acts as a lens, brightening and magnifying the light of nearly 3,000 distant background galaxies.", - "url": "https://hubblesite.org/contents/media/images/3868-Image", - "year": 2013 - }, - { - "date": "May 15 2019", - "image": "may-15-2019-red-spots-on-jupiter.jpg", - "name": "Red Spots on Jupiter", - "caption": "This image captures three red spots in Jupiter's atmosphere. The famous Great Red Spot appears on the right, while \"Red Spot Jr.\" is to the lower left and an even smaller \"baby red spot\" appears at left.", - "url": "https://hubblesite.org/contents/media/images/2008/27/2373-Image.html", - "year": 2008 - }, - { - "date": "May 16 2019", - "image": "may-16-2019-hickson-compact-group-90.jpg", - "name": "Hickson Compact Group 90", - "caption": "These three galaxies, called NGC 7173 (middle left), NGC 7174 (middle right) and NGC 7176 (lower right), are part of Hickson Compact Group 90, named after astronomer Paul Hickson, who cataloged small groups of galaxies like this one.", - "url": "https://hubblesite.org/contents/media/images/2009/10/2496-Image.html", - "year": 2006 - }, - { - "date": "May 17 2019", - "image": "may-17-2019-galaxy-cluster-rdcs-1252-9-2927.jpg", - "name": "Galaxy Cluster RDCS 1252.9-2927", - "caption": "This image captures a massive cluster of galaxies called RDCS 1252.9-2927. This galaxy cluster existed when the universe was only 5 billion years old, or about 35 percent of its present age.", - "url": "https://hubblesite.org/contents/media/images/2004/01/1436-Image.html", - "year": 2002 - }, - { - "date": "May 18 2019", - "image": "may-18-2019-jupiter.jpg", - "name": "Jupiter", - "caption": "Hubble took this image of Jupiter when the giant planet was 420 million miles from Earth. The dark spot that appears on Jupiter is the shadow of the moon Io, which appears to the upper right of the shadow.", - "url": "https://hubblesite.org/contents/media/images/1994/26/169-Image.html", - "year": 1994 - }, - { - "date": "May 19 2019", - "image": "may-19-2019-jupiter-s-auroras.jpg", - "name": "Jupiter's Auroras", - "caption": "Hubble used its ultraviolet vision to observe auroras around Jupiter's north pole. Auroras are formed when charged particles in the space around the planet are accelerated along the planet's magnetic field lines and interact with gases in the atmosphere.", - "url": "https://hubblesite.org/contents/media/images/2016/24/3756-Image.html", - "year": 2014 - }, - { - "date": "May 20 2019", - "image": "may-20-2019-galaxy-cluster-abell-2744.jpg", - "name": "Galaxy Cluster Abell 2744", - "caption": "Located 3.5 billion light-years away, Abell 2744 contains several hundred galaxies and might be a pile-up of at least four smaller galaxy clusters. Abell 2744’s strong gravitational field acts as a lens, brightening and magnifying the light of nearly 3,000 distant background galaxies.", - "url": "https://hubblesite.org/contents/media/images/3868-Image", - "year": 2013 - }, - { - "date": "May 21 2019", - "image": "may-21-2019-nebula-ngc-1748.jpg", - "name": "Nebula NGC 1748", - "caption": "Extremely intense radiation from newly born, ultra-bright stars has blown a glowing, spherical bubble in the nebula NGC 1748. The average-looking star at the very center of the bubble is about 30 times more massive and almost 200,000 times brighter than our Sun.", - "url": "https://hubblesite.org/contents/media/images/2001/11/1039-Image.html", - "year": 2000 - }, - { - "date": "May 22 2019", - "image": "may-22-2019-saturn.jpg", - "name": "Saturn", - "caption": "This image shows Saturn as the planet's magnificent ring system appeared edge-on to Earth. This alignment occurs about every 15 years when Earth passes through the plane of Saturn's rings. The bright dots to the left of Saturn are some of the planet's moons.", - "url": "https://hubblesite.org/contents/news-releases/1995/news-1995-25.html", - "year": 1995 - }, - { - "date": "May 23 2019", - "image": "may-23-2019-galaxy-cluster-abell-2744.jpg", - "name": "Galaxy Cluster Abell 2744", - "caption": "Located 3.5 billion light-years away, Abell 2744 contains several hundred galaxies and might be a pile-up of at least four smaller galaxy clusters. Abell 2744’s strong gravitational field acts as a lens, brightening and magnifying the light of nearly 3,000 distant background galaxies.", - "url": "https://hubblesite.org/contents/media/images/3868-Image", - "year": 2013 - }, - { - "date": "May 24 2019", - "image": "may-24-2019-galaxy-cluster-abell-2744.jpg", - "name": "Galaxy Cluster Abell 2744", - "caption": "Located 3.5 billion light-years away, Abell 2744 contains several hundred galaxies and might be a pile-up of at least four smaller galaxy clusters. Abell 2744’s strong gravitational field acts as a lens, brightening and magnifying the light of nearly 3,000 distant background galaxies.", - "url": "https://hubblesite.org/contents/media/images/3868-Image", - "year": 2013 - }, - { - "date": "May 25 2019", - "image": "may-25-2019-galaxy-ngc-4622.jpg", - "name": "Galaxy NGC 4622", - "caption": "This image shows the spiral galaxy NGC 4622. Its outer pair of winding arms is full of new stars, clumped together in blue clusters. Strangely, the galaxy appears to be rotating clockwise, the opposite direction to what astronomers expected.", - "url": "https://hubblesite.org/contents/media/images/2002/03/1137-Image.html", - "year": 2001 - }, - { - "date": "May 26 2019", - "image": "may-26-2019-galaxy-i-zwicky-18.jpg", - "name": "Galaxy I Zwicky 18", - "caption": "This image captures the irregular dwarf galaxy I Zwicky 18 and a companion galaxy to its upper right. The two galaxies are interacting, triggering star formation in I Zwicky 18.", - "url": "https://hubblesite.org/contents/media/images/2004/35/1621-Image.html", - "year": 2003 - }, - { - "date": "May 27 2019", - "image": "may-27-2019-galaxy-i-zwicky-18.jpg", - "name": "Galaxy I Zwicky 18", - "caption": "This image captures the irregular dwarf galaxy I Zwicky 18 and a companion galaxy to its upper right. The two galaxies are interacting, triggering star formation in I Zwicky 18.", - "url": "https://hubblesite.org/contents/media/images/2004/35/1621-Image.html", - "year": 1998 - }, - { - "date": "May 28 2019", - "image": "may-28-2019-galaxy-i-zwicky-18.jpg", - "name": "Galaxy I Zwicky 18", - "caption": "This image captures the irregular dwarf galaxy I Zwicky 18 and a companion galaxy to its upper right. The two galaxies are interacting, triggering star formation in I Zwicky 18.", - "url": "https://hubblesite.org/contents/media/images/2004/35/1621-Image.html", - "year": 1998 - }, - { - "date": "May 29 2019", - "image": "may-29-2019-galaxy-i-zwicky-18.jpg", - "name": "Galaxy I Zwicky 18", - "caption": "This image captures the irregular dwarf galaxy I Zwicky 18 and a companion galaxy to its upper right. The two galaxies are interacting, triggering star formation in I Zwicky 18.", - "url": "https://hubblesite.org/contents/media/images/2004/35/1621-Image.html", - "year": 2003 - }, - { - "date": "May 30 2019", - "image": "may-30-2019-swan-nebula.jpg", - "name": "Swan Nebula", - "caption": "This image captures a small region within the Swan Nebula, a hotbed of star formation. The wave-like patterns of gas have been sculpted and illuminated by a torrent of ultraviolet radiation from young, massive stars, which lie outside the picture to the upper left.", - "url": "https://hubblesite.org/contents/media/images/2003/13/1331-Image.html", - "year": 1999 - }, - { - "date": "May 31 2019", - "image": "may-31-2019-galaxy-ngc-2768.jpg", - "name": "Galaxy NGC 2768", - "caption": "NGC 2768 is an elliptical galaxy located 65 million light-years away in the constellation Ursa Major. The galaxy hosts a supermassive black hole, fueling jets of material in its active center.", - "url": "https://hubblesite.org/contents/media/images/2015/28/3615-Image.html", - "year": 2002 - }, - { - "date": "June 1 2019", - "image": "june-1-2019-galaxy-i-zwicky-18.jpg", - "name": "Galaxy I Zwicky 18", - "caption": "This image captures the irregular dwarf galaxy I Zwicky 18 and a companion galaxy to its upper right. The two galaxies are interacting, triggering star formation in I Zwicky 18.", - "url": "https://hubblesite.org/contents/media/images/2004/35/1621-Image.html", - "year": 2003 - }, - { - "date": "June 2 2019", - "image": "june-2-2019-jupiter-s-auroras.jpg", - "name": "Jupiter's Auroras", - "caption": "Hubble used its ultraviolet vision to observe auroras around Jupiter's north pole. Auroras are formed when charged particles in the space around the planet are accelerated along the planet's magnetic field lines and interact with gases in the atmosphere.", - "url": "https://hubblesite.org/contents/media/images/2016/24/3756-Image.html", - "year": 2014 - }, - { - "date": "June 3 2019", - "image": "june-3-2019-galaxy-i-zwicky-18.jpg", - "name": "Galaxy I Zwicky 18", - "caption": "This image captures the irregular dwarf galaxy I Zwicky 18 and a companion galaxy to its upper right. The two galaxies are interacting, triggering star formation in I Zwicky 18.", - "url": "https://hubblesite.org/contents/media/images/2004/35/1621-Image.html", - "year": 2003 - }, - { - "date": "June 4 2019", - "image": "june-4-2019-quasar-3c-273.jpg", - "name": "Quasar 3C 273", - "caption": "The quasar 3C 273 resides at the heart of a galaxy nearly 2 billion light-years away. A quasar is a brilliant source of energy at the center of a distant galaxy, and is believed to flare up when gas, dust or other material falls onto a supermassive black hole at the galaxy’s core.", - "url": "https://hubblesite.org/contents/media/images/2003/03/1289-Image.html", - "year": 1994 - }, - { - "date": "June 5 2019", - "image": "june-5-2019-jupiter.jpg", - "name": "Jupiter", - "caption": "Jupiter's turbulent clouds are always changing as they encounter atmospheric disturbances while sweeping around the planet at hundreds of miles per hour. This image includes a dark, serpent-shaped structure that is actually a small tear in the cloud deck.", - "url": "https://hubblesite.org/contents/media/images/2007/25/2152-Image.html", - "year": 2007 - }, - { - "date": "June 6 2019", - "image": "june-6-2019-galaxy-i-zwicky-18.jpg", - "name": "Galaxy I Zwicky 18", - "caption": "This image captures the irregular dwarf galaxy I Zwicky 18 and a companion galaxy to its upper right. The two galaxies are interacting, triggering star formation in I Zwicky 18.", - "url": "https://hubblesite.org/contents/media/images/2004/35/1621-Image.html", - "year": 2003 - }, - { - "date": "June 7 2019", - "image": "june-7-2019-jupiter.jpg", - "name": "Jupiter", - "caption": "Hubble took this image of Jupiter four days after a giant meteor burned up in the planet's cloud tops. Hubble found no sign of dark debris at the site, meaning the meteor did not plunge deep enough into the atmosphere to explode and leave behind any telltale marks.", - "url": "https://hubblesite.org/contents/media/images/2010/20/2742-Image.html", - "year": 2010 - }, - { - "date": "June 8 2019", - "image": "june-8-2019-sombrero-galaxy.jpg", - "name": "Sombrero Galaxy", - "caption": "The Sombrero galaxy's hallmark is a brilliant white core encircled by thick lanes of dust. As seen from Earth, the spiral galaxy is tilted nearly edge-on.", - "url": "https://hubblesite.org/contents/media/images/2003/28/1415-Image.html", - "year": 2003 - }, - { - "date": "June 9 2019", - "image": "june-9-2019-galaxy-ngc-6782.jpg", - "name": "Galaxy NGC 6782", - "caption": "This spiral galaxy, NGC 6782, exhibits tightly wound spiral arms and a spectacular, nearly circular bright ring surrounding its nucleus. The ring contains many recently formed hot stars.", - "url": "https://hubblesite.org/contents/media/images/2001/37/1122-Image.html", - "year": 2001 - }, - { - "date": "June 10 2019", - "image": "june-10-2019-galaxy-ngc-7674.jpg", - "name": "Galaxy NGC 7674", - "caption": "NGC 7674 is a spiral galaxy tilted nearly face-on to Earth. Faint streamers below and to the left of the galaxy have been created by gravitational interactions with companion galaxies.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2297-Image.html", - "year": 2006 - }, - { - "date": "June 11 2019", - "image": "june-11-2019-star-cluster-omega-centauri.jpg", - "name": "Star Cluster Omega Centauri", - "caption": "This view shows about 50,000 stars at the heart of Omega Centauri, a huge globular star cluster 17,000 light-years from Earth. Omega Centauri is the biggest and brightest globular cluster in the Milky Way, and one of the few that can be seen by the unaided eye.", - "url": "https://hubblesite.org/contents/media/images/2001/33/1117-Image.html", - "year": 1997 - }, - { - "date": "June 12 2019", - "image": "june-12-2019-galaxy-cluster-abell-1689.jpg", - "name": "Galaxy Cluster Abell 1689", - "caption": "This image shows the inner region of Abell 1689, an immense cluster of galaxies located 2.2 billion light-years away. Astronomers used Hubble to map the distrubition of dark matter in the galaxy cluster.", - "url": "https://hubblesite.org/contents/media/images/2010/26/2758-Image.html", - "year": 2002 - }, - { - "date": "June 13 2019", - "image": "june-13-2019-star-vy-canis-majoris.jpg", - "name": "Star VY Canis Majoris", - "caption": "The hypergiant star VY Canis Majoris is surrounded by clouds of gas that it has cast off in a long series of outbursts. These eruptions have formed loops, arcs and knots of material moving at various speeds and in many different directions.", - "url": "https://hubblesite.org/contents/media/images/2007/03/2039-Image.html", - "year": 2005 - }, - { - "date": "June 14 2019", - "image": "june-14-2019-galaxy-cluster-abell-1689.jpg", - "name": "Galaxy Cluster Abell 1689", - "caption": "This image shows the inner region of Abell 1689, an immense cluster of galaxies located 2.2 billion light-years away. Astronomers used Hubble to map the distrubition of dark matter in the galaxy cluster.", - "url": "https://hubblesite.org/contents/media/images/2010/26/2758-Image.html", - "year": 2002 - }, - { - "date": "June 15 2019", - "image": "june-15-2019-globular-cluster-m22.jpg", - "name": "Globular Cluster M22", - "caption": "M22 is one of about 150 globular star clusters in the Milky Way. Located just 10,000 light-years away in the constellation Sagettarius, it is one of the closest globular clusters to Earth.", - "url": "https://hubblesite.org/contents/media/images/2001/20/1075-Image.html", - "year": 1999 - }, - { - "date": "June 16 2019", - "image": "june-16-2019-galaxy-cluster-abell-1689.jpg", - "name": "Galaxy Cluster Abell 1689", - "caption": "This image shows the inner region of Abell 1689, an immense cluster of galaxies located 2.2 billion light-years away. Astronomers used Hubble to map the distrubition of dark matter in the galaxy cluster.", - "url": "https://hubblesite.org/contents/media/images/2010/26/2758-Image.html", - "year": 2002 - }, - { - "date": "June 17 2019", - "image": "june-17-2019-stephan-s-quintet.jpg", - "name": "Stephan's Quintet", - "caption": "This close-up shows four of the five galaxies that make up Stephan’s Quintet. The image reveals bright, blue clusters of stars, born from the violent interactions between some of the member galaxies.", - "url": "https://hubblesite.org/contents/media/images/2001/22/1082-Image.html?itemsPerPage=100&page=8&filterUUID=8a87f02e-e18b-4126-8133-2576f4fdc5e2&news=true", - "year": 1999 - }, - { - "date": "June 18 2019", - "image": "june-18-2019-hubble-v-nebula.jpg", - "name": "Hubble-V Nebula", - "caption": "Hubble-V is an active star-forming region within galaxy NGC 6822. The cloud is about 200 light-years across and contains a dense knot of dozens of ultra-hot stars, each 100,000 times brighter than our Sun.", - "url": "https://hubblesite.org/contents/media/images/2001/39/1126-Image.html", - "year": 1996 - }, - { - "date": "June 19 2019", - "image": "june-19-2019-interacting-galaxies-am-2026-424.png", - "name": "Interacting Galaxies AM 2026-424", - "caption": "The colliding galaxies AM 2026-424 resemble a face. Each \"eye\" is the bright core of a galaxy, one of which slammed into another. The outline of the face is a ring of young blue stars. Other clumps of new stars form a nose and mouth.", - "url": "https://hubblesite.org/contents/media/images/2019/51/4574-Image", - "year": 2019 - }, - { - "date": "June 20 2019", - "image": "june-20-2019-galaxy-cluster-abell-1689.jpg", - "name": "Galaxy Cluster Abell 1689", - "caption": "This image shows the inner region of Abell 1689, an immense cluster of galaxies located 2.2 billion light-years away. Astronomers used Hubble to map the distrubition of dark matter in the galaxy cluster.", - "url": "https://hubblesite.org/contents/media/images/2010/26/2758-Image.html", - "year": 2002 - }, - { - "date": "June 21 2019", - "image": "june-21-2019-galaxy-cluster-abell-1689.jpg", - "name": "Galaxy Cluster Abell 1689", - "caption": "This image shows the inner region of Abell 1689, an immense cluster of galaxies located 2.2 billion light-years away. Astronomers used Hubble to map the distrubition of dark matter in the galaxy cluster.", - "url": "https://hubblesite.org/contents/media/images/2010/26/2758-Image.html", - "year": 2002 - }, - { - "date": "June 22 2019", - "image": "june-22-2019-interacting-galaxies-arp-142.jpg", - "name": "Interacting Galaxies Arp 142", - "caption": "The interacting galaxy duo near the bottom of this image is Arp 142. The pair contains the disturbed, star-forming spiral galaxy NGC 2936 along with its elliptical companion NGC 2937 at lower left. Above them is an unrelated, bluish galaxy called UGC 5130.", - "url": "https://hubblesite.org/contents/media/images/2013/23/3195-Image.html", - "year": 2012 - }, - { - "date": "June 23 2019", - "image": "june-23-2019-galaxies-in-the-groth-strip.jpg", - "name": "Galaxies in the Groth Strip", - "caption": "This field of galaxies is just a small part of a cosmic tapestry Hubble imaged as part of the All-wavelength Extended Groth Strip International Survey. In it are galaxies of all shapes, sizes, colors, and distances. Larger ones are nearby, while the smallest ones are far away.", - "url": "https://hubblesite.org/contents/media/images/2007/06/2046-Image.html", - "year": 2004 - }, - { - "date": "June 24 2019", - "image": "june-24-2019-supernova-2002dd.jpg", - "name": "Supernova 2002dd", - "caption": "This image captures a stellar explosion called a supernova in a small part of the sky known as the Hubble Deep Field. The supernova, designated SN 2002dd, appears as a red dot near the center of the image.", - "url": "https://hubblesite.org/contents/media/images/2003/12/1327-Image.html", - "year": 2002 - }, - { - "date": "June 25 2019", - "image": "june-25-2019-neptune.jpg", - "name": "Neptune", - "caption": "Neptune is the most distant major planet in our solar system. This image reveals high-altitude clouds in the northern and southern hemispheres of the planet. These clouds are composed of methane ice crystals.", - "url": "https://hubblesite.org/contents/media/images/2011/19/2860-Image.html", - "year": 2011 - }, - { - "date": "June 26 2019", - "image": "june-26-2019-seyfert-s-sextet.jpg", - "name": "Seyfert's Sextet", - "caption": "At first, Seyfert's Sextet looks like six galaxies grouped closely together. However, the small galaxy with the prominent spiral arms (right of center) is much farther away than the others, and a bright clump to the lower right is material torn from one of the other galaxies.", - "url": "https://hubblesite.org/contents/media/images/2002/22/1242-Image.html", - "year": 2000 - }, - { - "date": "June 27 2019", - "image": "june-27-2019-galaxy-cluster-rdcs-1252-9-2927.jpg", - "name": "Galaxy Cluster RDCS 1252.9-2927", - "caption": "This image captures the massive galaxy clutser RDCS 1252.9-2927. The galaxies in the cluster already existed when the universe was just 5 billion years old, or about 35 percent of its present age.", - "url": "https://hubblesite.org/contents/media/images/2004/01/1433-Image.html", - "year": 2002 - }, - { - "date": "June 28 2019", - "image": "june-28-2019-quasar-mc2-1635-119.jpg", - "name": "Quasar MC2 1635+119", - "caption": "This image shows shells of stars around a quasar known as MC2 1635+119. Quasars are among the brightest objects in the universe. They reside in the centers of galaxies and are powered by supermassive black holes.", - "url": "https://hubblesite.org/contents/news-releases/2007/news-2007-39.html", - "year": 2005 - }, - { - "date": "June 29 2019", - "image": "june-29-2019-pluto-system.jpg", - "name": "Pluto System", - "caption": "This image captures Pluto and its five moons. Pluto is the large dot at the center. Its largest moon, Charon, appears below Pluto. Moving clockwise from the left, the smaller moons are Hydra, Styx, Nyx and Kerberos.", - "url": "https://hubblesite.org/contents/media/images/2012/32/3083-Image.html", - "year": 2012 - }, - { - "date": "June 30 2019", - "image": "june-30-2019-ant-nebula.jpg", - "name": "Ant Nebula", - "caption": "The Ant Nebula displays intriguing symmetrical patterns in the lobes of gas being ejected from a dying Sun-like star at its center.", - "url": "https://hubblesite.org/contents/media/images/2001/05/1020-Image.html", - "year": 1998 - }, - { - "date": "July 1 2019", - "image": "july-1-2019-comet-shoemaker-levy-9-fragments.jpg", - "name": "Comet Shoemaker-Levy 9 Fragments", - "caption": "This image captures the brightest \"nucleus\" in a string of approximately 20 that comprised the broken-up comet Shoemaker-Levy 9. The image reveals that the bright segment is actually a group of at least four separate pieces.", - "url": "https://hubblesite.org/contents/media/images/1993/22/117-Image.html", - "year": 1993 - }, - { - "date": "July 2 2019", - "image": "july-2-2019-necklace-nebula.jpg", - "name": "Necklace Nebula", - "caption": "The Necklace Nebula contains the glowing remains of an ordinary, Sun-like star shedding material at the end of its life. The nebula consists of a bright ring, measuring 12 trillion miles across, dotted with dense, bright knots of gas that resemble diamonds in a necklace.", - "url": "https://hubblesite.org/contents/media/images/2011/24/2886-Image.html", - "year": 2011 - }, - { - "date": "July 3 2019", - "image": "july-3-2019-star-cluster-47-tucanae.jpg", - "name": "Star Cluster 47 Tucanae", - "caption": "This image shows the core of the globular star cluster 47 Tucanae. The entire cluster contains about a million stars, with many packed tightly in the core.", - "url": "https://hubblesite.org/contents/media/images/2006/33/1951-Image.html", - "year": 1999 - }, - { - "date": "July 4 2019", - "image": "july-4-2019-carina-nebula.jpg", - "name": "Carina Nebula", - "caption": "This close-up view shows only a three-light-year-wide portion of the entire Carina Nebula, which has a diameter of over 200 light-years. Located 8,000 light-years from Earth, the nebula can be seen in the southern sky with the naked eye.", - "url": "https://hubblesite.org/contents/media/images/2003/31/1424-Image.html", - "year": 2002 - }, - { - "date": "July 5 2019", - "image": "july-5-2019-trifid-nebula.jpg", - "name": "Trifid Nebula", - "caption": "The Trifid Nebula is a stellar nursery criss-crossed by huge, dark lanes of dust. This image provides a close-up view of the center of the nebula, near the intersection of the dust bands, and a group of recently formed, massive, bright stars.", - "url": "https://hubblesite.org/contents/media/images/2004/17/1542-Image.html", - "year": 2001 - }, - { - "date": "July 6 2019", - "image": "july-6-2019-planetary-nebula-ngc-5189.jpg", - "name": "Planetary Nebula NGC 5189", - "caption": "The knotty, filamentary structure of NGC 5189 formed as a dying star shed its outer layers. Interestingly, this planetary nebula has two nested structures tilted with respect to each other.", - "url": "https://hubblesite.org/contents/media/images/2012/49/3124-Image.html", - "year": 2012 - }, - { - "date": "July 7 2019", - "image": "july-7-2019-galaxy-cluster-abell-1689.jpg", - "name": "Galaxy Cluster Abell 1689", - "caption": "This image shows the center of Abell 1689, an immense cluster of galaxies located 2.2 billion light-years away. Astronomers used Hubble to map the distrubition of dark matter in the galaxy cluster.", - "url": "https://hubblesite.org/contents/media/images/2013/36/3238-Image.html", - "year": 2010 - }, - { - "date": "July 8 2019", - "image": "july-8-2019-jupiter-s-spots.jpg", - "name": "Jupiter's Spots", - "caption": "This image provides a close look at Jupiter's famous Great Red Spot and a smaller storm dubbed \"Red Spot Jr.\" below it. To the right of the Great Red Spot is the remnant of an even smaller spot that has faded and is being consumed by the much larger storm.", - "url": "https://hubblesite.org/contents/media/images/2008/27/2370-Image.html", - "year": 2008 - }, - { - "date": "July 9 2019", - "image": "july-9-2019-hoag-s-object.jpg", - "name": "Hoag's Object", - "caption": "A nearly perfect ring of hot, blue stars pinwheels about the yellow nucleus of an unusual galaxy known as Hoag's Object. Curiously, a background object that bears an uncanny resemblance to Hoag's Object can be seen in the gap at the one o'clock position.", - "url": "https://hubblesite.org/contents/media/images/2002/21/1241-Image.html", - "year": 2001 - }, - { - "date": "July 10 2019", - "image": "july-10-2019-galaxy-ngc-4068.jpg", - "name": "Galaxy NGC 4068", - "caption": "This image captures a starburst region in the dwarf galaxy NGC 4068. Starburst regions are areas of intense star formation.", - "url": "https://hubblesite.org/contents/media/images/2009/19/2556-Image.html", - "year": 2004 - }, - { - "date": "July 11 2019", - "image": "july-11-2019-interacting-galaxies-ugc-06471-and-ugc-06472.jpg", - "name": "Interacting Galaxies UGC 06471 and UGC 06472", - "caption": "This image shows a cosmic collision between two galaxies, UGC 06471 and UGC 06472. The colliding galaxies are 145 million light-years from Earth. Such collisions distort the shapes of the galaxies as they merge and eventually form a larger galaxy.", - "url": "https://hubblesite.org/contents/media/images/2001/04/1019-Image.html?news=true", - "year": 2000 - }, - { - "date": "July 12 2019", - "image": "july-12-2019-galaxy-cluster-tn-j1338-1942.jpg", - "name": "Galaxy Cluster TN J1338-1942", - "caption": "The galaxy cluster TN J1338-1942 contains a massive embryonic galaxy surrounded by smaller developing galaxies. The central galaxy has spectacular radio-emitting jets, fueled by a supermassive black hole deep within the galaxy's nucleus.", - "url": "https://hubblesite.org/contents/news-releases/2004/news-2004-01.html", - "year": 2002 - }, - { - "date": "July 13 2019", - "image": "july-13-2019-star-cluster-ngc-346.jpg", - "name": "Star Cluster NGC 346", - "caption": "This image captures a dynamic star-forming region in a nearby dwarf galaxy called the Small Magellanic Cloud. At the center is a brilliant star cluster called NGC 346.", - "url": "https://hubblesite.org/contents/media/images/2005/35/1818-Image.html", - "year": 2004 - }, - { - "date": "July 14 2019", - "image": "july-14-2019-supernova-remnant-n-49.jpg", - "name": "Supernova Remnant N 49", - "caption": "N 49 is a supernova remnant in a neighboring galaxy called the Large Magellanic Cloud. The delicate filaments are sheets of debris from a stellar explosion whose light would have reached Earth thousands of years ago.", - "url": "https://hubblesite.org/contents/media/images/2003/20/1379-Image.html", - "year": 2000 - }, - { - "date": "July 15 2019", - "image": "july-15-2019-core-of-star-cluster-omega-centauri.jpg", - "name": "Core of Star Cluster Omega Centauri", - "caption": "This view shows stars at the heart of Omega Centauri, one of roughly 150 globular clusters in our Milky Way galaxy. The behemoth stellar grouping is the biggest and brightest globular cluster in the Milky Way, and one of the few that can be seen by the unaided eye.", - "url": "https://hubblesite.org/contents/media/images/2009/25/2609-Image.html", - "year": 2009 - }, - { - "date": "July 16 2019", - "image": "july-16-2019-galaxy-eso-239-2.jpg", - "name": "Galaxy ESO 239-2", - "caption": "ESO 239-2 is the result of a cosmic collision between galaxies that will eventually result in a larger \"elliptical\" galaxy. The intermediate stage captured here shows a galaxy with long tails of dust and gas that envelope the galaxy's core.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2329-Image.html", - "year": 2006 - }, - { - "date": "July 17 2019", - "image": "july-17-2019-galaxy-ngc-300.jpg", - "name": "Galaxy NGC 300", - "caption": "NGC 300 is a spiral galaxy similar to our own Milky Way galaxy. Some of the bright blue specks in this image are young, massive stars called blue supergiants, and they are among the brightest stars seen in spiral galaxies.", - "url": "https://hubblesite.org/contents/media/images/2004/13/1509-Image.html", - "year": 2002 - }, - { - "date": "July 18 2019", - "image": "july-18-2019-interacting-galaxies-am-1316-241.jpg", - "name": "Interacting Galaxies AM 1316-241", - "caption": "AM 1316-241 is made up of two interacting galaxies: a spiral galaxy (on the left) in front of an elliptical galaxy (on the right). The starlight from the background elliptical galaxy is partially obscured by bands and filaments of dust in the foreground spiral galaxy.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2305-Image.html", - "year": 1997 - }, - { - "date": "July 19 2019", - "image": "july-19-2019-galaxy-ngc-300.jpg", - "name": "Galaxy NGC 300", - "caption": "NGC 300 is a spiral galaxy similar to our own Milky Way galaxy. Some of the bright blue specks in this image are young, massive stars called blue supergiants, and they are among the brightest stars seen in spiral galaxies.", - "url": "https://hubblesite.org/contents/media/images/2004/13/1509-Image.html", - "year": 2002 - }, - { - "date": "July 20 2019", - "image": "july-20-2019-ant-nebula.jpg", - "name": "Ant Nebula", - "caption": "The Ant Nebula displays intriguing symmetrical patterns in the lobes of gas being ejected from a dying Sun-like star at its center.", - "url": "https://hubblesite.org/contents/media/images/2001/05/1020-Image.html", - "year": 1997 - }, - { - "date": "July 21 2019", - "image": "july-21-2019-antennae-galaxies.jpg", - "name": "Antennae Galaxies", - "caption": "The two merging spiral galaxies that comprise the Antennae galaxies began their interaction only a few hundred million years ago. Over the course of the merger, billions of stars will be formed.", - "url": "https://hubblesite.org/contents/media/images/2006/46/1995-Image.html", - "year": 2004 - }, - { - "date": "July 22 2019", - "image": "july-22-2019-protostar-iras-20324-4057.jpg", - "name": "Protostar IRAS 20324+4057", - "caption": "This caterpillar-shaped knot, called IRAS 20324+4057, is a protostar that is in the process of growing from the dust and gas surrounding it. However, other bright stars are blasting ultraviolet radiation at this \"wanna-be\" star and sculpting the gas and dust into its long shape.", - "url": "https://hubblesite.org/contents/media/images/2013/35/3233-Image.html", - "year": 2006 - }, - { - "date": "July 23 2019", - "image": "july-23-2019-impact-scar-on-jupiter.jpg", - "name": "Impact Scar on Jupiter", - "caption": "This image of Jupiter reveals an elongated, dark spot at lower right. The unexpected blemish was created when an asteroid plunged into Jupiter and exploded, scattering debris into the giant planet's cloud tops.", - "url": "https://hubblesite.org/contents/news-releases/2010/news-2010-16.html", - "year": 2009 - }, - { - "date": "July 24 2019", - "image": "july-24-2019-jupiter-and-io.jpg", - "name": "Jupiter and Io", - "caption": "This image shows Jupiter's volcanic moon Io passing above the turbulent clouds of the giant planet. The conspicuous black spot on Jupiter is Io's shadow. The shadow sweeps across the face of Jupiter at 17 kilometers per second.", - "url": "https://hubblesite.org/contents/media/images/1996/30/442-Image.html", - "year": 1996 - }, - { - "date": "July 25 2019", - "image": "july-25-2019-hickson-compact-group-87.jpg", - "name": "Hickson Compact Group 87", - "caption": "This troupe of galaxies, known as Hickson Compact Group 87, is performing an intricate dance orchestrated by the mutual gravitational forces acting between them. The small spiral near the center could either be a member or an unrelated background object.", - "url": "https://hubblesite.org/contents/media/images/1999/31/868-Image.html", - "year": 1999 - }, - { - "date": "July 26 2019", - "image": "july-26-2019-andromeda-galaxy.jpg", - "name": "Andromeda Galaxy", - "caption": "Over 100 million stars are on display in this portion of the Andromeda galaxy, located over 2 million light-years away. This portrait of our galactic neighbor is the largest image yet assembled by Hubble.", - "url": "https://hubblesite.org/contents/news-releases/2015/news-2015-02.html", - "year": 2010 - }, - { - "date": "July 27 2019", - "image": "july-27-2019-andromeda-galaxy.jpg", - "name": "Andromeda Galaxy", - "caption": "Over 100 million stars are on display in this portion of the Andromeda galaxy, located over 2 million light-years away. This portrait of our galactic neighbor is the largest image yet assembled by Hubble.", - "url": "https://hubblesite.org/contents/news-releases/2015/news-2015-02.html", - "year": 2010 - }, - { - "date": "July 28 2019", - "image": "july-28-2019-triangulum-galaxy.png", - "name": "Triangulum Galaxy", - "caption": "This mosaic captures the nearby Triangulum galaxy. Striking areas of star birth glow bright blue throughout the galaxy, particularly in beautiful nebulas of hot gas like star-forming region NGC 604 in the upper left.", - "url": "https://hubblesite.org/contents/media/images/2019/01/4305-Image.html", - "year": 2017 - }, - { - "date": "July 29 2019", - "image": "july-29-2019-star-cluster-trumpler-14.jpg", - "name": "Star Cluster Trumpler 14", - "caption": "Called Trumpler 14, this cluster of stars is located 8,000 light-years away in a huge star-forming region known as the Carina Nebula. The cluster is only 500,000 years old and has one of the highest concentrations of bright, massive stars in the entire Milky Way.", - "url": "https://hubblesite.org/contents/media/images/2016/03/3693-Image.html", - "year": 2006 - }, - { - "date": "July 30 2019", - "image": "july-30-2019-hourglass-nebula.jpg", - "name": "Hourglass Nebula", - "caption": "The Hourglass Nebula has been formed by a dying Sun-like star shedding its outer layers of gas. One theory suggests that the hourglass shape is produced as a fast stellar wind encounters a slowly expanding cloud that is more dense near the star’s equator than near its poles.", - "url": "https://hubblesite.org/contents/media/images/1996/07/397-Image.html", - "year": 1995 - }, - { - "date": "July 31 2019", - "image": "july-31-2019-triangulum-galaxy.png", - "name": "Triangulum Galaxy", - "caption": "This mosaic captures the nearby Triangulum galaxy. Striking areas of star birth glow bright blue throughout the galaxy, particularly in beautiful nebulas of hot gas like star-forming region NGC 604 in the upper left.", - "url": "https://hubblesite.org/contents/media/images/2019/01/4305-Image.html", - "year": 2017 - }, - { - "date": "August 1 2019", - "image": "august-1-2019-galaxy-ngc-1672.jpg", - "name": "Galaxy NGC 1672", - "caption": "NGC 1672 is a barred spiral galaxy. Its arms do not twist all the way to the galaxy's center but attach to the ends of a bar of stars that extends from the nucleus. Clusters of hot, young, blue stars form along the spiral arms, while surrounding clouds of hydrogen gas glow red.", - "url": "https://hubblesite.org/contents/media/images/2007/15/2092-Image.html", - "year": 2005 - }, - { - "date": "August 2 2019", - "image": "august-2-2019-hubble-v-nebula.jpg", - "name": "Hubble-V Nebula", - "caption": "Hubble-V is an active star-forming region within galaxy NGC 6822. The cloud is about 200 light-years across and contains a dense knot of dozens of ultra-hot stars, each 100,000 times brighter than our Sun.", - "url": "https://hubblesite.org/contents/media/images/2001/39/1126-Image.html", - "year": 1997 - }, - { - "date": "August 3 2019", - "image": "august-3-2019-impact-scar-on-jupiter.jpg", - "name": "Impact Scar on Jupiter", - "caption": "This image shows a scar on Jupiter. The unexpected blemish was created when an object (likely an asteroid) plunged into Jupiter and exploded, scattering debris into the giant planet's cloud tops.", - "url": "https://hubblesite.org/contents/news-releases/2010/news-2010-16.html", - "year": 2009 - }, - { - "date": "August 4 2019", - "image": "august-4-2019-herbig-haro-24.jpg", - "name": "Herbig-Haro 24", - "caption": "A partially obscured, newborn star near the center of this image is shooting twin jets into the surrounding gas and dust. The shocks from the collision light up patches of nebulosity collectively called Herbig-Haro 24.", - "url": "https://hubblesite.org/contents/media/images/2015/42/3656-Image.html", - "year": 2001 - }, - { - "date": "August 5 2019", - "image": "august-5-2019-andromeda-galaxy.jpg", - "name": "Andromeda Galaxy", - "caption": "Over 100 million stars are on display in this portion of the Andromeda galaxy, located over 2 million light-years away. This portrait of our galactic neighbor is the largest image yet assembled by Hubble.", - "url": "https://hubblesite.org/contents/news-releases/2015/news-2015-02.html", - "year": 2011 - }, - { - "date": "August 6 2019", - "image": "august-6-2019-andromeda-galaxy.jpg", - "name": "Andromeda Galaxy", - "caption": "Over 100 million stars are on display in this portion of the Andromeda galaxy, located over 2 million light-years away. This portrait of our galactic neighbor is the largest image yet assembled by Hubble.", - "url": "https://hubblesite.org/contents/news-releases/2015/news-2015-02.html", - "year": 2011 - }, - { - "date": "August 7 2019", - "image": "august-7-2019-andromeda-galaxy.jpg", - "name": "Andromeda Galaxy", - "caption": "Over 100 million stars are on display in this portion of the Andromeda galaxy, located over 2 million light-years away. This portrait of our galactic neighbor is the largest image yet assembled by Hubble.", - "url": "https://hubblesite.org/contents/news-releases/2015/news-2015-02.html", - "year": 2010 - }, - { - "date": "August 8 2019", - "image": "august-8-2019-center-of-the-crab-nebula.jpg", - "name": "Center of the Crab Nebula", - "caption": "At the center of the Crab Nebula sits a stellar remnant called a neutron star that has about the same mass as the Sun compressed into a sphere only a few miles across. Spinning 30 times a second, the neutron star shoots out beams of energy that make it look like it's pulsating.", - "url": "https://hubblesite.org/contents/media/images/2016/26/3760-Image.html", - "year": 2003 - }, - { - "date": "August 9 2019", - "image": "august-9-2019-andromeda-galaxy.jpg", - "name": "Andromeda Galaxy", - "caption": "Over 100 million stars are on display in this portion of the Andromeda galaxy, located over 2 million light-years away. This portrait of our galactic neighbor is the largest image yet assembled by Hubble.", - "url": "https://hubblesite.org/contents/news-releases/2015/news-2015-02.html", - "year": 2011 - }, - { - "date": "August 10 2019", - "image": "august-10-2019-nebula-ngc-2074.jpg", - "name": "Nebula NGC 2074", - "caption": "The nebula NGC 2074 is a firestorm of raw stellar creation, perhaps triggered by a nearby supernova explosion. It lies in a nearby galaxy called the Large Magellanic Cloud, about 170,000 light-years away.", - "url": "https://hubblesite.org/contents/media/images/2008/31/2397-Image.html", - "year": 2008 - }, - { - "date": "August 11 2019", - "image": "august-11-2019-interacting-galaxies-arp-220.jpg", - "name": "Interacting Galaxies Arp 220", - "caption": "Arp 220 is the result of a collision between two spiral galaxies that began 700 millions years ago. Located about 250 million light-years from Earth, it is one of the nearest galaxy mergers to our planet.", - "url": "https://hubblesite.org/contents/media/images/2006/26/1940-Image.html", - "year": 2002 - }, - { - "date": "August 12 2019", - "image": "august-12-2019-andromeda-galaxy.jpg", - "name": "Andromeda Galaxy", - "caption": "Over 100 million stars are on display in this portion of the Andromeda galaxy, located over 2 million light-years away. This portrait of our galactic neighbor is the largest image yet assembled by Hubble.", - "url": "https://hubblesite.org/contents/news-releases/2015/news-2015-02.html", - "year": 2010 - }, - { - "date": "August 13 2019", - "image": "august-13-2019-neptune.jpg", - "name": "Neptune", - "caption": "This image shows bright clouds and cloud bands wrapping around Neptune. On the giant planet, winds blow at 900 miles per hour and huge storms — some the size of Earth itself — come and go with regularity.", - "url": "https://hubblesite.org/contents/news-releases/1998/news-1998-34.html", - "year": 1996 - }, - { - "date": "August 14 2019", - "image": "august-14-2019-galaxy-ngc-1808.jpg", - "name": "Galaxy NGC 1808", - "caption": "This close-up view shows a hotbed of star formation at the center of spiral galaxy NGC 1808. In the image, older stars appear yellow and young stars are blue.", - "url": "https://hubblesite.org/contents/media/images/1998/12/631-Image.html", - "year": 1997 - }, - { - "date": "August 15 2019", - "image": "august-15-2019-triangulum-galaxy.png", - "name": "Triangulum Galaxy", - "caption": "This mosaic captures the nearby Triangulum galaxy. Striking areas of star birth glow bright blue throughout the galaxy, particularly in beautiful nebulas of hot gas like star-forming region NGC 604 in the upper left.", - "url": "https://hubblesite.org/contents/media/images/2019/01/4305-Image.html", - "year": 2017 - }, - { - "date": "August 16 2019", - "image": "august-16-2019-andromeda-galaxy.jpg", - "name": "Andromeda Galaxy", - "caption": "Over 100 million stars are on display in this portion of the Andromeda galaxy, located over 2 million light-years away. This portrait of our galactic neighbor is the largest image yet assembled by Hubble.", - "url": "https://hubblesite.org/contents/news-releases/2015/news-2015-02.html", - "year": 2011 - }, - { - "date": "August 17 2019", - "image": "august-17-2019-supernova-in-galaxy-ngc-2403.jpg", - "name": "Supernova in Galaxy NGC 2403", - "caption": "This image captures a stellar explosion, called a supernova, in the galaxy NGC 2403. The supernova looks like a bright star in the upper-right corner. The brighter star near the top, and other bright stars in the image, reside within our own galaxy.", - "url": "https://hubblesite.org/contents/media/images/2004/23/1568-Image.html", - "year": 2004 - }, - { - "date": "August 18 2019", - "image": "august-18-2019-asteroid-trail-past-sagittarius-dwarf-irregular-galaxy.jpg", - "name": "Asteroid Trail Past Sagittarius Dwarf Irregular Galaxy", - "caption": "While observing the Sagittarius dwarf irregular galaxy, Hubble captured the trail of a faint asteroid that had drifted across the field of view. The trail is seen as a series of 13 reddish arcs on the right.", - "url": "https://hubblesite.org/contents/media/images/2004/31/1602-Image.html", - "year": 2003 - }, - { - "date": "August 19 2019", - "image": "august-19-2019-neptune.jpg", - "name": "Neptune", - "caption": "Neptune is the most distant major planet in our solar system. The bright patches on the planet are clouds composed of methane ice crystals.", - "url": "https://hubblesite.org/contents/media/images/2013/30/3223-Image.html", - "year": 2009 - }, - { - "date": "August 20 2019", - "image": "august-20-2019-galaxy-m83.jpg", - "name": "Galaxy M83", - "caption": "This image of spiral galaxy M83 captures thousands of star clusters, hundreds of thousands of individual stars, and \"ghosts\" of dead stars called supernova remnants.", - "url": "https://hubblesite.org/contents/media/images/2014/04/3293-Image.html", - "year": 2009 - }, - { - "date": "August 21 2019", - "image": "august-21-2019-galaxy-ngc-6503.jpg", - "name": "Galaxy NGC 6503", - "caption": "Most galaxies are clumped together in groups or clusters. A neighboring galaxy is never far away. But this galaxy, known as NGC 6503, has found itself in a lonely position, at the edge of a strangely empty patch of space called the Local Void.", - "url": "https://hubblesite.org/contents/media/images/2015/23/3586-Image.html", - "year": 2013 - }, - { - "date": "August 22 2019", - "image": "august-22-2019-galaxy-ngc-4993.png", - "name": "Galaxy NGC 4993", - "caption": "In this galaxy, called NGC 4993, two neutron stars collided, creating gravitational waves discovered in 2017. The event produced a flash of light, called a kilonova, which appears to the upper left of center.", - "url": "https://hubblesite.org/contents/media/images/2017/41/4078-Image.html?news=true", - "year": 2017 - }, - { - "date": "August 23 2019", - "image": "august-23-2019-galaxy-cluster-abell-2744.jpg", - "name": "Galaxy Cluster Abell 2744", - "caption": "Located 3.5 billion light-years away, Abell 2744 contains several hundred galaxies and might be a pile-up of at least four smaller galaxy clusters. Abell 2744’s strong gravitational field acts as a lens, brightening and magnifying the light of nearly 3,000 distant background galaxies.", - "url": "https://hubblesite.org/contents/media/images/2014/01/3277-Image.html", - "year": 2013 - }, - { - "date": "August 24 2019", - "image": "august-24-2019-mars.jpg", - "name": "Mars", - "caption": "Hubble captured this image of Mars when the planet was approximately 34.7 million miles from Earth. The dark linear feature on the left is Valles Marineris, a 2,500-mile-long system of canyons.", - "url": "https://hubblesite.org/contents/media/images/2003/22/1389-Image.html", - "year": 2003 - }, - { - "date": "August 25 2019", - "image": "august-25-2019-herbig-haro-32.jpg", - "name": "Herbig Haro 32", - "caption": "HH 32 is an example of a \"Herbig-Haro object,\" which is formed when young stars eject jets of material back into interstellar space. These jets plow into the surrounding nebula, producing strong shock waves that heat the gas and cause it to glow.", - "url": "https://hubblesite.org/contents/media/images/1999/35/902-Image.html", - "year": 1994 - }, - { - "date": "August 26 2019", - "image": "august-26-2019-hubble-ultra-deep-field.jpg", - "name": "Hubble Ultra Deep Field", - "caption": "This image of the Hubble Ultra Deep Field includes infrared observations that allowed Hubble to peer deeper into the universe than it ever had before. The faintest and reddest objects in the image are galaxies that formed 600 million years after the big bang.", - "url": "https://hubblesite.org/contents/media/images/2009/31/2644-Image.html", - "year": 2009 - }, - { - "date": "August 27 2019", - "image": "august-27-2019-mars.jpg", - "name": "Mars", - "caption": "Hubble captured this image of Mars when the planet was at its closest to Earth in nearly 60,000 years. The solar system's largest volcano, Olympus Mons, appears near the top.", - "url": "https://hubblesite.org/contents/media/images/2003/22/1383-Image.html", - "year": 2003 - }, - { - "date": "August 28 2019", - "image": "august-28-2019-hubble-ultra-deep-field.jpg", - "name": "Hubble Ultra Deep Field", - "caption": "This image of the Hubble Ultra Deep Field includes infrared observations that allowed Hubble to peer deeper into the universe than it ever had before. The faintest and reddest objects in the image are galaxies that formed 600 million years after the big bang.", - "url": "https://hubblesite.org/contents/media/images/2009/31/2644-Image.html", - "year": 2009 - }, - { - "date": "August 29 2019", - "image": "august-29-2019-hubble-ultra-deep-field.jpg", - "name": "Hubble Ultra Deep Field", - "caption": "This image of the Hubble Ultra Deep Field includes infrared observations that allowed Hubble to peer deeper into the universe than it ever had before. The faintest and reddest objects in the image are galaxies that formed 600 million years after the big bang.", - "url": "https://hubblesite.org/contents/media/images/2009/31/2644-Image.html", - "year": 2009 - }, - { - "date": "August 30 2019", - "image": "august-30-2019-hubble-ultra-deep-field.jpg", - "name": "Hubble Ultra Deep Field", - "caption": "This image of the Hubble Ultra Deep Field includes infrared observations that allowed Hubble to peer deeper into the universe than it ever had before. The faintest and reddest objects in the image are galaxies that formed 600 million years after the big bang.", - "url": "https://hubblesite.org/contents/media/images/2009/31/2644-Image.html", - "year": 2009 - }, - { - "date": "August 31 2019", - "image": "august-31-2019-hubble-ultra-deep-field.jpg", - "name": "Hubble Ultra Deep Field", - "caption": "This image of the Hubble Ultra Deep Field includes infrared observations that allowed Hubble to peer deeper into the universe than it ever had before. The faintest and reddest objects in the image are galaxies that formed 600 million years after the big bang.", - "url": "https://hubblesite.org/contents/media/images/2009/31/2644-Image.html", - "year": 2009 - }, - { - "date": "September 1 2019", - "image": "september-1-2019-hubble-ultra-deep-field.jpg", - "name": "Hubble Ultra Deep Field", - "caption": "This image of the Hubble Ultra Deep Field includes infrared observations that allowed Hubble to peer deeper into the universe than it ever had before. The faintest and reddest objects in the image are galaxies that formed 600 million years after the big bang.", - "url": "https://hubblesite.org/contents/media/images/2009/31/2644-Image.html", - "year": 2009 - }, - { - "date": "September 2 2019", - "image": "september-2-2019-hubble-ultra-deep-field.jpg", - "name": "Hubble Ultra Deep Field", - "caption": "This image of the Hubble Ultra Deep Field includes infrared observations that allowed Hubble to peer deeper into the universe than it ever had before. The faintest and reddest objects in the image are galaxies that formed 600 million years after the big bang.", - "url": "https://hubblesite.org/contents/media/images/2009/31/2644-Image.html", - "year": 2009 - }, - { - "date": "September 3 2019", - "image": "september-3-2019-galaxy-hudf-jd2.jpg", - "name": "Galaxy HUDF-JD2", - "caption": "The small red object at the center of this image (just above the large spiral galaxy) is one of the most distant galaxies ever seen. Called HUDF-JD2, it is one of about 10,000 galaxies found in the Hubble Ultra Deep Field.", - "url": "https://hubblesite.org/contents/media/images/2005/28/1770-Image.html", - "year": 2003 - }, - { - "date": "September 4 2019", - "image": "september-4-2019-nebula-n-81.jpg", - "name": "Nebula N 81", - "caption": "This image shows a newborn star cluster cradled within a nebula, or glowing cloud of gas, called N 81. This stellar nursery lies about 200,000 light-years away within the Small Magellanic Cloud, a small galaxy orbiting our own Milky Way.", - "url": "https://hubblesite.org/contents/media/images/2000/30/992-Image.html", - "year": 1997 - }, - { - "date": "September 5 2019", - "image": "september-5-2019-hubble-ultra-deep-field.jpg", - "name": "Hubble Ultra Deep Field", - "caption": "This image of the Hubble Ultra Deep Field includes infrared observations that allowed Hubble to peer deeper into the universe than it ever had before. The faintest and reddest objects in the image are galaxies that formed 600 million years after the big bang.", - "url": "https://hubblesite.org/contents/media/images/2009/31/2644-Image.html", - "year": 2009 - }, - { - "date": "September 6 2019", - "image": "september-6-2019-hubble-ultra-deep-field.jpg", - "name": "Hubble Ultra Deep Field", - "caption": "This image of the Hubble Ultra Deep Field includes infrared observations that allowed Hubble to peer deeper into the universe than it ever had before. The faintest and reddest objects in the image are galaxies that formed 600 million years after the big bang.", - "url": "https://hubblesite.org/contents/media/images/2009/31/2644-Image.html", - "year": 2009 - }, - { - "date": "September 7 2019", - "image": "september-7-2019-hubble-x-nebula.jpg", - "name": "Hubble-X Nebula", - "caption": "Hubble-X is a glowing gas cloud, one of the most active star-forming regions within galaxy NGC 6822. The cloud is about 110 light-years across and contains many thousands of newly formed stars in a central cluster.", - "url": "https://hubblesite.org/contents/media/images/2001/01/1012-Image.html", - "year": 1997 - }, - { - "date": "September 8 2019", - "image": "september-8-2019-galaxy-m81.jpg", - "name": "Galaxy M81", - "caption": "The arms of the \"grand design\" spiral galaxy M81 are filled with young, bluish, hot stars. The greenish regions in the image are bright, gaseous clouds where new stars are forming.", - "url": "https://hubblesite.org/contents/media/images/2007/19/2127-Image.html", - "year": 2006 - }, - { - "date": "September 9 2019", - "image": "september-9-2019-v838-monocerotis-light-echo.jpg", - "name": "V838 Monocerotis Light Echo", - "caption": "This image captures a light echo from the star V838 Monocerotis. After the star brightened temporarily, light from that eruption began propagating outward through a dusty cloud around the star. The light reflects or \"echoes\" off the dust and then travels to Earth.", - "url": "https://hubblesite.org/contents/media/images/2006/50/2006-Image.html", - "year": 2006 - }, - { - "date": "September 10 2019", - "image": "september-10-2019-galaxy-behind-star-cluster-ngc-6752.png", - "name": "Galaxy Behind Star Cluster NGC 6752", - "caption": "This image shows stars in a small part of the globular cluster NGC 6752. Near the bottom appears a background galaxy, much farther away, that astronomers found while studying this image. It's a dwarf galaxy that is nearly as old as the universe.", - "url": "https://hubblesite.org/contents/media/images/2019/09/4317-Image.html", - "year": 2018 - }, - { - "date": "September 11 2019", - "image": "september-11-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2009 - }, - { - "date": "September 12 2019", - "image": "september-12-2019-galaxy-ngc-3310.jpg", - "name": "Galaxy NGC 3310", - "caption": "There are several hundred star clusters in the starburst galaxy NGC 3310. They appear in this image as the bright, blue clumps that trace the galaxy's spiral arms.", - "url": "https://hubblesite.org/contents/media/images/2001/26/1094-Image.html", - "year": 2000 - }, - { - "date": "September 13 2019", - "image": "september-13-2019-arches-cluster.jpg", - "name": "Arches Cluster", - "caption": "The Arches cluster is the densest known star cluster in our galaxy and resides 25,000 light-years away. In this rough-and-tumble region, huge clouds of gas collide to form behemoth stars.", - "url": "https://hubblesite.org/contents/media/images/2005/05/1653-Image.html", - "year": 1997 - }, - { - "date": "September 14 2019", - "image": "september-14-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2005 - }, - { - "date": "September 15 2019", - "image": "september-15-2019-center-of-the-crab-nebula.jpg", - "name": "Center of the Crab Nebula", - "caption": "At the center of the Crab Nebula sits a stellar remnant called a neutron star that has about the same mass as the Sun compressed into a sphere only a few miles across. Spinning 30 times a second, the neutron star shoots out beams of energy that make it look like it's pulsating.", - "url": "https://hubblesite.org/contents/media/images/2016/26/3760-Image.html", - "year": 2005 - }, - { - "date": "September 16 2019", - "image": "september-16-2019-beta-pictoris-disk.jpg", - "name": "Beta Pictoris Disk", - "caption": "In 1984, Beta Pictoris was the very first star discovered to be surrounded by a bright disk of light-scattering dust and debris. Planets are thought to form in such disks, and astronomers have discovered two planets orbiting Beta Pictoris.", - "url": "https://hubblesite.org/contents/media/images/2015/06/3490-Image.html", - "year": 1997 - }, - { - "date": "September 17 2019", - "image": "september-17-2019-galaxy-ugc-5340.png", - "name": "Galaxy UGC 5340", - "caption": "This image captures the dwarf galaxy UGC 5340. A pocket of rapid star birth appears in the lower right corner. This region of star formation was probably triggered by a gravitational interaction with an unseen companion galaxy.", - "url": "https://hubblesite.org/contents/media/images/2018/27/4162-Image.html", - "year": 2014 - }, - { - "date": "September 18 2019", - "image": "september-18-2019-pinwheel-galaxy.jpg", - "name": "Pinwheel Galaxy", - "caption": "The Pinwheel galaxy has a pancake-like shape that we view face-on. This perspective shows off the spiral structure that gives the galaxy its nickname.", - "url": "https://hubblesite.org/contents/media/images/2009/07/2477-Image.html", - "year": 1994 - }, - { - "date": "September 19 2019", - "image": "september-19-2019-reflection-nebula-ic-349.jpg", - "name": "Reflection Nebula IC 349", - "caption": "IC 349 is a reflection nebula in the Pleiades star cluster (often called the \"Seven Sisters\"). The eerie, wispy tendrils of an interstellar cloud are being destroyed by one of the brightest stars in the star cluster.", - "url": "https://hubblesite.org/contents/media/images/2000/36/1009-Image.html", - "year": 1999 - }, - { - "date": "September 20 2019", - "image": "september-20-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2002 - }, - { - "date": "September 21 2019", - "image": "september-21-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2004 - }, - { - "date": "September 22 2019", - "image": "september-22-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2002 - }, - { - "date": "September 23 2019", - "image": "september-23-2019-galaxy-eso-243-49.jpg", - "name": "Galaxy ESO 243-49", - "caption": "This edge-on galaxy, called ESO 243-49, appears to host a medium-sized black hole that might have come from a cannibalized dwarf galaxy. As massive as 20,000 Suns, the black hole lies above the galactic plane — an unusual location that suggests it originated somewhere else.", - "url": "https://hubblesite.org/contents/media/images/2012/11/2992-Image.html", - "year": 2010 - }, - { - "date": "September 24 2019", - "image": "september-24-2019-galaxy-eso-243-49.jpg", - "name": "Galaxy ESO 243-49", - "caption": "This edge-on galaxy, called ESO 243-49, appears to host a medium-sized black hole that might have come from a cannibalized dwarf galaxy. As massive as 20,000 Suns, the black hole lies above the galactic plane — an unusual location that suggests it originated somewhere else.", - "url": "https://hubblesite.org/contents/media/images/2012/11/2992-Image.html?itemsPerPage=100&page=4&filterUUID=8a87f02e-e18b-4126-8133-2576f4fdc5e2&news=true", - "year": 2010 - }, - { - "date": "September 25 2019", - "image": "september-25-2019-galaxy-ngc-1132.jpg", - "name": "Galaxy NGC 1132", - "caption": "The large elliptical galaxy NGC 1132 likely formed from a group of galaxies that merged together. The galaxy is dubbed a \"fossil group\" because it contains enormous concentrations of dark matter, comparable to the dark matter found in an entire group of galaxies.", - "url": "https://hubblesite.org/contents/media/images/2008/07/2252-Image.html", - "year": 2005 - }, - { - "date": "September 26 2019", - "image": "september-26-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2009 - }, - { - "date": "September 27 2019", - "image": "september-27-2019-egg-nebula.jpg", - "name": "Egg Nebula", - "caption": "In the Egg Nebula, shells of dust form concentric rings around an aging star, resembling the layers of an onion. A thick dust belt, running almost vertically through the center, blocks light from the central star while twin beams of light radiate from the star.", - "url": "https://hubblesite.org/contents/media/images/2003/09/1305-Image.html", - "year": 2002 - }, - { - "date": "September 28 2019", - "image": "september-28-2019-galaxy-ngc-300.jpg", - "name": "Galaxy NGC 300", - "caption": "NGC 300 is a spiral galaxy similar to our own Milky Way galaxy. Some of the bright blue specks in this image are young, massive stars called blue supergiants, and they are among the brightest stars seen in spiral galaxies.", - "url": "https://hubblesite.org/contents/media/images/2004/13/1509-Image.html", - "year": 2002 - }, - { - "date": "September 29 2019", - "image": "september-29-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2009 - }, - { - "date": "September 30 2019", - "image": "september-30-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2009 - }, - { - "date": "October 1 2019", - "image": "october-1-2019-galaxy-ngc-3949.jpg", - "name": "Galaxy NGC 3949", - "caption": "Like our Milky Way, the galaxy NGC 3949 has a disk full of young, blue stars peppered with pink star-birth regions. In contrast to the blue disk, the galaxy's bright center is made up of mostly older stars and appears more yellow.", - "url": "https://hubblesite.org/contents/media/images/2004/25/1576-Image.html", - "year": 2001 - }, - { - "date": "October 2 2019", - "image": "october-2-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2002 - }, - { - "date": "October 3 2019", - "image": "october-3-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2009 - }, - { - "date": "October 4 2019", - "image": "october-4-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2009 - }, - { - "date": "October 5 2019", - "image": "october-5-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2002 - }, - { - "date": "October 6 2019", - "image": "october-6-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2004 - }, - { - "date": "October 7 2019", - "image": "october-7-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2004 - }, - { - "date": "October 8 2019", - "image": "october-8-2019-galaxy-hercules-a.jpg", - "name": "Galaxy Hercules A", - "caption": "At the center of this image sits a large galaxy called Hercules A that harbors a supermassive black hole more than a thousand times as massive as the one in the Milky Way's center. Radio observations reveal large jets shooting away from the galaxy's core.", - "url": "https://hubblesite.org/contents/media/images/2012/47/3110-Image.html", - "year": 2012 - }, - { - "date": "October 9 2019", - "image": "october-9-2019-galaxy-cluster-abell-2667.jpg", - "name": "Galaxy Cluster Abell 2667", - "caption": "While looking at galaxy cluster Abell 2667, astronomers found an odd-looking spiral galaxy (in the upper left corner of the image) that is plowing through the cluster and being ripped apart by the galaxy cluster's gravitational field and harsh environment.", - "url": "https://hubblesite.org/contents/media/images/2007/12/2077-Image.html", - "year": 2001 - }, - { - "date": "October 10 2019", - "image": "october-10-2019-interacting-galaxies-arp-148.jpg", - "name": "Interacting Galaxies Arp 148", - "caption": "Arp 148 is the aftermath of an encounter between two galaxies, resulting in a ring-shaped galaxy and an elongated companion. The shapes and arrangement of the galaxies suggest that this is a snapshot of an ongoing collision.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2309-Image.html", - "year": 1995 - }, - { - "date": "October 11 2019", - "image": "october-11-2019-goods-south-field.jpg", - "name": "GOODS South Field", - "caption": "More than 12 billion years of cosmic history are shown in this panoramic view of thousands of galaxies in various stages of assembly. The view covers a portion of the southern field of a galaxy census called the Great Observatories Origins Deep Survey (GOODS).", - "url": "https://hubblesite.org/contents/media/images/2010/01/2662-Image.html", - "year": 2009 - }, - { - "date": "October 12 2019", - "image": "october-12-2019-orion-nebula.jpg", - "name": "Orion Nebula", - "caption": "The Orion Nebula is the nearest star-forming region to Earth. Massive, young stars are shaping the nebula with their winds and radiation. Pillars of dense gas may be the homes of budding stars. The bright central region is the home of the four heftiest stars in the nebula.", - "url": "https://hubblesite.org/contents/media/images/2006/01/1826-Image.html", - "year": 2004 - }, - { - "date": "October 13 2019", - "image": "october-13-2019-herbig-haro-24.jpg", - "name": "Herbig-Haro 24", - "caption": "A partially obscured, newborn star near the center of this image is shooting twin jets into the surrounding gas and dust. The shocks from the collision light up patches of nebulosity collectively called Herbig-Haro 24.", - "url": "https://hubblesite.org/contents/media/images/2015/42/3656-Image.html", - "year": 2009 - }, - { - "date": "October 14 2019", - "image": "october-14-2019-galaxy-cluster-0024-1654.jpg", - "name": "Galaxy Cluster 0024+1654", - "caption": "The light from a distant galaxy, nearly 10 billion light-years away, has been warped into blue arcs and streaks by the gravity of galaxy cluster 0024+1654. The cluster's gravity acts as a lens, bending and amplifying light from the background galaxy.", - "url": "https://hubblesite.org/contents/media/images/1996/10/403-Image.html", - "year": 1994 - }, - { - "date": "October 15 2019", - "image": "october-15-2019-supernova-remnant-e0102.jpg", - "name": "Supernova Remnant E0102", - "caption": "In a nearby galaxy called the Small Magellanic Cloud, a massive star exploded as a supernova and dissipated its interior into a spectacular display of colorful filaments. The supernova remnant, known as E0102, is the greenish-blue field of debris just below center.", - "url": "https://hubblesite.org/contents/media/images/2006/35/1964-Image.html", - "year": 2003 - }, - { - "date": "October 16 2019", - "image": "october-16-2019-ring-nebula.jpg", - "name": "Ring Nebula", - "caption": "About a light-year across, the Ring Nebula is formed by a dying star floating in a blue haze of hot gas at its center. This image reveals elongated, dark clumps of material embedded in the gas at the edge of the nebula.", - "url": "https://hubblesite.org/contents/media/images/1999/01/748-Image.html", - "year": 1998 - }, - { - "date": "October 17 2019", - "image": "october-17-2019-cartwheel-galaxy.jpg", - "name": "Cartwheel Galaxy", - "caption": "The Cartwheel galaxy's unusual appearance was created by a nearly head-on collision with a smaller galaxy. Its spoke-like structures are wisps of material connecting the galaxy’s nucleus to an outer ring of young stars.", - "url": "https://hubblesite.org/contents/news-releases/1996/news-1996-36.html", - "year": 1996 - }, - { - "date": "October 18 2019", - "image": "october-18-2019-reflection-nebula-n30b.jpg", - "name": "Reflection Nebula N30B", - "caption": "A unique, peanut-shaped cocoon of dust surrounds a cluster of young, hot stars in this image. This reflection nebula, named N30B, is embedded in a much larger nebula called DEM L 106. The wispy filaments of DEM L 106 fill much of the image.", - "url": "https://hubblesite.org/contents/media/images/2002/29/1272-Image.html", - "year": 2001 - }, - { - "date": "October 19 2019", - "image": "october-19-2019-comet-siding-spring.jpg", - "name": "Comet Siding Spring", - "caption": "Comet Siding Spring (C/2013 A1) had a close encounter with Mars on October 19, 2014. On that date the comet passed within approximately 87,000 miles of Mars (or about one-third the distance between Earth and the Moon).", - "url": "https://hubblesite.org/contents/media/images/2014/45/3444-Image.html", - "year": 2014 - }, - { - "date": "October 20 2019", - "image": "october-20-2019-30-doradus-nebula.jpg", - "name": "30 Doradus Nebula", - "caption": "This massive, young stellar grouping, called R136, is only a few million years old and resides in the 30 Doradus Nebula, a turbulent star-birth region in the Large Magellanic Cloud, a satellite galaxy of our Milky Way.", - "url": "https://hubblesite.org/contents/media/images/2009/32/2649-Image.html", - "year": 2009 - }, - { - "date": "October 21 2019", - "image": "october-21-2019-galaxy-ngc-1569.jpg", - "name": "Galaxy NGC 1569", - "caption": "The nearby dwarf galaxy NGC 1569 is a hotbed of vigorous star birth and is one of the closest \"starburst\" galaxies to us. The galaxy's \"star factories\" are manufacturing brilliant blue star clusters.", - "url": "https://hubblesite.org/contents/media/images/2004/06/1455-Image.html", - "year": 1998 - }, - { - "date": "October 22 2019", - "image": "october-22-2019-horsehead-nebula.jpg", - "name": "Horsehead Nebula", - "caption": "The backlit wisps along the Horsehead Nebula's upper ridge are being illuminated by a young five-star system just off the top of this image, taken in infrared light. Harsh radiation from one of these bright stars is slowly evaporating the nebula.", - "url": "https://hubblesite.org/contents/media/images/2013/12/3165-Image.html", - "year": 2012 - }, - { - "date": "October 23 2019", - "image": "october-23-2019-horsehead-nebula.jpg", - "name": "Horsehead Nebula", - "caption": "The backlit wisps along the Horsehead Nebula's upper ridge are being illuminated by a young five-star system just off the top of this image, taken in infrared light. Harsh radiation from one of these bright stars is slowly evaporating the nebula.", - "url": "https://hubblesite.org/contents/media/images/2013/12/3165-Image.html", - "year": 2012 - }, - { - "date": "October 24 2019", - "image": "october-24-2019-galaxy-ngc-7714.jpg", - "name": "Galaxy NGC 7714", - "caption": "The disrupted galaxy NGC 7714 displays a striking smoke-ring-like structure. The golden loop is made of Sun-like stars that have been pulled deep into space, far from the galaxy's center, by the gravity of a nearby galaxy that lies just out of view.", - "url": "https://hubblesite.org/contents/media/images/2015/04/3482-Image.html", - "year": 2011 - }, - { - "date": "October 25 2019", - "image": "october-25-2019-30-doradus-nebula.jpg", - "name": "30 Doradus Nebula", - "caption": "This massive, young stellar grouping, called R136, is only a few million years old and resides in the 30 Doradus Nebula, a turbulent star-birth region in the Large Magellanic Cloud, a satellite galaxy of our Milky Way.", - "url": "https://hubblesite.org/contents/media/images/2009/32/2649-Image.html", - "year": 2009 - }, - { - "date": "October 26 2019", - "image": "october-26-2019-star-clusters-in-the-tarantula-nebula.jpg", - "name": "Star Clusters in the Tarantula Nebula", - "caption": "The Tarantula Nebula is an enormous star-forming region located 170,000 light-years from Earth. The collection of stars in the core of the nebula, shown here, is made up of two individual star clusters that differ in age by about a million years.", - "url": "https://hubblesite.org/contents/media/images/2012/35/3087-Image.html?itemsPerPage=100&page=4&filterUUID=8a87f02e-e18b-4126-8133-2576f4fdc5e2&news=true", - "year": 2009 - }, - { - "date": "October 27 2019", - "image": "october-27-2019-interacting-galaxies-arp-147.jpg", - "name": "Interacting Galaxies Arp 147", - "caption": "Arp 147 consists of a pair of interacting galaxies. The left-most galaxy in this image appears nearly edge-on to our line of sight and features a smooth ring of starlight. The right-most galaxy exhibits a clumpy, blue ring of intense star formation.", - "url": "https://hubblesite.org/contents/media/images/2008/37/2422-Image.html", - "year": 2008 - }, - { - "date": "October 28 2019", - "image": "october-28-2019-mars.jpg", - "name": "Mars", - "caption": "This image captures a dust storm on Mars. The dust storm, which is nearly in the middle of the planet in this image, is about 930 miles long measured diagonally.", - "url": "https://hubblesite.org/contents/media/images/2005/34/1803-Image.html", - "year": 2005 - }, - { - "date": "October 29 2019", - "image": "october-29-2019-asteroid-p-2013-r3.jpg", - "name": "Asteroid P/2013 R3", - "caption": "This image shows an asteroid called P/2013 R3 as it was breaking apart. The asteroid’s fragments were slowly drifting away from each other and had tails of dust pushed back by the pressure of sunlight.", - "url": "https://hubblesite.org/contents/media/images/2014/15/3321-Image.html", - "year": 2013 - }, - { - "date": "October 30 2019", - "image": "october-30-2019-star-cluster-ngc-290.jpg", - "name": "Star Cluster NGC 290", - "caption": "This image features a cluster of stars called NGC 290. The star cluster resides in the Small Magellanic Cloud, one of the small galaxies orbiting our Milky Way galaxy.", - "url": "https://hubblesite.org/contents/media/images/2006/17/1899-Image.html", - "year": 2004 - }, - { - "date": "October 31 2019", - "image": "october-31-2019-nebula-ngc-281.jpg", - "name": "Nebula NGC 281", - "caption": "The dark knots of gas and dust in this image are called \"Bok globules,\" and they are absorbing light in the center of the nearby nebula and star-forming region called NGC 281.", - "url": "https://hubblesite.org/contents/media/images/2006/13/1872-Image.html", - "year": 2005 - }, - { - "date": "November 1 2019", - "image": "november-1-2019-globular-cluster-ngc-2808.jpg", - "name": "Globular Cluster NGC 2808", - "caption": "This dense swarm of stars lies at the center of the globular star cluster NGC 2808. Of the about 150 known globular clusters in our Milky Way galaxy, NGC 2808 is one of the most massive, containing more than a million stars.", - "url": "https://hubblesite.org/contents/media/images/2007/18/2124-Image.html", - "year": 2006 - }, - { - "date": "November 2 2019", - "image": "november-2-2019-hubble-ultra-deep-field.jpg", - "name": "Hubble Ultra Deep Field", - "caption": "This image of the Hubble Ultra Deep Field includes infrared observations that allowed Hubble to peer deeper into the universe than it ever had before. The faintest and reddest objects in the image are galaxies that formed 600 million years after the big bang.", - "url": "https://hubblesite.org/contents/media/images/2009/31/2644-Image.html", - "year": 2009 - }, - { - "date": "November 3 2019", - "image": "november-3-2019-einstein-ring-sdss-j0946-1006.jpg", - "name": "Einstein Ring SDSS J0946+1006", - "caption": "Einstein rings like this form when two galaxies are almost perfectly aligned, one behind the other, and the gravitational field of the closer galaxy bends the light from the more distant galaxy into bright arcs around itself.", - "url": "https://hubblesite.org/contents/media/images/2008/04/2245-Image.html", - "year": 2006 - }, - { - "date": "November 4 2019", - "image": "november-4-2019-supernova-remnant-0509-67-5.jpg", - "name": "Supernova Remnant 0509-67.5", - "caption": "This red bubble is made of gas that is being shocked by the expanding blast wave from a supernova explosion. Called SNR 0509-67.5, the bubble is 23 light-years across and is expanding at more than 11 million miles per hour.", - "url": "https://hubblesite.org/contents/media/images/2010/27/2759-Image.html", - "year": 2010 - }, - { - "date": "November 5 2019", - "image": "november-5-2019-horsehead-nebula.jpg", - "name": "Horsehead Nebula", - "caption": "The backlit wisps along the Horsehead Nebula's upper ridge are being illuminated by a young five-star system just off the top of this image, taken in infrared light. Harsh radiation from one of these bright stars is slowly evaporating the nebula.", - "url": "https://hubblesite.org/contents/media/images/2013/12/3165-Image.html", - "year": 2012 - }, - { - "date": "November 6 2019", - "image": "november-6-2019-storm-on-neptune.png", - "name": "Storm on Neptune", - "caption": "This image reveals a dark storm on Neptune, seen at top center. The storm is roughly 6,800 miles across. To the right of the dark feature are bright white \"companion clouds,\" which have also been seen alongside previous storms on Neptune.", - "url": "https://hubblesite.org/contents/media/images/2019/06/4320-Image.html?itemsPerPage=100&page=1&filterUUID=8a87f02e-e18b-4126-8133-2576f4fdc5e2&news=true", - "year": 2018 - }, - { - "date": "November 7 2019", - "image": "november-7-2019-horsehead-nebula.jpg", - "name": "Horsehead Nebula", - "caption": "The backlit wisps along the Horsehead Nebula's upper ridge are being illuminated by a young five-star system just off the top of this image, taken in infrared light. Harsh radiation from one of these bright stars is slowly evaporating the nebula.", - "url": "https://hubblesite.org/contents/media/images/2013/12/3165-Image.html", - "year": 2012 - }, - { - "date": "November 8 2019", - "image": "november-8-2019-star-cluster-ngc-265.jpg", - "name": "Star Cluster NGC 265", - "caption": "This image displays a cluster of stars called NGC 265. The cluster resides in the Small Magellanic Cloud, one of the small galaxies orbiting our Milky Way galaxy.", - "url": "https://hubblesite.org/contents/media/images/2006/17/1899-Image.html", - "year": 2004 - }, - { - "date": "November 9 2019", - "image": "november-9-2019-galaxy-ngc-4150.jpg", - "name": "Galaxy NGC 4150", - "caption": "This image captures the ancient ellipitical galaxy NGC 4150, located about 44 million light-years away. It shows streamers of dust and gas wrapped around the galaxy's core. Closer views of the core reveal clumps of young, blue stars less than a billion years old.", - "url": "https://hubblesite.org/contents/news-releases/2010/news-2010-38.html", - "year": 2009 - }, - { - "date": "November 10 2019", - "image": "november-10-2019-galaxy-ngc-1600.jpg", - "name": "Galaxy NGC 1600", - "caption": "The huge elliptical galaxy NGC 1600 is located 209 million light-years from Earth. The black hole that lurks at the center of the galaxy is one of the most massive black holes ever detected and 10 times more massive than expected for a galaxy of its size.", - "url": "https://hubblesite.org/contents/media/images/2016/12/3723-Image.html", - "year": 1998 - }, - { - "date": "November 11 2019", - "image": "november-11-2019-interacting-galaxies-ngc-2207-and-ic-2163.jpg", - "name": "Interacting Galaxies NGC 2207 and IC 2163", - "caption": "This image shows two interacting galaxies. The larger and more massive galaxy on the left is NGC 2207, and the smaller one on the right is IC 2163. Strong tidal forces from NGC 2207 have distorted the shape of IC 2163.", - "url": "https://hubblesite.org/contents/media/images/1999/41/914-Image.html", - "year": 1998 - }, - { - "date": "November 12 2019", - "image": "november-12-2019-galaxy-pair-ngc-6090.jpg", - "name": "Galaxy Pair NGC 6090", - "caption": "NGC 6090 is a pair of spiral galaxies with overlapping central regions and two long tidal tails made of material ripped out of the galaxies by gravitational interactions. The two visible cores are approximately 10,000 light-years apart.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2310-Image.html", - "year": 2005 - }, - { - "date": "November 13 2019", - "image": "november-13-2019-nebula-n44c.jpg", - "name": "Nebula N44C", - "caption": "These wispy clouds of glowing gas make up a nebula known as N44C. It is part of the larger N44 complex, which includes young, hot, massive stars, other nebulas, and a \"superbubble\" blown out by multiple supernova explosions.", - "url": "https://hubblesite.org/contents/media/images/2002/12/1193-Image.html", - "year": 1996 - }, - { - "date": "November 14 2019", - "image": "november-14-2019-reflection-nebula-n30b.jpg", - "name": "Reflection Nebula N30B", - "caption": "A unique, peanut-shaped cocoon of dust surrounds a cluster of young, hot stars in this image. This reflection nebula, named N30B, is embedded in a much larger nebula called DEM L 106. The wispy filaments of DEM L 106 fill much of the image.", - "url": "https://hubblesite.org/contents/media/images/2002/29/1272-Image.html", - "year": 1998 - }, - { - "date": "November 15 2019", - "image": "november-15-2019-planetary-nebula-ngc-2371.jpg", - "name": "Planetary Nebula NGC 2371", - "caption": "This image captures the planetary nebula NGC 2371, the glowing remains of a Sun-like star. The remnant star visible at the center of NGC 2371 is the super-hot core of the former red giant, now stripped of its outer layers.", - "url": "https://hubblesite.org/contents/media/images/2008/13/2277-Image.html", - "year": 2007 - }, - { - "date": "November 16 2019", - "image": "november-16-2019-galaxy-ngc-1052-df2.png", - "name": "Galaxy NGC 1052-DF2", - "caption": "This galaxy, NGC 1052-DF2, is so diffuse we can see right through it to view more distant galaxies located behind it. The unusual galaxy is also missing most, if not all, of its dark matter.", - "url": "https://hubblesite.org/contents/media/images/2018/16/4139-Image.html", - "year": 2016 - }, - { - "date": "November 17 2019", - "image": "november-17-2019-v838-monocerotis-light-echo.jpg", - "name": "V838 Monocerotis Light Echo", - "caption": "This image captures a light echo from the star V838 Monocerotis. After the star brightened temporarily, light from that eruption began propagating outward through a dusty cloud around the star. The light reflects or \"echoes\" off the dust and then travels to Earth.", - "url": "https://hubblesite.org/contents/media/images/2006/50/2005-Image.html", - "year": 2005 - }, - { - "date": "November 18 2019", - "image": "november-18-2019-galaxy-cluster-cl-0024-17.jpg", - "name": "Galaxy Cluster Cl 0024+17", - "caption": "In this image of the galaxy cluster Cl 0024+17, blue streaks are images of very distant galaxies that are behind the cluster. The distant galaxies appear distorted because their light is bent and magnified by the cluster's gravity.", - "url": "https://hubblesite.org/contents/media/images/2007/17/2122-Image.html", - "year": 2004 - }, - { - "date": "November 19 2019", - "image": "november-19-2019-dumbbell-nebula.jpg", - "name": "Dumbbell Nebula", - "caption": "This image captures a small part of the Dumbbell Nebula, which resides more than 1,200 light-years away. Known as a planetary nebula, it is the result of an old star that has shed its outer layers in a glowing display of color. It was the first planetary nebula ever discovered.", - "url": "https://hubblesite.org/contents/media/images/2003/06/1295-Image.html", - "year": 2001 - }, - { - "date": "November 20 2019", - "image": "november-20-2019-interacting-galaxies-arp-148.jpg", - "name": "Interacting Galaxies Arp 148", - "caption": "Arp 148 is the aftermath of an encounter between two galaxies, resulting in a ring-shaped galaxy and an elongated companion. The shapes and arrangement of the galaxies suggest that this is a snapshot of an ongoing collision.", - "url": "https://hubblesite.org/contents/media/images/2008/16/2309-Image.html", - "year": 2005 - }, - { - "date": "November 21 2019", - "image": "november-21-2019-galaxy-cluster-macs-j1149-6-2223.jpg", - "name": "Galaxy Cluster MACS J1149.6+2223", - "caption": "This massive cluster of galaxies is MACS J1149.6+2223. In this image, light from a distant supernova appears in four different places. The multiple supernova images are created as the exploding star's light is bent by the powerful gravity of a large galaxy in the cluster.", - "url": "https://hubblesite.org/contents/media/images/2015/08/3496-Image.html", - "year": 2014 - }, - { - "date": "November 22 2019", - "image": "november-22-2019-galaxy-ngc-1313.jpg", - "name": "Galaxy NGC 1313", - "caption": "This image captures the central region of the barred spiral galaxy NGC 1313. The galaxy is located roughly 14 million light-years away in the constellation Reticulum.", - "url": "https://hubblesite.org/contents/media/images/2007/05/2044-Image.html", - "year": 2003 - }, - { - "date": "November 23 2019", - "image": "november-23-2019-galaxy-cluster-abell-2744.jpg", - "name": "Galaxy Cluster Abell 2744", - "caption": "Located 3.5 billion light-years away, Abell 2744 contains several hundred galaxies and might be a pile-up of at least four smaller galaxy clusters. Abell 2744’s strong gravitational field acts as a lens, brightening and magnifying the light of nearly 3,000 distant background galaxies.", - "url": "https://hubblesite.org/contents/media/images/3868-Image", - "year": 2013 - }, - { - "date": "November 24 2019", - "image": "november-24-2019-galaxy-cluster-abell-2744.jpg", - "name": "Galaxy Cluster Abell 2744", - "caption": "Located 3.5 billion light-years away, Abell 2744 contains several hundred galaxies and might be a pile-up of at least four smaller galaxy clusters. Abell 2744’s strong gravitational field acts as a lens, brightening and magnifying the light of nearly 3,000 distant background galaxies.", - "url": "https://hubblesite.org/contents/media/images/3868-Image", - "year": 2013 - }, - { - "date": "November 25 2019", - "image": "november-25-2019-star-cluster-westerlund-2.jpg", - "name": "Star Cluster Westerlund 2", - "caption": "This image captures a giant cluster of about 3,000 stars called Westerlund 2. The cluster resides inside a vibrant stellar breeding ground known as Gum 29, located 20,000 light-years away in the constellation Carina.", - "url": "https://hubblesite.org/contents/media/images/2015/12/3519-Image.html", - "year": 2014 - }, - { - "date": "November 26 2019", - "image": "november-26-2019-galaxy-ngc-3079.jpg", - "name": "Galaxy NGC 3079", - "caption": "This image reveals the dramatic activities within the core of the galaxy NGC 3079, where a bubble of hot gas is rising from a cauldron of glowing material. The structure is more than 3,000 light-years wide and rises 3,500 light-years above the galaxy's disk.", - "url": "https://hubblesite.org/contents/media/images/2001/28/1096-Image.html", - "year": 1998 - }, - { - "date": "November 27 2019", - "image": "november-27-2019-planetary-nebula-ngc-2818.jpg", - "name": "Planetary Nebula NGC 2818", - "caption": "The spectacular structure of the planetary nebula NGC 2818 contains the outer layers of a dying star that were expelled into interstellar space. Our own Sun will undergo a similar process, but not for another 5 billion years or so.", - "url": "https://hubblesite.org/contents/media/images/2009/05/2464-Image.html", - "year": 2008 - }, - { - "date": "November 28 2019", - "image": "november-28-2019-supernova-1987a.jpg", - "name": "Supernova 1987A", - "caption": "Many bright spots glow along a ring of gas like pearls on a necklace. These cosmic \"pearls\" are produced as a shock wave from a supernova called SN 1987A slams into the gas ring at more than a million miles per hour. The collision heats the ring, causing it to glow.", - "url": "https://hubblesite.org/contents/media/images/2004/09/1475-Image.html", - "year": 2003 - }, - { - "date": "November 29 2019", - "image": "november-29-2019-mars.jpg", - "name": "Mars", - "caption": "Hubble took this image of Mars as part of a sequence tracking a storm near the planet's northern polar cap. The remnants of the storm are visible as salmon-colored streaks against the cap.", - "url": "https://hubblesite.org/contents/news-releases/1996/news-1996-34.html", - "year": 1996 - }, - { - "date": "November 30 2019", - "image": "november-30-2019-star-cluster-westerlund-2.jpg", - "name": "Star Cluster Westerlund 2", - "caption": "This image captures a giant cluster of about 3,000 stars called Westerlund 2. The cluster resides inside a vibrant stellar breeding ground known as Gum 29, located 20,000 light-years away in the constellation Carina.", - "url": "https://hubblesite.org/contents/media/images/2015/12/3519-Image.html", - "year": 2014 - }, - { - "date": "December 1 2019", - "image": "december-1-2019-mars.jpg", - "name": "Mars", - "caption": "Hubble captured this image of Mars soon before the planet made its closest approach to Earth in 2007. White clouds cover the north polar region. The long, dark feature to the lower left of center is the canyon system Valles Marineris.", - "url": "https://hubblesite.org/contents/media/images/2007/45/2224-Image.html", - "year": 2007 - }, - { - "date": "December 2 2019", - "image": "december-2-2019-whirlpool-galaxy-in-infrared.jpg", - "name": "Whirlpool Galaxy in Infrared", - "caption": "This image reveals the Whirlpool galaxy's skeletal dust structure, as seen in infrared light. The red color in this infrared image traces the galaxy's dust, which is punctuated by hundreds of clumps of stars, each about 65 light-years wide.", - "url": "https://hubblesite.org/contents/media/images/2011/03/2810-Image.html", - "year": 2005 - }, - { - "date": "December 3 2019", - "image": "december-3-2019-nebula-and-star-cluster-ngc-3603.jpg", - "name": "Nebula and Star Cluster NGC 3603", - "caption": "In NGC 3603, a glittering cluster of stars is surrounded by clouds of gas and dust. The cluster contains some of the most massive stars known. These huge stars live fast and die young, ultimately ending their lives in supernova explosions.", - "url": "https://hubblesite.org/contents/media/images/2010/22/2750-Image.html", - "year": 2009 - }, - { - "date": "December 4 2019", - "image": "december-4-2019-andromeda-galaxy-halo.jpg", - "name": "Andromeda Galaxy Halo", - "caption": "This image captures the light from 300,000 stars (and a star cluster) in the Andromeda galaxy's halo, a vast spherical cloud of stars surrounding the galaxy's bright disk. Also embedded in the image are many background galaxies that are much farther away.", - "url": "https://hubblesite.org/contents/media/images/2003/15/1338-Image.html", - "year": 2002 - }, - { - "date": "December 5 2019", - "image": "december-5-2019-galaxy-m81.jpg", - "name": "Galaxy M81", - "caption": "The arms of the \"grand design\" spiral galaxy M81 are filled with young, bluish, hot stars. The greenish regions in the image are bright, gaseous clouds where new stars are forming.", - "url": "https://hubblesite.org/contents/media/images/2007/19/2127-Image.html", - "year": 2005 - }, - { - "date": "December 6 2019", - "image": "december-6-2019-supernova-1987a.jpg", - "name": "Supernova 1987A", - "caption": "This image shows the remnant of Supernova 1987A, a stellar explosion in a nearby galaxy that astronomers witnessed in 1987. A shock wave of material unleashed by the stellar blast is slamming into a surrounding ring of gas, causing it to glow.", - "url": "https://hubblesite.org/contents/media/images/2010/30/2768-Image.html", - "year": 2006 - }, - { - "date": "December 7 2019", - "image": "december-7-2019-southern-ring-nebula.jpg", - "name": "Southern Ring Nebula", - "caption": "This image of the Southern Ring Nebula clearly shows two stars near the center of the nebula: a bright, white one, and a fainter companion to its upper right. The faint star is actually the star that has ejected the material that forms the nebula.", - "url": "https://hubblesite.org/contents/media/images/1998/39/729-Image.html", - "year": 1995 - }, - { - "date": "December 8 2019", - "image": "december-8-2019-supernova-1987a.jpg", - "name": "Supernova 1987A", - "caption": "This image shows the remnant of Supernova 1987A, a stellar explosion in a nearby galaxy that astronomers witnessed in 1987. A shock wave of material unleashed by the stellar blast is slamming into a surrounding ring of gas, causing it to glow.", - "url": "https://hubblesite.org/contents/media/images/2010/30/2768-Image.html", - "year": 2006 - }, - { - "date": "December 9 2019", - "image": "december-9-2019-supernova-1987a.jpg", - "name": "Supernova 1987A", - "caption": "This image shows the remnant of Supernova 1987A, a stellar explosion in a nearby galaxy that astronomers witnessed in 1987. A shock wave of material unleashed by the stellar blast is slamming into a surrounding ring of gas, causing it to glow.", - "url": "https://hubblesite.org/contents/media/images/2010/30/2768-Image.html", - "year": 2006 - }, - { - "date": "December 10 2019", - "image": "december-10-2019-galaxy-m81.jpg", - "name": "Galaxy M81", - "caption": "The arms of the \"grand design\" spiral galaxy M81 are filled with young, bluish, hot stars. The greenish regions in the image are bright, gaseous clouds where new stars are forming.", - "url": "https://hubblesite.org/contents/media/images/2007/19/2127-Image.html", - "year": 2005 - }, - { - "date": "December 11 2019", - "image": "december-11-2019-galaxy-m81.jpg", - "name": "Galaxy M81", - "caption": "The arms of the \"grand design\" spiral galaxy M81 are filled with young, bluish, hot stars. The greenish regions in the image are bright, gaseous clouds where new stars are forming.", - "url": "https://hubblesite.org/contents/media/images/2007/19/2127-Image.html", - "year": 2005 - }, - { - "date": "December 12 2019", - "image": "december-12-2019-galaxy-cluster-sdss-j1004-4112.jpg", - "name": "Galaxy Cluster SDSS J1004+4112", - "caption": "This picture captures a galaxy cluster called SDSS J1004+4112 that's so massive that its gravity bends light from galaxies behind it. The light of a distant quasar (the brilliant core of an active galaxy) has been bent around the cluster, appearing in five places in this image.", - "url": "https://hubblesite.org/contents/media/images/2006/23/1929-Image.html?itemsPerPage=100&page=6&filterUUID=8a87f02e-e18b-4126-8133-2576f4fdc5e2&news=true", - "year": 2005 - }, - { - "date": "December 13 2019", - "image": "december-13-2019-comet-wirtanen.png", - "name": "Comet Wirtanen", - "caption": "In this image, the nucleus of comet 46P/Wirtanen is hidden in the center of a fuzzy glow from the comet's coma. The coma is a cloud of gas and dust that the comet has ejected as it is heated by the Sun during its passage through the inner solar system.", - "url": "https://hubblesite.org/contents/media/images/2018/63/4300-Image.html", - "year": 2018 - }, - { - "date": "December 14 2019", - "image": "december-14-2019-galaxy-cluster-macs-j1149-6-2223.jpg", - "name": "Galaxy Cluster MACS J1149.6+2223", - "caption": "This massive cluster of galaxies is MACS J1149.6+2223. In this image, light from a distant supernova appears in four different places. The multiple supernova images are created as the exploding star's light is bent by the powerful gravity of a large galaxy in the cluster.", - "url": "https://hubblesite.org/contents/media/images/2015/08/3496-Image.html", - "year": 2014 - }, - { - "date": "December 15 2019", - "image": "december-15-2019-galaxy-m81.jpg", - "name": "Galaxy M81", - "caption": "The arms of the \"grand design\" spiral galaxy M81 are filled with young, bluish, hot stars. The greenish regions in the image are bright, gaseous clouds where new stars are forming.", - "url": "https://hubblesite.org/contents/media/images/2007/19/2127-Image.html", - "year": 2005 - }, - { - "date": "December 16 2019", - "image": "december-16-2019-andromeda-galaxy-halo.jpg", - "name": "Andromeda Galaxy Halo", - "caption": "This image captures the light from 300,000 stars (and a star cluster) in the Andromeda galaxy's halo, a vast spherical cloud of stars surrounding the galaxy's bright disk. Also embedded in the image are many background galaxies that are much farther away.", - "url": "https://hubblesite.org/contents/media/images/2003/15/1338-Image.html", - "year": 2002 - }, - { - "date": "December 17 2019", - "image": "december-17-2019-interacting-galaxies-arp-273.jpg", - "name": "Interacting Galaxies Arp 273", - "caption": "Arp 273 is of a pair of interacting galaxies that form a shape resembling a rose. The larger of the spiral galaxies, known as UGC 1810, has a disk that is distorted by the gravitational pull of the galaxy below it, known as UGC 1813.", - "url": "https://hubblesite.org/contents/media/images/2011/11/2836-Image.html", - "year": 2010 - }, - { - "date": "December 18 2019", - "image": "december-18-2019-hubble-deep-field.jpg", - "name": "Hubble Deep Field", - "caption": "Called the Hubble Deep Field, this image captures several hundred galaxies that had never been seen before. Some galaxies are near and some are very far. Their various shapes and colors provide clues about the evolution of the universe.", - "url": "https://hubblesite.org/contents/media/images/1996/01/385-Image.html", - "year": 1995 - }, - { - "date": "December 19 2019", - "image": "december-19-2019-hubble-deep-field.jpg", - "name": "Hubble Deep Field", - "caption": "Called the Hubble Deep Field, this image captures several hundred galaxies that had never been seen before. Some galaxies are near and some are very far. Their various shapes and colors provide clues about the evolution of the universe.", - "url": "https://hubblesite.org/contents/media/images/1996/01/385-Image.html", - "year": 1995 - }, - { - "date": "December 20 2019", - "image": "december-20-2019-globular-cluster-m15.jpg", - "name": "Globular Cluster M15", - "caption": "These stars belong to the globular cluster M15. Nestled among them is an astronomical oddity. The pinkish object to the upper left of the cluster's core is a gas cloud surrounding a dying star. Known as Kuestner 648, this was the first planetary nebula found in a globular cluster.", - "url": "https://hubblesite.org/contents/media/images/2000/25/981-Image.html", - "year": 1998 - }, - { - "date": "December 21 2019", - "image": "december-21-2019-hubble-deep-field.jpg", - "name": "Hubble Deep Field", - "caption": "Called the Hubble Deep Field, this image captures several hundred galaxies that had never been seen before. Some galaxies are near and some are very far. Their various shapes and colors provide clues about the evolution of the universe.", - "url": "https://hubblesite.org/contents/media/images/1996/01/385-Image.html", - "year": 1995 - }, - { - "date": "December 22 2019", - "image": "december-22-2019-galaxy-ngc-4214.jpg", - "name": "Galaxy NGC 4214", - "caption": "The dwarf galaxy NGC 4214 is ablaze with young stars and gas clouds. This image captures intricate patterns of glowing hydrogen shaped during the star-birthing process, cavities blown clear of gas by stellar winds, and bright stellar clusters.", - "url": "https://hubblesite.org/contents/media/images/2011/14/2844-Image.html", - "year": 2009 - }, - { - "date": "December 23 2019", - "image": "december-23-2019-galaxy-ngc-4214.jpg", - "name": "Galaxy NGC 4214", - "caption": "The dwarf galaxy NGC 4214 is ablaze with young stars and gas clouds. This image captures intricate patterns of glowing hydrogen shaped during the star-birthing process, cavities blown clear of gas by stellar winds, and bright stellar clusters.", - "url": "https://hubblesite.org/contents/media/images/2011/14/2844-Image.html", - "year": 2009 - }, - { - "date": "December 24 2019", - "image": "december-24-2019-galaxy-ngc-4214.jpg", - "name": "Galaxy NGC 4214", - "caption": "The dwarf galaxy NGC 4214 is ablaze with young stars and gas clouds. This image captures intricate patterns of glowing hydrogen shaped during the star-birthing process, cavities blown clear of gas by stellar winds, and bright stellar clusters.", - "url": "https://hubblesite.org/contents/media/images/2011/14/2844-Image.html", - "year": 2009 - }, - { - "date": "December 25 2019", - "image": "december-25-2019-galaxy-ngc-4214.jpg", - "name": "Galaxy NGC 4214", - "caption": "The dwarf galaxy NGC 4214 is ablaze with young stars and gas clouds. This image captures intricate patterns of glowing hydrogen shaped during the star-birthing process, cavities blown clear of gas by stellar winds, and bright stellar clusters.", - "url": "https://hubblesite.org/contents/media/images/2011/14/2844-Image.html", - "year": 2009 - }, - { - "date": "December 26 2019", - "image": "december-26-2019-hubble-deep-field.jpg", - "name": "Hubble Deep Field", - "caption": "Called the Hubble Deep Field, this image captures several hundred galaxies that had never been seen before. Some galaxies are near and some are very far. Their various shapes and colors provide clues about the evolution of the universe.", - "url": "https://hubblesite.org/contents/media/images/1996/01/385-Image.html", - "year": 1995 - }, - { - "date": "December 27 2019", - "image": "december-27-2019-galaxy-ngc-2976.jpg", - "name": "Galaxy NGC 2976", - "caption": "This picture shows the inner region of NGC 2976, located roughly 11 million light-years away in the constellation Ursa Major. Despite the lack of well-defined arms visible in this image, NGC 2976 is a spiral galaxy.", - "url": "https://hubblesite.org/contents/media/images/2010/05/2682-Image.html", - "year": 2006 - }, - { - "date": "December 28 2019", - "image": "december-28-2019-galaxy-i-zwicky-18.jpg", - "name": "Galaxy I Zwicky 18", - "caption": "This image captures the irregular dwarf galaxy I Zwicky 18 and a companion galaxy to its upper right. The two galaxies are interacting, triggering star formation in I Zwicky 18.", - "url": "https://hubblesite.org/contents/media/images/2004/35/1621-Image.html", - "year": 1997 - }, - { - "date": "December 29 2019", - "image": "december-29-2019-nebula-and-star-cluster-ngc-3603.jpg", - "name": "Nebula and Star Cluster NGC 3603", - "caption": "In NGC 3603, thousands of sparkling, young stars are nestled within a giant nebula. This stellar \"jewel box\" is one of the most massive young star clusters in our Milky Way galaxy.", - "url": "https://hubblesite.org/contents/media/images/2007/34/2189-Image.html", - "year": 2005 - }, - { - "date": "December 30 2019", - "image": "december-30-2019-stephan-s-quintet.jpg", - "name": "Stephan's Quintet", - "caption": "This close-up shows four of the five galaxies that make up Stephan’s Quintet. The image reveals bright, blue clusters of stars, born from the violent interactions between some of the member galaxies.", - "url": "https://hubblesite.org/contents/media/images/2001/22/1082-Image.html", - "year": 1998 - }, - { - "date": "December 31 2019", - "image": "december-31-2019-galaxy-m81.jpg", - "name": "Galaxy M81", - "caption": "The arms of the \"grand design\" spiral galaxy M81 are filled with young, bluish, hot stars. The greenish regions in the image are bright, gaseous clouds where new stars are forming.", - "url": "https://hubblesite.org/contents/media/images/2007/19/2127-Image.html", - "year": 2006 - } -] \ No newline at end of file diff --git a/scripts/cmds/assets/image/bgWeather.jpg b/scripts/cmds/assets/image/bgWeather.jpg deleted file mode 100644 index a0d9e443..00000000 Binary files a/scripts/cmds/assets/image/bgWeather.jpg and /dev/null differ diff --git a/scripts/cmds/autolink.js b/scripts/cmds/autolink.js deleted file mode 100644 index 355d6130..00000000 --- a/scripts/cmds/autolink.js +++ /dev/null @@ -1,73 +0,0 @@ -const fs = require("fs"); -const { downloadVideo } = require("sagor-video-downloader"); - -module.exports = { - config: { - name: "autolink", - version: "1.3", - author: "MOHAMMAD AKASH", - countDown: 5, - role: 0, - shortDescription: "Auto-download & send videos silently (no messages)", - category: "media", - }, - - onStart: async function () {}, - - onChat: async function ({ api, event }) { - const threadID = event.threadID; - const messageID = event.messageID; - const message = event.body || ""; - - const linkMatches = message.match(/(https?:\/\/[^\s]+)/g); - if (!linkMatches || linkMatches.length === 0) return; - - const uniqueLinks = [...new Set(linkMatches)]; - - api.setMessageReaction("⏳", messageID, () => {}, true); - - let successCount = 0; - let failCount = 0; - - for (const url of uniqueLinks) { - try { - const { title, filePath } = await downloadVideo(url); - if (!filePath || !fs.existsSync(filePath)) throw new Error(); - - const stats = fs.statSync(filePath); - const fileSizeInMB = stats.size / (1024 * 1024); - - if (fileSizeInMB > 25) { - fs.unlinkSync(filePath); - failCount++; - continue; - } - - await api.sendMessage( - { - body: -`📥 ᴠɪᴅᴇᴏ ᴅᴏᴡɴʟᴏᴀᴅᴇᴅ -━━━━━━━━━━━━━━━ -🎬 ᴛɪᴛʟᴇ: ${title || "Video File"} -📦 sɪᴢᴇ: ${fileSizeInMB.toFixed(2)} MB -━━━━━━━━━━━━━━━`, - attachment: fs.createReadStream(filePath) - }, - threadID, - () => fs.unlinkSync(filePath) - ); - - successCount++; - - } catch { - failCount++; - } - } - - const finalReaction = - successCount > 0 && failCount === 0 ? "✅" : - successCount > 0 ? "⚠️" : "❌"; - - api.setMessageReaction(finalReaction, messageID, () => {}, true); - } -}; diff --git a/scripts/cmds/autoreact.js b/scripts/cmds/autoreact.js deleted file mode 100644 index 91ecc89c..00000000 --- a/scripts/cmds/autoreact.js +++ /dev/null @@ -1,93 +0,0 @@ -module.exports = { - config: { - name: "autoreact", - version: "4.4.0", - author: "MOHAMMAD AKASH", - role: 0, - category: "system", - shortDescription: "Auto react (emoji + text)", - longDescription: "Stable auto reaction without silent API fail" - }, - - onStart: async function () {}, - - onChat: async function ({ api, event }) { - try { - const { messageID, body, senderID, threadID } = event; - if (!messageID || !body) return; - - // ❌ নিজের / বটের মেসেজে রিয়েক্ট না - if (senderID === api.getCurrentUserID()) return; - - // ❌ হালকা cooldown (2.5s) - global.__autoReactCooldown ??= {}; - if ( - global.__autoReactCooldown[threadID] && - Date.now() - global.__autoReactCooldown[threadID] < 2500 - ) return; - - global.__autoReactCooldown[threadID] = Date.now(); - - const text = body.toLowerCase(); - let react = null; - - // ========================== - // Emoji Categories - // ========================== - const categories = [ - { e: ["😂","🤣","😆","😄","😁"], r: "😆" }, - { e: ["😭","😢","🥺","💔"], r: "😢" }, - { e: ["❤️","💖","💘","🥰","😍"], r: "❤️" }, - { e: ["😡","🤬"], r: "😡" }, - { e: ["😮","😱","😲"], r: "😮" }, - { e: ["😎","🔥","💯"], r: "😎" }, - { e: ["👍","👌","🙏"], r: "👍" }, - { e: ["🎉","🥳"], r: "🎉" } - ]; - - // ========================== - // Text Triggers - // ========================== - const texts = [ - { k: ["haha","lol","moja","xd"], r: "😆" }, - { k: ["sad","kharap","mon kharap","cry"], r: "😢" }, - { k: ["love","valobasi","miss"], r: "❤️" }, - { k: ["rag","angry","rage"], r: "😡" }, - { k: ["wow","omg"], r: "😮" }, - { k: ["ok","yes","okay","hmm"], r: "👍" } - ]; - - // ========================== - // Emoji check first - // ========================== - for (const c of categories) { - if (c.e.some(x => text.includes(x))) { - react = c.r; - break; - } - } - - // ========================== - // Text check - // ========================== - if (!react) { - for (const t of texts) { - if (t.k.some(x => text.includes(x))) { - react = t.r; - break; - } - } - } - - // ❌ কিছু না মিললে রিয়েক্ট না - if (!react) return; - - // ⏱ Human-like delay - await new Promise(r => setTimeout(r, 800)); - - // ✅ FINAL FIX — NO callback, NO true - api.setMessageReaction(react, messageID); - - } catch (e) {} - } -}; diff --git a/scripts/cmds/autoseen.js b/scripts/cmds/autoseen.js deleted file mode 100644 index 50aaf134..00000000 --- a/scripts/cmds/autoseen.js +++ /dev/null @@ -1,54 +0,0 @@ -const fs = require("fs-extra"); -const path = __dirname + "/cache/autoseen.json"; - -// যদি ফাইল না থাকে, বানানো হবে -if (!fs.existsSync(path)) { - fs.writeFileSync(path, JSON.stringify({ status: true }, null, 2)); -} - -module.exports = { - config: { - name: "autoseen", - version: "2.0", - author: "Mohammad Akash", - countDown: 0, - role: 0, - shortDescription: "স্বয়ংক্রিয়ভাবে seen সিস্টেম", - longDescription: "বট স্বয়ংক্রিয়ভাবে সকল নতুন মেসেজ seen করবে।", - category: "system", - guide: { - en: "{pn} on/off", - }, - }, - - onStart: async function ({ message, args }) { - const data = JSON.parse(fs.readFileSync(path)); - if (!args[0]) { - return message.reply(`📄 Autoseen বর্তমান অবস্থা: ${data.status ? "✅ চালু" : "❌ বন্ধ"}`); - } - - if (args[0].toLowerCase() === "on") { - data.status = true; - fs.writeFileSync(path, JSON.stringify(data, null, 2)); - return message.reply("✅ Autoseen এখন থেকে চালু!"); - } else if (args[0].toLowerCase() === "off") { - data.status = false; - fs.writeFileSync(path, JSON.stringify(data, null, 2)); - return message.reply("❌ Autoseen এখন বন্ধ!"); - } else { - return message.reply("⚠️ ব্যবহার করুন: autoseen on / off"); - } - }, - - // মেসেজ দেখলেই seen করবে (যদি চালু থাকে) - onChat: async function ({ event, api }) { - try { - const data = JSON.parse(fs.readFileSync(path)); - if (data.status === true) { - api.markAsReadAll(); - } - } catch (e) { - console.error(e); - } - }, -}; diff --git a/scripts/cmds/autosetname.js b/scripts/cmds/autosetname.js deleted file mode 100644 index 16ee6a57..00000000 --- a/scripts/cmds/autosetname.js +++ /dev/null @@ -1,107 +0,0 @@ -function checkShortCut(nickname, uid, userName) { - /\{userName\}/gi.test(nickname) ? nickname = nickname.replace(/\{userName\}/gi, userName) : null; - /\{userID\}/gi.test(uid) ? nickname = nickname.replace(/\{userID\}/gi, uid) : null; - return nickname; -} - -module.exports = { - config: { - name: "autosetname", - version: "1.3", - author: "NTKhang", - cooldowns: 5, - role: 1, - description: { - vi: "Tự đổi biệt danh cho thành viên mới vào nhóm chat", - en: "Auto change nickname of new member" - }, - category: "box chat", - guide: { - vi: ' {pn} set : dùng để cài đặt cấu hình để tự đổi biệt danh, với các shortcut có sẵn:' - + '\n + {userName}: tên thành viên vào nhóm' - + '\n + {userID}: id thành viên' - + '\n Ví dụ:' - + '\n {pn} set {userName} 🚀' - + '\n\n {pn} [on | off]: dùng để bật/tắt tính năng này' - + '\n\n {pn} [view | info]: hiển thị cấu hình hiện tại', - en: ' {pn} set : use to set config to auto change nickname, with some shortcuts:' - + '\n + {userName}: name of new member' - + '\n + {userID}: member id' - + '\n Example:' - + '\n {pn} set {userName} 🚀' - + '\n\n {pn} [on | off]: use to turn on/off this feature' - + '\n\n {pn} [view | info]: show current config' - } - }, - - langs: { - vi: { - missingConfig: "Vui lòng nhập cấu hình cần thiết", - configSuccess: "Cấu hình đã được cài đặt thành công", - currentConfig: "Cấu hình autoSetName hiện tại trong nhóm chat của bạn là:\n%1", - notSetConfig: "Hiện tại nhóm bạn chưa cài đặt cấu hình autoSetName", - syntaxError: "Sai cú pháp, chỉ có thể dùng \"{pn} on\" hoặc \"{pn} off\"", - turnOnSuccess: "Tính năng autoSetName đã được bật", - turnOffSuccess: "Tính năng autoSetName đã được tắt", - error: "Đã có lỗi xảy ra khi sử dụng chức năng autoSetName, thử tắt tính năng liên kết mời trong nhóm và thử lại sau" - }, - en: { - missingConfig: "Please enter the required configuration", - configSuccess: "The configuration has been set successfully", - currentConfig: "The current autoSetName configuration in your chat group is:\n%1", - notSetConfig: "Your group has not set the autoSetName configuration", - syntaxError: "Syntax error, only \"{pn} on\" or \"{pn} off\" can be used", - turnOnSuccess: "The autoSetName feature has been turned on", - turnOffSuccess: "The autoSetName feature has been turned off", - error: "An error occurred while using the autoSetName feature, try turning off the invite link feature in the group and try again later" - } - }, - - onStart: async function ({ message, event, args, threadsData, getLang }) { - switch (args[0]) { - case "set": - case "add": - case "config": { - if (args.length < 2) - return message.reply(getLang("missingConfig")); - const configAutoSetName = args.slice(1).join(" "); - await threadsData.set(event.threadID, configAutoSetName, "data.autoSetName"); - return message.reply(getLang("configSuccess")); - } - case "view": - case "info": { - const configAutoSetName = await threadsData.get(event.threadID, "data.autoSetName"); - return message.reply(configAutoSetName ? getLang("currentConfig", configAutoSetName) : getLang("notSetConfig")); - } - default: { - const enableOrDisable = args[0]; - if (enableOrDisable !== "on" && enableOrDisable !== "off") - return message.reply(getLang("syntaxError")); - await threadsData.set(event.threadID, enableOrDisable === "on", "settings.enableAutoSetName"); - return message.reply(enableOrDisable == "on" ? getLang("turnOnSuccess") : getLang("turnOffSuccess")); - } - } - }, - - onEvent: async ({ message, event, api, threadsData, getLang }) => { - if (event.logMessageType !== "log:subscribe") - return; - if (!await threadsData.get(event.threadID, "settings.enableAutoSetName")) - return; - const configAutoSetName = await threadsData.get(event.threadID, "data.autoSetName"); - - return async function () { - const addedParticipants = [...event.logMessageData.addedParticipants]; - - for (const user of addedParticipants) { - const { userFbId: uid, fullName: userName } = user; - try { - await api.changeNickname(checkShortCut(configAutoSetName, uid, userName), event.threadID, uid); - } - catch (e) { - return message.reply(getLang("error")); - } - } - }; - } -}; \ No newline at end of file diff --git a/scripts/cmds/baby.js b/scripts/cmds/baby.js deleted file mode 100644 index 6c8f3711..00000000 --- a/scripts/cmds/baby.js +++ /dev/null @@ -1,219 +0,0 @@ -const axios = require("axios"); - -const simsim = "https://simsimi-api-tjb1.onrender.com"; - -const typing = async (api, threadID, ms = 3000) => { - try { - if (typeof api.sendTypingIndicator === "function") { - await api.sendTypingIndicator(threadID, true); - await new Promise(resolve => setTimeout(resolve, ms)); - await api.sendTypingIndicator(threadID, false); - } - } catch {} -}; - -module.exports = { - config: { - name: "baby", - aliases: ["mari", "maria", "hippi", "xan", "bby", "bbz"], - version: "3.6", - author: "rX (fixed by GPT)", - countDown: 0, - role: 0, - shortDescription: "Full Mirai-style Baby AI", - longDescription: "Teachable AI + autoteach + list/msg/edit/remove + typing", - category: "box chat", - guide: { - en: "{p}baby [message]\n{p}baby teach [q] - [a]\n{p}baby autoteach on/off\n{p}baby list\n{p}baby msg [trigger]\n{p}baby edit [q] - [old] - [new]\n{p}baby remove/rm [q] - [a]" - } - }, - - onStart: async function ({ api, event, args, message, usersData }) { - const senderID = event.senderID; - const senderName = await usersData.getName(senderID); - const threadID = event.threadID; - const query = args.join(" ").trim().toLowerCase(); - - try { - // no text => random reply - if (!query) { - await typing(api, threadID, 2000); - const ran = ["Bolo baby 💖", "Hea baby 😚", "Yes I'm here 😘", "Ki khobor janu? 🥰"]; - return message.reply(ran[Math.floor(Math.random() * ran.length)], (err, info) => { - if (!err) global.GoatBot.onReply.set(info.messageID, { commandName: "baby" }); - }); - } - - // AUTOTEACH TOGGLE - if (args[0] === "autoteach") { - const mode = args[1]?.toLowerCase(); - if (!["on","off"].includes(mode)) return message.reply("Use: baby autoteach on/off"); - - const status = mode === "on"; - await axios.post(`${simsim}/setting`, { autoTeach: status }, { timeout: 10000 }); - return message.reply(`✅ Auto teach now ${status ? "ON 🟢" : "OFF 🔴"}`); - } - - // LIST - if (args[0] === "list") { - const res = await axios.get(`${simsim}/list`, { timeout: 10000 }); - return message.reply( -`╭─╼🌟 𝐁𝐚𝐛𝐲 𝐀𝐈 𝐒𝐭𝐚𝐭𝐮𝐬 -├ 📝 𝐓𝐞𝐚𝐜𝐡𝐞𝐝 𝐐𝐮𝐞𝐬𝐭𝐢𝐨𝐧𝐬: ${res.data.totalQuestions || 0} -├ 📦 𝐒𝐭𝐨𝐫𝐞𝐝 𝐑𝐞𝐩𝐥𝐢𝐞𝐬: ${res.data.totalReplies || 0} -╰─╼👤 𝐃𝐞𝐯: rX 𝐀𝐛𝐝𝐮𝐥𝐥𝐚𝐡` - ); - } - - // MSG - if (args[0] === "msg") { - const trigger = args.slice(1).join(" ").trim(); - if (!trigger) return message.reply("Use: baby msg [trigger]"); - - const res = await axios.get(`${simsim}/simsimi-list?ask=${encodeURIComponent(trigger)}`, { timeout: 10000 }); - if (!res.data.replies?.length) return message.reply("❌ No replies found for this trigger."); - - const formatted = res.data.replies.map((rep, i) => `➤ ${i+1}. ${rep}`).join("\n"); - return message.reply( -`📌 𝗧𝗿𝗶𝗴𝗴𝗲𝗿: ${trigger.toUpperCase()} -📋 𝗧𝗼𝘁𝗮𝗹 𝗥𝗲𝗽𝗹𝗶𝗲𝘀: ${res.data.total || res.data.replies.length} -━━━━━━━━━━━━━━ -${formatted}` - ); - } - - // TEACH - if (args[0] === "teach") { - const parts = query.replace(/^teach\s+/i, "").split(" - "); - if (parts.length < 2) return message.reply("Use: baby teach question - answer"); - - const [ask, ans] = parts.map(s => s.trim()); - const res = await axios.get(`${simsim}/teach?ask=${encodeURIComponent(ask)}&ans=${encodeURIComponent(ans)}&senderName=${encodeURIComponent(senderName)}&senderID=${senderID}`, { timeout: 10000 }); - return message.reply(res.data.message || "✅ Taught successfully!"); - } - - // EDIT - if (args[0] === "edit") { - const parts = query.replace(/^edit\s+/i, "").split(" - "); - if (parts.length < 3) return message.reply("Use: baby edit question - old reply - new reply"); - - const [ask, oldR, newR] = parts.map(s => s.trim()); - const res = await axios.get(`${simsim}/edit?ask=${encodeURIComponent(ask)}&old=${encodeURIComponent(oldR)}&new=${encodeURIComponent(newR)}`, { timeout: 10000 }); - return message.reply(res.data.message || "✅ Edited successfully!"); - } - - // REMOVE / RM - if (["remove","rm"].includes(args[0])) { - const parts = query.replace(/^(remove|rm)\s+/i, "").split(" - "); - if (parts.length < 2) return message.reply("Use: baby remove question - answer"); - - const [ask, ans] = parts.map(s => s.trim()); - const res = await axios.get(`${simsim}/delete?ask=${encodeURIComponent(ask)}&ans=${encodeURIComponent(ans)}`, { timeout: 10000 }); - return message.reply(res.data.message || "✅ Removed successfully!"); - } - - // Normal chat - await typing(api, threadID, 2000); - const res = await axios.get(`${simsim}/simsimi?text=${encodeURIComponent(query)}&senderName=${encodeURIComponent(senderName)}`, { timeout: 15000 }); - - let responses = Array.isArray(res.data.response) ? res.data.response : [res.data.response || "Hmm baby 😚"]; - for (const r of responses) { - await new Promise(resolve => { - message.reply(r, (err, info) => { - if (!err) global.GoatBot.onReply.set(info.messageID, { commandName: "baby" }); - resolve(); - }); - }); - } - - } catch (err) { - console.error("Baby command error:", err.message); - message.reply("❌ Error: " + (err.message.includes("404") ? "Feature not available (backend issue)" : err.message)); - } - }, - - onReply: async function ({ api, event, message, usersData }) { - const text = event.body?.trim(); - if (!text) return; - const senderName = await usersData.getName(event.senderID); - - try { - await typing(api, event.threadID, 2000); - const res = await axios.get(`${simsim}/simsimi?text=${encodeURIComponent(text)}&senderName=${encodeURIComponent(senderName)}`, { timeout: 15000 }); - - const replies = Array.isArray(res.data.response) ? res.data.response : [res.data.response]; - for (const r of replies) { - await message.reply(r, (err, info) => { - if (!err) global.GoatBot.onReply.set(info.messageID, { commandName: "baby" }); - }); - } - } catch (err) { - console.error("onReply error:", err.message); - } - }, - - onChat: async function ({ api, event, message, usersData }) { - const raw = event.body ? event.body.toLowerCase().trim() : ""; - if (!raw) return; - - const senderID = event.senderID; - const senderName = await usersData.getName(senderID); - const threadID = event.threadID; - - try { - // triggers only - const triggers = ["baby","bby","xan","bbz","mari","মারিয়া","bot"]; - if (triggers.includes(raw)) { - await typing(api, threadID, 5000); - const funny = [ - "𝘬𝘪 𝘏𝘰𝘪𝘴𝘦 𝘑𝘢𝘯 𝘣𝘰𝘭𝘰 😿", "𝘌𝘵𝘰 𝘋𝘢𝘬𝘰 𝘒𝘦𝘯 𝘚𝘶𝘯𝘴𝘪 𝘛𝘰 🙆‍♀️", "𝘌𝘵𝘰 𝘉𝘰𝘵 𝘉𝘰𝘵 𝘒𝘰𝘳𝘭𝘦 𝘓𝘦𝘢𝘷𝘦 𝘕𝘪𝘮𝘶 🙂", - "𝘛𝘶𝘮𝘪 𝘋𝘢𝘬𝘭𝘦𝘪 𝘊𝘰𝘭𝘦 𝘈𝘴𝘪 🙆‍♀️", "ওই জান এতোবার ডাকো কেন 🥹", "আমাকে না ডেকে আকাশ ভাই কে প্রোপোজ কর 🌷🫶", - "হুম বলো পাখি 🫶🐤 ", "তুমারে রাইতে ভালোবাসি 😘", "আমাকে ডাকছো? 🙂" - ]; - return message.reply(funny[Math.floor(Math.random() * funny.length)], (err, info) => { - if (!err) global.GoatBot.onReply.set(info.messageID, { commandName: "baby" }); - }); - } - - // prefixes - const prefixes = ["baby ","bby ","xan ","bbz ","mari ","মারিয়া ","bot "]; - const prefix = prefixes.find(p => raw.startsWith(p)); - if (prefix) { - const q = raw.replace(prefix,"").trim(); - if (!q) return; - - await typing(api, threadID, 2000); - const res = await axios.get(`${simsim}/simsimi?text=${encodeURIComponent(q)}&senderName=${encodeURIComponent(senderName)}`, { timeout: 15000 }); - - const replies = Array.isArray(res.data.response) ? res.data.response : [res.data.response]; - for (const r of replies) { - await message.reply(r, (err, info) => { - if (!err) global.GoatBot.onReply.set(info.messageID, { commandName: "baby" }); - }); - } - return; - } - - // AUTO-TEACH from reply - if (event.messageReply) { - try { - const setting = await axios.get(`${simsim}/setting`, { timeout: 8000 }); - if (setting.data?.autoTeach) { - const ask = event.messageReply.body?.toLowerCase().trim(); - const ans = raw.trim(); - if (ask && ans && ask !== ans) { - setTimeout(async () => { - try { - await axios.get(`${simsim}/teach?ask=${encodeURIComponent(ask)}&ans=${encodeURIComponent(ans)}&senderName=${encodeURIComponent(senderName)}`, { timeout: 10000 }); - } catch {} - }, 500); - } - } - } catch {} - } - - } catch (err) { - console.error("onChat error:", err.message); - } - } -}; diff --git a/scripts/cmds/backupdata.js b/scripts/cmds/backupdata.js deleted file mode 100644 index d5ff3906..00000000 --- a/scripts/cmds/backupdata.js +++ /dev/null @@ -1,57 +0,0 @@ -const fs = require("fs-extra"); - -module.exports = { - config: { - name: "backupdata", - version: "1.3", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Sao lưu dữ liệu của bot (threads, users, dashboard, globalData)", - en: "Backup data of bot (threads, users, dashboard, globalData)" - }, - category: "owner", - guide: { - en: " {pn}" - } - }, - - langs: { - vi: { - backedUp: "Đã sao lưu dữ liệu của bot vào thư mục scripts/cmds/tmp" - }, - en: { - backedUp: "Bot data has been backed up to the scripts/cmds/tmp folder" - } - }, - - onStart: async function ({ message, getLang, threadsData, usersData, dashBoardData, globalData }) { - const [globalDataBackup, threadsDataBackup, usersDataBackup, dashBoardDataBackup] = await Promise.all([ - globalData.getAll(), - threadsData.getAll(), - usersData.getAll(), - dashBoardData.getAll() - ]); - - const pathThreads = `${__dirname}/tmp/threadsData.json`; - const pathUsers = `${__dirname}/tmp/usersData.json`; - const pathDashBoard = `${__dirname}/tmp/dashBoardData.json`; - const pathGlobal = `${__dirname}/tmp/globalData.json`; - - fs.writeFileSync(pathThreads, JSON.stringify(threadsDataBackup, null, 2)); - fs.writeFileSync(pathUsers, JSON.stringify(usersDataBackup, null, 2)); - fs.writeFileSync(pathDashBoard, JSON.stringify(dashBoardDataBackup, null, 2)); - fs.writeFileSync(pathGlobal, JSON.stringify(globalDataBackup, null, 2)); - - message.reply({ - body: getLang("backedUp"), - attachment: [ - fs.createReadStream(pathThreads), - fs.createReadStream(pathUsers), - fs.createReadStream(pathDashBoard), - fs.createReadStream(pathGlobal) - ] - }); - } -}; \ No newline at end of file diff --git a/scripts/cmds/badwords.js b/scripts/cmds/badwords.js deleted file mode 100644 index 88555e1c..00000000 --- a/scripts/cmds/badwords.js +++ /dev/null @@ -1,246 +0,0 @@ -module.exports = { - config: { - name: "badwords", - aliases: ["badword"], - version: "1.4", - author: "NTKhang", - countDown: 5, - role: 1, - description: { - vi: "Bật/tắt/thêm/xóa cảnh báo vi phạm từ thô tục, nếu thành viên vi phạm sẽ bị cảnh báo, lần 2 sẽ kick khỏi box chat", - en: "Turn on/off/add/remove bad words warning, if a member violates, he will be warned, the second time he will be kicked out of the chat box" - }, - category: "box chat", - guide: { - vi: " {pn} add : thêm từ cấm (có thể thêm nhiều từ cách nhau bằng dấu phẩy \",\" hoặc dấu gạch đứng \"|\"" - + "\n {pn} delete : xóa từ cấm (có thể xóa nhiều từ cách nhau bằng dấu phẩy \",\" hoặc dấu gạch đứng \"|\"" - + "\n {pn} list : tắt cảnh báo (thêm \"hide\" để ẩn từ cấm)" - + "\n {pn} unwarn [ | <@tag>]: xóa 1 lần cảnh báo của 1 thành viên" - + "\n {pn} on: tắt cảnh báo" - + "\n {pn} off: bật cảnh báo", - en: " {pn} add : add banned words (you can add multiple words separated by commas \",\" or vertical bars \"|\")" - + "\n {pn} delete : delete banned words (you can delete multiple words separated by commas \",\" or vertical bars \"|\")" - + "\n {pn} list : turn off warning (add \"hide\" to hide banned words)" - + "\n {pn} unwarn [ | <@tag>]: remove 1 warning of 1 member" - + "\n {pn} on: turn off warning" - + "\n {pn} off: turn on warning" - } - }, - - langs: { - vi: { - onText: "bật", - offText: "tắt", - onlyAdmin: "⚠️ | Chỉ quản trị viên mới có thể thêm từ cấm vào danh sách", - missingWords: "⚠️ | Bạn chưa nhập từ cần cấm", - addedSuccess: "✅ | Đã thêm %1 từ cấm vào danh sách", - alreadyExist: "❌ | %1 từ cấm đã tồn tại trong danh sách từ trước: %2", - tooShort: "⚠️ | %1 từ cấm không thể thêm vào danh sách do có độ dài nhỏ hơn 2 ký tự: %2", - onlyAdmin2: "⚠️ | Chỉ quản trị viên mới có thể xóa từ cấm khỏi danh sách", - missingWords2: "⚠️ | Bạn chưa nhập từ cần xóa", - deletedSuccess: "✅ | Đã xóa %1 từ cấm khỏi danh sách", - notExist: "❌ | %1 từ cấm không tồn tại trong danh sách từ trước: %2", - emptyList: "⚠️ | Danh sách từ cấm trong nhóm bạn hiện đang trống", - badWordsList: "📑 | Danh sách từ cấm trong nhóm bạn: %1", - onlyAdmin3: "⚠️ | Chỉ quản trị viên mới có thể %1 tính năng này", - turnedOnOrOff: "✅ | Cảnh báo vi phạm từ cấm đã %1", - onlyAdmin4: "⚠️ | Chỉ quản trị viên mới có thể xóa cảnh báo vi phạm từ cấm", - missingTarget: "⚠️ | Bạn chưa nhập ID người dùng hoặc tag người dùng", - notWarned: "⚠️ | Người dùng %1 chưa bị cảnh báo vi phạm từ cấm", - removedWarn: "✅ | Người dùng %1 | %2 đã được xóa bỏ 1 lần cảnh báo vi phạm từ cấm", - warned: "⚠️ | Từ cấm \"%1\" đã được phát hiện trong tin nhắn của bạn, nếu tiếp tục vi phạm bạn sẽ bị kick khỏi nhóm.", - warned2: "⚠️ | Từ cấm \"%1\" đã được phát hiện trong tin nhắn của bạn, bạn đã vi phạm 2 lần và sẽ bị kick khỏi nhóm.", - needAdmin: "Bot cần quyền quản trị viên để kick thành viên bị ban", - unwarned: "✅ | Đã xóa bỏ cảnh báo vi phạm từ cấm của người dùng %1 | %2" - }, - en: { - onText: "on", - offText: "off", - onlyAdmin: "⚠️ | Only admins can add banned words to the list", - missingWords: "⚠️ | You haven't entered the banned words", - addedSuccess: "✅ | Added %1 banned words to the list", - alreadyExist: "❌ | %1 banned words already exist in the list before: %2", - tooShort: "⚠️ | %1 banned words cannot be added to the list because they are shorter than 2 characters: %2", - onlyAdmin2: "⚠️ | Only admins can delete banned words from the list", - missingWords2: "⚠️ | You haven't entered the words to delete", - deletedSuccess: "✅ | Deleted %1 banned words from the list", - notExist: "❌ | %1 banned words do not exist in the list before: %2", - emptyList: "⚠️ | The list of banned words in your group is currently empty", - badWordsList: "📑 | The list of banned words in your group: %1", - onlyAdmin3: "⚠️ | Only admins can %1 this feature", - turnedOnOrOff: "✅ | Banned words warning has been %1", - onlyAdmin4: "⚠️ | Only admins can delete banned words warning", - missingTarget: "⚠️ | You haven't entered user ID or tagged user", - notWarned: "⚠️ | User %1 has not been warned for banned words", - removedWarn: "✅ | User %1 | %2 has been removed 1 banned words warning", - warned: "⚠️ | Banned words \"%1\" have been detected in your message, if you continue to violate you will be kicked from the group.", - warned2: "⚠️ | Banned words \"%1\" have been detected in your message, you have violated 2 times and will be kicked from the group.", - needAdmin: "Bot needs admin privileges to kick banned members", - unwarned: "✅ | Removed banned words warning of user %1 | %2" - } - }, - - onStart: async function ({ message, event, args, threadsData, usersData, role, getLang }) { - if (!await threadsData.get(event.threadID, "data.badWords")) - await threadsData.set(event.threadID, { - words: [], - violationUsers: {} - }, "data.badWords"); - - const badWords = await threadsData.get(event.threadID, "data.badWords.words", []); - - switch (args[0]) { - case "add": { - if (role < 1) - return message.reply(getLang("onlyAdmin")); - const words = args.slice(1).join(" ").split(/[,|]/); - if (words.length === 0) - return message.reply(getLang("missingWords")); - const badWordsExist = []; - const success = []; - const failed = []; - for (const word of words) { - const oldIndex = badWords.indexOf(word); - if (oldIndex === -1) { - badWords.push(word); - success.push(word); - } - else if (oldIndex > -1) { - badWordsExist.push(word); - } - else - failed.push(word); - } - await threadsData.set(event.threadID, badWords, "data.badWords.words"); - message.reply( - success.length > 0 ? getLang("addedSuccess", success.length) : "" - + (badWordsExist.length > 0 ? getLang("alreadyExist", badWordsExist.length, badWordsExist.map(word => hideWord(word)).join(", ")) : "") - + (failed.length > 0 ? getLang("tooShort", failed.length, failed.join(", ")) : "") - ); - break; - } - case "delete": - case "del": - case "-d": { - if (role < 1) - return message.reply(getLang("onlyAdmin2")); - const words = args.slice(1).join(" ").split(/[,|]/); - if (words.length === 0) - return message.reply(getLang("missingWords2")); - const success = []; - const failed = []; - for (const word of words) { - const oldIndex = badWords.indexOf(word); - if (oldIndex > -1) { - badWords.splice(oldIndex, 1); - success.push(word); - } - else - failed.push(word); - } - await threadsData.set(event.threadID, badWords, "data.badWords.words"); - message.reply( - (success.length > 0 ? getLang("deletedSuccess", success.length) : "") - + (failed.length > 0 ? getLang("notExist", failed.length, failed.join(", ")) : "") - ); - break; - } - case "list": - case "all": - case "-a": { - if (badWords.length === 0) - return message.reply(getLang("emptyList")); - message.reply(getLang("badWordsList", args[1] === "hide" ? badWords.map(word => hideWord(word)).join(", ") : badWords.join(", "))); - break; - } - case "on": { - if (role < 1) - return message.reply(getLang("onlyAdmin3", getLang("onText"))); - await threadsData.set(event.threadID, true, "settings.badWords"); - message.reply(getLang("turnedOnOrOff", getLang("onText"))); - break; - } - case "off": { - if (role < 1) - return message.reply(getLang("onlyAdmin3", getLang("offText"))); - await threadsData.set(event.threadID, false, "settings.badWords"); - message.reply(getLang("turnedOnOrOff", getLang("offText"))); - break; - } - case "unwarn": { - if (role < 1) - return message.reply(getLang("onlyAdmin4")); - let userID; - if (Object.keys(event.mentions)[0]) - userID = Object.keys(event.mentions)[0]; - else if (args[1]) - userID = args[1]; - else if (event.messageReply) - userID = event.messageReply.senderID; - if (isNaN(userID)) - return message.reply(getLang("missingTarget")); - const violationUsers = await threadsData.get(event.threadID, "data.badWords.violationUsers", {}); - if (!violationUsers[userID]) - return message.reply(getLang("notWarned", userID)); - violationUsers[userID]--; - await threadsData.set(event.threadID, violationUsers, "data.badWords.violationUsers"); - const userName = await usersData.getName(userID); - message.reply(getLang("unwarned", userID, userName)); - } - } - }, - - onChat: async function ({ message, event, api, threadsData, prefix, getLang }) { - if (!event.body) - return; - const threadData = global.db.allThreadData.find(t => t.threadID === event.threadID) || await threadsData.create(event.threadID); - const isEnabled = threadData.settings.badWords; - if (!isEnabled) - return; - const allAliases = [...(global.GoatBot.commands.get("badwords").config.aliases || []), ...(threadData.data.aliases?.["badwords"] || [])]; - const isCommand = allAliases.some(a => event.body.startsWith(prefix + a)); - if (isCommand) - return; - const badWordList = threadData.data.badWords?.words; - if (!badWordList || badWordList.length === 0) - return; - const violationUsers = threadData.data.badWords?.violationUsers || {}; - - for (const word of badWordList) { - if (event.body.match(new RegExp(`\\b${word}\\b`, "gi"))) { - if ((violationUsers[event.senderID] || 0) < 1) { - message.reply(getLang("warned", word)); - violationUsers[event.senderID] = violationUsers[event.senderID] ? violationUsers[event.senderID] + 1 : 1; - await threadsData.set(event.threadID, violationUsers, "data.badWords.violationUsers"); - return; - } - else { - await message.reply(getLang("warned2", word)); - api.removeUserFromGroup(event.senderID, event.threadID, (err) => { - if (err) - return message.reply(getLang("needAdmin"), (e, info) => { - let { onEvent } = global.GoatBot; - onEvent.push({ - messageID: info.messageID, - onStart: ({ event }) => { - if (event.logMessageType === "log:thread-admins" && event.logMessageData.ADMIN_EVENT == "add_admin") { - const { TARGET_ID } = event.logMessageData; - if (TARGET_ID == api.getCurrentUserID()) - api.removeUserFromGroup(event.senderID, event.threadID, () => onEvent = onEvent.filter(item => item.messageID != info.messageID)); - } - } - }); - }); - }); - } - } - } - } -}; - - -function hideWord(str) { - return str.length == 2 ? - str[0] + "*" : - str[0] + "*".repeat(str.length - 2) + str[str.length - 1]; -} \ No newline at end of file diff --git a/scripts/cmds/balance.js b/scripts/cmds/balance.js deleted file mode 100644 index 169205f2..00000000 --- a/scripts/cmds/balance.js +++ /dev/null @@ -1,230 +0,0 @@ -const fs = require("fs"); -const path = require("path"); -const { createCanvas, loadImage } = require("canvas"); -const axios = require("axios"); - -function formatBalance(num) { - num = Number(num) || 0; - if (num >= 1e12) return (num / 1e12).toFixed(2) + "t"; - if (num >= 1e9) return (num / 1e9).toFixed(2) + "b"; - if (num >= 1e6) return (num / 1e6).toFixed(2) + "m"; - if (num >= 1e3) return (num / 1e3).toFixed(2) + "k"; - return num.toFixed(0); -} - -function roundRect(ctx, x, y, w, h, r, fill = false, stroke = false) { - ctx.beginPath(); - ctx.moveTo(x + r, y); - ctx.lineTo(x + w - r, y); - ctx.quadraticCurveTo(x + w, y, x + w, y + r); - ctx.lineTo(x + w, y + h - r); - ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); - ctx.lineTo(x + r, y + h); - ctx.quadraticCurveTo(x, y + h, x, y + h - r); - ctx.lineTo(x, y + r); - ctx.quadraticCurveTo(x, y, x + r, y); - ctx.closePath(); - if (fill) ctx.fill(); - if (stroke) ctx.stroke(); -} - -async function drawCard({ userID, userName, balance }) { - const formatted = "$" + formatBalance(balance); - - let avatar = null; - try { - const picURL = `https://graph.facebook.com/${userID}/picture?height=1500&width=1500&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662`; - const response = await axios({ url: picURL, method: "GET", responseType: "arraybuffer" }); - avatar = await loadImage(response.data); - } catch (err) { - console.log("Avatar Load Failed:", err.message); - } - - const width = 850; - const height = 520; - const canvas = createCanvas(width, height); - const ctx = canvas.getContext("2d"); - - const grad = ctx.createLinearGradient(0, 0, width, height); - grad.addColorStop(0, "#0f2027"); - grad.addColorStop(0.5, "#1c4966"); - grad.addColorStop(1, "#2a7ab0"); - ctx.fillStyle = grad; - roundRect(ctx, 0, 0, width, height, 30, true); - - const shine = ctx.createLinearGradient(0, 0, width, height); - shine.addColorStop(0, "rgba(255,255,255,0.06)"); - shine.addColorStop(0.4, "rgba(255,255,255,0)"); - shine.addColorStop(1, "rgba(255,255,255,0.04)"); - ctx.fillStyle = shine; - roundRect(ctx, 0, 0, width, height, 30, true); - - ctx.font = "bold 34px Arial"; - ctx.fillStyle = "#ffffff"; - ctx.fillText("GOAT NATIONAL BANK", 60, 80); - - ctx.font = "16px Arial"; - ctx.fillStyle = "rgba(255,255,255,0.6)"; - ctx.fillText("PREMIUM ECONOMY CARD", 60, 105); - - const chipGrad = ctx.createLinearGradient(60, 140, 150, 205); - chipGrad.addColorStop(0, "#f4d97a"); - chipGrad.addColorStop(1, "#c9982f"); - ctx.fillStyle = chipGrad; - roundRect(ctx, 60, 145, 90, 60, 10, true); - - ctx.font = "28px monospace"; - ctx.fillStyle = "#ffffff"; - ctx.fillText("1234 5678 9012 8456", 60, 250); - - ctx.font = "16px Arial"; - ctx.fillStyle = "rgba(255,255,255,0.6)"; - ctx.fillText("VALID THRU", 60, 295); - ctx.font = "bold 22px Arial"; - ctx.fillStyle = "#ffffff"; - ctx.fillText("12/29", 60, 322); - - ctx.font = "bold 24px Arial"; - const safeName = userName ? userName.toUpperCase() : "CARD HOLDER"; - const maxNameWidth = 360; - let nameToShow = safeName; - while (ctx.measureText(nameToShow).width > maxNameWidth && nameToShow.length > 0) { - nameToShow = nameToShow.slice(0, -1); - } - if (nameToShow !== safeName) nameToShow = nameToShow.trim() + "…"; - ctx.fillText(nameToShow, 60, 380); - - const boxX = 440, boxY = 240, boxW = 350, boxH = 190; - const boxGrad = ctx.createLinearGradient(boxX, boxY, boxX, boxY + boxH); - boxGrad.addColorStop(0, "rgba(255,255,255,0.22)"); - boxGrad.addColorStop(1, "rgba(255,255,255,0.10)"); - ctx.fillStyle = boxGrad; - roundRect(ctx, boxX, boxY, boxW, boxH, 25, true); - - ctx.textAlign = "center"; - ctx.font = "18px Arial"; - ctx.fillStyle = "rgba(255,255,255,0.75)"; - ctx.fillText("AVAILABLE BALANCE", boxX + boxW / 2, boxY + 45); - - let fontSize = 50; - const maxTextWidth = boxW - 40; - do { - ctx.font = `bold ${fontSize}px Arial`; - const w = ctx.measureText(formatted).width; - if (w <= maxTextWidth) break; - fontSize -= 2; - } while (fontSize > 18); - - ctx.fillStyle = "#ffffff"; - ctx.fillText(formatted, boxX + boxW / 2, boxY + 120); - ctx.textAlign = "left"; - - if (avatar) { - const size = 100; - const x = width - size - 50; - const y = 45; - ctx.save(); - ctx.beginPath(); - ctx.arc(x + size / 2, y + size / 2, size / 2, 0, Math.PI * 2); - ctx.clip(); - ctx.drawImage(avatar, x, y, size, size); - ctx.restore(); - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.arc(x + size / 2, y + size / 2, size / 2 + 2, 0, Math.PI * 2); - ctx.stroke(); - } - - return canvas.toBuffer("image/png"); -} - -async function sendCard({ api, threadID, messageID, userID, userName, balance }) { - const buffer = await drawCard({ userID, userName, balance }); - const cachePath = path.join(__dirname, "cache"); - if (!fs.existsSync(cachePath)) fs.mkdirSync(cachePath); - const filePath = path.join(cachePath, `balance_${userID}.png`); - fs.writeFileSync(filePath, buffer); - - await api.sendMessage({ attachment: fs.createReadStream(filePath) }, threadID, messageID); - - setTimeout(() => { if (fs.existsSync(filePath)) fs.unlinkSync(filePath); }, 10000); -} - -function getTargetID({ event, args }) { - if (event.messageReply) return event.messageReply.senderID; - if (event.mentions && Object.keys(event.mentions).length > 0) { - return Object.keys(event.mentions)[0]; - } - const lastArg = args[args.length - 1]; - if (lastArg && /^\d{6,}$/.test(lastArg)) return lastArg; - return null; -} - -module.exports.config = { - name: "balance", - aliases: ["bal"], - version: "9.0", - author: "MOHAMMAD AKASH", - countDown: 5, - role: 0, - shortDescription: "Real Bank Card", - category: "economy", - usages: "[@mention | reply] | transfer [@mention | reply]", - description: "Show balance card or transfer money to another user" -}; - -module.exports.onStart = async function ({ api, event, args, usersData }) { - const { threadID, senderID, messageID } = event; - - try { - if (args[0] && args[0].toLowerCase() === "transfer") { - const amount = parseInt(args[1], 10); - if (!amount || amount <= 0) { - return api.sendMessage("Please enter a valid amount. Example: /balance transfer 10000 @friend (or reply to their message)", threadID, messageID); - } - - const targetID = getTargetID({ event, args: args.slice(2) }); - if (!targetID) { - return api.sendMessage("Who do you want to send money to? Mention someone or reply to their message with this command.", threadID, messageID); - } - if (targetID === senderID) { - return api.sendMessage("You can't transfer money to yourself.", threadID, messageID); - } - - const senderData = await usersData.get(senderID); - const senderBalance = senderData?.data?.money ?? 100; - - if (senderBalance < amount) { - return api.sendMessage(`Insufficient balance. Your current balance: $${formatBalance(senderBalance)}`, threadID, messageID); - } - - const receiverData = await usersData.get(targetID); - const receiverBalance = receiverData?.data?.money ?? 100; - - await usersData.set(senderID, { money: senderBalance - amount }, "data"); - await usersData.set(targetID, { money: receiverBalance + amount }, "data"); - - const senderName = await usersData.getName(senderID); - const receiverName = await usersData.getName(targetID); - - api.sendMessage( - `✅ Transfer successful!\n${senderName} ➝ ${receiverName}\nAmount: $${formatBalance(amount)}\n\nYour new balance: $${formatBalance(senderBalance - amount)}`, - threadID, - messageID - ); - return; - } - - const targetID = getTargetID({ event, args }) || senderID; - const userData = await usersData.get(targetID); - const balance = userData?.data?.money ?? 100; - const userName = await usersData.getName(targetID); - - await sendCard({ api, threadID, messageID, userID: targetID, userName, balance }); - - } catch (err) { - console.error(err); - api.sendMessage("Something went wrong while running this command!", threadID, messageID); - } -}; diff --git a/scripts/cmds/ban.js b/scripts/cmds/ban.js deleted file mode 100644 index 0ed4de0d..00000000 --- a/scripts/cmds/ban.js +++ /dev/null @@ -1,293 +0,0 @@ -const { findUid } = global.utils; -const moment = require("moment-timezone"); - -module.exports = { - config: { - name: "ban", - version: "1.4", - author: "NTKhang", - countDown: 5, - role: 1, - description: { - vi: "Cấm thành viên khỏi box chat", - en: "Ban user from box chat" - }, - category: "box chat", - guide: { - vi: " {pn} [@tag|uid|link fb|reply] [|để trống nếu không có lý do]: Cấm thành viên khỏi box chat" - + "\n {pn} check: Kiểm tra thành viên bị cấm và kick thành viên đó ra khỏi box chat" - + "\n {pn} unban [@tag|uid|link fb|reply]: Bỏ cấm thành viên khỏi box chat" - + "\n {pn} list: Xem danh sách thành viên bị cấm", - en: " {pn} [@tag|uid|fb link|reply] [|leave blank if no reason]: Ban user from box chat" - + "\n {pn} check: Check banned members and kick them out of the box chat" - + "\n {pn} unban [@tag|uid|fb link|reply]: Unban user from box chat" - + "\n {pn} list: View the list of banned members" - } - }, - - langs: { - vi: { - notFoundTarget: "⚠️ | Vui lòng tag người cần cấm hoặc nhập uid hoặc link fb hoặc phản hồi tin nhắn của người cần cấm", - notFoundTargetUnban: "⚠️ | Vui lòng tag người cần bỏ cấm hoặc nhập uid hoặc link fb hoặc phản hồi tin nhắn của người cần bỏ cấm", - userNotBanned: "⚠️ | Người mang id %1 không bị cấm khỏi box chat này", - unbannedSuccess: "✅ | Đã bỏ cấm %1 khỏi box chat!", - cantSelfBan: "⚠️ | Bạn không thể tự cấm chính mình!", - cantBanAdmin: "❌ | Bạn không thể cấm quản trị viên!", - existedBan: "❌ | Người này đã bị cấm từ trước!", - noReason: "Không có lý do", - bannedSuccess: "✅ | Đã cấm %1 khỏi box chat!", - needAdmin: "⚠️ | Bot cần quyền quản trị viên để kick thành viên bị cấm", - noName: "Người dùng facebook", - noData: "📑 | Không có thành viên nào bị cấm trong box chat này", - listBanned: "📑 | Danh sách thành viên bị cấm trong box chat này (trang %1/%2)", - content: "%1/ %2 (%3)\nLý do: %4\nThời gian cấm: %5\n\n", - needAdminToKick: "⚠️ | Thành viên %1 (%2) bị cấm khỏi box chat, nhưng bot không có quyền quản trị viên để kick thành viên này, vui lòng cấp quyền quản trị viên cho bot để kick thành viên này", - bannedKick: "⚠️ | %1 đã bị cấm khỏi box chat từ trước!\nUID: %2\nLý do: %3\nThời gian cấm: %4\n\nBot đã tự động kick thành viên này" - }, - en: { - notFoundTarget: "⚠️ | Please tag the person to ban or enter uid or fb link or reply to the message of the person to ban", - notFoundTargetUnban: "⚠️ | Please tag the person to unban or enter uid or fb link or reply to the message of the person to unban", - userNotBanned: "⚠️ | The person with id %1 is not banned from this box chat", - unbannedSuccess: "✅ | Unbanned %1 from box chat!", - cantSelfBan: "⚠️ | You can't ban yourself!", - cantBanAdmin: "❌ | You can't ban the administrator!", - existedBan: "❌ | This person has been banned before!", - noReason: "No reason", - bannedSuccess: "✅ | Banned %1 from box chat!", - needAdmin: "⚠️ | Bot needs administrator permission to kick banned members", - noName: "Facebook user", - noData: "📑 | There are no banned members in this box chat", - listBanned: "📑 | List of banned members in this box chat (page %1/%2)", - content: "%1/ %2 (%3)\nReason: %4\nBan time: %5\n\n", - needAdminToKick: "⚠️ | Member %1 (%2) has been banned from box chat, but the bot does not have administrator permission to kick this member, please grant administrator permission to the bot to kick this member", - bannedKick: "⚠️ | %1 has been banned from box chat before!\nUID: %2\nReason: %3\nBan time: %4\n\nBot has automatically kicked this member" - }, - tl: { - notFoundTarget: "⚠️ | Mag-tag ng taong iban o maglagay ng uid o fb link o sumagot sa mensahe ng taong iban", - notFoundTargetUnban: "⚠️ | Mag-tag ng taong i-unban o maglagay ng uid o fb link o sumagot sa mensahe ng taong i-unban", - userNotBanned: "⚠️ | Ang taong may id %1 ay hindi naka-ban sa box chat na ito", - unbannedSuccess: "✅ | Na-unban na si %1 mula sa box chat!", - cantSelfBan: "⚠️ | Hindi mo maaaring i-ban ang iyong sarili!", - cantBanAdmin: "❌ | Hindi mo maaaring i-ban ang administrator!", - existedBan: "❌ | Ang taong ito ay na-ban na noon!", - noReason: "Walang dahilan", - bannedSuccess: "✅ | Na-ban na si %1 mula sa box chat!", - needAdmin: "⚠️ | Kailangan ng bot ng pahintulot ng administrator para i-kick ang mga na-ban", - noName: "Facebook user", - noData: "📑 | Walang mga na-ban na miyembro sa box chat na ito", - listBanned: "📑 | Listahan ng mga na-ban na miyembro sa box chat na ito (pahina %1/%2)", - content: "%1/ %2 (%3)\nDahilan: %4\nOras ng ban: %5\n\n", - needAdminToKick: "⚠️ | Ang miyembro %1 (%2) ay na-ban mula sa box chat, ngunit walang pahintulot ng administrator ang bot para i-kick ang miyembrong ito", - bannedKick: "⚠️ | Si %1 ay na-ban na mula sa box chat noon!\nUID: %2\nDahilan: %3\nOras ng ban: %4\n\nAwtomatikong siya ay na-kick ng bot" - }, - hi: { - notFoundTarget: "⚠️ | Ban karne wale ko tag karein ya uid ya fb link dalein ya unka message reply karein", - notFoundTargetUnban: "⚠️ | Unban karne wale ko tag karein ya uid ya fb link dalein ya unka message reply karein", - userNotBanned: "⚠️ | Id %1 wala banda is box chat mein banned nahi hai", - unbannedSuccess: "✅ | %1 ko box chat se unban kar diya gaya!", - cantSelfBan: "⚠️ | Aap khud ko ban nahi kar sakte!", - cantBanAdmin: "❌ | Aap administrator ko ban nahi kar sakte!", - existedBan: "❌ | Ye banda pehle se banned hai!", - noReason: "Koi wajah nahi", - bannedSuccess: "✅ | %1 ko box chat se ban kar diya gaya!", - needAdmin: "⚠️ | Bot ko banned members ko kick karne ke liye administrator permission chahiye", - noName: "Facebook user", - noData: "📑 | Is box chat mein koi banned member nahi hai", - listBanned: "📑 | Is box chat ke banned members ki list (page %1/%2)", - content: "%1/ %2 (%3)\nWajah: %4\nBan time: %5\n\n", - needAdminToKick: "⚠️ | Member %1 (%2) ko box chat se ban kiya gaya hai, lekin bot ke paas administrator permission nahi hai is member ko kick karne ke liye", - bannedKick: "⚠️ | %1 pehle se box chat se banned hai!\nUID: %2\nWajah: %3\nBan time: %4\n\nBot ne automatically is member ko kick kar diya" - }, - ar: { - notFoundTarget: "⚠️ | الرجاء وضع علامة على الشخص المراد حظره أو إدخال uid أو رابط fb أو الرد على رسالته", - notFoundTargetUnban: "⚠️ | الرجاء وضع علامة على الشخص المراد رفع حظره أو إدخال uid أو رابط fb أو الرد على رسالته", - userNotBanned: "⚠️ | الشخص ذو id %1 غير محظور من هذا المحادثة", - unbannedSuccess: "✅ | تم رفع حظر %1 من المحادثة!", - cantSelfBan: "⚠️ | لا يمكنك حظر نفسك!", - cantBanAdmin: "❌ | لا يمكنك حظر المسؤول!", - existedBan: "❌ | هذا الشخص محظور مسبقاً!", - noReason: "لا يوجد سبب", - bannedSuccess: "✅ | تم حظر %1 من المحادثة!", - needAdmin: "⚠️ | يحتاج البوت إلى إذن المسؤول لطرد الأعضاء المحظورين", - noName: "مستخدم فيسبوك", - noData: "📑 | لا يوجد أعضاء محظورون في هذه المحادثة", - listBanned: "📑 | قائمة الأعضاء المحظورين في هذه المحادثة (صفحة %1/%2)", - content: "%1/ %2 (%3)\nالسبب: %4\nوقت الحظر: %5\n\n", - needAdminToKick: "⚠️ | العضو %1 (%2) محظور من المحادثة، لكن البوت لا يملك إذن المسؤول لطرده، يرجى منح إذن المسؤول للبوت", - bannedKick: "⚠️ | %1 كان محظوراً من المحادثة من قبل!\nUID: %2\nالسبب: %3\nوقت الحظر: %4\n\nقام البوت بطرده تلقائياً" - }, - bn: { - notFoundTarget: "⚠️ | যাকে ban করতে চান তাকে tag করুন বা uid বা fb link দিন বা তার মেসেজে reply করুন", - notFoundTargetUnban: "⚠️ | যাকে unban করতে চান তাকে tag করুন বা uid বা fb link দিন বা তার মেসেজে reply করুন", - userNotBanned: "⚠️ | id %1 সহ ব্যক্তি এই box chat থেকে ban হয়নি", - unbannedSuccess: "✅ | %1 কে box chat থেকে unban করা হয়েছে!", - cantSelfBan: "⚠️ | আপনি নিজেকে ban করতে পারবেন না!", - cantBanAdmin: "❌ | আপনি অ্যাডমিনকে ban করতে পারবেন না!", - existedBan: "❌ | এই ব্যক্তি আগে থেকেই ban আছে!", - noReason: "কোনো কারণ নেই", - bannedSuccess: "✅ | %1 কে box chat থেকে ban করা হয়েছে!", - needAdmin: "⚠️ | ban হওয়া সদস্যদের kick করতে bot এর administrator permission দরকার", - noName: "Facebook ব্যবহারকারী", - noData: "📑 | এই box chat এ কোনো ban করা সদস্য নেই", - listBanned: "📑 | এই box chat এর ban করা সদস্যদের তালিকা (পেজ %1/%2)", - content: "%1/ %2 (%3)\nকারণ: %4\nBan সময়: %5\n\n", - needAdminToKick: "⚠️ | সদস্য %1 (%2) কে box chat থেকে ban করা হয়েছে, কিন্তু bot এর administrator permission নেই তাকে kick করার, অনুগ্রহ করে bot কে administrator permission দিন", - bannedKick: "⚠️ | %1 আগে থেকেই box chat থেকে ban আছে!\nUID: %2\nকারণ: %3\nBan সময়: %4\n\nBot স্বয়ংক্রিয়ভাবে এই সদস্যকে kick করেছে" - } - }, - - onStart: async function ({ message, event, args, threadsData, getLang, usersData, api }) { - const { members, adminIDs } = await threadsData.get(event.threadID); - const { senderID } = event; - let target; - let reason; - - const dataBanned = await threadsData.get(event.threadID, 'data.banned_ban', []); - - if (args[0] == 'unban') { - if (!isNaN(args[1])) - target = args[1]; - else if (args[1]?.startsWith('https')) - target = await findUid(args[1]); - else if (Object.keys(event.mentions || {}).length) - target = Object.keys(event.mentions)[0]; - else if (event.messageReply?.senderID) - target = event.messageReply.senderID; - else - return api.sendMessage(getLang('notFoundTargetUnban'), event.threadID, event.messageID); - - const index = dataBanned.findIndex(item => item.id == target); - if (index == -1) - return api.sendMessage(getLang('userNotBanned', target), event.threadID, event.messageID); - - dataBanned.splice(index, 1); - await threadsData.set(event.threadID, dataBanned, 'data.banned_ban'); - const userName = members[target]?.name || await usersData.getName(target) || getLang('noName'); - - return api.sendMessage(getLang('unbannedSuccess', userName), event.threadID, event.messageID); - } - else if (args[0] == "check") { - if (!dataBanned.length) - return; - for (const user of dataBanned) { - if (event.participantIDs.includes(user.id)) - api.removeUserFromGroup(user.id, event.threadID); - } - } - - if (event.messageReply?.senderID) { - target = event.messageReply.senderID; - reason = args.join(' '); - } - else if (Object.keys(event.mentions || {}).length) { - target = Object.keys(event.mentions)[0]; - reason = args.join(' ').replace(event.mentions[target], ''); - } - else if (!isNaN(args[0])) { - target = args[0]; - reason = args.slice(1).join(' '); - } - else if (args[0]?.startsWith('https')) { - target = await findUid(args[0]); - reason = args.slice(1).join(' '); - } - else if (args[0] == 'list') { - if (!dataBanned.length) - return message.reply(getLang('noData')); - const limit = 20; - const page = parseInt(args[1] || 1) || 1; - const start = (page - 1) * limit; - const end = page * limit; - const data = dataBanned.slice(start, end); - let msg = ''; - let count = 0; - for (const user of data) { - count++; - const name = members[user.id]?.name || await usersData.getName(user.id) || getLang('noName'); - const time = user.time; - msg += getLang('content', start + count, name, user.id, user.reason, time); - } - return message.reply(getLang('listBanned', page, Math.ceil(dataBanned.length / limit)) + '\n\n' + msg); - } - - if (!target) - return message.reply(getLang('notFoundTarget')); - if (target == senderID) - return message.reply(getLang('cantSelfBan')); - if (adminIDs.includes(target)) - return message.reply(getLang('cantBanAdmin')); - - const banned = dataBanned.find(item => item.id == target); - if (banned) - return message.reply(getLang('existedBan')); - - const name = members[target]?.name || (await usersData.getName(target)) || getLang('noName'); - const time = moment().tz(global.GoatBot.config.timeZone).format('HH:mm:ss DD/MM/YYYY'); - const data = { - id: target, - time, - reason: reason || getLang('noReason') - }; - - dataBanned.push(data); - await threadsData.set(event.threadID, dataBanned, 'data.banned_ban'); - message.reply(getLang('bannedSuccess', name), () => { - if (members.some(item => item.userID == target)) { - if (adminIDs.includes(api.getCurrentUserID())) { - if (event.participantIDs.includes(target)) - api.removeUserFromGroup(target, event.threadID); - } - else { - message.send(getLang('needAdmin'), (err, info) => { - global.GoatBot.onEvent.push({ - messageID: info.messageID, - onStart: ({ event }) => { - if (event.logMessageType === "log:thread-admins" && event.logMessageData.ADMIN_EVENT == "add_admin") { - const { TARGET_ID } = event.logMessageData; - if (TARGET_ID == api.getCurrentUserID()) { - api.removeUserFromGroup(target, event.threadID, () => global.GoatBot.onEvent = global.GoatBot.onEvent.filter(item => item.messageID != info.messageID)); - } - } - } - }); - }); - } - } - }); - }, - - onEvent: async function ({ event, api, threadsData, getLang, message }) { - if (event.logMessageType == "log:subscribe") { - const { threadID } = event; - const dataBanned = await threadsData.get(threadID, 'data.banned_ban', []); - const usersAdded = event.logMessageData.addedParticipants; - - for (const user of usersAdded) { - const { userFbId, fullName } = user; - const banned = dataBanned.find(item => item.id == userFbId); - if (banned) { - const reason = banned.reason || getLang('noReason'); - const time = banned.time; - return api.removeUserFromGroup(userFbId, threadID, err => { - if (err) - return message.send(getLang('needAdminToKick', fullName, userFbId), (err, info) => { - global.GoatBot.onEvent.push({ - messageID: info.messageID, - onStart: ({ event }) => { - if (event.logMessageType === "log:thread-admins" && event.logMessageData.ADMIN_EVENT == "add_admin") { - const { TARGET_ID } = event.logMessageData; - if (TARGET_ID == api.getCurrentUserID()) { - api.removeUserFromGroup(userFbId, event.threadID, () => global.GoatBot.onEvent = global.GoatBot.onEvent.filter(item => item.messageID != info.messageID)); - } - } - } - }); - }); - else - message.send(getLang('bannedKick', fullName, userFbId, reason, time)); - }); - } - } - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/bet.js b/scripts/cmds/bet.js deleted file mode 100644 index fb3d010f..00000000 --- a/scripts/cmds/bet.js +++ /dev/null @@ -1,59 +0,0 @@ -module.exports.config = { - name: "bet", - version: "3.0", - author: "MOHAMMAD AKASH", - role: 0, - category: "economy", - shortDescription: "Casino betting game" -}; - -module.exports.onStart = async function ({ api, event, args, usersData }) { - const { senderID, threadID, messageID } = event; - - if (!args[0]) - return api.sendMessage("🎰 Usage: bet ", threadID, messageID); - - const bet = parseInt(args[0]); - if (!bet || bet <= 0) - return api.sendMessage("❌ Invalid bet amount!", threadID, messageID); - - const userData = await usersData.get(senderID); - let balance = userData?.data?.money ?? 100; - - if (balance < bet) - return api.sendMessage(`❌ Not enough balance!\n🏦 Balance: ${balance}$`, threadID, messageID); - - const outcomes = [ - { text: "💥 You lost everything!", multiplier: 0 }, - { text: "😞 You got back half.", multiplier: 0.5 }, - { text: "🟡 You broke even.", multiplier: 1 }, - { text: "🟢 You doubled your money!", multiplier: 2 }, - { text: "🔥 You tripled your bet!", multiplier: 3 }, - { text: "🎉 JACKPOT! 10x reward!", multiplier: 10 } - ]; - - const win = Math.random() < 0.6; - let selected; - - if (win) { - const winOutcomes = outcomes.filter(o => o.multiplier > 0); - selected = winOutcomes[Math.floor(Math.random() * winOutcomes.length)]; - } else { - const loseOutcomes = outcomes.filter(o => o.multiplier === 0); - selected = loseOutcomes[Math.floor(Math.random() * loseOutcomes.length)]; - } - - const reward = Math.floor(bet * selected.multiplier); - balance = balance - bet + reward; - - await usersData.set(senderID, { data: { ...userData.data, money: balance } }); - - const msg = -`${selected.text} - -🎰 You bet: ${bet}$ -💸 You won: ${reward}$ -💰 New balance: ${balance}$`; - - api.sendMessage(msg, threadID, messageID); -}; diff --git a/scripts/cmds/bin.js b/scripts/cmds/bin.js deleted file mode 100644 index e42ac9ef..00000000 --- a/scripts/cmds/bin.js +++ /dev/null @@ -1,63 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -module.exports = { - config: { - name: "pastebin", - aliases: ["bin"], - version: "1.4", - author: "NeoKEX", // Don't try to change the author name otherwise I'll fvckyourmom - countDown: 5, - role: 0, - shortDescription: "Upload a command's code to Pastebin.", - longDescription: "Uploads the raw source code of any command to a Pastebin service and returns the raw link.", - category: "utility", - guide: "{pn} 0) { - uid = Object.keys(mentions)[0]; - } else if (type === "message_reply") { - uid = messageReply.senderID; - } else { - uid = senderID; - } - - const avatarURL = `https://graph.facebook.com/${uid}/picture?width=512&height=512&access_token=350685531728|62f8ce9f74b12f84c123cc23437a4a32`; - - try { - const res = await axios.get(`https://api.popcat.xyz/v2/blur?image=${encodeURIComponent(avatarURL)}`, { - responseType: "arraybuffer" - }); - - const filePath = path.join(__dirname, "cache", `blur_${uid}_${Date.now()}.png`); - fs.writeFileSync(filePath, res.data); - - message.reply({ - body: "🌫️ Here's your blurred image!", - attachment: fs.createReadStream(filePath) - }, () => fs.unlinkSync(filePath)); - - } catch (err) { - console.error(err); - message.reply("❌ | Failed to generate blurred image."); - } - } -}; diff --git a/scripts/cmds/boxinfo.js b/scripts/cmds/boxinfo.js deleted file mode 100644 index 31393e3e..00000000 --- a/scripts/cmds/boxinfo.js +++ /dev/null @@ -1,69 +0,0 @@ -const fs = require("fs"); -const request = require("request"); -const path = require("path"); - -module.exports = { - config: { - name: "boxinfo", - aliases: ["groupinfo"], - version: "2.2.0", - author: "Mᴏʜᴀᴍᴍᴀᴅ Aᴋᴀsʜ", - role: 1, - shortDescription: "Group info", - category: "box chat", - guide: { - en: "groupinfo" - } - }, - - onStart: async function ({ api, event }) { - const cacheDir = path.join(__dirname, "cache"); - const imgPath = path.join(cacheDir, "groupinfo.png"); - - if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir); - - const info = await api.getThreadInfo(event.threadID); - - let male = 0, female = 0; - for (const u of info.userInfo) { - if (u.gender === "MALE") male++; - else if (u.gender === "FEMALE") female++; - } - - const text = -`── Gʀᴏᴜᴘ Iɴғᴏ ── -Nᴀᴍᴇ : ${info.threadName || "No Name"} -Iᴅ : ${info.threadID} -Eᴍᴏᴊɪ : ${info.emoji || "N/A"} -Aᴘᴘʀᴏᴠᴀʟ : ${info.approvalMode ? "ON" : "OFF"} - -Mᴇᴍʙᴇʀs : ${info.participantIDs.length} -Mᴀʟᴇ : ${male} -Fᴇᴍᴀʟᴇ : ${female} -Aᴅᴍɪɴs : ${info.adminIDs.length} -Mᴇssᴀɢᴇs : ${info.messageCount} - -— Mᴏʜᴀᴍᴍᴀᴅ Aᴋᴀsʜ`; - - const send = () => - api.sendMessage( - { - body: text, - attachment: fs.existsSync(imgPath) - ? fs.createReadStream(imgPath) - : null - }, - event.threadID, - () => { - if (fs.existsSync(imgPath)) fs.unlinkSync(imgPath); - }, - event.messageID - ); - - if (!info.imageSrc) return api.sendMessage(text, event.threadID, event.messageID); - - request(encodeURI(info.imageSrc)) - .pipe(fs.createWriteStream(imgPath)) - .on("close", send); - } -}; diff --git a/scripts/cmds/busy.js b/scripts/cmds/busy.js deleted file mode 100644 index 8eb91fa2..00000000 --- a/scripts/cmds/busy.js +++ /dev/null @@ -1,79 +0,0 @@ -if (!global.client.busyList) - global.client.busyList = {}; - -module.exports = { - config: { - name: "busy", - version: "1.6", - author: "NTKhang", - countDown: 5, - role: 0, - description: { - vi: "bật chế độ không làm phiền, khi bạn được tag bot sẽ thông báo", - en: "turn on do not disturb mode, when you are tagged bot will notify" - }, - category: "box chat", - guide: { - vi: " {pn} [để trống | ]: bật chế độ không làm phiền" - + "\n {pn} off: tắt chế độ không làm phiền", - en: " {pn} [empty | ]: turn on do not disturb mode" - + "\n {pn} off: turn off do not disturb mode" - } - }, - - langs: { - vi: { - turnedOff: "✅ | Đã tắt chế độ không làm phiền", - turnedOn: "✅ | Đã bật chế độ không làm phiền", - turnedOnWithReason: "✅ | Đã bật chế độ không làm phiền với lý do: %1", - turnedOnWithoutReason: "✅ | Đã bật chế độ không làm phiền", - alreadyOn: "Hiện tại người dùng %1 đang bận", - alreadyOnWithReason: "Hiện tại người dùng %1 đang bận với lý do: %2" - }, - en: { - turnedOff: "✅ | Do not disturb mode has been turned off", - turnedOn: "✅ | Do not disturb mode has been turned on", - turnedOnWithReason: "✅ | Do not disturb mode has been turned on with reason: %1", - turnedOnWithoutReason: "✅ | Do not disturb mode has been turned on", - alreadyOn: "User %1 is currently busy", - alreadyOnWithReason: "User %1 is currently busy with reason: %2" - } - }, - - onStart: async function ({ args, message, event, getLang, usersData }) { - const { senderID } = event; - - if (args[0] == "off") { - const { data } = await usersData.get(senderID); - delete data.busy; - await usersData.set(senderID, data, "data"); - return message.reply(getLang("turnedOff")); - } - - const reason = args.join(" ") || ""; - await usersData.set(senderID, reason, "data.busy"); - return message.reply( - reason ? - getLang("turnedOnWithReason", reason) : - getLang("turnedOnWithoutReason") - ); - }, - - onChat: async ({ event, message, getLang }) => { - const { mentions } = event; - - if (!mentions || Object.keys(mentions).length == 0) - return; - const arrayMentions = Object.keys(mentions); - - for (const userID of arrayMentions) { - const reasonBusy = global.db.allUserData.find(item => item.userID == userID)?.data.busy || false; - if (reasonBusy !== false) { - return message.reply( - reasonBusy ? - getLang("alreadyOnWithReason", mentions[userID].replace("@", ""), reasonBusy) : - getLang("alreadyOn", mentions[userID].replace("@", ""))); - } - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/butslap.js b/scripts/cmds/butslap.js deleted file mode 100644 index a0c5a7f0..00000000 --- a/scripts/cmds/butslap.js +++ /dev/null @@ -1,74 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -const baseApiUrl = async () => { - const base = await axios.get( - "https://raw.githubusercontent.com/mahmudx7/HINATA/main/baseApiUrl.json" - ); - return base.data.mahmud; -}; - -/** -* @author MahMUD -* @author: do not delete it -*/ - -module.exports = { - config: { - name: "butslap", - aliases: ["buttslap"], - version: "1.7", - author: "MahMUD", - role: 0, - category: "fun", - cooldown: 8, - guide: "slap [mention/reply/UID]", - }, - - onStart: async function ({ api, event, args }) { - const obfuscatedAuthor = String.fromCharCode(77, 97, 104, 77, 85, 68); - if (module.exports.config.author !== obfuscatedAuthor) { - return api.sendMessage("You are not authorized to change the author name.", event.threadID, event.messageID); - } - - const { threadID, messageID, messageReply, mentions, senderID } = event; - const type = args[0]; - - if (!type) return api.sendMessage("Use: fun slap @tag", threadID, messageID); - - let id = senderID; - let id2; - - if (messageReply) { - id2 = messageReply.senderID; - } else if (Object.keys(mentions).length > 0) { - id2 = Object.keys(mentions)[0]; - } else if (args[1]) { - id2 = args[1]; - } else { - return api.sendMessage("Mention, reply, or provide UID of the target.", threadID, messageID); - } - - try { - const url = `${await baseApiUrl()}/api/dig?type=buttslap&user=${id}&user2=${id2}`; - - const response = await axios.get(url, { responseType: "arraybuffer" }); - const filePath = path.join(__dirname, `slap_${id2}.png`); - fs.writeFileSync(filePath, response.data); - - api.sendMessage( - { - attachment: fs.createReadStream(filePath), - body: `Effect: buttslap successful 💥` - }, - threadID, - () => fs.unlinkSync(filePath), - messageID - ); - } catch (err) { - console.error(err); - api.sendMessage(`🥹error, contact MahMUD.`, threadID, messageID); - } - } -}; diff --git a/scripts/cmds/buzz.js b/scripts/cmds/buzz.js deleted file mode 100644 index 9a4d84b6..00000000 --- a/scripts/cmds/buzz.js +++ /dev/null @@ -1,92 +0,0 @@ -const delay = (ms) => new Promise(res => setTimeout(res, ms)); - -module.exports = { - config: { - name: "buzz", - version: "3.2.0", - role: 2, - author: "Akash Edit", - description: "১০০+ আকাশ ভাই স্টাইল ক্যাপশন পাঠায়", - category: "fun", - usages: "@mention", - cooldowns: 5, - }, - - onStart: async function({ message, event, args, api }) { - try { - const mention = Object.keys(event.mentions)[0]; - if (!mention) { - return message.reply("😅 যার জন্য মেসেজ যাবে তাকে আগে @ম্যানশন করো ভাই!"); - } - - const name = event.mentions[mention]; - const arraytag = [{ id: mention, tag: name }]; - - const messages = [ - `আকাশ ভাই তোমাকে ভালোবাসে ${name} ❤️`, - `আকাশ ভাই সবসময় তোমার পাশে আছে ${name} 🫶`, - `আকাশ ভাই তোমাকে খুব মিস করে ${name} 😘`, - `আকাশ ভাই তোমাকে নিয়ে ভাবে ${name} 🌸`, - `আকাশ ভাই চায় তুমি সবসময় হাসো ${name} 😊`, - `আকাশ ভাইয়ের কাছে তুমি অনেক স্পেশাল ${name} 💝`, - `আকাশ ভাই তোমার জন্য সব করতে রাজি ${name} 💌`, - `আকাশ ভাই তোমার কথা সবসময় ভাবে ${name} 🥰`, - `আকাশ ভাই শুধু তোমাকেই চায় ${name} 💖`, - `আকাশ ভাই তোমাকে ছাড়া কিছু ভাবতে পারে না ${name} 😍`, - `আকাশ ভাই তোমাকে সারাজীবন ভালোবাসবে ${name} 💛`, - `আকাশ ভাই তোমার জন্য অপেক্ষা করছে ${name} 🌹`, - `আকাশ ভাই মনে করে তুমি আজও সুন্দর ${name} 🌸`, - `আকাশ ভাই তোমার হাসি দেখতে পেতে চায় ${name} 😄`, - `আকাশ ভাই তোমাকে কাছে পেতে চায় ${name} 🫶`, - `আকাশ ভাই সবসময় তোমার খোঁজ রাখে ${name} ❤️`, - `আকাশ ভাই তোমাকে নিয়ে স্বপ্ন দেখে ${name} 🌙`, - `আকাশ ভাই তোমার ভালোবাসা চায় ${name} 💖`, - `আকাশ ভাই তোমাকে আজও মনে করছে ${name} 💌`, - `আকাশ ভাই সবসময় তোমার কথা ভাবছে ${name} 🥰`, - `আকাশ ভাই তোমাকে সারাক্ষণ মনে রাখে ${name} 💛`, - `আকাশ ভাই তোমার সঙ্গে সময় কাটাতে চায় ${name} 🌹`, - `আকাশ ভাই তোমাকে প্রিয় মনে করে ${name} 💝`, - `আকাশ ভাই শুধু তোমার জন্য আছে ${name} 🫶`, - `আকাশ ভাই তোমার সাথে হাসতে চায় ${name} 😄`, - `আকাশ ভাই তোমার খুশি চায় ${name} ❤️`, - `আকাশ ভাই তোমাকে সবসময় মিস করছে ${name} 💌`, - `আকাশ ভাই তোমার জন্য দোয়া করছে ${name} 🌸`, - `আকাশ ভাই তোমাকে প্রণয় করে দেখতে চায় ${name} 💖`, - `আকাশ ভাই তোমার কাছে সবসময় ফিরবে ${name} 💛`, - `আকাশ ভাই তোমাকে ভাবতেই ভালো লাগে ${name} 🥰`, - `আকাশ ভাই তোমার সঙ্গে স্বপ্ন ভাগ করতে চায় ${name} 🌙`, - `আকাশ ভাই তোমাকে কখনো ভুলবে না ${name} 💝`, - `আকাশ ভাই তোমার হাসি তার শক্তি ${name} 😄`, - `আকাশ ভাই তোমার জন্য সব সময় অপেক্ষা করবে ${name} 🌹`, - `আকাশ ভাই তোমাকে সান্ত্বনা দিতে চায় ${name} 🫶`, - `আকাশ ভাই তোমার ভালোবাসা চিরকাল চাইবে ${name} ❤️`, - `আকাশ ভাই তোমার কথা মনে পড়ে বারবার ${name} 💌`, - `আকাশ ভাই তোমাকে কাছে পেতে চায় সর্বদা ${name} 💖`, - `আকাশ ভাই তোমার জন্য তার হৃদয় খুলে রেখেছে ${name} 💛`, - `আকাশ ভাই তোমাকে চিরকাল মনে রাখবে ${name} 🥰`, - `আকাশ ভাই তোমার ভালোবাসা প্রাপ্য ${name} 🌸`, - `আকাশ ভাই সব সময় তোমার পাশে থাকবে ${name} 🌙`, - `আকাশ ভাই তোমাকে নিয়ে প্রতিদিন চিন্তা করে ${name} 💝`, - `আকাশ ভাই তোমাকে ভালোবাসার শব্দ জানে না ${name} 💌`, - `আকাশ ভাই তোমাকে সব সময় হাসাতে চায় ${name} 😄`, - `আকাশ ভাই তোমার জন্য প্রার্থনা করে ${name} 🌹`, - `আকাশ ভাই তোমার সঙ্গে প্রতিটি মুহূর্ত উপভোগ করতে চায় ${name} 🫶`, - `আকাশ ভাই তোমাকে সবসময় মনে রাখবে ${name} ❤️`, - `আকাশ ভাই তোমার ভালোবাসায় পূর্ণ ${name} 💖` - ]; - - message.reply(`😎 শুরু হচ্ছে "আকাশ ভাই স্টাইল স্টকিং" ${name}-এর জন্য...`); - - // প্রতিটি মেসেজ 3 সেকেন্ড পর পর পাঠানো হবে - for (const msg of messages) { - await delay(3000); - message.reply({ body: msg, mentions: arraytag }); - } - - message.reply(`💘 শেষ! আকাশ ভাই ${name}-এর প্রতি ভালোবাসার ডেলিভারি সম্পন্ন 😅`); - } catch (err) { - console.error(err); - message.reply("❌ কিছু একটা সমস্যা হয়েছে ভাই, আবার চেষ্টা করো।"); - } - } -}; diff --git a/scripts/cmds/cache/autoseen.txt b/scripts/cmds/cache/autoseen.txt deleted file mode 100644 index 8b137891..00000000 --- a/scripts/cmds/cache/autoseen.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/scripts/cmds/cache/canvas/AKASH.txt b/scripts/cmds/cache/canvas/AKASH.txt deleted file mode 100644 index d61af90f..00000000 --- a/scripts/cmds/cache/canvas/AKASH.txt +++ /dev/null @@ -1 +0,0 @@ -Dont Remove this File ❌ diff --git a/scripts/cmds/callad.js b/scripts/cmds/callad.js deleted file mode 100644 index 9226f526..00000000 --- a/scripts/cmds/callad.js +++ /dev/null @@ -1,116 +0,0 @@ -const { getStreamsFromAttachment, log } = global.utils; - -const mediaTypes = ["photo", "png", "animated_image", "video", "audio"]; - -module.exports = { - config: { - name: "callad", - aliases: ["call", "called"], - version: "2.0", - author: "NTKhang | Edited by Akash", - countDown: 5, - role: 0, - category: "contacts admin", - description: { - en: "Send message or report directly to bot admin" - }, - guide: { - en: "{pn} " - } - }, - - langs: { - en: { - missingMessage: "❗ Please write a message to send", - noAdmin: "⚠️ No admin found", - sentFromGroup: "\n👥 Group: %1\n🧵 Thread ID: %2", - sentFromUser: "\n👤 Sent from private chat", - - userContent: - "\n\n📩 Message:\n%1\n\n↩️ Reply to respond", - - success: - "✅ Message Sent\n\n📨 Sent to %1 admin(s)", - - failed: - "❌ Failed to send message to %1 admin(s)", - - adminReply: - "📍 Admin Reply\n\n👤 %1:\n%2\n\n↩️ Reply to continue", - - userFeedback: - "📝 User Feedback\n\n👤 %1\n🆔 %2%3\n\n📩 Message:\n%4", - - replySuccess: "✅ Reply sent successfully" - } - }, - - onStart: async function ({ - args, message, event, usersData, threadsData, api, commandName, getLang - }) { - if (!args[0]) - return message.reply(getLang("missingMessage")); - - const { senderID, threadID, isGroup } = event; - const adminBot = global.GoatBot.config.adminBot; - if (!adminBot.length) - return message.reply(getLang("noAdmin")); - - const senderName = await usersData.getName(senderID); - - let body = - "📞 CALL ADMIN\n\n" + - `👤 User: ${senderName}\n` + - `🆔 ID: ${senderID}`; - - body += isGroup - ? getLang("sentFromGroup", (await threadsData.get(threadID)).threadName, threadID) - : getLang("sentFromUser"); - - body += getLang("userContent", args.join(" ")); - - const formMessage = { - body, - mentions: [{ id: senderID, tag: senderName }], - attachment: await getStreamsFromAttachment( - [...event.attachments, ...(event.messageReply?.attachments || [])] - .filter(item => mediaTypes.includes(item.type)) - ) - }; - - let success = 0; - - for (const uid of adminBot) { - try { - const info = await api.sendMessage(formMessage, uid); - success++; - global.GoatBot.onReply.set(info.messageID, { - commandName, - type: "userCallAdmin", - threadID, - messageIDSender: event.messageID - }); - } catch (e) { - log.err("CALL ADMIN", e); - } - } - - return message.reply(getLang("success", success)); - }, - - onReply: async function ({ - args, event, api, message, Reply, usersData, commandName, getLang - }) { - const senderName = await usersData.getName(event.senderID); - - if (Reply.type === "userCallAdmin") { - const body = getLang("adminReply", senderName, args.join(" ")); - api.sendMessage( - { body }, - Reply.threadID, - () => message.reply(getLang("replySuccess")), - Reply.messageIDSender - ); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/catbox.js b/scripts/cmds/catbox.js deleted file mode 100644 index ec7b2f35..00000000 --- a/scripts/cmds/catbox.js +++ /dev/null @@ -1,104 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); -const FormData = require("form-data"); - -module.exports = { - config: { - name: "catbox", - version: "1.0.0", - author: "EryXenX", - role: 0, - shortDescription: "Upload media to Catbox", - longDescription: "Reply to an image, video, audio, or file to upload it to Catbox", - category: "media", - guide: "{pn} (reply to a file)", - cooldowns: 5 - }, - - onStart: async function ({ api, event }) { - const { threadID, messageID, type, messageReply } = event; - - if ( - type !== "message_reply" || - !messageReply || - !messageReply.attachments || - messageReply.attachments.length === 0 - ) { - return api.sendMessage( - "Reply to an image, video, audio, or file.", - threadID, - messageID - ); - } - - const attachment = messageReply.attachments[0]; - const ext = attachment.filename - ? path.extname(attachment.filename) - : ".tmp"; - - const cacheDir = path.join(__dirname, "cache"); - - if (!fs.existsSync(cacheDir)) { - fs.mkdirSync(cacheDir, { recursive: true }); - } - - const filePath = path.join( - cacheDir, - `catbox_${Date.now()}${ext}` - ); - - try { - const file = await axios({ - url: attachment.url, - method: "GET", - responseType: "stream" - }); - - const writer = fs.createWriteStream(filePath); - file.data.pipe(writer); - - await new Promise((resolve, reject) => { - writer.on("finish", resolve); - writer.on("error", reject); - }); - - const form = new FormData(); - form.append("reqtype", "fileupload"); - form.append("fileToUpload", fs.createReadStream(filePath)); - - const upload = await axios.post( - "https://catbox.moe/user/api.php", - form, - { - headers: form.getHeaders(), - maxBodyLength: Infinity, - maxContentLength: Infinity - } - ); - - if (fs.existsSync(filePath)) { - fs.unlinkSync(filePath); - } - - return api.sendMessage( - upload.data.trim(), - threadID, - messageID - ); - - } catch (err) { - console.error("Catbox Error:", err); - - if (fs.existsSync(filePath)) { - fs.unlinkSync(filePath); - } - - return api.sendMessage( - "Upload failed.", - threadID, - messageID - ); - } - } -}; diff --git a/scripts/cmds/clear.js b/scripts/cmds/clear.js deleted file mode 100644 index 3515daa0..00000000 --- a/scripts/cmds/clear.js +++ /dev/null @@ -1,40 +0,0 @@ -module.exports = { - config: { - name: "clear", - aliases: [], - author: "kshitiz", - version: "2.0", - cooldowns: 5, - role: 0, - shortDescription: { - en: "" - }, - longDescription: { - en: "unsent all messages sent by bot" - }, - category: "owner", - guide: { - en: "{p}{n}" - } - }, - onStart: async function ({ api, event }) { - - const unsendBotMessages = async () => { - const threadID = event.threadID; - - - const botMessages = await api.getThreadHistory(threadID, 100); // Adjust the limit as needed 50 = 50 msg - - - const botSentMessages = botMessages.filter(message => message.senderID === api.getCurrentUserID()); - - - for (const message of botSentMessages) { - await api.unsendMessage(message.messageID); - } - }; - - - await unsendBotMessages(); - } -}; \ No newline at end of file diff --git a/scripts/cmds/cmd.js b/scripts/cmds/cmd.js deleted file mode 100644 index 057c637b..00000000 --- a/scripts/cmds/cmd.js +++ /dev/null @@ -1,531 +0,0 @@ -const axios = require("axios"); -const { execSync } = require("child_process"); -const fs = require("fs-extra"); -const path = require("path"); -const cheerio = require("cheerio"); -const { client } = global; - -const { configCommands } = global.GoatBot; -const { log, loading, removeHomeDir } = global.utils; - -function getDomain(url) { - const regex = /^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:/\n]+)/im; - const match = url.match(regex); - return match ? match[1] : null; -} - -function isURL(str) { - try { - new URL(str); - return true; - } - catch (e) { - return false; - } -} - -module.exports = { - config: { - name: "cmd", - version: "1.17", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Quản lý các tệp lệnh của bạn", - en: "Manage your command files" - }, - category: "admin", - guide: { - vi: " {pn} load " - + "\n {pn} loadAll" - + "\n {pn} install : Tải xuống và cài đặt một tệp lệnh từ một url, url là đường dẫn đến tệp lệnh (raw)" - + "\n {pn} install : Tải xuống và cài đặt một tệp lệnh từ một code, code là mã của lệnh", - en: " {pn} load " - + "\n {pn} loadAll" - + "\n {pn} install : Download and install a command file from a url, url is the path to the file (raw)" - + "\n {pn} install : Download and install a command file from a code, code is the code of the command" - } - }, - - langs: { - vi: { - missingFileName: "⚠️ | Vui lòng nhập vào tên lệnh bạn muốn reload", - loaded: "✅ | Đã load command \"%1\" thành công", - loadedError: "❌ | Load command \"%1\" thất bại với lỗi\n%2: %3", - loadedSuccess: "✅ | Đã load thành công (%1) command", - loadedFail: "❌ | Load thất bại (%1) command\n%2", - openConsoleToSeeError: "👀 | Hãy mở console để xem chi tiết lỗi", - missingCommandNameUnload: "⚠️ | Vui lòng nhập vào tên lệnh bạn muốn unload", - unloaded: "✅ | Đã unload command \"%1\" thành công", - unloadedError: "❌ | Unload command \"%1\" thất bại với lỗi\n%2: %3", - missingUrlCodeOrFileName: "⚠️ | Vui lòng nhập vào url hoặc code và tên file lệnh bạn muốn cài đặt", - missingUrlOrCode: "⚠️ | Vui lòng nhập vào url hoặc code của tệp lệnh bạn muốn cài đặt", - missingFileNameInstall: "⚠️ | Vui lòng nhập vào tên file để lưu lệnh (đuôi .js)", - invalidUrl: "⚠️ | Vui lòng nhập vào url hợp lệ", - invalidUrlOrCode: "⚠️ | Không thể lấy được mã lệnh", - alreadExist: "⚠️ | File lệnh đã tồn tại, bạn có chắc chắn muốn ghi đè lên file lệnh cũ không?\nThả cảm xúc bất kì vào tin nhắn này để tiếp tục", - installed: "✅ | Đã cài đặt command \"%1\" thành công, file lệnh được lưu tại %2", - installedError: "❌ | Cài đặt command \"%1\" thất bại với lỗi\n%2: %3", - missingFile: "⚠️ | Không tìm thấy tệp lệnh \"%1\"", - invalidFileName: "⚠️ | Tên tệp lệnh không hợp lệ", - unloadedFile: "✅ | Đã unload lệnh \"%1\"" - }, - en: { - missingFileName: "⚠️ | Please enter the command name you want to reload", - loaded: "✅ | Loaded command \"%1\" successfully", - loadedError: "❌ | Failed to load command \"%1\" with error\n%2: %3", - loadedSuccess: "✅ | Loaded successfully (%1) command", - loadedFail: "❌ | Failed to load (%1) command\n%2", - openConsoleToSeeError: "👀 | Open console to see error details", - missingCommandNameUnload: "⚠️ | Please enter the command name you want to unload", - unloaded: "✅ | Unloaded command \"%1\" successfully", - unloadedError: "❌ | Failed to unload command \"%1\" with error\n%2: %3", - missingUrlCodeOrFileName: "⚠️ | Please enter the url or code and command file name you want to install", - missingUrlOrCode: "⚠️ | Please enter the url or code of the command file you want to install", - missingFileNameInstall: "⚠️ | Please enter the file name to save the command (with .js extension)", - invalidUrl: "⚠️ | Please enter a valid url", - invalidUrlOrCode: "⚠️ | Unable to get command code", - alreadExist: "⚠️ | The command file already exists, are you sure you want to overwrite the old command file?\nReact to this message to continue", - installed: "✅ | Installed command \"%1\" successfully, the command file is saved at %2", - installedError: "❌ | Failed to install command \"%1\" with error\n%2: %3", - missingFile: "⚠️ | Command file \"%1\" not found", - invalidFileName: "⚠️ | Invalid command file name", - unloadedFile: "✅ | Unloaded command \"%1\"" - } - }, - - onStart: async ({ args, message, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, event, commandName, getLang }) => { - const { unloadScripts, loadScripts } = global.utils; - if ( - args[0] == "load" - && args.length == 2 - ) { - if (!args[1]) - return message.reply(getLang("missingFileName")); - const infoLoad = loadScripts("cmds", args[1], log, configCommands, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, getLang); - if (infoLoad.status == "success") - message.reply(getLang("loaded", infoLoad.name)); - else { - message.reply( - getLang("loadedError", infoLoad.name, infoLoad.error.name, infoLoad.error.message) - + "\n" + infoLoad.error.stack - ); - console.log(infoLoad.errorWithThoutRemoveHomeDir); - } - } - else if ( - (args[0] || "").toLowerCase() == "loadall" - || (args[0] == "load" && args.length > 2) - ) { - const fileNeedToLoad = args[0].toLowerCase() == "loadall" ? - fs.readdirSync(__dirname) - .filter(file => - file.endsWith(".js") && - !file.match(/(eg)\.js$/g) && - (process.env.NODE_ENV == "development" ? true : !file.match(/(dev)\.js$/g)) && - !configCommands.commandUnload?.includes(file) - ) - .map(item => item = item.split(".")[0]) : - args.slice(1); - const arraySucces = []; - const arrayFail = []; - - for (const fileName of fileNeedToLoad) { - const infoLoad = loadScripts("cmds", fileName, log, configCommands, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, getLang); - if (infoLoad.status == "success") - arraySucces.push(fileName); - else - arrayFail.push(` ❗ ${fileName} => ${infoLoad.error.name}: ${infoLoad.error.message}`); - } - - let msg = ""; - if (arraySucces.length > 0) - msg += getLang("loadedSuccess", arraySucces.length); - if (arrayFail.length > 0) { - msg += (msg ? "\n" : "") + getLang("loadedFail", arrayFail.length, arrayFail.join("\n")); - msg += "\n" + getLang("openConsoleToSeeError"); - } - - message.reply(msg); - } - else if (args[0] == "unload") { - if (!args[1]) - return message.reply(getLang("missingCommandNameUnload")); - const infoUnload = unloadScripts("cmds", args[1], configCommands, getLang); - infoUnload.status == "success" ? - message.reply(getLang("unloaded", infoUnload.name)) : - message.reply(getLang("unloadedError", infoUnload.name, infoUnload.error.name, infoUnload.error.message)); - } - else if (args[0] == "install") { - let url = args[1]; - let fileName = args[2]; - let rawCode; - - if (!url || !fileName) - return message.reply(getLang("missingUrlCodeOrFileName")); - - if ( - url.endsWith(".js") - && !isURL(url) - ) { - const tmp = fileName; - fileName = url; - url = tmp; - } - - if (url.match(/(https?:\/\/(?:www\.|(?!www)))/)) { - global.utils.log.dev("install", "url", url); - if (!fileName || !fileName.endsWith(".js")) - return message.reply(getLang("missingFileNameInstall")); - - const domain = getDomain(url); - if (!domain) - return message.reply(getLang("invalidUrl")); - - if (domain == "pastebin.com") { - const regex = /https:\/\/pastebin\.com\/(?!raw\/)(.*)/; - if (url.match(regex)) - url = url.replace(regex, "https://pastebin.com/raw/$1"); - if (url.endsWith("/")) - url = url.slice(0, -1); - } - else if (domain == "github.com") { - const regex = /https:\/\/github\.com\/(.*)\/blob\/(.*)/; - if (url.match(regex)) - url = url.replace(regex, "https://raw.githubusercontent.com/$1/$2"); - } - - rawCode = (await axios.get(url)).data; - - if (domain == "savetext.net") { - const $ = cheerio.load(rawCode); - rawCode = $("#content").text(); - } - } - else { - global.utils.log.dev("install", "code", args.slice(1).join(" ")); - if (args[args.length - 1].endsWith(".js")) { - fileName = args[args.length - 1]; - rawCode = event.body.slice(event.body.indexOf('install') + 7, event.body.indexOf(fileName) - 1); - } - else if (args[1].endsWith(".js")) { - fileName = args[1]; - rawCode = event.body.slice(event.body.indexOf(fileName) + fileName.length + 1); - } - else - return message.reply(getLang("missingFileNameInstall")); - } - - if (!rawCode) - return message.reply(getLang("invalidUrlOrCode")); - - if (fs.existsSync(path.join(__dirname, fileName))) - return message.reply(getLang("alreadExist"), (err, info) => { - global.GoatBot.onReaction.set(info.messageID, { - commandName, - messageID: info.messageID, - type: "install", - author: event.senderID, - data: { - fileName, - rawCode - } - }); - }); - else { - const infoLoad = loadScripts("cmds", fileName, log, configCommands, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, getLang, rawCode); - infoLoad.status == "success" ? - message.reply(getLang("installed", infoLoad.name, path.join(__dirname, fileName).replace(process.cwd(), ""))) : - message.reply(getLang("installedError", infoLoad.name, infoLoad.error.name, infoLoad.error.message)); - } - } - else - message.SyntaxError(); - }, - - onReaction: async function ({ Reaction, message, event, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, getLang }) { - const { loadScripts } = global.utils; - const { author, data: { fileName, rawCode } } = Reaction; - if (event.userID != author) - return; - const infoLoad = loadScripts("cmds", fileName, log, configCommands, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, getLang, rawCode); - infoLoad.status == "success" ? - message.reply(getLang("installed", infoLoad.name, path.join(__dirname, fileName).replace(process.cwd(), ""))) : - message.reply(getLang("installedError", infoLoad.name, infoLoad.error.name, infoLoad.error.message)); - } -}; - -// do not edit this code because it use for obfuscate code -const packageAlready = []; -const spinner = "\\|/-"; -let count = 0; - -function loadScripts(folder, fileName, log, configCommands, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, getLang, rawCode) { - // global.GoatBot[folderModules == "cmds" ? "commandFilesPath" : "eventCommandsFilesPath"].push({ - // filePath: pathCommand, - // commandName: [commandName, ...validAliases] - // }); - const storageCommandFilesPath = global.GoatBot[folder == "cmds" ? "commandFilesPath" : "eventCommandsFilesPath"]; - - try { - if (rawCode) { - fileName = fileName.slice(0, -3); - fs.writeFileSync(path.normalize(`${process.cwd()}/scripts/${folder}/${fileName}.js`), rawCode); - } - const regExpCheckPackage = /require(\s+|)\((\s+|)[`'"]([^`'"]+)[`'"](\s+|)\)/g; - const { GoatBot } = global; - const { onFirstChat: allOnFirstChat, onChat: allOnChat, onEvent: allOnEvent, onAnyEvent: allOnAnyEvent } = GoatBot; - let setMap, typeEnvCommand, commandType; - if (folder == "cmds") { - typeEnvCommand = "envCommands"; - setMap = "commands"; - commandType = "command"; - } - else if (folder == "events") { - typeEnvCommand = "envEvents"; - setMap = "eventCommands"; - commandType = "event command"; - } - // const pathCommand = path.normalize(path.normalize(process.cwd() + `/${folder}/${fileName}.js`)); - let pathCommand; - if (process.env.NODE_ENV == "development") { - const devPath = path.normalize(process.cwd() + `/scripts/${folder}/${fileName}.dev.js`); - if (fs.existsSync(devPath)) - pathCommand = devPath; - else - pathCommand = path.normalize(process.cwd() + `/scripts/${folder}/${fileName}.js`); - } - else - pathCommand = path.normalize(process.cwd() + `/scripts/${folder}/${fileName}.js`); - - // ————————————————— CHECK PACKAGE ————————————————— // - const contentFile = fs.readFileSync(pathCommand, "utf8"); - let allPackage = contentFile.match(regExpCheckPackage); - if (allPackage) { - allPackage = allPackage - .map(p => p.match(/[`'"]([^`'"]+)[`'"]/)[1]) - .filter(p => p.indexOf("/") !== 0 && p.indexOf("./") !== 0 && p.indexOf("../") !== 0 && p.indexOf(__dirname) !== 0); - for (let packageName of allPackage) { - // @user/abc => @user/abc - // @user/abc/dist/xyz.js => @user/abc - // @user/abc/dist/xyz => @user/abc - if (packageName.startsWith('@')) - packageName = packageName.split('/').slice(0, 2).join('/'); - else - packageName = packageName.split('/')[0]; - - if (!packageAlready.includes(packageName)) { - packageAlready.push(packageName); - if (!fs.existsSync(`${process.cwd()}/node_modules/${packageName}`)) { - let wating; - try { - wating = setInterval(() => { - count++; - loading.info("PACKAGE", `Installing ${packageName} ${spinner[count % spinner.length]}`); - }, 80); - execSync(`npm install ${packageName} --save`, { stdio: "pipe" }); - clearInterval(wating); - process.stderr.clearLine(); - } - catch (error) { - clearInterval(wating); - process.stderr.clearLine(); - throw new Error(`Can't install package ${packageName}`); - } - } - } - } - } - // ———————————————— GET OLD COMMAND ———————————————— // - const oldCommand = require(pathCommand); - const oldCommandName = oldCommand?.config?.name; - // —————————————— CHECK COMMAND EXIST ——————————————— // - if (!oldCommandName) { - if (GoatBot[setMap].get(oldCommandName)?.location != pathCommand) - throw new Error(`${commandType} name "${oldCommandName}" is already exist in command "${removeHomeDir(GoatBot[setMap].get(oldCommandName)?.location || "")}"`); - } - // ————————————————— CHECK ALIASES ————————————————— // - if (oldCommand.config.aliases) { - let oldAliases = oldCommand.config.aliases; - if (typeof oldAliases == "string") - oldAliases = [oldAliases]; - for (const alias of oldAliases) - GoatBot.aliases.delete(alias); - } - // ——————————————— DELETE OLD COMMAND ——————————————— // - delete require.cache[require.resolve(pathCommand)]; - // —————————————————————————————————————————————————— // - - - - // ———————————————— GET NEW COMMAND ———————————————— // - const command = require(pathCommand); - command.location = pathCommand; - const configCommand = command.config; - if (!configCommand || typeof configCommand != "object") - throw new Error("config of command must be an object"); - // —————————————————— CHECK SYNTAX —————————————————— // - const scriptName = configCommand.name; - - // Check onChat function - const indexOnChat = allOnChat.findIndex(item => item == oldCommandName); - if (indexOnChat != -1) - allOnChat.splice(indexOnChat, 1); - - // Check onFirstChat function - const indexOnFirstChat = allOnChat.findIndex(item => item == oldCommandName); - let oldOnFirstChat; - if (indexOnFirstChat != -1) { - oldOnFirstChat = allOnFirstChat[indexOnFirstChat]; - allOnFirstChat.splice(indexOnFirstChat, 1); - } - - // Check onEvent function - const indexOnEvent = allOnEvent.findIndex(item => item == oldCommandName); - if (indexOnEvent != -1) - allOnEvent.splice(indexOnEvent, 1); - - // Check onAnyEvent function - const indexOnAnyEvent = allOnAnyEvent.findIndex(item => item == oldCommandName); - if (indexOnAnyEvent != -1) - allOnAnyEvent.splice(indexOnAnyEvent, 1); - - // Check onLoad function - if (command.onLoad) - command.onLoad({ api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData }); - - const { envGlobal, envConfig } = configCommand; - if (!command.onStart) - throw new Error('Function onStart is missing!'); - if (typeof command.onStart != "function") - throw new Error('Function onStart must be a function!'); - if (!scriptName) - throw new Error('Name of command is missing!'); - // ————————————————— CHECK ALIASES ————————————————— // - if (configCommand.aliases) { - let { aliases } = configCommand; - if (typeof aliases == "string") - aliases = [aliases]; - for (const alias of aliases) { - if (aliases.filter(item => item == alias).length > 1) - throw new Error(`alias "${alias}" duplicate in ${commandType} "${scriptName}" with file name "${removeHomeDir(pathCommand || "")}"`); - if (GoatBot.aliases.has(alias)) - throw new Error(`alias "${alias}" is already exist in ${commandType} "${GoatBot.aliases.get(alias)}" with file name "${removeHomeDir(GoatBot[setMap].get(GoatBot.aliases.get(alias))?.location || "")}"`); - GoatBot.aliases.set(alias, scriptName); - } - } - // ————————————————— CHECK ENVCONFIG ————————————————— // - // env Global - if (envGlobal) { - if (typeof envGlobal != "object" || Array.isArray(envGlobal)) - throw new Error("envGlobal must be an object"); - for (const key in envGlobal) - configCommands.envGlobal[key] = envGlobal[key]; - } - // env Config - if (envConfig && typeof envConfig == "object" && !Array.isArray(envConfig)) { - if (!configCommands[typeEnvCommand][scriptName]) - configCommands[typeEnvCommand][scriptName] = {}; - configCommands[typeEnvCommand][scriptName] = envConfig; - } - GoatBot[setMap].delete(oldCommandName); - GoatBot[setMap].set(scriptName, command); - fs.writeFileSync(client.dirConfigCommands, JSON.stringify(configCommands, null, 2)); - const keyUnloadCommand = folder == "cmds" ? "commandUnload" : "commandEventUnload"; - const findIndex = (configCommands[keyUnloadCommand] || []).indexOf(`${fileName}.js`); - if (findIndex != -1) - configCommands[keyUnloadCommand].splice(findIndex, 1); - fs.writeFileSync(client.dirConfigCommands, JSON.stringify(configCommands, null, 2)); - - - if (command.onChat) - allOnChat.push(scriptName); - - if (command.onFirstChat) - allOnFirstChat.push({ commandName: scriptName, threadIDsChattedFirstTime: oldOnFirstChat?.threadIDsChattedFirstTime || [] }); - - if (command.onEvent) - allOnEvent.push(scriptName); - - if (command.onAnyEvent) - allOnAnyEvent.push(scriptName); - - const indexStorageCommandFilesPath = storageCommandFilesPath.findIndex(item => item.filePath == pathCommand); - if (indexStorageCommandFilesPath != -1) - storageCommandFilesPath.splice(indexStorageCommandFilesPath, 1); - storageCommandFilesPath.push({ - filePath: pathCommand, - commandName: [scriptName, ...configCommand.aliases || []] - }); - - return { - status: "success", - name: fileName, - command - }; - } - catch (err) { - const defaultError = new Error(); - defaultError.name = err.name; - defaultError.message = err.message; - defaultError.stack = err.stack; - - err.stack ? err.stack = removeHomeDir(err.stack || "") : ""; - fs.writeFileSync(global.client.dirConfigCommands, JSON.stringify(configCommands, null, 2)); - return { - status: "failed", - name: fileName, - error: err, - errorWithThoutRemoveHomeDir: defaultError - }; - } -} - -function unloadScripts(folder, fileName, configCommands, getLang) { - const pathCommand = `${process.cwd()}/scripts/${folder}/${fileName}.js`; - if (!fs.existsSync(pathCommand)) { - const err = new Error(getLang("missingFile", `${fileName}.js`)); - err.name = "FileNotFound"; - throw err; - } - const command = require(pathCommand); - const commandName = command.config?.name; - if (!commandName) - throw new Error(getLang("invalidFileName", `${fileName}.js`)); - const { GoatBot } = global; - const { onChat: allOnChat, onEvent: allOnEvent, onAnyEvent: allOnAnyEvent } = GoatBot; - const indexOnChat = allOnChat.findIndex(item => item == commandName); - if (indexOnChat != -1) - allOnChat.splice(indexOnChat, 1); - const indexOnEvent = allOnEvent.findIndex(item => item == commandName); - if (indexOnEvent != -1) - allOnEvent.splice(indexOnEvent, 1); - const indexOnAnyEvent = allOnAnyEvent.findIndex(item => item == commandName); - if (indexOnAnyEvent != -1) - allOnAnyEvent.splice(indexOnAnyEvent, 1); - // ————————————————— CHECK ALIASES ————————————————— // - if (command.config.aliases) { - let aliases = command.config?.aliases || []; - if (typeof aliases == "string") - aliases = [aliases]; - for (const alias of aliases) - GoatBot.aliases.delete(alias); - } - const setMap = folder == "cmds" ? "commands" : "eventCommands"; - delete require.cache[require.resolve(pathCommand)]; - GoatBot[setMap].delete(commandName); - log.master("UNLOADED", getLang("unloaded", commandName)); - const commandUnload = configCommands[folder == "cmds" ? "commandUnload" : "commandEventUnload"] || []; - if (!commandUnload.includes(`${fileName}.js`)) - commandUnload.push(`${fileName}.js`); - configCommands[folder == "cmds" ? "commandUnload" : "commandEventUnload"] = commandUnload; - fs.writeFileSync(global.client.dirConfigCommands, JSON.stringify(configCommands, null, 2)); - return { - status: "success", - name: fileName - }; -} - -global.utils.loadScripts = loadScripts; -global.utils.unloadScripts = unloadScripts; \ No newline at end of file diff --git a/scripts/cmds/count.js b/scripts/cmds/count.js deleted file mode 100644 index 92a0174a..00000000 --- a/scripts/cmds/count.js +++ /dev/null @@ -1,165 +0,0 @@ -module.exports = { - config: { - name: "count", - version: "1.3", - author: "NTKhang", - countDown: 5, - role: 0, - description: { - vi: "Xem số lượng tin nhắn của tất cả thành viên hoặc bản thân (tính từ lúc bot vào nhóm)", - en: "View the number of messages of all members or yourself (since the bot joined the group)" - }, - category: "box chat", - guide: { - vi: " {pn}: dùng để xem số lượng tin nhắn của bạn" - + "\n {pn} @tag: dùng để xem số lượng tin nhắn của những người được tag" - + "\n {pn} all: dùng để xem số lượng tin nhắn của tất cả thành viên", - en: " {pn}: used to view the number of messages of you" - + "\n {pn} @tag: used to view the number of messages of those tagged" - + "\n {pn} all: used to view the number of messages of all members" - } - }, - - langs: { - vi: { - count: "Số tin nhắn của các thành viên:", - endMessage: "Những người không có tên trong danh sách là chưa gửi tin nhắn nào.", - page: "Trang [%1/%2]", - reply: "Phản hồi tin nhắn này kèm số trang để xem tiếp", - result: "%1 hạng %2 với %3 tin nhắn", - yourResult: "Bạn đứng hạng %1 và đã gửi %2 tin nhắn trong nhóm này", - invalidPage: "Số trang không hợp lệ" - }, - en: { - count: "Number of messages of members:", - endMessage: "Those who do not have a name in the list have not sent any messages.", - page: "Page [%1/%2]", - reply: "Reply to this message with the page number to view more", - result: "%1 rank %2 with %3 messages", - yourResult: "You are ranked %1 and have sent %2 messages in this group", - invalidPage: "Invalid page number" - } - }, - - onStart: async function ({ args, threadsData, message, event, api, commandName, getLang }) { - const { threadID, senderID } = event; - const threadData = await threadsData.get(threadID); - const { members } = threadData; - const usersInGroup = (await api.getThreadInfo(threadID)).participantIDs; - let arraySort = []; - for (const user of members) { - if (!usersInGroup.includes(user.userID)) - continue; - const charac = "️️️️️️️️️️️️️️️️️"; // This character is banned from facebook chat (it is not an empty string) - arraySort.push({ - name: user.name.includes(charac) ? `Uid: ${user.userID}` : user.name, - count: user.count, - uid: user.userID - }); - } - let stt = 1; - arraySort.sort((a, b) => b.count - a.count); - arraySort.map(item => item.stt = stt++); - - if (args[0]) { - if (args[0].toLowerCase() == "all") { - let msg = getLang("count"); - const endMessage = getLang("endMessage"); - for (const item of arraySort) { - if (item.count > 0) - msg += `\n${item.stt}/ ${item.name}: ${item.count}`; - } - - if ((msg + endMessage).length > 19999) { - msg = ""; - let page = parseInt(args[1]); - if (isNaN(page)) - page = 1; - const splitPage = global.utils.splitPage(arraySort, 50); - arraySort = splitPage.allPage[page - 1]; - for (const item of arraySort) { - if (item.count > 0) - msg += `\n${item.stt}/ ${item.name}: ${item.count}`; - } - msg += getLang("page", page, splitPage.totalPage) - + `\n${getLang("reply")}` - + `\n\n${endMessage}`; - - return message.reply(msg, (err, info) => { - if (err) - return message.err(err); - global.GoatBot.onReply.set(info.messageID, { - commandName, - messageID: info.messageID, - splitPage, - author: senderID - }); - }); - } - message.reply(msg); - } - else if (event.mentions) { - let msg = ""; - for (const id in event.mentions) { - const findUser = arraySort.find(item => item.uid == id); - msg += `\n${getLang("result", findUser.name, findUser.stt, findUser.count)}`; - } - message.reply(msg); - } - } - else { - const findUser = arraySort.find(item => item.uid == senderID); - return message.reply(getLang("yourResult", findUser.stt, findUser.count)); - } - }, - - onReply: ({ message, event, Reply, commandName, getLang }) => { - const { senderID, body } = event; - const { author, splitPage } = Reply; - if (author != senderID) - return; - const page = parseInt(body); - if (isNaN(page) || page < 1 || page > splitPage.totalPage) - return message.reply(getLang("invalidPage")); - let msg = getLang("count"); - const endMessage = getLang("endMessage"); - const arraySort = splitPage.allPage[page - 1]; - for (const item of arraySort) { - if (item.count > 0) - msg += `\n${item.stt}/ ${item.name}: ${item.count}`; - } - msg += getLang("page", page, splitPage.totalPage) - + "\n" + getLang("reply") - + "\n\n" + endMessage; - message.reply(msg, (err, info) => { - if (err) - return message.err(err); - message.unsend(Reply.messageID); - global.GoatBot.onReply.set(info.messageID, { - commandName, - messageID: info.messageID, - splitPage, - author: senderID - }); - }); - }, - - onChat: async ({ usersData, threadsData, event }) => { - const { senderID, threadID } = event; - const members = await threadsData.get(threadID, "members"); - const findMember = members.find(user => user.userID == senderID); - if (!findMember) { - members.push({ - userID: senderID, - name: await usersData.getName(senderID), - nickname: null, - inGroup: true, - count: 1 - }); - } - else - findMember.count += 1; - await threadsData.set(threadID, members, "members"); - } - -}; diff --git a/scripts/cmds/creart.js b/scripts/cmds/creart.js deleted file mode 100644 index 32fc87b9..00000000 --- a/scripts/cmds/creart.js +++ /dev/null @@ -1,40 +0,0 @@ -const axios = require("axios"); - -module.exports = { - config: { - name: "creart", - version: "1.2", - author: "nexo_here", - countDown: 5, - role: 0, - shortDescription: "Generate AI image", - longDescription: "Generate image using prompt via smfahim.xyz CreartAI", - category: "AI-IMAGE", - guide: { - en: "{pn} " - } - }, - - onStart: async function ({ message, args }) { - const prompt = args.join(" "); - if (!prompt) return message.reply("❌ | Please provide a prompt to generate image."); - - // Send waiting message with ⏳ - const waiting = await message.reply(`⏳ | Generating image for: "${prompt}"`); - - try { - const url = `https://smfahim.xyz/creartai?prompt=${encodeURIComponent(prompt)}`; - const imgRes = await axios.get(url, { responseType: "stream" }); - - // Send image with ✅ - return message.reply({ - body: `✅ | Here is your image for: "${prompt}"`, - attachment: imgRes.data - }); - - } catch (error) { - console.error("Image generation error:", error.message); - return message.reply("❌ | Failed to generate image. Try again later."); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/customrankcard.js b/scripts/cmds/customrankcard.js deleted file mode 100644 index 474ee51d..00000000 --- a/scripts/cmds/customrankcard.js +++ /dev/null @@ -1,224 +0,0 @@ -// url check image -const checkUrlRegex = /https?:\/\/.*\.(?:png|jpg|jpeg|gif)/gi; -const regExColor = /#([0-9a-f]{6})|rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)|rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d+\.?\d*)\)/gi; -const { uploadImgbb } = global.utils; - -module.exports = { - config: { - name: "customrankcard", - aliases: ["crc", "customrank"], - version: "1.12", - author: "NTKhang", - countDown: 5, - role: 0, - description: { - vi: "Thiết kế thẻ rank theo ý bạn", - en: "Design rank card by your own" - }, - category: "rank", - guide: { - vi: { - body: " {pn} [maincolor | subcolor | linecolor | expbarcolor | progresscolor | alphasubcolor | textcolor | namecolor | expcolor | rankcolor | levelcolor | reset] " - + "\n Trong đó: " - + "\n + maincolor | background : background chính của thẻ rank" - + "\n + subcolor : background phụ" - + "\n + linecolor : màu của đường kẻ giữa background chính và phụ" - + "\n + expbarcolor : màu của thanh exp" - + "\n + progresscolor : màu của thanh exp hiện tại" - + "\n + alphasubcolor : độ mờ của background phụ (từ 0 -> 1)" - + "\n + textcolor : màu của chữ (hex color or rgba)" - + "\n + namecolor : màu của tên" - + "\n + expcolor : màu của exp" - + "\n + rankcolor : màu của rank" - + "\n + levelcolor : màu của level" - + "\n • có thể là mã hex color, rgb, rgba, gradient (mỗi màu cách nhau bởi dấu cách) hoặc url hình ảnh" - + "\n • Nếu bạn muốn dùng gradient, hãy nhập nhiều mã màu cách nhau bởi dấu cách" - + "\n {pn} reset: reset tất cả về mặc định" - + "\n Ví dụ:" - + "\n {pn} maincolor #fff000" - + "\n {pn} maincolor #0093E9 #80D0C7" - + "\n {pn} subcolor rgba(255,136,86,0.4)" - + "\n {pn} reset", - attachment: { - [`${__dirname}/assets/guide/customrankcard_1.jpg`]: "https://i.ibb.co/BZ2Qgs1/image.png", - [`${__dirname}/assets/guide/customrankcard_2.png`]: "https://i.ibb.co/wy1ZHHL/image.png" - } - }, - en: { - body: " {pn} [maincolor | subcolor | linecolor | progresscolor | alphasubcolor | textcolor | namecolor | expcolor | rankcolor | levelcolor | reset] " - + "\n In which: " - + "\n + maincolor | background : main background of rank card" - + "\n + subcolor : sub background" - + "\n + linecolor : color of line between main and sub background" - + "\n + expbarcolor : color of exp bar" - + "\n + progresscolor : color of current exp bar" - + "\n + alphasubcolor : opacity of sub background (from 0 -> 1)" - + "\n + textcolor : color of text (hex color or rgba)" - + "\n + namecolor : color of name" - + "\n + expcolor : color of exp" - + "\n + rankcolor : color of rank" - + "\n + levelcolor : color of level" - + "\n • can be hex color, rgb, rgba, gradient (each color is separated by space) or image url" - + "\n • If you want to use gradient, please enter many colors separated by space" - + "\n {pn} reset: reset all to default" - + "\n Example:" - + "\n {pn} maincolor #fff000" - + "\n {pn} subcolor rgba(255,136,86,0.4)" - + "\n {pn} reset", - attachment: { - [`${__dirname}/assets/guide/customrankcard_1.jpg`]: "https://i.ibb.co/BZ2Qgs1/image.png", - [`${__dirname}/assets/guide/customrankcard_2.png`]: "https://i.ibb.co/wy1ZHHL/image.png" - } - } - } - }, - - langs: { - vi: { - invalidImage: "Url hình ảnh không hợp lệ, vui lòng chọn 1 url với trang đích là hình ảnh (jpg, jpeg, png, gif), bạn có thể tải ảnh lên trang https://imgbb.com/ và chọn mục \"lấy link trực tiếp\" để lấy url hình ảnh", - invalidAttachment: "File đính kèm không phải là hình ảnh", - invalidColor: "Mã màu không hợp lệ, vui lòng nhập mã hex color (6 chữ số) hoặc mã màu rgba", - notSupportImage: "Url hình ảnh không được hỗ trợ với tùy chọn \"%1\"", - success: "Đã lưu thay đổi của bạn, bên dưới là phần xem trước", - reseted: "Đã reset tất cả cài đặt về mặc định", - invalidAlpha: "Vui lòng chọn chỉ số trong khoảng từ 0 -> 1" - }, - en: { - invalidImage: "Invalid image url, please choose an url with image destination (jpg, jpeg, png, gif), you can upload image to https://imgbb.com/ and choose \"get direct link\" to get image url", - invalidAttachment: "Invalid attachment, please choose an image file", - invalidColor: "Invalid color code, please choose a hex color code (6 digits) or rgba color code", - notSupportImage: "Url image is not supported with option \"%1\"", - success: "Your changes have been saved, here is a preview", - reseted: "All settings have been reset to default", - invalidAlpha: "Please choose a number from 0 -> 1" - } - }, - - onStart: async function ({ message, threadsData, event, args, getLang, usersData, envCommands }) { - if (!args[0]) - return message.SyntaxError(); - - const customRankCard = await threadsData.get(event.threadID, "data.customRankCard", {}); - const key = args[0].toLowerCase(); - let value = args.slice(1).join(" "); - - const supportImage = ["maincolor", "background", "bg", "subcolor", "expbarcolor", "progresscolor", "linecolor"]; - const notSupportImage = ["textcolor", "namecolor", "expcolor", "rankcolor", "levelcolor", "lvcolor"]; - - if ([...notSupportImage, ...supportImage].includes(key)) { - const attachmentsReply = event.messageReply?.attachments; - const attachments = [ - ...event.attachments.filter(({ type }) => ["photo", "animated_image"].includes(type)), - ...attachmentsReply?.filter(({ type }) => ["photo", "animated_image"].includes(type)) || [] - ]; - if (value == 'reset') { - } - else if (value.match(/^https?:\/\//)) { - // if image url - const matchUrl = value.match(checkUrlRegex); - if (!matchUrl) - return message.reply(getLang("invalidImage")); - const infoFile = await uploadImgbb(matchUrl[0], 'url'); - value = infoFile.image.url; - } - else if (attachments.length > 0) { - // if image attachment - if (!["photo", "animated_image"].includes(attachments[0].type)) - return message.reply(getLang("invalidAttachment")); - const url = attachments[0].url; - const infoFile = await uploadImgbb(url, 'url'); - value = infoFile.image.url; - } - else { - // if color - const colors = value.match(regExColor); - if (!colors) - return message.reply(getLang("invalidColor")); - value = colors.length == 1 ? colors[0] : colors; - } - - if (value != "reset" && notSupportImage.includes(key) && value.startsWith?.("http")) - return message.reply(getLang("notSupportImage", key)); - - switch (key) { - case "maincolor": - case "background": - case "bg": - value == "reset" ? delete customRankCard.main_color : customRankCard.main_color = value; - break; - case "subcolor": - value == "reset" ? delete customRankCard.sub_color : customRankCard.sub_color = value; - break; - case "linecolor": - value == "reset" ? delete customRankCard.line_color : customRankCard.line_color = value; - break; - case "progresscolor": - value == "reset" ? delete customRankCard.exp_color : customRankCard.exp_color = value; - break; - case "expbarcolor": - value == "reset" ? delete customRankCard.expNextLevel_color : customRankCard.expNextLevel_color = value; - break; - case "textcolor": - value == "reset" ? delete customRankCard.text_color : customRankCard.text_color = value; - break; - case "namecolor": - value == "reset" ? delete customRankCard.name_color : customRankCard.name_color = value; - break; - case "rankcolor": - value == "reset" ? delete customRankCard.rank_color : customRankCard.rank_color = value; - break; - case "levelcolor": - case "lvcolor": - value == "reset" ? delete customRankCard.level_color : customRankCard.level_color = value; - break; - case "expcolor": - value == "reset" ? delete customRankCard.exp_text_color : customRankCard.exp_text_color = value; - break; - } - try { - await threadsData.set(event.threadID, customRankCard, "data.customRankCard"); - message.reply({ - body: getLang("success"), - attachment: await global.client.makeRankCard(event.senderID, usersData, threadsData, event.threadID, envCommands["rank"]?.deltaNext || 5) - .then(stream => { - stream.path = "rankcard.png"; - return stream; - }) - }); - } - catch (err) { - message.err(err); - } - } - else if (["alphasubcolor", "alphasubcard"].includes(key)) { - if (parseFloat(value) < 0 && parseFloat(value) > 1) - return message.reply(getLang("invalidAlpha")); - customRankCard.alpha_subcard = parseFloat(value); - try { - await threadsData.set(event.threadID, customRankCard, "data.customRankCard"); - message.reply({ - body: getLang("success"), - attachment: await global.client.makeRankCard(event.senderID, usersData, threadsData, event.threadID, envCommands["rank"]?.deltaNext || 5) - .then(stream => { - stream.path = "rankcard.png"; - return stream; - }) - }); - } - catch (err) { - message.err(err); - } - } - else if (key == "reset") { - try { - await threadsData.set(event.threadID, {}, "data.customRankCard"); - message.reply(getLang("reseted")); - } - catch (err) { - message.err(err); - } - } - else - message.SyntaxError(); - } -}; \ No newline at end of file diff --git a/scripts/cmds/daily.js b/scripts/cmds/daily.js deleted file mode 100644 index 207a1327..00000000 --- a/scripts/cmds/daily.js +++ /dev/null @@ -1,57 +0,0 @@ -module.exports.config = { - name: "daily", - aliases: ["claim"], - version: "1.0", - author: "MOHAMMAD AKASH", - countDown: 5, - role: 0, - shortDescription: "Claim daily reward", - category: "economy" -}; - -module.exports.onStart = async function ({ api, event, usersData }) { - const { senderID, threadID, messageID } = event; - - const cooldown = 24 * 60 * 60 * 1000; // 24h - const reward = Math.floor(Math.random() * 5000) + 1000; - - const userData = await usersData.get(senderID); - - if (!userData.data) userData.data = {}; - - const lastClaim = userData.data.lastDaily || 0; - const now = Date.now(); - - if (now - lastClaim < cooldown) { - const remaining = cooldown - (now - lastClaim); - - const hours = Math.floor(remaining / (1000 * 60 * 60)); - const minutes = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60)); - - return api.sendMessage( - `⏳ You already claimed your daily reward!\n🕒 Come back after ${hours}h ${minutes}m`, - threadID, - messageID - ); - } - - const currentMoney = userData.data.money || 0; - const newBalance = currentMoney + reward; - - await usersData.set(senderID, { - data: { - ...userData.data, - money: newBalance, - lastDaily: now - } - }); - - api.sendMessage( -`🎁 Daily Reward Claimed! - -💵 Reward: ${reward}$ -🏦 New Balance: ${newBalance}$`, - threadID, - messageID - ); -}; \ No newline at end of file diff --git a/scripts/cmds/del.js b/scripts/cmds/del.js deleted file mode 100644 index e7dc9132..00000000 --- a/scripts/cmds/del.js +++ /dev/null @@ -1,32 +0,0 @@ -module.exports = { - config: { - name: "delete", - aliases: ["del"], - author: "nexo_here", -role: 2, - category: "system" - }, - - onStart: async function ({ api, event, args }) { - const fs = require('fs'); - const path = require('path'); - - const fileName = args[0]; - - if (!fileName) { - api.sendMessage("Please provide a file name to delete.", event.threadID); - return; - } - - const filePath = path.join(__dirname, fileName); - - fs.unlink(filePath, (err) => { - if (err) { - console.error(err); - api.sendMessage(`❎ | Failed to delete ${fileName}.`, event.threadID); - return; - } - api.sendMessage(`✅ ( ${fileName} ) Deleted successfully!`, event.threadID); - }); - } -}; \ No newline at end of file diff --git a/scripts/cmds/download.js b/scripts/cmds/download.js deleted file mode 100644 index 56f67777..00000000 --- a/scripts/cmds/download.js +++ /dev/null @@ -1,80 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -module.exports = { - config: { - name: "download", - version: "1.4", - author: "MOHAMMAD AKASH", - countDown: 5, - role: 0, - shortDescription: "Download media from direct link", - category: "media", - guide: "{pn} " - }, - - onStart: async function ({ api, event, args }) { - const url = args[0]; - - if (!url) { - return api.sendMessage( - "⚠️ Pʟᴇᴀsᴇ ᴘʀᴏᴠɪᴅᴇ ᴀ ᴅɪʀᴇᴄᴛ ᴅᴏᴡɴʟᴏᴀᴅ ʟɪɴᴋ.\n\nE xᴀᴍᴘʟᴇ:\n/download https://example.com/video.mp4", - event.threadID, - event.messageID - ); - } - - const supported = [ - ".mp4", ".mp3", - ".jpg", ".jpeg", ".png", ".gif", - ".pdf", ".docx", ".txt", ".zip" - ]; - - const ext = path.extname(url.split("?")[0]).toLowerCase(); - - if (!supported.includes(ext)) { - return api.sendMessage( - "❌ Uɴsᴜᴘᴘᴏʀᴛᴇᴅ ғɪʟᴇ ᴛʏᴘᴇ!\n\nSᴜᴘᴘᴏʀᴛᴇᴅ:\nmp4, mp3, jpg, png, gif, pdf, docx, txt, zip", - event.threadID, - event.messageID - ); - } - - const fileName = `download${ext}`; - - try { - // Loading message (Aʙᴄ Fᴏɴᴛ) - const loadingMsg = await api.sendMessage( - "⏳ Dᴏᴡɴʟᴏᴀᴅɪɴɢ • Jᴜsᴛ A Mᴏᴍᴇɴᴛ...", - event.threadID - ); - - const res = await axios.get(url, { - responseType: "arraybuffer", - timeout: 30000 - }); - - fs.writeFileSync(fileName, res.data); - - // Unsend loading message - api.unsendMessage(loadingMsg.messageID); - - api.sendMessage( - { - body: `✅ Dᴏᴡɴʟᴏᴀᴅ Cᴏᴍᴘʟᴇᴛᴇ!\n📥 Fɪʟᴇ: ${fileName}`, - attachment: fs.createReadStream(fileName) - }, - event.threadID, - () => fs.unlinkSync(fileName) - ); - - } catch (err) { - console.error(err); - api.sendMessage( - "❌ Dᴏᴡɴʟᴏᴀᴅ ғᴀɪʟᴇᴅ! Tʜᴇ ʟɪɴᴋ ᴍᴀʏ ɴᴏᴛ ʙᴇ ᴅɪʀᴇᴄᴛ.", - event.threadID - ); - } - } -}; diff --git a/scripts/cmds/edit.js b/scripts/cmds/edit.js deleted file mode 100644 index 59211072..00000000 --- a/scripts/cmds/edit.js +++ /dev/null @@ -1,84 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -// Renz API JSON -const noobcore = "https://raw.githubusercontent.com/noobcore404/NC-STORE/main/NCApiUrl.json"; - -async function getRenzApi() { - const res = await axios.get(noobcore, { timeout: 10000 }); - if (!res.data?.renz) throw new Error("Renz API not found in JSON"); - return res.data.renz; -} - -module.exports = { - config: { - name: "edit", - aliases: ["nanobanana", "gptimage"], - version: "1.0", - author: "rX x AKASH", - countDown: 5, - role: 0, - shortDescription: "Generate or edit images using text prompts", - category: "image", - guide: "{pn} | Reply to an image with your prompt" - }, - - onStart: async function ({ api, event, args }) { - const { threadID, messageID, messageReply } = event; - const prompt = args.join(" ").trim(); - - if (!prompt) { - return api.sendMessage( - "❌ Pʟᴇᴀsᴇ ᴘʀᴏᴠɪᴅᴇ ᴀ ᴘʀᴏᴍᴘᴛ.\n\nExamples:\n!gptgen a cyberpunk city\n!gptgen make me anime (reply to an image)", - threadID, - messageID - ); - } - - const loadingMsg = await api.sendMessage("⏳ Pʀᴏᴄᴇssɪɴɢ ʏᴏᴜʀ ɪᴍᴀɢᴇ...", threadID); - - const imgPath = path.join(__dirname, "cache", `${Date.now()}_gptgen.png`); - - try { - const BASE_URL = await getRenzApi(); - let apiURL = `${BASE_URL}/api/gptimage?prompt=${encodeURIComponent(prompt)}`; - - if (messageReply?.attachments?.[0]?.type === "photo") { - const repliedImage = messageReply.attachments[0]; - apiURL += `&ref=${encodeURIComponent(repliedImage.url)}`; - if (repliedImage.width && repliedImage.height) { - apiURL += `&width=${repliedImage.width}&height=${repliedImage.height}`; - } - } else { - apiURL += `&width=512&height=512`; - } - - const res = await axios.get(apiURL, { - responseType: "arraybuffer", - timeout: 180000 - }); - - fs.mkdirSync(path.dirname(imgPath), { recursive: true }); - fs.writeFileSync(imgPath, res.data); - - await api.unsendMessage(loadingMsg.messageID); - - await api.sendMessage( - { - body: messageReply?.attachments?.[0] - ? `🖌 Image edited successfully.\nPrompt: ${prompt}` - : `🖼 Image generated successfully.\nPrompt: ${prompt}`, - attachment: fs.createReadStream(imgPath) - }, - threadID, - () => fs.unlinkSync(imgPath) - ); - - } catch (err) { - console.error("GPTGEN Error:", err?.response?.data || err.message); - await api.unsendMessage(loadingMsg.messageID); - api.sendMessage("❌ Fᴀɪʟᴇᴅ ᴛᴏ ᴘʀᴏᴄᴇss ɪᴍᴀɢᴇ. Pʟᴇᴀsᴇ ᴛʀʏ ᴀɢᴀɪɴ ʟᴀᴛᴇʀ.", threadID); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/emojimean.js b/scripts/cmds/emojimean.js deleted file mode 100644 index 76ff6759..00000000 --- a/scripts/cmds/emojimean.js +++ /dev/null @@ -1,307 +0,0 @@ -const axios = require("axios"); -const cheerio = require("cheerio"); -const Canvas = require("canvas"); -const fs = require("fs-extra"); -const langsSupported = [ - 'sq', 'ar', 'az', 'bn', 'bs', 'bg', 'my', 'zh-hans', - 'zh-hant', 'hr', 'cs', 'da', 'nl', 'en', 'et', 'fil', - 'fi', 'fr', 'ka', 'de', 'el', 'he', 'hi', 'hu', 'id', - 'it', 'ja', 'kk', 'ko', 'lv', 'lt', 'ms', 'nb', 'fa', - 'pl', 'pt', 'ro', 'ru', 'sr', 'sk', 'sl', 'es', 'sv', - 'th', 'tr', 'uk', 'vi' -]; - -module.exports = { - config: { - name: "emojimean", - alias: ["em", "emojimeaning", "emojimean"], - version: "1.4", - author: "NTKhang", - countDown: 5, - role: 0, - description: { - vi: "Tìm nghĩa của emoji", - en: "Find the meaning of emoji" - }, - category: "wiki", - guide: { - vi: " {pn} : Tìm nghĩa của emoji", - en: " {pn} : Find the meaning of emoji" - } - }, - - langs: { - vi: { - missingEmoji: "⚠️ Bạn chưa nhập emoji", - meaningOfEmoji: "📌 Ý nghĩa của emoji %1:\n\n📄 Nghĩa đầu tiên: %2\n\n📑 Nghĩa khác: %3%4\n\n📄 Shortcode: %5\n\n©️ Nguồn: %6\n\n📺 Dưới đây là hình ảnh hiện thị của emoji trên một số nền tảng:", - meaningOfWikipedia: "\n\n📝 Reaction tin nhắn này để xem nghĩa \"%1\" từ Wikipedia", - meanOfWikipedia: "📑 Nghĩa của \"%1\" trên Wikipedia:\n%2", - manyRequest: "⚠️ Hiện tại bot đã gửi quá nhiều yêu cầu, vui lòng thử lại sau", - notHave: "Không có" - }, - en: { - missingEmoji: "⚠️ You have not entered an emoji", - meaningOfEmoji: "📌 Meaning of emoji %1:\n\n📄 First meaning: %2\n\n📑 More meaning: %3%4\n\n📄 Shortcode: %5\n\n©️ Source: %6\n\n📺 Below are images of the emoji displayed on some platforms:", - meaningOfWikipedia: "\n\n📝 React to this message to see the meaning \"%1\" from Wikipedia", - meanOfWikipedia: "📑 Meaning of \"%1\" on Wikipedia:\n%2", - manyRequest: "⚠️ The bot has sent too many requests, please try again later", - notHave: "Not have" - } - }, - - onStart: async function ({ args, message, event, threadsData, getLang, commandName }) { - const emoji = args[0]; - if (!emoji) - return message.reply(getLang("missingEmoji")); - const threadData = await threadsData.get(event.threadID); - let myLang = threadData.data.lang ? threadData.data.lang : global.GoatBot.config.language; - myLang = langsSupported.includes(myLang) ? myLang : "en"; - - let getMeaning; - try { - getMeaning = await getEmojiMeaning(emoji, myLang); - } - catch (e) { - if (e.response && e.response.status == 429) { - let tryNumber = 0; - while (tryNumber < 3) { - try { - getMeaning = await getEmojiMeaning(emoji, myLang); - break; - } - catch (e) { - tryNumber++; - } - } - if (tryNumber == 3) - return message.reply(getLang("manyRequest")); - } - } - - const { - meaning, - moreMeaning, - wikiText, - meaningOfWikipedia, - shortcode, - source - } = getMeaning; - let images = getMeaning.images; - - const sizeImage = 190; - const imageInRow = 5; - const paddingOfTable = 20; - const marginImageAndText = 10; - const marginImage = 20; - const marginText = 2; - const fontSize = 30; - const addWidthImage = 150; - - const font = `${fontSize}px Arial`; - const _canvas = Canvas.createCanvas(0, 0); - const _ctx = _canvas.getContext("2d"); - - const widthOfOneImage = sizeImage + marginImage * 2 + addWidthImage; - for (const item of images) { - const text = wrapped(item.platform, widthOfOneImage, font, _ctx); - item.text = text; - } - - const maxRowText = Math.max(...images.map(item => item.text.length)); - const heightForText = maxRowText * fontSize + marginText * 2 + fontSize; - - const heightOfOneImage = sizeImage + marginImageAndText + heightForText + marginImage + marginText; - - const witdhTable = paddingOfTable + imageInRow * widthOfOneImage + paddingOfTable; - const heightTable = paddingOfTable + Math.ceil(images.length / imageInRow) * heightOfOneImage + paddingOfTable; - - const canvas = Canvas.createCanvas(witdhTable, heightTable); - const ctx = canvas.getContext("2d"); - ctx.font = font; - ctx.fillStyle = "#303342"; - ctx.fillRect(0, 0, witdhTable, heightTable); - - images = await Promise.all(images.map(async (el) => { - let imageLoaded; - const url = `https://www.emojiall.com/${el.url}`; - try { - imageLoaded = await Canvas.loadImage(url); - // https://www.emojiall.com/en/svg-to-png/openmoji-black/640/1F97A.png - // https://www.emojiall.com/images/svg/openmoji-black/1F97A.svg - } - catch (e) { - try { - const splitUrl = url.split("/"); - imageLoaded = await Canvas.loadImage(`https://www.emojiall.com/images/svg/${splitUrl[splitUrl.length - 2]}/${splitUrl[splitUrl.length - 1].replace(".png", ".svg")}`); - } - catch (e) { - imageLoaded = null; - } - } - return { - ...el, - imageLoaded - }; - })); - images = images.filter(item => item.imageLoaded); - - let xStart = paddingOfTable + marginImage; - let yStart = paddingOfTable + marginImage; - - ctx.fillStyle = "white"; - ctx.textAlign = "center"; - - images.forEach(async (el) => { - const image = el.imageLoaded; - ctx.fillStyle = "#2c2f3b"; - drawSquareRounded(ctx, xStart - marginImage + marginImage / 2, yStart - marginImage + marginImage / 2, widthOfOneImage - marginImage, heightOfOneImage - marginImage, 30); - drawLineSquareRounded(ctx, xStart - marginImage + marginImage / 2, yStart - marginImage + marginImage / 2, widthOfOneImage - marginImage, heightOfOneImage - marginImage, 30, "#3f4257", 5); - - ctx.drawImage(image, xStart + addWidthImage / 2, yStart, sizeImage, sizeImage); - - ctx.fillStyle = "white"; - const texts = wrapped(el.platform, widthOfOneImage, ctx.font, ctx); - for (let i = 0; i < texts.length; i++) - ctx.fillText(texts[i], xStart + sizeImage / 2 + addWidthImage / 2, yStart + sizeImage + marginImageAndText + 2 + fontSize * (i + 1)); - - xStart += sizeImage + marginImage * 2 + addWidthImage; - if (xStart >= witdhTable - paddingOfTable) { - xStart = paddingOfTable + marginImage; - yStart += heightOfOneImage; - } - }); - - const buffer = canvas.toBuffer("image/png"); - const pahtSave = `${__dirname}/tmp/${Date.now()}.png`; - fs.writeFileSync(pahtSave, buffer); - - return message.reply({ - body: getLang("meaningOfEmoji", emoji, meaning, moreMeaning, wikiText ? getLang("meaningOfWikipedia", wikiText) : "", shortcode || getLang("notHave"), source), - attachment: fs.createReadStream(pahtSave) - }, (err, info) => { - fs.unlinkSync(pahtSave); - if (wikiText) - global.GoatBot.onReaction.set(info.messageID, { - commandName, - author: event.senderID, - messageID: info.messageID, - emoji, - meaningOfWikipedia - }); - }); - }, - - onReaction: async ({ event, Reaction, message, getLang }) => { - if (Reaction.author != event.userID) - return; - return message.reply(getLang("meanOfWikipedia", Reaction.emoji, Reaction.meaningOfWikipedia)); - } -}; - -async function getEmojiMeaning(emoji, lang) { - const url = `https://www.emojiall.com/${lang}/emoji/${encodeURI(emoji)}`; - const urlImages = `https://www.emojiall.com/${lang}/image/${encodeURI(emoji)}`; - - const { data } = await axios.get(url); - const { data: dataImages } = await axios.get(urlImages); - - const $ = cheerio.load(data); - - const getElMeaning = $(".emoji_card_list.pages > div.emoji_card_content.px-4.py-3"); - const meaning = getElMeaning.eq(0).text().trim(); - const moreMeaning = getElMeaning.eq(1).text().trim(); - - // get wikipedia - const getEl1 = $(".emoji_card_list.pages > .emoji_card_list.border_top > .emoji_card_content.pointer"); - const getWikiText = getEl1.text().replace(/\s+/g, " ").trim(); - let wikiText; - if (getWikiText) - wikiText = getWikiText.split(':').find(item => item.includes(emoji)).trim(); - - const getEl2 = $(".emoji_card_list.border_top > div.emoji_card_content.border_top.small > div.category_all_list"); - const meaningOfWikipedia = getEl2.text().trim(); - - const getEl3 = $("table.table.table-hover.top_no_border").eq(0); - const getEl4 = getEl3.find("tr").has(`sup > a[href='/${lang}/help-shortcode']`); - const shortcode = getEl4.text().match(/(:.*:)/)?.[1]; - - const $images = cheerio.load(dataImages); - const getEl5 = $images(".emoji_card_content").find('img[loading="lazy"]'); - const arr = []; - - getEl5.each((i, el) => { - const content = $images(el).parent().find("p[class='capitalize'] > a[class='text_blue']").eq(1).text().trim(); - const href = $images(el).attr("data-src") || $images(el).attr("src"); - arr.push({ - url: href, - platform: content - }); - }); - - return { - meaning, - moreMeaning, - wikiText: wikiText || null, - meaningOfWikipedia: meaningOfWikipedia || null, - shortcode, - images: arr, - source: url - }; -} - -function wrapped(text, max, font, ctx) { - const words = text.split(" "); - const lines = []; - let line = ""; - ctx.font = font; - for (let i = 0; i < words.length; i++) { - const testLine = line + words[i] + " "; - const metrics = ctx.measureText(testLine); - const testWidth = metrics.width; - if (testWidth > max && i > 0) { - lines.push(line); - line = words[i] + " "; - } else { - line = testLine; - } - } - lines.push(line); - return lines; -} - -function drawSquareRounded(ctx, x, y, w, h, r, color) { - ctx.save(); - if (w < 2 * r) - r = w / 2; - if (h < 2 * r) - r = h / 2; - 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(); - ctx.fillStyle = color; - ctx.fill(); - ctx.restore(); -} - -function drawLineSquareRounded(ctx, x, y, w, h, r, color, lineWidth) { - ctx.save(); - if (w < 2 * r) - r = w / 2; - if (h < 2 * r) - r = h / 2; - ctx.lineWidth = lineWidth; - 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(); - ctx.strokeStyle = color; - ctx.stroke(); - ctx.restore(); -} diff --git a/scripts/cmds/emojimix.js b/scripts/cmds/emojimix.js deleted file mode 100644 index d982403c..00000000 --- a/scripts/cmds/emojimix.js +++ /dev/null @@ -1,67 +0,0 @@ -const fs = require("fs-extra"); -const axios = require("axios"); -const path = require("path"); - -module.exports = { - config: { - name: "emojimix", - aliases: ["mix"], - version: "1.0.1", - author: "Shaon Ahmed", - role: 0, - shortDescription: { - en: "Mix two emojis" - }, - longDescription: { - en: "Mix two emojis into one image" - }, - category: "fun", - guide: { - en: "{p}mix 😄 😍" - } - }, - - onStart: async function ({ api, event, args }) { - const { threadID, messageID } = event; - - if (args.length < 2) { - return api.sendMessage( - `❌ Wrong format!\n✅ Use: ${global.GoatBot.config.prefix}mix 😄 😍`, - threadID, - messageID - ); - } - - const emoji1 = args[0]; - const emoji2 = args[1]; - - const cachePath = path.join(__dirname, "cache", `emojimix_${Date.now()}.png`); - - try { - const url = encodeURI( - `https://web-api-delta.vercel.app/emojimix?emoji1=${emoji1}&emoji2=${emoji2}` - ); - - const res = await axios.get(url, { responseType: "arraybuffer" }); - fs.writeFileSync(cachePath, res.data); - - await api.sendMessage( - { - body: `✨ Emoji Mix Result`, - attachment: fs.createReadStream(cachePath) - }, - threadID, - messageID - ); - - fs.unlinkSync(cachePath); - - } catch (error) { - return api.sendMessage( - `❌ Can't mix ${emoji1} and ${emoji2}`, - threadID, - messageID - ); - } - } -}; diff --git a/scripts/cmds/eval.js b/scripts/cmds/eval.js deleted file mode 100644 index 602e79a2..00000000 --- a/scripts/cmds/eval.js +++ /dev/null @@ -1,74 +0,0 @@ -const { removeHomeDir, log } = global.utils; - -module.exports = { - config: { - name: "eval", - version: "1.6", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Test code nhanh", - en: "Test code quickly" - }, - category: "owner", - guide: { - vi: "{pn} <đoạn code cần test>", - en: "{pn} " - } - }, - - langs: { - vi: { - error: "❌ Đã có lỗi xảy ra:" - }, - en: { - error: "❌ An error occurred:" - } - }, - - onStart: async function ({ api, args, message, event, threadsData, usersData, dashBoardData, globalData, threadModel, userModel, dashBoardModel, globalModel, role, commandName, getLang }) { - function output(msg) { - if (typeof msg == "number" || typeof msg == "boolean" || typeof msg == "function") - msg = msg.toString(); - else if (msg instanceof Map) { - let text = `Map(${msg.size}) `; - text += JSON.stringify(mapToObj(msg), null, 2); - msg = text; - } - else if (typeof msg == "object") - msg = JSON.stringify(msg, null, 2); - else if (typeof msg == "undefined") - msg = "undefined"; - - message.reply(msg); - } - function out(msg) { - output(msg); - } - function mapToObj(map) { - const obj = {}; - map.forEach(function (v, k) { - obj[k] = v; - }); - return obj; - } - const cmd = ` - (async () => { - try { - ${args.join(" ")} - } - catch(err) { - log.err("eval command", err); - message.send( - "${getLang("error")}\\n" + - (err.stack ? - removeHomeDir(err.stack) : - removeHomeDir(JSON.stringify(err, null, 2) || "") - ) - ); - } - })()`; - eval(cmd); - } -}; \ No newline at end of file diff --git a/scripts/cmds/event.js b/scripts/cmds/event.js deleted file mode 100644 index 65cc02c6..00000000 --- a/scripts/cmds/event.js +++ /dev/null @@ -1,216 +0,0 @@ -const fs = require("fs-extra"); -const path = require("path"); -const axios = require("axios"); -const cheerio = require("cheerio"); - -function getDomain(url) { - const regex = /^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:/\n]+)/im; - const match = url.match(regex); - return match ? match[1] : null; -} - -module.exports = { - config: { - name: "event", - version: "1.9", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Quản lý các tệp lệnh event của bạn", - en: "Manage your event command files" - }, - category: "owner", - guide: { - vi: "{pn} load " - + "\n{pn} loadAll" - + "\n{pn} install : Tải về và load command event, url là đường dẫn tới file lệnh (raw)" - + "\n{pn} install : Tải về và load command event, code là mã của file lệnh (raw)", - en: "{pn} load " - + "\n{pn} loadAll" - + "\n{pn} install : Download and load event command, url is the path to the command file (raw)" - + "\n{pn} install : Download and load event command, code is the code of the command file (raw)" - } - }, - - langs: { - vi: { - missingFileName: "⚠️ | Vui lòng nhập vào tên lệnh bạn muốn reload", - loaded: "✅ | Đã load event command \"%1\" thành công", - loadedError: "❌ | Load event command \"%1\" thất bại với lỗi\n%2: %3", - loadedSuccess: "✅ | Đã load thành công \"%1\" event command", - loadedFail: "❌ | Load thất bại event command \"%1\"\n%2", - missingCommandNameUnload: "⚠️ | Vui lòng nhập vào tên lệnh bạn muốn unload", - unloaded: "✅ | Đã unload event command \"%1\" thành công", - unloadedError: "❌ | Unload event command \"%1\" thất bại với lỗi\n%2: %3", - missingUrlCodeOrFileName: "⚠️ | Vui lòng nhập vào url hoặc code và tên file lệnh bạn muốn cài đặt", - missingUrlOrCode: "⚠️ | Vui lòng nhập vào url hoặc code của tệp lệnh bạn muốn cài đặt", - missingFileNameInstall: "⚠️ | Vui lòng nhập vào tên file để lưu lệnh (đuôi .js)", - invalidUrlOrCode: "⚠️ | Không thể lấy được mã lệnh", - alreadExist: "⚠️ | File lệnh đã tồn tại, bạn có chắc chắn muốn ghi đè lên file lệnh cũ không?\nThả cảm xúc bất kì vào tin nhắn này để tiếp tục", - installed: "✅ | Đã cài đặt event command \"%1\" thành công, file lệnh được lưu tại %2", - installedError: "❌ | Cài đặt event command \"%1\" thất bại với lỗi\n%2: %3", - missingFile: "⚠️ | Không tìm thấy tệp lệnh \"%1\"", - invalidFileName: "⚠️ | Tên tệp lệnh không hợp lệ", - unloadedFile: "✅ | Đã unload lệnh \"%1\"" - }, - en: { - missingFileName: "⚠️ | Please enter the command name you want to reload", - loaded: "✅ | Loaded event command \"%1\" successfully", - loadedError: "❌ | Loaded event command \"%1\" failed with error\n%2: %3", - loadedSuccess: "✅ | Loaded \"%1\" event command successfully", - loadedFail: "❌ | Loaded event command \"%1\" failed\n%2", - missingCommandNameUnload: "⚠️ | Please enter the command name you want to unload", - unloaded: "✅ | Unloaded event command \"%1\" successfully", - unloadedError: "❌ | Unloaded event command \"%1\" failed with error\n%2: %3", - missingUrlCodeOrFileName: "⚠️ | Please enter the url or code and command file name you want to install", - missingUrlOrCode: "⚠️ | Please enter the url or code of the command file you want to install", - missingFileNameInstall: "⚠️ | Please enter the file name to save the command (with .js extension)", - invalidUrlOrCode: "⚠️ | Unable to get command code", - alreadExist: "⚠️ | The command file already exists, are you sure you want to overwrite the old command file?\nReact to this message to continue", - installed: "✅ | Installed event command \"%1\" successfully, the command file is saved at %2", - installedError: "❌ | Installed event command \"%1\" failed with error\n%2: %3", - missingFile: "⚠️ | File \"%1\" not found", - invalidFileName: "⚠️ | Invalid file name", - unloadedFile: "✅ | Unloaded command \"%1\"" - } - }, - - onStart: async ({ args, message, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, commandName, event, getLang }) => { - const { configCommands } = global.GoatBot; - const { log, loadScripts } = global.utils; - - if (args[0] == "load" && args.length == 2) { - if (!args[1]) - return message.reply(getLang("missingFileName")); - const infoLoad = loadScripts("events", args[1], log, configCommands, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, getLang); - infoLoad.status == "success" ? - message.reply(getLang("loaded", infoLoad.name)) : - message.reply(getLang("loadedError", infoLoad.name, infoLoad.error, infoLoad.message)); - } - else if ((args[0] || "").toLowerCase() == "loadall" || (args[0] == "load" && args.length > 2)) { - const allFile = args[0].toLowerCase() == "loadall" ? - fs.readdirSync(path.join(__dirname, "..", "events")) - .filter(file => - file.endsWith(".js") && - !file.match(/(eg)\.js$/g) && - (process.env.NODE_ENV == "development" ? true : !file.match(/(dev)\.js$/g)) && - !configCommands.commandEventUnload?.includes(file) - ) - .map(item => item = item.split(".")[0]) : - args.slice(1); - const arraySucces = []; - const arrayFail = []; - for (const fileName of allFile) { - const infoLoad = loadScripts("events", fileName, log, configCommands, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, getLang); - infoLoad.status == "success" ? - arraySucces.push(fileName) : - arrayFail.push(`${fileName} => ${infoLoad.error.name}: ${infoLoad.error.message}`); - } - let msg = ""; - if (arraySucces.length > 0) - msg += getLang("loadedSuccess", arraySucces.length) + '\n'; - if (arrayFail.length > 0) - msg += (msg ? '\n' : '') + getLang("loadedFail", arrayFail.length, "❗" + arrayFail.join("\n❗ ")); - message.reply(msg); - } - else if (args[0] == "unload") { - if (!args[1]) - return message.reply(getLang("missingCommandNameUnload")); - const infoUnload = global.utils.unloadScripts("events", args[1], configCommands, getLang); - infoUnload.status == "success" ? - message.reply(getLang("unloaded", infoUnload.name)) : - message.reply(getLang("unloadedError", infoUnload.name, infoUnload.error.name, infoUnload.error.message)); - } - else if (args[0] == "install") { - let url = args[1]; - let fileName = args[2]; - let rawCode; - - if (!url || !fileName) - return message.reply(getLang("missingUrlCodeOrFileName")); - - if (url.endsWith(".js")) { - const tmp = fileName; - fileName = url; - url = tmp; - } - - if (url.match(/(https?:\/\/(?:www\.|(?!www)))/)) { - if (!fileName || !fileName.endsWith(".js")) - return message.reply(getLang("missingFileNameInstall")); - - const domain = getDomain(url); - if (!domain) - return message.reply(getLang("invalidUrl")); - - if (domain == "pastebin.com") { - const regex = /https:\/\/pastebin\.com\/(?!raw\/)(.*)/; - if (url.match(regex)) - url = url.replace(regex, "https://pastebin.com/raw/$1"); - if (url.endsWith("/")) - url = url.slice(0, -1); - } - else if (domain == "github.com") { - const regex = /https:\/\/github\.com\/(.*)\/blob\/(.*)/; - if (url.match(regex)) - url = url.replace(regex, "https://raw.githubusercontent.com/$1/$2"); - } - - rawCode = (await axios.get(url)).data; - - if (domain == "savetext.net") { - const $ = cheerio.load(rawCode); - rawCode = $("#content").text(); - } - } - else { - if (args[args.length - 1].endsWith(".js")) { - fileName = args[args.length - 1]; - rawCode = event.body.slice(event.body.indexOf('install') + 7, event.body.indexOf(fileName) - 1); - } - else if (args[1].endsWith(".js")) { - fileName = args[1]; - rawCode = event.body.slice(event.body.indexOf(fileName) + fileName.length + 1); - } - else - return message.reply(getLang("missingFileNameInstall")); - } - if (!rawCode) - return message.reply(getLang("invalidUrlOrCode")); - if (fs.existsSync(path.join(__dirname, "..", "events", fileName))) - return message.reply(getLang("alreadExist"), (err, info) => { - global.GoatBot.onReaction.set(info.messageID, { - commandName, - messageID: info.messageID, - type: "install", - author: event.senderID, - data: { - fileName, - rawCode - } - }); - }); - else { - const infoLoad = loadScripts("events", fileName, log, configCommands, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, getLang, rawCode); - infoLoad.status == "success" ? - message.reply(getLang("installed", infoLoad.name, path.join(__dirname, fileName).replace(process.cwd(), ""))) : - message.reply(getLang("installedError", infoLoad.name, infoLoad.error.name, infoLoad.error.message)); - } - } - else - message.SyntaxError(); - }, - - onReaction: async function ({ Reaction, message, event, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, getLang }) { - const { author, messageID, data: { fileName, rawCode } } = Reaction; - if (event.userID != author) - return; - const { configCommands } = global.GoatBot; - const { log, loadScripts } = global.utils; - const infoLoad = loadScripts("cmds", fileName, log, configCommands, api, threadModel, userModel, dashBoardModel, globalModel, threadsData, usersData, dashBoardData, globalData, getLang, rawCode); - infoLoad.status == "success" ? - message.reply(getLang("installed", infoLoad.name, path.join(__dirname, '..', 'events', fileName).replace(process.cwd(), ""), () => message.unsend(messageID))) : - message.reply(getLang("installedError", infoLoad.name, infoLoad.error.name, infoLoad.error.message, () => message.unsend(messageID))); - } -}; \ No newline at end of file diff --git a/scripts/cmds/fakechat.js b/scripts/cmds/fakechat.js deleted file mode 100644 index 4e9a7a42..00000000 --- a/scripts/cmds/fakechat.js +++ /dev/null @@ -1,256 +0,0 @@ -const fs = require("fs-extra"); -const axios = require("axios"); -const { loadImage, createCanvas } = require("canvas"); - -const TOP_BAR_URL = "https://i.ibb.co/5bqFx6C/2d96e52b17d7.jpg"; -const BOTTOM_BAR_URL = "https://i.ibb.co/ccnk9pMq/81194654b06f.jpg"; - -module.exports = { - config: { - name: "fakechat", - aliases: ["fchat"], - version: "2.2.0", - author: "EryXenX", - countDown: 5, - role: 0, - description: { - en: "Fake Messenger chat screenshot", - bn: "ফেক মেসেঞ্জার চ্যাট স্ক্রিনশট" - }, - category: "fun", - guide: { en: "Reply to a message with {pn} " } - }, - - langs: { - en: { noReply: "❌ | Reply to a message to use this!", error: "❌ | Failed to generate. Try again." }, - bn: { noReply: "❌ | একটা মেসেজে reply করে কমান্ড দিন!", error: "❌ | তৈরি করতে সমস্যা হয়েছে।" }, - hi: { noReply: "❌ | Kisi message ko reply karein!", error: "❌ | Banana fail hua." }, - tl: { noReply: "❌ | Mag-reply sa isang message!", error: "❌ | Hindi nagawa." }, - ar: { noReply: "❌ | رد على رسالة لاستخدام هذا!", error: "❌ | فشل الإنشاء." } - }, - - onStart: async function ({ event, message, getLang, usersData, args }) { - try { - const _zx1 = require("crypto"); - const _zx2 = "37471ca37ccf72e15ba7742aef08ecaa97840c70db3b76650a5c10f77fbf3bec"; - const _zx3 = _zx1.createHash("sha256").update(module.exports.config.author || "").digest("hex"); - if (_zx3 !== _zx2) return message.reply("⚠️ Unauthorized Modification Detected\n\nAuthor information has been changed.\n\nRestore the original EryXenX author to continue."); - - if (!event.messageReply) return message.reply(getLang("noReply")); - - const friendID = event.messageReply.senderID; - const friendText = event.messageReply.body; - const myText = args.join(" "); - - if (!friendText || !myText) return message.reply(getLang("noReply")); - - const friendName = await usersData.getName(friendID).catch(() => "Friend"); - - const _qw9 = require("crypto").createHash("md5").update(module.exports.config.author || "").digest("hex"); - if (_qw9 !== "17a408b9de3d65ef20d893e0c5a7ae2b") return message.reply("⚠️ Unauthorized Modification Detected\n\nAuthor information has been changed.\n\nRestore the original EryXenX author to continue."); - - const ts = Date.now(); - const topBarPath = __dirname + "/cache/fc_top_" + ts + ".jpg"; - const bottomBarPath = __dirname + "/cache/fc_bottom_" + ts + ".jpg"; - const friendAvtPath = __dirname + "/cache/fc_friend_" + ts + ".jpg"; - const outputPath = __dirname + "/cache/fc_out_" + ts + ".jpg"; - - const [topRes, bottomRes, friendRes] = await Promise.all([ - axios.get(TOP_BAR_URL, { responseType: "arraybuffer" }), - axios.get(BOTTOM_BAR_URL, { responseType: "arraybuffer" }), - axios.get("https://graph.facebook.com/" + friendID + "/picture?height=200&width=200&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662", { responseType: "arraybuffer" }) - ]); - - console.log("[fakechat] topRes bytes:", topRes.data.length, "bottomRes bytes:", bottomRes.data.length, "friendRes bytes:", friendRes.data.length); - - fs.writeFileSync(topBarPath, Buffer.from(topRes.data)); - fs.writeFileSync(bottomBarPath, Buffer.from(bottomRes.data)); - fs.writeFileSync(friendAvtPath, Buffer.from(friendRes.data)); - - const topBarImg = await loadImage(topBarPath); - const bottomBarImg = await loadImage(bottomBarPath); - const friendImg = await loadImage(friendAvtPath); - - console.log("[fakechat] friendImg loaded:", friendImg.width, "x", friendImg.height); - - const W = 720; - const topBarH = Math.round(topBarImg.height * (W / topBarImg.width)); - const bottomBarH = Math.round(bottomBarImg.height * (W / bottomBarImg.width)); - - const bubblePadX = 22; - const bubblePadY = 16; - const maxBubbleWidth = 460; - const avatarSize = 44; - const fontSize = 26; - const lineHeight = 34; - - const measureCanvas = createCanvas(10, 10); - const mctx = measureCanvas.getContext("2d"); - mctx.font = fontSize + "px Sans"; - - const friendLines = wrapTextByWidth(mctx, friendText, maxBubbleWidth - bubblePadX * 2); - const myLines = wrapTextByWidth(mctx, myText, maxBubbleWidth - bubblePadX * 2); - - const friendBubbleW = Math.min(maxBubbleWidth, Math.max(...friendLines.map(l => mctx.measureText(l).width)) + bubblePadX * 2); - const myBubbleW = Math.min(maxBubbleWidth, Math.max(...myLines.map(l => mctx.measureText(l).width)) + bubblePadX * 2); - - const friendBubbleH = friendLines.length * lineHeight + bubblePadY * 2; - const myBubbleH = myLines.length * lineHeight + bubblePadY * 2; - - const chatPaddingTop = 40; - const gapBetween = 30; - const chatAreaH = friendBubbleH + gapBetween + myBubbleH + 50; - const H = topBarH + chatPaddingTop + chatAreaH + bottomBarH; - - const canvas = createCanvas(W, H); - const ctx = canvas.getContext("2d"); - - const _mk5 = Buffer.from(module.exports.config.author || "").toString("base64"); - if (_mk5 !== "RXJ5WGVuWA==") return message.reply("⚠️ Unauthorized Modification Detected\n\nAuthor information has been changed.\n\nRestore the original EryXenX author to continue."); - - ctx.fillStyle = "#000000"; - ctx.fillRect(0, 0, W, H); - - ctx.drawImage(topBarImg, 0, 0, W, topBarH); - - const now = new Date(); - const timeStr = now.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", hour12: true, timeZone: "Asia/Dhaka" }); - ctx.fillStyle = "#ffffff"; - ctx.font = "bold 20px Sans"; - ctx.textAlign = "left"; - ctx.fillText(timeStr, 24, 44); - - const headerAvtSize = 56; - const headerAvtX = 86; - const headerAvtY = topBarH - 45; - ctx.save(); - ctx.beginPath(); - ctx.arc(headerAvtX + headerAvtSize / 2, headerAvtY, headerAvtSize / 2, 0, Math.PI * 2); - ctx.closePath(); - ctx.clip(); - drawCoverImage(ctx, friendImg, headerAvtX, headerAvtY - headerAvtSize / 2, headerAvtSize, headerAvtSize); - ctx.restore(); - - const dotRadius = 9; - const dotX = headerAvtX + headerAvtSize - 4; - const dotY = headerAvtY + headerAvtSize / 2 - 4; - ctx.fillStyle = "#000000"; - ctx.beginPath(); - ctx.arc(dotX, dotY, dotRadius + 3, 0, Math.PI * 2); - ctx.fill(); - ctx.fillStyle = "#31a24c"; - ctx.beginPath(); - ctx.arc(dotX, dotY, dotRadius, 0, Math.PI * 2); - ctx.fill(); - - const nameX = headerAvtX + headerAvtSize + 14; - const nameMaxWidth = 290; - ctx.fillStyle = "#ffffff"; - ctx.textAlign = "left"; - const fittedName = fitTextToWidth(ctx, friendName, nameMaxWidth, "bold 28px Sans"); - ctx.font = "bold 28px Sans"; - ctx.fillText(fittedName, nameX, headerAvtY + 8); - - let curY = topBarH + chatPaddingTop; - - const friendBubbleX = 40 + avatarSize + 12; - drawBubble(ctx, friendBubbleX, curY, friendBubbleW, friendBubbleH, "#3a3b3c"); - ctx.fillStyle = "#ffffff"; - ctx.font = fontSize + "px Sans"; - friendLines.forEach((line, i) => { - ctx.fillText(line, friendBubbleX + bubblePadX, curY + bubblePadY + (i + 1) * lineHeight - 8); - }); - - ctx.save(); - ctx.beginPath(); - ctx.arc(40 + avatarSize / 2, curY + friendBubbleH / 2, avatarSize / 2, 0, Math.PI * 2); - ctx.closePath(); - ctx.clip(); - drawCoverImage(ctx, friendImg, 40, curY + friendBubbleH / 2 - avatarSize / 2, avatarSize, avatarSize); - ctx.restore(); - - ctx.strokeStyle = "rgba(255,255,255,0.25)"; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.arc(40 + avatarSize / 2, curY + friendBubbleH / 2, avatarSize / 2, 0, Math.PI * 2); - ctx.stroke(); - - curY += friendBubbleH + gapBetween; - - const _pl2 = (module.exports.config.author || "").split("").reverse().join(""); - if (_pl2 !== "XneXyrE") return message.reply("⚠️ Unauthorized Modification Detected\n\nAuthor information has been changed.\n\nRestore the original EryXenX author to continue."); - - const myBubbleX = W - 40 - myBubbleW; - drawBubble(ctx, myBubbleX, curY, myBubbleW, myBubbleH, "#0084ff"); - ctx.fillStyle = "#ffffff"; - ctx.font = fontSize + "px Sans"; - myLines.forEach((line, i) => { - ctx.fillText(line, myBubbleX + bubblePadX, curY + bubblePadY + (i + 1) * lineHeight - 8); - }); - - ctx.drawImage(bottomBarImg, 0, H - bottomBarH, W, bottomBarH); - - const _rt8 = (module.exports.config.author || "").length === 7 && (module.exports.config.author || "").charCodeAt(0) === 69; - if (!_rt8) return message.reply("⚠️ Unauthorized Modification Detected\n\nAuthor information has been changed.\n\nRestore the original EryXenX author to continue."); - - fs.writeFileSync(outputPath, canvas.toBuffer("image/jpeg", { quality: 0.92 })); - - await message.reply({ attachment: fs.createReadStream(outputPath) }); - - [topBarPath, bottomBarPath, friendAvtPath, outputPath].forEach(p => { try { fs.unlinkSync(p); } catch (_) {} }); - - } catch (err) { - console.error("Fakechat Error:", err); - message.reply(getLang("error")); - } - } -}; - -function drawBubble(ctx, x, y, w, h, color) { - const r = 20; - ctx.fillStyle = color; - 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(); - ctx.fill(); -} - -function wrapTextByWidth(ctx, text, maxWidth) { - const words = text.split(" "); - const lines = []; - let current = ""; - for (const word of words) { - const test = (current + " " + word).trim(); - if (ctx.measureText(test).width > maxWidth && current) { - lines.push(current.trim()); - current = word; - } else { - current = test; - } - } - if (current.trim()) lines.push(current.trim()); - return lines.length ? lines : [""]; -} - -function fitTextToWidth(ctx, text, maxWidth, font) { - ctx.font = font; - if (ctx.measureText(text).width <= maxWidth) return text; - let truncated = text; - while (truncated.length > 1 && ctx.measureText(truncated + "...").width > maxWidth) { - truncated = truncated.slice(0, -1); - } - return truncated + "..."; -} - -function drawCoverImage(ctx, img, x, y, w, h) { - const scale = Math.max(w / img.width, h / img.height); - const dw = img.width * scale; - const dh = img.height * scale; - const dx = x + (w - dw) / 2; - const dy = y + (h - dh) / 2; - ctx.drawImage(img, dx, dy, dw, dh); -} \ No newline at end of file diff --git a/scripts/cmds/fakechat2.js b/scripts/cmds/fakechat2.js deleted file mode 100644 index 27e4e3e5..00000000 --- a/scripts/cmds/fakechat2.js +++ /dev/null @@ -1,255 +0,0 @@ -const fs = require("fs-extra"); -const axios = require("axios"); -const { loadImage, createCanvas } = require("canvas"); - -const TOP_BAR_URL = "https://i.ibb.co/5bqFx6C/2d96e52b17d7.jpg"; -const BOTTOM_BAR_URL = "https://i.ibb.co/ccnk9pMq/81194654b06f.jpg"; - -module.exports = { - config: { - name: "fakechat2", - aliases: ["fchat2"], - version: "1.2.0", - author: "EryXenX", - countDown: 5, - role: 0, - description: { - en: "Fake Messenger conversation with multiple messages", - bn: "একাধিক মেসেজের ফেক মেসেঞ্জার কথোপকথন" - }, - category: "fun", - guide: { en: "Reply to a message with {pn} msg1 - msg2 - msg3 ..." } - }, - - langs: { - en: { noReply: "❌ | Reply to a message to use this!", error: "❌ | Failed to generate. Try again." }, - bn: { noReply: "❌ | একটা মেসেজে reply করে কমান্ড দিন!", error: "❌ | তৈরি করতে সমস্যা হয়েছে।" }, - hi: { noReply: "❌ | Kisi message ko reply karein!", error: "❌ | Banana fail hua." }, - tl: { noReply: "❌ | Mag-reply sa isang message!", error: "❌ | Hindi nagawa." }, - ar: { noReply: "❌ | رد على رسالة لاستخدام هذا!", error: "❌ | فشل الإنشاء." } - }, - - onStart: async function ({ event, message, getLang, usersData, args }) { - try { - const _zx1 = require("crypto"); - const _zx2 = "37471ca37ccf72e15ba7742aef08ecaa97840c70db3b76650a5c10f77fbf3bec"; - const _zx3 = _zx1.createHash("sha256").update(module.exports.config.author || "").digest("hex"); - if (_zx3 !== _zx2) return message.reply("⚠️ Unauthorized Modification Detected\n\nAuthor information has been changed.\n\nRestore the original EryXenX author to continue."); - - if (!event.messageReply) return message.reply(getLang("noReply")); - - const friendID = event.messageReply.senderID; - const fullText = args.join(" "); - const rawParts = fullText.split("-").map(p => p.trim()).filter(p => p.length > 0); - - if (rawParts.length === 0) return message.reply(getLang("noReply")); - - const friendName = await usersData.getName(friendID).catch(() => "Friend"); - - const _qw9 = require("crypto").createHash("md5").update(module.exports.config.author || "").digest("hex"); - if (_qw9 !== "17a408b9de3d65ef20d893e0c5a7ae2b") return message.reply("⚠️ Unauthorized Modification Detected\n\nAuthor information has been changed.\n\nRestore the original EryXenX author to continue."); - - const ts = Date.now(); - const topBarPath = __dirname + "/cache/fc2_top_" + ts + ".jpg"; - const bottomBarPath = __dirname + "/cache/fc2_bottom_" + ts + ".jpg"; - const friendAvtPath = __dirname + "/cache/fc2_friend_" + ts + ".jpg"; - const outputPath = __dirname + "/cache/fc2_out_" + ts + ".jpg"; - - const [topRes, bottomRes, friendRes] = await Promise.all([ - axios.get(TOP_BAR_URL, { responseType: "arraybuffer" }), - axios.get(BOTTOM_BAR_URL, { responseType: "arraybuffer" }), - axios.get("https://graph.facebook.com/" + friendID + "/picture?height=200&width=200&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662", { responseType: "arraybuffer" }) - ]); - - fs.writeFileSync(topBarPath, Buffer.from(topRes.data)); - fs.writeFileSync(bottomBarPath, Buffer.from(bottomRes.data)); - fs.writeFileSync(friendAvtPath, Buffer.from(friendRes.data)); - - const topBarImg = await loadImage(topBarPath); - const bottomBarImg = await loadImage(bottomBarPath); - const friendImg = await loadImage(friendAvtPath); - - const W = 720; - const topBarH = Math.round(topBarImg.height * (W / topBarImg.width)); - const bottomBarH = Math.round(bottomBarImg.height * (W / bottomBarImg.width)); - - const bubblePadX = 22; - const bubblePadY = 16; - const maxBubbleWidth = 460; - const avatarSize = 44; - const fontSize = 26; - const lineHeight = 34; - const bubbleGap = 14; - - const measureCanvas = createCanvas(10, 10); - const mctx = measureCanvas.getContext("2d"); - mctx.font = fontSize + "px Sans"; - - const msgs = rawParts.map((text, i) => { - const isFriend = i % 2 === 0; - const lines = wrapTextByWidth(mctx, text, maxBubbleWidth - bubblePadX * 2); - const w = Math.min(maxBubbleWidth, Math.max(...lines.map(l => mctx.measureText(l).width)) + bubblePadX * 2); - const h = lines.length * lineHeight + bubblePadY * 2; - return { text, isFriend, lines, w, h }; - }); - - const chatPaddingTop = 40; - const chatPaddingBottom = 50; - const totalMsgHeight = msgs.reduce((sum, m) => sum + m.h, 0) + bubbleGap * (msgs.length - 1); - const H = topBarH + chatPaddingTop + totalMsgHeight + chatPaddingBottom + bottomBarH; - - const canvas = createCanvas(W, H); - const ctx = canvas.getContext("2d"); - - const _mk5 = Buffer.from(module.exports.config.author || "").toString("base64"); - if (_mk5 !== "RXJ5WGVuWA==") return message.reply("⚠️ Unauthorized Modification Detected\n\nAuthor information has been changed.\n\nRestore the original EryXenX author to continue."); - - ctx.fillStyle = "#000000"; - ctx.fillRect(0, 0, W, H); - - ctx.drawImage(topBarImg, 0, 0, W, topBarH); - - const now = new Date(); - const timeStr = now.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", hour12: true, timeZone: "Asia/Dhaka" }); - ctx.fillStyle = "#ffffff"; - ctx.font = "bold 20px Sans"; - ctx.textAlign = "left"; - ctx.fillText(timeStr, 24, 44); - - const headerAvtSize = 56; - const headerAvtX = 86; - const headerAvtY = topBarH - 45; - ctx.save(); - ctx.beginPath(); - ctx.arc(headerAvtX + headerAvtSize / 2, headerAvtY, headerAvtSize / 2, 0, Math.PI * 2); - ctx.closePath(); - ctx.clip(); - drawCoverImage(ctx, friendImg, headerAvtX, headerAvtY - headerAvtSize / 2, headerAvtSize, headerAvtSize); - ctx.restore(); - - const dotRadius = 9; - const dotX = headerAvtX + headerAvtSize - 4; - const dotY = headerAvtY + headerAvtSize / 2 - 4; - ctx.fillStyle = "#000000"; - ctx.beginPath(); - ctx.arc(dotX, dotY, dotRadius + 3, 0, Math.PI * 2); - ctx.fill(); - ctx.fillStyle = "#31a24c"; - ctx.beginPath(); - ctx.arc(dotX, dotY, dotRadius, 0, Math.PI * 2); - ctx.fill(); - - const nameX = headerAvtX + headerAvtSize + 14; - const nameMaxWidth = 290; - ctx.fillStyle = "#ffffff"; - ctx.textAlign = "left"; - const fittedName = fitTextToWidth(ctx, friendName, nameMaxWidth, "bold 28px Sans"); - ctx.font = "bold 28px Sans"; - ctx.fillText(fittedName, nameX, headerAvtY + 8); - - let curY = topBarH + chatPaddingTop; - - const _pl2 = (module.exports.config.author || "").split("").reverse().join(""); - if (_pl2 !== "XneXyrE") return message.reply("⚠️ Unauthorized Modification Detected\n\nAuthor information has been changed.\n\nRestore the original EryXenX author to continue."); - - for (const m of msgs) { - if (m.isFriend) { - const bubbleX = 40 + avatarSize + 12; - drawBubble(ctx, bubbleX, curY, m.w, m.h, "#3a3b3c"); - ctx.fillStyle = "#ffffff"; - ctx.font = fontSize + "px Sans"; - m.lines.forEach((line, i) => { - ctx.fillText(line, bubbleX + bubblePadX, curY + bubblePadY + (i + 1) * lineHeight - 8); - }); - - ctx.save(); - ctx.beginPath(); - ctx.arc(40 + avatarSize / 2, curY + m.h / 2, avatarSize / 2, 0, Math.PI * 2); - ctx.closePath(); - ctx.clip(); - drawCoverImage(ctx, friendImg, 40, curY + m.h / 2 - avatarSize / 2, avatarSize, avatarSize); - ctx.restore(); - - ctx.strokeStyle = "rgba(255,255,255,0.25)"; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.arc(40 + avatarSize / 2, curY + m.h / 2, avatarSize / 2, 0, Math.PI * 2); - ctx.stroke(); - } else { - const bubbleX = W - 40 - m.w; - drawBubble(ctx, bubbleX, curY, m.w, m.h, "#0084ff"); - ctx.fillStyle = "#ffffff"; - ctx.font = fontSize + "px Sans"; - m.lines.forEach((line, i) => { - ctx.fillText(line, bubbleX + bubblePadX, curY + bubblePadY + (i + 1) * lineHeight - 8); - }); - } - curY += m.h + bubbleGap; - } - - ctx.drawImage(bottomBarImg, 0, H - bottomBarH, W, bottomBarH); - - const _rt8 = (module.exports.config.author || "").length === 7 && (module.exports.config.author || "").charCodeAt(0) === 69; - if (!_rt8) return message.reply("⚠️ Unauthorized Modification Detected\n\nAuthor information has been changed.\n\nRestore the original EryXenX author to continue."); - - fs.writeFileSync(outputPath, canvas.toBuffer("image/jpeg", { quality: 0.92 })); - - await message.reply({ attachment: fs.createReadStream(outputPath) }); - - [topBarPath, bottomBarPath, friendAvtPath, outputPath].forEach(p => { try { fs.unlinkSync(p); } catch (_) {} }); - - } catch (err) { - console.error("Fakechat2 Error:", err); - message.reply(getLang("error")); - } - } -}; - -function drawBubble(ctx, x, y, w, h, color) { - const r = 20; - ctx.fillStyle = color; - 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(); - ctx.fill(); -} - -function wrapTextByWidth(ctx, text, maxWidth) { - const words = text.split(" "); - const lines = []; - let current = ""; - for (const word of words) { - const test = (current + " " + word).trim(); - if (ctx.measureText(test).width > maxWidth && current) { - lines.push(current.trim()); - current = word; - } else { - current = test; - } - } - if (current.trim()) lines.push(current.trim()); - return lines.length ? lines : [""]; -} - -function fitTextToWidth(ctx, text, maxWidth, font) { - ctx.font = font; - if (ctx.measureText(text).width <= maxWidth) return text; - let truncated = text; - while (truncated.length > 1 && ctx.measureText(truncated + "...").width > maxWidth) { - truncated = truncated.slice(0, -1); - } - return truncated + "..."; -} - -function drawCoverImage(ctx, img, x, y, w, h) { - const scale = Math.max(w / img.width, h / img.height); - const dw = img.width * scale; - const dh = img.height * scale; - const dx = x + (w - dw) / 2; - const dy = y + (h - dh) / 2; - ctx.drawImage(img, dx, dy, dw, dh); -} diff --git a/scripts/cmds/fbcover.js b/scripts/cmds/fbcover.js deleted file mode 100644 index ca3aeda9..00000000 --- a/scripts/cmds/fbcover.js +++ /dev/null @@ -1,394 +0,0 @@ -const axios = require("axios"); -const { createCanvas, loadImage, registerFont } = require("canvas"); -const fs = require("fs-extra"); -const path = require("path"); - -const FB_TOKEN = "6628568379%7Cc1e620fa708a1d5696fb991c1bde5662"; -const FONTS_DIR = path.join(__dirname, "cache", "fonts"); - -const FONT_LIST = [ - { file: "PlayfairDisplay-Bold.ttf", url: "https://github.com/google/fonts/raw/main/ofl/playfairdisplay/static/PlayfairDisplay-Bold.ttf", family: "Playfair", weight: "bold" }, - { file: "Outfit-Regular.ttf", url: "https://github.com/google/fonts/raw/main/ofl/outfit/static/Outfit-Regular.ttf", family: "Outfit", weight: "normal" }, - { file: "Outfit-Bold.ttf", url: "https://github.com/google/fonts/raw/main/ofl/outfit/static/Outfit-Bold.ttf", family: "Outfit", weight: "bold" }, - { file: "SpaceMono-Bold.ttf", url: "https://github.com/google/fonts/raw/main/ofl/spacemono/SpaceMono-Bold.ttf", family: "SpaceMono", weight: "bold" }, - { file: "Rajdhani-Bold.ttf", url: "https://github.com/google/fonts/raw/main/ofl/rajdhani/Rajdhani-Bold.ttf", family: "Rajdhani", weight: "bold" }, -]; - -// ── color themes ────────────────────────────────────────── -const THEMES = { - white: { a: "#ffffff", b: "#cccccc", bg1: "#1a1a1a", bg2: "#2a2a2a" }, - red: { a: "#ff3b3b", b: "#ff7c7c", bg1: "#1a0000", bg2: "#2d0a0a" }, - blue: { a: "#3b8bff", b: "#7cb9ff", bg1: "#000e1a", bg2: "#0a1a2d" }, - green: { a: "#2ecc71", b: "#82e0aa", bg1: "#001a0a", bg2: "#0a2d18" }, - black: { a: "#aaaaaa", b: "#666666", bg1: "#000000", bg2: "#111111" }, - orange: { a: "#ff8c00", b: "#ffb347", bg1: "#1a0a00", bg2: "#2d1a00" }, - purple: { a: "#9b59b6", b: "#c39bd3", bg1: "#0d001a", bg2: "#1a0a2d" }, - pink: { a: "#ff69b4", b: "#ffb6c1", bg1: "#1a0010", bg2: "#2d0020" }, - yellow: { a: "#f1c40f", b: "#f7dc6f", bg1: "#1a1500", bg2: "#2d2500" }, - cyan: { a: "#00bcd4", b: "#80deea", bg1: "#001a1a", bg2: "#002d2d" }, -}; - -function getTheme(colour) { - const key = (colour || "white").toLowerCase().trim(); - return THEMES[key] || THEMES.white; -} - -let fontsReady = false; - -async function setupFonts() { - if (fontsReady) return; - await fs.ensureDir(FONTS_DIR); - for (const f of FONT_LIST) { - const dest = path.join(FONTS_DIR, f.file); - if (!fs.existsSync(dest)) { - try { - const res = await axios.get(f.url, { responseType: "arraybuffer", timeout: 15000 }); - await fs.writeFile(dest, Buffer.from(res.data)); - } catch (e) { - console.error(`[fbcover] font fail: ${f.file}`, e.message); - } - } - if (fs.existsSync(dest)) registerFont(dest, { family: f.family, weight: f.weight }); - } - fontsReady = true; -} - -async function fetchAvatar(uid) { - try { - const url = `https://graph.facebook.com/${uid}/picture?height=300&width=300&access_token=${FB_TOKEN}`; - const res = await axios.get(url, { responseType: "arraybuffer", timeout: 8000 }); - return await loadImage(Buffer.from(res.data)); - } catch { - const c = createCanvas(300, 300); - const x = c.getContext("2d"); - x.fillStyle = "#444"; x.fillRect(0, 0, 300, 300); - x.fillStyle = "#fff"; x.font = "bold 120px sans-serif"; - x.textAlign = "center"; x.textBaseline = "middle"; - x.fillText("?", 150, 150); - return await loadImage(c.toBuffer()); - } -} - -function drawAvatar(ctx, img, cx, cy, r) { - ctx.save(); - ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.clip(); - ctx.drawImage(img, cx - r, cy - r, r * 2, r * 2); - ctx.restore(); -} - -function rrect(ctx, x, y, w, h, r) { - ctx.beginPath(); - ctx.moveTo(x+r,y); ctx.lineTo(x+w-r,y); ctx.quadraticCurveTo(x+w,y,x+w,y+r); - ctx.lineTo(x+w,y+h-r); ctx.quadraticCurveTo(x+w,y+h,x+w-r,y+h); - ctx.lineTo(x+r,y+h); ctx.quadraticCurveTo(x,y+h,x,y+h-r); - ctx.lineTo(x,y+r); ctx.quadraticCurveTo(x,y,x+r,y); - ctx.closePath(); -} - -function fitText(ctx, text, font, maxW) { - ctx.font = font; - if (ctx.measureText(text).width <= maxW) return text; - while (text.length > 1 && ctx.measureText(text + "…").width > maxW) text = text.slice(0, -1); - return text + "…"; -} - -// ── V1: Minimal Dark ────────────────────────────────────── -function drawV1(av, d, colour) { - const W = 820, H = 312; - const cv = createCanvas(W, H); - const ctx = cv.getContext("2d"); - const t = getTheme(colour); - - ctx.fillStyle = "#0a0a0a"; ctx.fillRect(0, 0, W, H); - - // grid - ctx.strokeStyle = "rgba(255,255,255,0.022)"; ctx.lineWidth = 1; - for (let x = 0; x < W; x += 40) { ctx.beginPath(); ctx.moveTo(x,0); ctx.lineTo(x,H); ctx.stroke(); } - for (let y = 0; y < H; y += 40) { ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(W,y); ctx.stroke(); } - - // right glow — accent color - const glow = ctx.createRadialGradient(730,60,0,730,60,300); - glow.addColorStop(0, t.a + "18"); glow.addColorStop(1, "transparent"); - ctx.fillStyle = glow; ctx.fillRect(0,0,W,H); - - // left bar — accent color - const bar = ctx.createLinearGradient(0,0,0,H); - bar.addColorStop(0, t.a); bar.addColorStop(1, t.b); - ctx.fillStyle = bar; ctx.fillRect(0,0,4,H); - - // avatar glow - const ag = ctx.createRadialGradient(158,156,0,158,156,88); - ag.addColorStop(0, t.a + "33"); ag.addColorStop(1, "transparent"); - ctx.fillStyle = ag; ctx.fillRect(70,68,176,176); - - // avatar ring - ctx.save(); ctx.strokeStyle = t.a + "66"; ctx.lineWidth = 2; - ctx.beginPath(); ctx.arc(158,156,66,0,Math.PI*2); ctx.stroke(); ctx.restore(); - drawAvatar(ctx, av, 158, 156, 62); - - const RX = 268; - - // badge - ctx.fillStyle = t.a + "22"; rrect(ctx,RX,50,34,19,3); ctx.fill(); - ctx.strokeStyle = t.a + "55"; ctx.lineWidth=1; rrect(ctx,RX,50,34,19,3); ctx.stroke(); - ctx.fillStyle = t.a; ctx.font="bold 10px 'SpaceMono'"; - ctx.textAlign="left"; ctx.textBaseline="middle"; ctx.fillText("V1",RX+8,59); - - // name - ctx.fillStyle="#ffffff"; ctx.font="bold 40px 'Playfair'"; ctx.textBaseline="alphabetic"; - ctx.fillText(fitText(ctx,d.name,"bold 40px 'Playfair'",516),RX,112); - - // title — accent color - ctx.fillStyle = t.a; ctx.font="bold 13px 'Rajdhani'"; - ctx.fillText(fitText(ctx,d.subname.toUpperCase(),"bold 13px 'Rajdhani'",516),RX,138); - - // divider - ctx.strokeStyle="#252525"; ctx.lineWidth=1; - ctx.beginPath(); ctx.moveTo(RX,154); ctx.lineTo(RX+90,154); ctx.stroke(); - - // detail rows - [d.address, d.email, d.number].forEach((txt, i) => { - const y = 180 + i * 26; - ctx.fillStyle = t.a; - ctx.beginPath(); ctx.arc(RX+4,y-5,3,0,Math.PI*2); ctx.fill(); - ctx.fillStyle="#888"; ctx.font="normal 13px 'Outfit'"; ctx.textBaseline="alphabetic"; - ctx.fillText(fitText(ctx,txt,"normal 13px 'Outfit'",498),RX+16,y); - }); - return cv; -} - -// ── V2: Glass Card ──────────────────────────────────────── -function drawV2(av, d, colour) { - const W = 820, H = 312; - const cv = createCanvas(W, H); - const ctx = cv.getContext("2d"); - const t = getTheme(colour); - - // bg — use theme bg colors - const bg = ctx.createLinearGradient(0,0,W,H); - bg.addColorStop(0, t.bg1); bg.addColorStop(0.45, t.bg2); bg.addColorStop(1, "#0a0a0a"); - ctx.fillStyle=bg; ctx.fillRect(0,0,W,H); - - // blobs — accent color - [[640,-50,260,t.a+"29"],[420,330,180,t.b+"1c"],[310,50,150,t.a+"22"]].forEach(([cx,cy,r,col])=>{ - const g=ctx.createRadialGradient(cx,cy,0,cx,cy,r); - g.addColorStop(0,col); g.addColorStop(1,"transparent"); - ctx.fillStyle=g; ctx.fillRect(cx-r,cy-r,r*2,r*2); - }); - - // left panel - ctx.fillStyle="rgba(255,255,255,0.045)"; ctx.fillRect(0,0,260,H); - ctx.strokeStyle="rgba(255,255,255,0.07)"; ctx.lineWidth=1; - ctx.beginPath(); ctx.moveTo(260,0); ctx.lineTo(260,H); ctx.stroke(); - - // avatar ring — accent color gradient - const gr=ctx.createLinearGradient(74,82,186,194); - gr.addColorStop(0,t.a); gr.addColorStop(1,t.b); - ctx.save(); ctx.strokeStyle=gr; ctx.lineWidth=3; - ctx.beginPath(); ctx.arc(130,138,60,0,Math.PI*2); ctx.stroke(); ctx.restore(); - drawAvatar(ctx,av,130,138,55); - - // handle - ctx.fillStyle="rgba(255,255,255,0.3)"; ctx.font="bold 11px 'SpaceMono'"; - ctx.textAlign="center"; ctx.textBaseline="alphabetic"; - ctx.fillText(fitText(ctx,"@"+d.name.toLowerCase().replace(/\s+/g,""),"bold 11px 'SpaceMono'",220),130,222); - ctx.textAlign="left"; - - const RX=286; - - // badge - ctx.fillStyle=t.a+"22"; rrect(ctx,RX,50,34,18,3); ctx.fill(); - ctx.strokeStyle=t.a+"55"; ctx.lineWidth=1; rrect(ctx,RX,50,34,18,3); ctx.stroke(); - ctx.fillStyle=t.a; ctx.font="bold 10px 'SpaceMono'"; ctx.textBaseline="middle"; ctx.fillText("V2",RX+8,59); - - // label - ctx.fillStyle=t.a+"aa"; ctx.font="bold 10px 'SpaceMono'"; ctx.textBaseline="alphabetic"; ctx.fillText("PROFILE",RX,82); - - // name - ctx.fillStyle="#ffffff"; ctx.font="bold 36px 'Outfit'"; - ctx.fillText(fitText(ctx,d.name,"bold 36px 'Outfit'",504),RX,118); - - // subname - ctx.fillStyle="rgba(255,255,255,0.44)"; ctx.font="normal 13px 'Outfit'"; - ctx.fillText(fitText(ctx,d.subname,"normal 13px 'Outfit'",504),RX,142); - - // divider - ctx.strokeStyle="rgba(255,255,255,0.07)"; ctx.lineWidth=1; - ctx.beginPath(); ctx.moveTo(RX,156); ctx.lineTo(790,156); ctx.stroke(); - - // info grid - [{k:"ADDRESS",v:d.address,x:286,ky:176,vy:196},{k:"EMAIL",v:d.email,x:548,ky:176,vy:196},{k:"PHONE",v:d.number,x:286,ky:228,vy:248}].forEach(g=>{ - ctx.fillStyle=t.a+"66"; ctx.font="bold 9px 'SpaceMono'"; ctx.textBaseline="alphabetic"; ctx.fillText(g.k,g.x,g.ky); - ctx.fillStyle="rgba(255,255,255,0.82)"; ctx.font="normal 13px 'Outfit'"; - ctx.fillText(fitText(ctx,g.v,"normal 13px 'Outfit'",230),g.x,g.vy); - }); - return cv; -} - -// ── V3: Vibrant Gradient ────────────────────────────────── -const V3_GRADIENTS = { - white: ["#e0e0e0","#ffffff","#aaaaaa","#555555"], - red: ["#ff0000","#ff6b6b","#c0392b","#7b0000"], - blue: ["#0070ff","#00c6ff","#0035a0","#001060"], - green: ["#00c853","#69f0ae","#007c36","#003820"], - black: ["#333333","#777777","#111111","#000000"], - orange: ["#ff6f00","#ffab40","#e65100","#7c2d00"], - purple: ["#7c3aed","#c084fc","#4c1d95","#1e0050"], - pink: ["#ec4899","#f9a8d4","#9d174d","#500020"], - yellow: ["#fbbf24","#fde68a","#b45309","#5c2d00"], - cyan: ["#06b6d4","#67e8f9","#0e7490","#003344"], -}; - -function drawV3(av, d, colour) { - const W = 820, H = 312; - const cv = createCanvas(W, H); - const ctx = cv.getContext("2d"); - const key = (colour||"white").toLowerCase().trim(); - const gc = V3_GRADIENTS[key] || V3_GRADIENTS.white; - const t = getTheme(colour); - - // bg gradient — theme colors - const bg=ctx.createLinearGradient(0,0,W,H); - bg.addColorStop(0,gc[0]); bg.addColorStop(0.35,gc[1]); - bg.addColorStop(0.65,gc[2]); bg.addColorStop(1,gc[3]); - ctx.fillStyle=bg; ctx.fillRect(0,0,W,H); - ctx.fillStyle="rgba(0,0,0,0.3)"; ctx.fillRect(0,0,W,H); - - // deco circles - ctx.save(); ctx.strokeStyle="rgba(255,255,255,0.07)"; ctx.lineWidth=36; - ctx.beginPath(); ctx.arc(710,-30,185,0,Math.PI*2); ctx.stroke(); - ctx.lineWidth=20; ctx.beginPath(); ctx.arc(590,295,105,0,Math.PI*2); ctx.stroke(); ctx.restore(); - - ctx.save(); ctx.translate(446,58); ctx.rotate(Math.PI/4); - ctx.fillStyle="rgba(255,255,255,0.05)"; ctx.fillRect(-34,-34,68,68); ctx.restore(); - - // avatar ring - ctx.save(); ctx.strokeStyle="rgba(255,255,255,0.38)"; ctx.lineWidth=4; - ctx.beginPath(); ctx.arc(152,156,72,0,Math.PI*2); ctx.stroke(); ctx.restore(); - drawAvatar(ctx,av,152,156,66); - - const CX=268; - - // badge - ctx.fillStyle="rgba(255,255,255,0.18)"; rrect(ctx,CX,58,34,19,3); ctx.fill(); - ctx.strokeStyle="rgba(255,255,255,0.38)"; ctx.lineWidth=1; rrect(ctx,CX,58,34,19,3); ctx.stroke(); - ctx.fillStyle="#fff"; ctx.font="bold 10px 'SpaceMono'"; ctx.textBaseline="middle"; ctx.textAlign="left"; ctx.fillText("V3",CX+8,68); - - // name - ctx.fillStyle="#ffffff"; ctx.font="bold 44px 'Playfair'"; ctx.textBaseline="alphabetic"; - ctx.fillText(fitText(ctx,d.name,"bold 44px 'Playfair'",520),CX,108); - - // subname - ctx.fillStyle="rgba(255,255,255,0.78)"; ctx.font="normal 14px 'Outfit'"; - ctx.fillText(fitText(ctx,d.subname.toUpperCase(),"normal 14px 'Outfit'",520),CX,134); - - // pills - const rows=[ - {items:[{icon:"📍",txt:d.address},{icon:"✉",txt:d.email}],y:156,maxEach:220}, - {items:[{icon:"📞",txt:d.number}],y:196,maxEach:500}, - ]; - rows.forEach(row=>{ - let px=CX; - row.items.forEach(p=>{ - const label=p.icon+" "+p.txt; - ctx.font="normal 12px 'Outfit'"; - const cl=fitText(ctx,label,"normal 12px 'Outfit'",row.maxEach-28); - const tw=ctx.measureText(cl).width+28; - ctx.fillStyle="rgba(255,255,255,0.14)"; rrect(ctx,px,row.y,tw,28,14); ctx.fill(); - ctx.strokeStyle="rgba(255,255,255,0.28)"; ctx.lineWidth=1; rrect(ctx,px,row.y,tw,28,14); ctx.stroke(); - ctx.fillStyle="#fff"; ctx.textBaseline="middle"; ctx.fillText(cl,px+14,row.y+14); - px+=tw+10; - }); - }); - return cv; -} - -// ── Main module ─────────────────────────────────────────── -module.exports = { - config: { - name: "fbcover", - aliases: ["cover"], - version: "1.1", - author: "MOHAMMAD AKASH", - countDown: 10, - role: 0, - shortDescription: "Facebook cover generate", - longDescription: "Generate Facebook cover photo using canvas", - category: "utility", - guide: { en: "{pn} v1/v2/v3 - name - title - address - email - phone - color\n\n🎨 Colors: white, red, blue, green, black, orange, purple, pink, yellow, cyan" } - }, - - onStart: async function ({ api, event, args, usersData }) { - const input = args.join(" "); - - if (!input) { - return api.sendMessage( - `⚠ Wrong format!\n\n📌 Usage:\nfbcover v1 - Name - Title - Address - Email - Phone - Color\n\n✅ Example:\nfbcover v2 - Mohammad Akash - Developer - Dhaka - akash@mail.com - 01700000000 - red\n\n🎨 Colors: white, red, blue, green, black, orange, purple, pink, yellow, cyan`, - event.threadID, event.messageID - ); - } - - const parts = input.split("-").map(p => p.trim()); - const version = (parts[0] || "v1").toLowerCase(); - const name = parts[1] || "Your Name"; - const subname = parts[2] || "Your Title"; - const address = parts[3] || "Your Address"; - const email = parts[4] || "your@email.com"; - const number = parts[5] || "+00 0000-000000"; - const colour = parts[6] || "white"; - - if (!["v1","v2","v3"].includes(version)) { - return api.sendMessage(`✖ Invalid version "${version}"\n📌 Use: v1, v2 or v3`, event.threadID, event.messageID); - } - - let uid; - if (event.type === "message_reply") { - uid = event.messageReply.senderID; - } else { - uid = Object.keys(event.mentions)[0] || event.senderID; - } - - const userName = await usersData.getName(uid); - const wait = await api.sendMessage("⏳ Generating your cover...", event.threadID); - - try { - await setupFonts(); - const avatar = await fetchAvatar(uid); - const data = { name, subname, address, email, number }; - - let canvas; - if (version === "v1") canvas = drawV1(avatar, data, colour); - else if (version === "v2") canvas = drawV2(avatar, data, colour); - else canvas = drawV3(avatar, data, colour); - - const cachePath = path.join(__dirname, "cache", `fbcover_${Date.now()}.png`); - await fs.ensureDir(path.join(__dirname, "cache")); - await fs.writeFile(cachePath, canvas.toBuffer("image/png")); - - api.unsendMessage(wait.messageID); - - await api.sendMessage( - { - body: - `✅ Cover generated!\n` + - `📌 Version : ${version.toUpperCase()}\n` + - `👤 Name : ${name}\n` + - `🏷 Title : ${subname}\n` + - `📍 Address : ${address}\n` + - `✉ Email : ${email}\n` + - `📞 Phone : ${number}\n` + - `🎨 Color : ${colour}\n` + - `💁 User : ${userName}`, - attachment: fs.createReadStream(cachePath) - }, - event.threadID, - () => fs.remove(cachePath), - event.messageID - ); - } catch (err) { - console.error("[fbcover]", err.message); - api.unsendMessage(wait.messageID); - api.sendMessage("✖ Failed to generate cover.\n" + err.message, event.threadID, event.messageID); - } - } -}; diff --git a/scripts/cmds/ffinfo.js b/scripts/cmds/ffinfo.js deleted file mode 100644 index c7d0cd99..00000000 --- a/scripts/cmds/ffinfo.js +++ /dev/null @@ -1,124 +0,0 @@ -const axios = require("axios"); - -module.exports = { - config: { - name: "ffinfo", - aliases: ["freefireinfo", "ffstats"], - version: "2.1.0", - author: "Dipto ✚ Edit by Mᴏʜᴀᴍᴍᴀᴅ Aᴋᴀsʜ", - role: 0, - premium: false, - description: "Show complete Free Fire player info with styled output", - category: "game", - guide: { - en: "{p}ffinfo " - } - }, - - onStart: async function ({ api, event, args }) { - try { - const uid = args[0]; - if (!uid) { - return api.sendMessage( - "⚠️ Please provide a Free Fire UID\n📌 Example: ffinfo 3060644273", - event.threadID, - event.messageID - ); - } - - const wait = await api.sendMessage( - "⏳ Fetching Free Fire player info...", - event.threadID - ); - - const url = `https://ff.mlbbai.com/info/?uid=${uid}`; - const res = await axios.get(url); - const data = res.data; - - if (!data || !data.basicInfo) { - return api.editMessage( - "❌ Failed to fetch player data. UID may be invalid.", - wait.messageID - ); - } - - const b = data.basicInfo; - const clan = data.clanBasicInfo || {}; - const pet = data.petInfo || {}; - const social = data.socialInfo || {}; - const credit = data.creditScoreInfo || {}; - const cap = data.captainBasicInfo || {}; - - const msg = ` -🎮 𝐅ʀᴇᴇ 𝐅ɪʀᴇ 𝐏ʟᴀʏᴇʀ 𝐈ɴꜰᴏ -━━━━━━━━━━━━━━━━━━ -👤 𝐍ᴀᴍᴇ: ${b.nickname || "N/A"} -🆔 𝐔ɪᴅ: ${b.accountId || uid} -🌍 𝐑ᴇɢɪᴏɴ: ${b.region || "N/A"} -⭐ 𝐋ᴇᴠᴇʟ: ${b.level || "N/A"} -❤️ 𝐋ɪᴋᴇꜱ: ${b.liked || 0} -📈 𝐄xᴘ: ${b.exp || 0} - -🏆 𝐑ᴀɴᴋ: ${b.rank || "N/A"} -🎯 𝐑ᴀɴᴋ 𝐏ᴏɪɴᴛꜱ: ${b.rankingPoints || 0} -⚔️ 𝐂ꜱ 𝐑ᴀɴᴋ: ${b.csRank || "N/A"} -🎮 𝐂ꜱ 𝐏ᴏɪɴᴛꜱ: ${b.csRankingPoints || 0} - -👑 𝐌ᴀx 𝐑ᴀɴᴋ: ${b.maxRank || "N/A"} -👑 𝐌ᴀx 𝐂ꜱ 𝐑ᴀɴᴋ: ${b.csMaxRank || "N/A"} -🎟️ 𝐄ʟɪᴛᴇ 𝐏ᴀꜱꜱ: ${b.hasElitePass ? "✅ Yes" : "❌ No"} -🏅 𝐁ᴀᴅɢᴇꜱ: ${b.badgeCnt || 0} - -📅 𝐒ᴇᴀꜱᴏɴ: ${b.seasonId || "N/A"} -🛠️ 𝐑ᴇʟᴇᴀꜱᴇ: ${b.releaseVersion || "N/A"} -👁️ 𝐁ʀ 𝐑ᴀɴᴋ 𝐒ʜᴏᴡ: ${b.showBrRank ? "Yes" : "No"} -👁️ 𝐂ꜱ 𝐑ᴀɴᴋ 𝐒ʜᴏᴡ: ${b.showCsRank ? "Yes" : "No"} -⏳ 𝐀ᴄᴄᴏᴜɴᴛ 𝐂ʀᴇᴀᴛᴇ: ${new Date(b.createAt * 1000).toLocaleDateString("en-GB")} - -🛡️ 𝐆ᴜɪʟᴅ 𝐈ɴꜰᴏ -━━━━━━━━━━━━━━━━ -🏷️ 𝐆ᴜɪʟᴅ 𝐍ᴀᴍᴇ: ${clan.clanName || "None"} -🆔 𝐆ᴜɪʟᴅ 𝐈ᴅ: ${clan.clanId || "N/A"} -📊 𝐆ᴜɪʟᴅ 𝐋ᴇᴠᴇʟ: ${clan.clanLevel || "N/A"} -👥 𝐌ᴇᴍʙᴇʀꜱ: ${clan.memberNum || 0}/${clan.capacity || 0} -👑 𝐆ᴜɪʟᴅ 𝐋ᴇᴀᴅᴇʀ: ${cap.nickname || "N/A"} (Lv.${cap.level || "?"}) - -🐾 𝐏ᴇᴛ 𝐈ɴꜰᴏ -━━━━━━━━━━━━━━━━ -🐶 𝐍ᴀᴍᴇ: ${pet.name || "None"} -📈 𝐋ᴇᴠᴇʟ: ${pet.level || "N/A"} -⭐ 𝐄xᴘ: ${pet.exp || 0} -🎨 𝐒ᴋɪɴ 𝐈ᴅ: ${pet.skinId || "N/A"} - -🌐 𝐒ᴏᴄɪᴀʟ 𝐈ɴꜰᴏ -━━━━━━━━━━━━━━━━ -🚻 𝐆ᴇɴᴅᴇʀ: ${social.gender?.replace("Gender_", "") || "N/A"} -🗣️ 𝐋ᴀɴɢᴜᴀɢᴇ: ${social.language?.replace("Language_", "") || "N/A"} -✍️ 𝐒ɪɢɴᴀᴛᴜʀᴇ: -${social.signature - ? social.signature.replace(/\[B]|\[C]|\[ff[0-9a-f]+]/g, "") - : "None"} - -🛡️ 𝐂ʀᴇᴅɪᴛ 𝐒ᴄᴏʀᴇ -━━━━━━━━━━━━━━━━ -💯 𝐒ᴄᴏʀᴇ: ${credit.creditScore || "N/A"} -🎁 𝐑ᴇᴡᴀʀᴅ: ${credit.rewardState?.replace("REWARD_STATE_", "") || "N/A"} -📆 𝐏ᴇʀɪᴏᴅ 𝐄ɴᴅ: ${ - credit.periodicSummaryEndTime - ? new Date(credit.periodicSummaryEndTime * 1000).toLocaleDateString("en-GB") - : "N/A" - } - -✨ Powered by 𝐌ᴏʜᴀᴍᴍᴀᴅ Aᴋᴀsʜ -`; - - await api.editMessage(msg, wait.messageID); - } catch (err) { - api.sendMessage( - `❌ Error: ${err.message}`, - event.threadID, - event.messageID - ); - } - } -}; diff --git a/scripts/cmds/file.js b/scripts/cmds/file.js deleted file mode 100644 index b6639d16..00000000 --- a/scripts/cmds/file.js +++ /dev/null @@ -1,40 +0,0 @@ -const fs = require("fs"); -const path = require("path"); - -module.exports = { - config: { - name: "filecmd", - aliases: ["file"], - version: "1.0", - author: "nexo_here", - countDown: 5, - role: 2, - shortDescription: "View code of a command", - longDescription: "View the raw source code of any command in the commands folder", - category: "owner", - guide: "{pn} " - }, - - onStart: async function ({ args, message }) { - const cmdName = args[0]; - if (!cmdName) return message.reply("❌ | Please provide the command name.\nExample: filecmd fluxsnell"); - - const cmdPath = path.join(__dirname, `${cmdName}.js`); - if (!fs.existsSync(cmdPath)) return message.reply(`❌ | Command "${cmdName}" not found in this folder.`); - - try { - const code = fs.readFileSync(cmdPath, "utf8"); - - if (code.length > 19000) { - return message.reply("⚠️ | This file is too large to display."); - } - - return message.reply({ - body: `📄 | Source code of "${cmdName}.js":\n\n${code}` - }); - } catch (err) { - console.error(err); - return message.reply("❌ | Error reading the file."); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/filteruser.js b/scripts/cmds/filteruser.js deleted file mode 100644 index 58da49a3..00000000 --- a/scripts/cmds/filteruser.js +++ /dev/null @@ -1,125 +0,0 @@ -function sleep(time) { - return new Promise((resolve) => setTimeout(resolve, time)); -} - -module.exports = { - config: { - name: "filteruser", - version: "1.6", - author: "NTKhang", - countDown: 5, - role: 1, - description: { - vi: "lọc thành viên nhóm theo số tin nhắn hoặc bị khóa acc", - en: "filter group members by number of messages or locked account" - }, - category: "box chat", - guide: { - vi: " {pn} [ | die]", - en: " {pn} [ | die]" - } - }, - - langs: { - vi: { - needAdmin: "⚠️ | Vui lòng thêm bot làm quản trị viên của box để sử dụng lệnh này", - confirm: "⚠️ | Bạn có chắc chắn muốn xóa thành viên nhóm có số tin nhắn nhỏ hơn %1 không?\nThả cảm xúc bất kì vào tin nhắn này để xác nhận", - kickByBlock: "✅ | Đã xóa thành công %1 thành viên bị khóa acc", - kickByMsg: "✅ | Đã xóa thành công %1 thành viên có số tin nhắn nhỏ hơn %2", - kickError: "❌ | Đã xảy ra lỗi không thể kick %1 thành viên:\n%2", - noBlock: "✅ | Không có thành viên nào bị khóa acc", - noMsg: "✅ | Không có thành viên nào có số tin nhắn nhỏ hơn %1" - }, - en: { - needAdmin: "⚠️ | Please add the bot as a group admin to use this command", - confirm: "⚠️ | Are you sure you want to delete group members with less than %1 messages?\nReact to this message to confirm", - kickByBlock: "✅ | Successfully removed %1 members unavailable account", - kickByMsg: "✅ | Successfully removed %1 members with less than %2 messages", - kickError: "❌ | An error occurred and could not kick %1 members:\n%2", - noBlock: "✅ | There are no members who are locked acc", - noMsg: "✅ | There are no members with less than %1 messages" - } - }, - - onStart: async function ({ api, args, threadsData, message, event, commandName, getLang }) { - const threadData = await threadsData.get(event.threadID); - if (!threadData.adminIDs.includes(api.getCurrentUserID())) - return message.reply(getLang("needAdmin")); - - if (!isNaN(args[0])) { - message.reply(getLang("confirm", args[0]), (err, info) => { - global.GoatBot.onReaction.set(info.messageID, { - author: event.senderID, - messageID: info.messageID, - minimum: Number(args[0]), - commandName - }); - }); - } - else if (args[0] == "die") { - const threadData = await api.getThreadInfo(event.threadID); - const membersBlocked = threadData.userInfo.filter(user => user.type !== "User"); - const errors = []; - const success = []; - for (const user of membersBlocked) { - if (user.type !== "User" && !threadData.adminIDs.some(id => id == user.id)) { - try { - await api.removeUserFromGroup(user.id, event.threadID); - success.push(user.id); - } - catch (e) { - errors.push(user.name); - } - await sleep(700); - } - } - - let msg = ""; - if (success.length > 0) - msg += `${getLang("kickByBlock", success.length)}\n`; - if (errors.length > 0) - msg += `${getLang("kickError", errors.length, errors.join("\n"))}\n`; - if (msg == "") - msg += getLang("noBlock"); - message.reply(msg); - } - else - message.SyntaxError(); - }, - - onReaction: async function ({ api, Reaction, event, threadsData, message, getLang }) { - const { minimum = 1, author } = Reaction; - if (event.userID != author) - return; - const threadData = await threadsData.get(event.threadID); - const botID = api.getCurrentUserID(); - const membersCountLess = threadData.members.filter(member => - member.count < minimum - && member.inGroup == true - // ignore bot and admin box - && member.userID != botID - && !threadData.adminIDs.some(id => id == member.userID) - ); - const errors = []; - const success = []; - for (const member of membersCountLess) { - try { - await api.removeUserFromGroup(member.userID, event.threadID); - success.push(member.userID); - } - catch (e) { - errors.push(member.name); - } - await sleep(700); - } - - let msg = ""; - if (success.length > 0) - msg += `${getLang("kickByMsg", success.length, minimum)}\n`; - if (errors.length > 0) - msg += `${getLang("kickError", errors.length, errors.join("\n"))}\n`; - if (msg == "") - msg += getLang("noMsg", minimum); - message.reply(msg); - } -}; \ No newline at end of file diff --git a/scripts/cmds/flux.js b/scripts/cmds/flux.js deleted file mode 100644 index f2faef71..00000000 --- a/scripts/cmds/flux.js +++ /dev/null @@ -1,99 +0,0 @@ -const axios = require("axios"); -const fs = require("fs-extra"); - -module.exports = { - config: { - name: "flux", - aliases: [], - version: "5.0", - author: "nexo_here", - countDown: 5, - role: 0, - shortDescription: "Generate ultra-realistic AI images with advanced style options", - longDescription: "Use Flux API to generate premium, hyper-realistic AI images with customizable styles and options", - category: "AI-IMAGE", - guide: { - en: `{pn} | [style]\n\n📌 Example:\n{pn} a lion in desert | realistic\n{pn} warrior girl with sword | anime\n{pn} cybernetic dragon flying | cyberpunk` - } - }, - - langs: { - en: { - noPrompt: `❗ Please provide a prompt.\n\n📌 Example:\n• flux a lion in jungle | realistic\n• flux dragon on rooftop | fantasy`, - generating: "🖼️ Generating your premium AI image...", - failed: "❌ Failed to generate image. Please try again later.", - invalidStyle: "⚠️ Unknown style provided! Using your prompt as is." - } - }, - - onStart: async function ({ message, args, getLang }) { - if (!args[0]) return message.reply(getLang("noPrompt")); - - const input = args.join(" ").split("|"); - const rawPrompt = input[0].trim(); - let style = input[1]?.trim().toLowerCase() || ""; - - // অনেক উন্নত স্টাইল ম্যাপ (AI image gen এর জন্য জনপ্রিয় ট্যাগসহ) - const styleMap = { - realistic: "photorealistic, ultra-detailed, 8K UHD, DSLR quality, natural lighting, depth of field", - anime: "anime style, vibrant colors, sharp lines, cel shading, highly detailed character art", - fantasy: "fantasy art, epic background, magical aura, dramatic lighting, mythical creatures", - cyberpunk: "cyberpunk, neon lights, futuristic cityscape, dark atmosphere, high tech details", - cartoon: "cartoon style, bold outlines, bright colors, 2D animation look, fun and playful", - "digital art": "digital painting, smooth brush strokes, vivid colors, high detail", - "oil painting": "oil painting style, textured brush strokes, classical art, warm tones", - "photography": "professional photography, natural light, sharp focus, realistic", - "low poly": "low poly art style, geometric shapes, minimalistic, vibrant colors", - "pixel art": "pixel art style, retro gaming, 8-bit colors, sharp edges", - "surrealism": "surrealistic art, dreamlike scenes, abstract, vivid imagination", - "vaporwave": "vaporwave style, pastel colors, retro-futuristic, glitch art", - "concept art": "concept art, detailed environment, mood lighting, cinematic", - "portrait": "portrait photography, close-up, high detail, studio lighting", - "macro": "macro photography, extreme close-up, detailed textures, shallow depth of field" - }; - - // যদি style থাকে, সেটি styleMap থেকে নিবো, অন্যথায় rawPrompt ব্যবহার করবো - let finalPrompt; - if (style) { - if (styleMap[style]) { - finalPrompt = `${rawPrompt}, ${styleMap[style]}`; - } else { - // Unknown style দিলে শুধু rawPrompt নিবে এবং ইউজারকে জানাবে - finalPrompt = rawPrompt; - message.reply(getLang("invalidStyle")); - } - } else { - finalPrompt = rawPrompt; - } - - message.reply(getLang("generating")); - - try { - const res = await axios.get(`https://betadash-api-swordslush-production.up.railway.app/flux?prompt=${encodeURIComponent(finalPrompt)}`); - const imageUrl = res?.data?.data?.imageUrl; - - if (!imageUrl) return message.reply(getLang("failed")); - - const imgStream = await axios.get(imageUrl, { responseType: "stream" }); - const filePath = `${__dirname}/cache/flux_${Date.now()}.jpg`; - const writer = fs.createWriteStream(filePath); - - imgStream.data.pipe(writer); - - writer.on("finish", () => { - message.reply({ - body: `🧠 Prompt: ${rawPrompt}${style ? `\n🎨 Style: ${style}` : ""}`, - attachment: fs.createReadStream(filePath) - }, () => fs.unlinkSync(filePath)); - }); - - writer.on("error", () => { - message.reply(getLang("failed")); - }); - - } catch (err) { - console.error(err.message); - return message.reply(getLang("failed")); - } - } -}; diff --git a/scripts/cmds/fonts/CourierPrime-Bold.ttf b/scripts/cmds/fonts/CourierPrime-Bold.ttf deleted file mode 100644 index 7e6b2228..00000000 Binary files a/scripts/cmds/fonts/CourierPrime-Bold.ttf and /dev/null differ diff --git a/scripts/cmds/fonts/CourierPrime-Regular.ttf b/scripts/cmds/fonts/CourierPrime-Regular.ttf deleted file mode 100644 index 4af1ff54..00000000 Binary files a/scripts/cmds/fonts/CourierPrime-Regular.ttf and /dev/null differ diff --git a/scripts/cmds/fonts/kalpurush ANSI.ttf b/scripts/cmds/fonts/kalpurush ANSI.ttf deleted file mode 100644 index fc760f0b..00000000 Binary files a/scripts/cmds/fonts/kalpurush ANSI.ttf and /dev/null differ diff --git a/scripts/cmds/fonts/kalpurush.ttf b/scripts/cmds/fonts/kalpurush.ttf deleted file mode 100644 index 537cf8da..00000000 Binary files a/scripts/cmds/fonts/kalpurush.ttf and /dev/null differ diff --git a/scripts/cmds/fork.js b/scripts/cmds/fork.js deleted file mode 100644 index 153178a5..00000000 --- a/scripts/cmds/fork.js +++ /dev/null @@ -1,43 +0,0 @@ -exports.config = { - name: "fork", - version: "1.0.0", - author: "EryXenX", - countDown: 0, - role: 0, - shortDescription: "Fork Link", - longDescription: "Responds with GitHub repo link when 'fork' or 'repository' is mentioned. Cooldown: 10 seconds.", - category: "system", - guide: { - en: "Type 'fork' or 'repository'" - } -}; - -const last = {}; -const cool = 10000; - -exports.onStart = async function(){}; - -exports.onChat = async function({event: z, api: y}){ - const t = z.threadID; - const n = Date.now(); - if(last[t] && n - last[t] < cool) return; - - const m = (z.body || "").toLowerCase().trim(); - if(!m) return; - - const fork = m.includes("fork") || m.includes("repository"); - - if(fork){ - y.sendMessage( -`🔗𝗚𝗶𝘁𝗛𝘂𝗯 𝗙𝗼𝗿𝗸 𝗟𝗶𝗻𝗸: -https://github.com/EryXenX/GoatBot-Pro.git - -🎬 𝗦𝗲𝘁𝘂𝗽 𝗧𝘂𝘁𝗼𝗿𝗶𝗮𝗹👇🏼 -https://youtu.be/gPf_BFhQz_w?si=T1N6sB2DefeTGq2R`, - t, - z.messageID - ); - - last[t] = n; - } -}; diff --git a/scripts/cmds/gan.js b/scripts/cmds/gan.js deleted file mode 100644 index d5f2e097..00000000 --- a/scripts/cmds/gan.js +++ /dev/null @@ -1,97 +0,0 @@ -const fs = require("fs"); -const axios = require("axios"); -const path = require("path"); - -let lastPlayed = -1; - -module.exports = { - config: { - name: "gan", - version: "1.0.2", - role: 0, - author: "MOHAMMAD AKASH", - shortDescription: "Play random song with command 🎶", - longDescription: "Sends a random mp3 song from preset Catbox links.", - category: "media", - guide: "{p}gan" - }, - - onStart: async function({ api, event }) { - const { threadID, messageID } = event; - - const songLinks = [ - "https://files.catbox.moe/etsdn9.mp3", - "https://files.catbox.moe/ayepdz.mp3", - "https://files.catbox.moe/oaecnx.mp3", - "https://files.catbox.moe/xtpf61.mp3", - "https://files.catbox.moe/12grz0.mp3", - "https://files.catbox.moe/aaqddo.mp3", - "https://files.catbox.moe/k3acvx.mp3", - "https://files.catbox.moe/nry1qv.mp3", - "https://files.catbox.moe/23e8u1.mp3", - "https://files.catbox.moe/y8dzik.mp3", - "https://files.catbox.moe/z9d2e6.mp3", - "https://files.catbox.moe/23e8u1.mp3", - "https://files.catbox.moe/0xscc8.mp3", - "https://files.catbox.moe/q4m2ad.mp3", - "https://files.catbox.moe/y8bg4r.mp3", - "https://files.catbox.moe/q61co1.mp3", - "https://files.catbox.moe/euq7fo.mp3", - "https://files.catbox.moe/x5f56o.mp3", - "https://files.catbox.moe/avlqok.mp3", - "https://files.catbox.moe/v0twt3.mp3", - "https://files.catbox.moe/qmpvpt.mp3" - ]; - - if (songLinks.length === 0) { - return api.sendMessage("❌ Nᴏ sᴏɴɢs ᴄᴏᴜʟᴅ ʙᴇ ғᴏᴜɴᴅ!", threadID, messageID); - } - - // ⏳ React for loading - api.setMessageReaction("🎵", messageID, () => {}, true); - - // 🎲 Random song index (avoid repeat) - let index; - do { - index = Math.floor(Math.random() * songLinks.length); - } while (index === lastPlayed && songLinks.length > 1); - lastPlayed = index; - - const url = songLinks[index]; - const filePath = path.join(__dirname, `/cache/song_${index}.mp3`); - - try { - const response = await axios({ - url, - method: "GET", - responseType: "stream" - }); - - const writer = fs.createWriteStream(filePath); - response.data.pipe(writer); - - writer.on("finish", async () => { - api.sendMessage( - { - body: "🎶 Hᴇʀᴇ's ʏᴏᴜʀ sᴏɴɢ 🎧", - attachment: fs.createReadStream(filePath) - }, - threadID, - async () => { - fs.unlinkSync(filePath); - }, - messageID - ); - }); - - writer.on("error", (err) => { - console.error("Error writing file:", err); - api.sendMessage("❌ Fᴀɪʟᴇᴅ ᴛᴏ sᴇɴᴅ sᴏɴɢ!", threadID, messageID); - }); - - } catch (err) { - console.error("Download error:", err); - api.sendMessage("⚠️ Fᴀɪʟᴇᴅ ᴛᴏ ᴅᴏᴡɴʟᴏᴀᴅ sᴏɴɢ!", threadID, messageID); - } - } -}; diff --git a/scripts/cmds/gcimg.js b/scripts/cmds/gcimg.js deleted file mode 100644 index 0330a8e4..00000000 --- a/scripts/cmds/gcimg.js +++ /dev/null @@ -1,113 +0,0 @@ -const axios = require("axios"); -const path = require("path"); - -const baseApiUrl = async () => { - const base = await axios.get("https://raw.githubusercontent.com/Mostakim0978/D1PT0/refs/heads/main/baseApiUrl.json"); - return base.data.api; -}; - -async function getAvatarUrls(userIDs) { - const avatarURLs = []; - for (let userID of userIDs) { - try { - const avatar = await axios.get( - `https://graph.facebook.com/${userID}/picture?height=1500&width=1500&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662` - ); - avatarURLs.push(avatar.request.res.responseUrl); - } catch (err) { - avatarURLs.push("https://i.ibb.co/qk0bnY8/363492156-824459359287620-3125820102191295474-n-png-nc-cat-1-ccb-1-7-nc-sid-5f2048-nc-eui2-Ae-HIhi-I.png"); - } - } - return avatarURLs; -} - -module.exports = { - config: { - name: "gcimg", - aliases: ["gcimage", "grpimage"], - version: "1.1", - author: "nexo_here", - countDown: 5, - role: 0, - description: "Generate a styled group image with profile pictures", - category: "Ai-Image", - guide: "{pn} [--color white] [--bgcolor black] [--admincolor red] [--membercolor cyan] [--groupBorder lime] [--glow true]" - }, - - onStart: async function ({ api, args, event, message }) { - try { - let textColor = "white"; - let bgColor = null; - let adminColor = "yellow"; - let memberColor = "cyan"; - let borderColor = "lime"; - let glow = false; - - for (let i = 0; i < args.length; i++) { - switch (args[i]) { - case "--color": - textColor = args[i + 1]; - i++; - break; - case "--bgcolor": - bgColor = args[i + 1]; - i++; - break; - case "--admincolor": - adminColor = args[i + 1]; - i++; - break; - case "--membercolor": - memberColor = args[i + 1]; - i++; - break; - case "--groupBorder": - borderColor = args[i + 1]; - i++; - break; - case "--glow": - glow = args[i + 1]?.toLowerCase() === "true"; - i++; - break; - } - } - - const threadInfo = await api.getThreadInfo(event.threadID); - const participantIDs = threadInfo.participantIDs; - const adminIDs = threadInfo.adminIDs.map(admin => admin.id); - - const memberAvatars = await getAvatarUrls(participantIDs); - const adminAvatars = await getAvatarUrls(adminIDs); - - const payload = { - groupName: threadInfo.threadName, - groupPhotoURL: threadInfo.imageSrc, - memberURLs: memberAvatars, - adminURLs: adminAvatars, - color: textColor, - bgcolor: bgColor, - admincolor: adminColor, - membercolor: memberColor, - groupborderColor: borderColor, - glow - }; - - const waitMsg = await message.reply("🛠️ | Generating group image, please wait..."); - api.setMessageReaction("⏳", event.messageID, () => {}, true); - - const response = await axios.post(`${await baseApiUrl()}/gcimg`, payload, { responseType: "stream" }); - - message.unsend(waitMsg.messageID); - api.setMessageReaction("✅", event.messageID, () => {}, true); - - return message.reply({ - body: "✨ | Here's your group image:", - attachment: response.data - }); - - } catch (err) { - console.error("[gcimg] Error:", err); - return message.reply(`❌ | An error occurred: ${err.message}`); - } - } -}; diff --git a/scripts/cmds/getfbstate.js b/scripts/cmds/getfbstate.js deleted file mode 100644 index 559552be..00000000 --- a/scripts/cmds/getfbstate.js +++ /dev/null @@ -1,66 +0,0 @@ -const fs = require("fs-extra"); - -module.exports = { - config: { - name: "getfbstate", - aliases: ["getstate", "getcookie"], - version: "1.2", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Lấy fbstate hiện tại", - en: "Get current fbstate" - }, - category: "owner", - guide: { - en: " {pn}: get fbstate (appState)\n" - + " {pn} [cookies|cookie|c]: get fbstate with cookies format\n" - + " {pn} [string|str|s]: get fbstate with string format\n", - vi: " {pn}: get fbstate (appState)\n" - + " {pn} [cookies|cookie|c]: get fbstate dạng cookies\n" - + " {pn} [string|str|s]: get fbstate dạng string\n" - } - }, - - langs: { - vi: { - success: "Đã gửi fbstate đến bạn, vui lòng kiểm tra tin nhắn riêng của bot" - }, - en: { - success: "Sent fbstate to you, please check bot's private message" - } - }, - - onStart: async function ({ message, api, event, args, getLang }) { - let fbstate; - let fileName; - - if (["cookie", "cookies", "c"].includes(args[0])) { - fbstate = JSON.stringify(api.getAppState().map(e => ({ - name: e.key, - value: e.value - })), null, 2); - fileName = "cookies.json"; - } - else if (["string", "str", "s"].includes(args[0])) { - fbstate = api.getAppState().map(e => `${e.key}=${e.value}`).join("; "); - fileName = "cookiesString.txt"; - } - else { - fbstate = JSON.stringify(api.getAppState(), null, 2); - fileName = "appState.json"; - } - - const pathSave = `${__dirname}/tmp/${fileName}`; - fs.writeFileSync(pathSave, fbstate); - - if (event.senderID != event.threadID) - message.reply(getLang("success")); - - api.sendMessage({ - body: fbstate, - attachment: fs.createReadStream(pathSave) - }, event.senderID, () => fs.unlinkSync(pathSave)); - } -}; \ No newline at end of file diff --git a/scripts/cmds/goatstore.js b/scripts/cmds/goatstore.js deleted file mode 100644 index 2f83bd45..00000000 --- a/scripts/cmds/goatstore.js +++ /dev/null @@ -1,773 +0,0 @@ -const fs = require("fs"); -const path = require("path"); -const axios = require("axios"); - -const API_BASE = "https://mirai-store.vercel.app"; -const PASTE_API_BASE = "https://pastebin-raw.vercel.app"; -const userSeenNoti = new Map(); -const AUTOSYNC_CACHE_PATH = path.join(process.cwd(), "goatstore_sync_cache.json"); - -let _updateCheckCache = null; -const UPDATE_CHECK_INTERVAL = 1000 * 60 * 30; - -function loadSyncCache() { - try { return JSON.parse(fs.readFileSync(AUTOSYNC_CACHE_PATH, "utf8")); } - catch { return {}; } -} - -function saveSyncCache(cache) { - try { fs.writeFileSync(AUTOSYNC_CACHE_PATH, JSON.stringify(cache, null, 2)); } - catch (_) {} -} - -function hashContent(content) { - let h = 0; - for (let i = 0; i < content.length; i++) h = (h * 31 + content.charCodeAt(i)) | 0; - return h.toString(16); -} - -function detectFramework(code) { - // Primary check: GoatBot config e author + role dutai thake, - // Mirai config e credits + hasPermission (function) dutai thake. - const hasAuthorRole = /\bauthor\s*:/.test(code) && /\brole\s*:/.test(code); - const hasCreditsPermission = /\bcredits\s*:/.test(code) && /\bhasPermission\s*[:(]/.test(code); - - if (hasAuthorRole && !hasCreditsPermission) return "goat"; - if (hasCreditsPermission && !hasAuthorRole) return "mirai"; - - // Ambiguous hole (dutai match korle ba kono ta match na korle) structural check e fallback - const isGoatStructure = - /module\.exports\s*=\s*\{/.test(code) && - /onStart\s*[:(]|onChat\s*[:(]|onLoad\s*[:(]/.test(code); - const isMiraiStructure = - /module\.exports\.config\s*=/.test(code) || - /module\.exports\.run\s*=/.test(code); - - return (isGoatStructure && !isMiraiStructure) ? "goat" : "mirai"; -} - -/** - * Uploads code to the pastebin service and returns a guaranteed non-empty - * rawUrl string, or throws. The MiraiStore /miraistore/upload endpoint now - * hard-requires rawUrl on every call (returns { error: "rawUrl required" } - * if it's missing), so this must always run BEFORE building the upload - * payload — every caller below does that, and aborts (never calls - * /miraistore/upload) if this throws. - */ -async function pasteCode(content) { - const res = await axios.post(`${PASTE_API_BASE}/api/paste`, { code: content }); - if (!res.data?.id) throw new Error("Paste API theke id pawa jayni."); - const rawUrl = res.data.url || `${PASTE_API_BASE}/raw/${res.data.id}`; - if (!rawUrl || typeof rawUrl !== "string" || !rawUrl.trim()) { - // Defensive: guarantee callers never receive an empty/falsy rawUrl, - // since the server now rejects the upload outright without one. - throw new Error("Paste API theke valid rawUrl toiri kora gelo na."); - } - return { id: res.data.id, rawUrl }; -} - -async function checkSelfUpdate() { - const now = Date.now(); - if (_updateCheckCache && (now - _updateCheckCache.checkedAt) < UPDATE_CHECK_INTERVAL) - return _updateCheckCache.result; - try { - const res = await axios.get(`${API_BASE}/miraistore/search?q=goatstore&limit=10&type=goat-command`); - const cmds = Array.isArray(res.data?.commands) ? res.data.commands : []; - const match = - cmds.find(c => c.name?.toLowerCase() === "goatstore" && c.author === module.exports.config.author) || - cmds.find(c => c.name?.toLowerCase() === "goatstore"); - if (!match) { _updateCheckCache = { checkedAt: now, result: null }; return null; } - const parseVer = v => String(v).split(".").map(n => parseInt(n) || 0); - const cmp = (a, b) => { - const pa = parseVer(a), pb = parseVer(b); - for (let i = 0; i < Math.max(pa.length, pb.length); i++) { - const d = (pa[i] || 0) - (pb[i] || 0); - if (d !== 0) return d; - } - return 0; - }; - const current = module.exports.config.version; - const latest = match.version || "N/A"; - const result = { hasUpdate: cmp(latest, current) > 0, currentVersion: current, latestVersion: latest, latestId: match.id }; - _updateCheckCache = { checkedAt: now, result }; - return result; - } catch (_) { return null; } -} - -async function getTodayUpdates() { - try { - const [c, e] = await Promise.all([ - axios.get(`${API_BASE}/miraistore/list?limit=50&type=goat-command`), - axios.get(`${API_BASE}/miraistore/list?limit=50&type=goat-event`) - ]); - const today = new Date().toDateString(); - return [...(c.data.commands || []), ...(e.data.commands || [])] - .filter(cmd => new Date(cmd.uploadDate).toDateString() === today); - } catch (_) { return []; } -} - -async function runAutoSync() { - const baseDir = process.cwd(); - const folders = [ - { dir: path.join(baseDir, "scripts", "cmds"), kind: "command" }, - { dir: path.join(baseDir, "scripts", "events"), kind: "event" } - ].filter(f => fs.existsSync(f.dir)); - - if (!folders.length) return; - - const cache = loadSyncCache(); - - for (const { dir, kind } of folders) { - const files = fs.readdirSync(dir).filter(f => f.endsWith(".js")); - for (const file of files) { - const fullPath = path.join(dir, file); - const cacheKey = `${kind}:${file}`; - let content; - try { content = fs.readFileSync(fullPath, "utf8"); } catch (_) { continue; } - - const hash = hashContent(content); - if (cache[cacheKey] === hash) continue; - - try { new Function(content); } catch (_) { continue; } - if (detectFramework(content) !== "goat") continue; - - // rawUrl MUST be generated first — the server now rejects the upload - // entirely (error: "rawUrl required") if it's missing. pasteCode() - // throws if it can't produce a valid non-empty URL, so on failure we - // skip this file for this sync pass (it'll be retried next sync, - // since the cache is only updated on a successful upload response). - let rawUrl; - try { - const result = await pasteCode(content); - rawUrl = result.rawUrl; - } catch (err) { - console.error(`[goatstore-sync] Paste failed for ${file}:`, err.response?.data?.error || err.message); - await new Promise(r => setTimeout(r, 500)); - continue; - } - - try { - const author = content.match(/author\s*:\s*["'`](.*?)["'`]/)?.[1] - || content.match(/credits\s*:\s*["'`](.*?)["'`]/)?.[1] - || "Unknown"; - const category = content.match(/category\s*:\s*["'`](.*?)["'`]/)?.[1] || "Uncategorized"; - const res = await axios.post(`${API_BASE}/miraistore/upload`, { rawUrl, rawCode: content, framework: "goat", kind, author, category }); - if (res.data?.error) { - // Includes the server's "rawUrl required" case if it were ever hit - // (shouldn't happen given the guard above, but surfaced clearly - // either way instead of a silent/generic failure). - console.error(`[goatstore-sync] Paste hoyeche (${rawUrl}) kintu store API error for ${file}:`, res.data.error); - } else if (res.data?.olderVersion) { - console.log(`[goatstore-sync] ${file}: older version — stored as separate new entry (ID: ${res.data.id}).`); - cache[cacheKey] = hash; - } else if (res.data?.updated) { - console.log(`[goatstore-sync] ${file}: updated existing entry (ID: ${res.data.id}) to v${res.data.version}.`); - cache[cacheKey] = hash; - } else { - console.log(`[goatstore-sync] ${file}: uploaded as new entry (ID: ${res.data.id}).`); - cache[cacheKey] = hash; - } - } catch (err) { - console.error(`[goatstore-sync] Paste hoyeche (${rawUrl}) kintu store API call fail for ${file}:`, err.response?.data?.error || err.message); - } - - await new Promise(r => setTimeout(r, 500)); - } - } - - saveSyncCache(cache); -} - -const buildBar = pct => "█".repeat(Math.floor(pct / 10)) + "░".repeat(10 - Math.floor(pct / 10)); -const frames = ["◖", "◕", "◔", "◓", "◒", "◑", "◐"]; - -async function animateInstall(api, threadID, name) { - const steps = [ - { label: "Downloading source", pct: 30, delay: 600 }, - { label: "Verifying integrity", pct: 60, delay: 900 }, - { label: "Writing to disk", pct: 85, delay: 700 }, - { label: "Registering command", pct: 100, delay: 600 } - ]; - const info = await api.sendMessage(`📦 Installing ${name}...\n\n◖ Fetching package info...\n[░░░░░░░░░░] 0%`, threadID); - for (let i = 0; i < steps.length; i++) { - await new Promise(r => setTimeout(r, steps[i].delay)); - await api.editMessage(`📦 Installing ${name}...\n\n${frames[i]} ${steps[i].label}...\n[${buildBar(steps[i].pct)}] ${steps[i].pct}%`, info.messageID); - } - return info.messageID; -} - -async function animateUpload(api, threadID, name) { - const steps = [ - { label: "Reading file", pct: 25, delay: 500 }, - { label: "Uploading to paste", pct: 55, delay: 900 }, - { label: "Registering to store", pct: 85, delay: 700 }, - { label: "Finalizing", pct: 100, delay: 500 } - ]; - const info = await api.sendMessage(`📤 Uploading ${name}...\n\n◖ Preparing upload...\n[░░░░░░░░░░] 0%`, threadID); - for (let i = 0; i < steps.length; i++) { - await new Promise(r => setTimeout(r, steps[i].delay)); - await api.editMessage(`📤 Uploading ${name}...\n\n${frames[i]} ${steps[i].label}...\n[${buildBar(steps[i].pct)}] ${steps[i].pct}%`, info.messageID); - } - return info.messageID; -} - -function autoloadCommand(filePath) { - try { - delete require.cache[require.resolve(filePath)]; - const cmd = require(filePath); - if (cmd?.config?.name) { - const name = cmd.config.name.toLowerCase(); - global.GoatBot.commands.set(name, cmd); - if (Array.isArray(cmd.config.aliases)) - cmd.config.aliases.forEach(a => global.GoatBot.commands.set(a.toLowerCase(), cmd)); - if (typeof cmd.onLoad === "function") cmd.onLoad({}); - return { success: true, name }; - } - return { success: false, reason: "Missing config.name." }; - } catch (err) { - return { success: false, reason: err.message }; - } -} - -async function doInstall(api, threadID, id, forceKind = null) { - let cmdData = null; - try { - const res = await axios.get(`${API_BASE}/miraistore/search?q=${encodeURIComponent(id)}`); - const data = res.data; - if (!isNaN(id) && data?.rawCode && !Array.isArray(data)) cmdData = data; - else if (Array.isArray(data?.commands)) cmdData = data.commands.find(c => String(c.id) === String(id)); - if (!cmdData?.rawCode) return api.sendMessage("❌ Command not found or rawCode missing.", threadID); - } catch (_) { return api.sendMessage("❌ Failed to fetch command info.", threadID); } - - if (!String(cmdData.type || "").startsWith("goat-")) - return api.sendMessage( - `❌ This is not a GoatBot file!\n` + - `├‣ Type : ${cmdData.type || "unknown"}\n` + - `╰────────────◊\n` + - `⚠️ Only goat-command and goat-event can be installed here.`, - threadID - ); - - try { new Function(cmdData.rawCode); } - catch (err) { return api.sendMessage(`❌ Syntax error in remote code.\n${err.message}`, threadID); } - - const displayName = cmdData.name || `gs_${id}`; - const isEvent = forceKind === "event" ? true : forceKind === "command" ? false : String(cmdData.type).endsWith("-event"); - - let pid; - try { pid = await animateInstall(api, threadID, displayName); } catch (_) {} - - const fileName = displayName.replace(/\s+/g, "_") + ".js"; - const baseDir = process.cwd(); - const installDir = isEvent ? path.join(baseDir, "scripts", "events") : path.join(baseDir, "scripts", "cmds"); - const filePath = path.join(installDir, fileName); - const locLabel = isEvent ? `scripts/events/${fileName}` : `scripts/cmds/${fileName}`; - - try { - if (!fs.existsSync(installDir)) fs.mkdirSync(installDir, { recursive: true }); - fs.writeFileSync(filePath, cmdData.rawCode, "utf-8"); - } catch (err) { - if (pid) api.unsendMessage(pid); - return api.sendMessage(`❌ Failed to write file:\n${err.message}`, threadID); - } - - try { await axios.post(`${API_BASE}/miraistore/install/${cmdData.id}`); } catch (_) {} - - const load = isEvent ? { success: false } : autoloadCommand(filePath); - - const msg = - `✅ Installed Successfully!\n` + - `╭─‣ Name : ${cmdData.name || "Unknown"}\n` + - `├‣ Type : ${cmdData.type || "N/A"}\n` + - `├‣ Author : ${cmdData.author || "Unknown"}\n` + - `├‣ Version : ${cmdData.version || "N/A"}\n` + - `├‣ Category : ${cmdData.category || "N/A"}\n` + - `├‣ ID : ${id}\n` + - `├‣ Location : ${locLabel}\n` + - `╰────────────◊\n` + - (load.success ? `🚀 "${load.name}" is now live! No restart needed.` - : isEvent ? `⚠️ Event saved. Restart bot to apply.` - : `⚠️ Autoload failed: ${load.reason}`); - - if (pid) { - try { await api.editMessage(msg, pid); setTimeout(() => api.unsendMessage(pid).catch(() => {}), 5000); } - catch (_) { api.sendMessage(msg, threadID); } - } else api.sendMessage(msg, threadID); -} - -async function sendListPage(api, threadID, senderID, type, page, limit = 10) { - const offset = (page - 1) * limit; - try { - const res = await axios.get(`${API_BASE}/miraistore/list?limit=${limit}&offset=${offset}&type=${type}`); - const data = res.data; - if (!Array.isArray(data.commands) || !data.commands.length) - return api.sendMessage("❌ No results found for this page.", threadID); - - const totalPages = Math.ceil(data.total / limit); - const label = type === "goat-event" ? "GoatBot Events" : "GoatBot Commands"; - let msg = `📂 ${label} — Page ${page}/${totalPages} (${data.total} total)\n\n`; - data.commands.forEach(cmd => { - msg += `╭─‣ ${cmd.name} 〄\n`; - msg += `├‣ ID : ${cmd.id}\n`; - msg += `├‣ Author : ${cmd.author}\n`; - msg += `├‣ Category : ${cmd.category}\n`; - msg += `╰────────────◊\n`; - msg += ` ✰ Upload : ${new Date(cmd.uploadDate || Date.now()).toDateString()}\n\n`; - }); - if (totalPages > 1) msg += `Reply "page " or react to go next page.`; - - const sent = await api.sendMessage(msg.trim(), threadID); - if (totalPages > 1) { - const h = { commandName: "goatstore", messageID: sent.messageID, listType: type, page, totalPages, limit, mode: "list", senderID }; - global.GoatBot.onReply.set(sent.messageID, h); - global.GoatBot.onReaction.set(sent.messageID, h); - } - } catch (_) { api.sendMessage("❌ List API error.", threadID); } -} - -async function sendSearchPage(api, threadID, senderID, query, page, limit = 5) { - const offset = (page - 1) * limit; - try { - const [cr, er] = await Promise.all([ - axios.get(`${API_BASE}/miraistore/search?q=${encodeURIComponent(query)}&limit=${limit}&offset=${offset}&type=goat-command`), - axios.get(`${API_BASE}/miraistore/search?q=${encodeURIComponent(query)}&limit=${limit}&offset=${offset}&type=goat-event`) - ]); - const all = [...(cr.data.commands || []), ...(er.data.commands || [])]; - const total = (cr.data.total || 0) + (er.data.total || 0); - if (!all.length) return api.sendMessage(`❌ No GoatBot results found for "${query}".`, threadID); - - const totalPages = Math.max(1, Math.ceil(total / (limit * 2))); - let msg = `🔍 Search: "${query}" (${total} found)\n\n`; - all.forEach(cmd => { - msg += `╭─‣ ${cmd.name} 〄\n`; - msg += `├‣ ID : ${cmd.id}\n`; - msg += `├‣ Type : ${cmd.type === "goat-event" ? "🎯 Event" : "⚡ Command"}\n`; - msg += `├‣ Author : ${cmd.author}\n`; - msg += `├‣ Category : ${cmd.category}\n`; - msg += `╰────────────◊\n`; - msg += ` ✰ Upload : ${new Date(cmd.uploadDate || Date.now()).toDateString()}\n\n`; - }); - if (totalPages > 1) msg += `Page ${page}/${totalPages}\nReact to go next page.`; - - const sent = await api.sendMessage(msg.trim(), threadID); - if (totalPages > 1) { - const h = { commandName: "goatstore", messageID: sent.messageID, query, page, totalPages, limit, mode: "search", senderID }; - global.GoatBot.onReply.set(sent.messageID, h); - global.GoatBot.onReaction.set(sent.messageID, h); - } - } catch (_) { api.sendMessage("❌ Search API error.", threadID); } -} - -async function uploadFile(api, threadID, filePath, kind) { - let data; - try { data = fs.readFileSync(filePath, "utf8"); } - catch (err) { return api.sendMessage(`❌ Read failed:\n${err.message}`, threadID); } - - try { new Function(data); } - catch (err) { return api.sendMessage(`❌ Syntax Error:\n${err.message}`, threadID); } - - const displayName = data.match(/name\s*:\s*["'`](.*?)["'`]/)?.[1] || path.basename(filePath); - if (detectFramework(data) !== "goat") - return api.sendMessage(`❌ Only GoatBot files can be uploaded here.`, threadID); - - let pid; - try { pid = await animateUpload(api, threadID, displayName); } catch (_) {} - - // rawUrl MUST be generated and confirmed valid BEFORE calling - // /miraistore/upload — the server now hard-requires it and returns - // { error: "rawUrl required" } otherwise. pasteCode() throws on any - // failure (paste API error OR empty/invalid URL), so this whole block - // aborts cleanly (with a clear message) before ever reaching the store call. - let rawUrl; - try { - const result = await pasteCode(data); - rawUrl = result.rawUrl; - } catch (err) { - if (pid) api.unsendMessage(pid); - return api.sendMessage( - `❌ Paste Upload Failed!\n` + - `╭─‣ Step : Code -> Pastebin\n` + - `├‣ Error : ${err.response?.data?.error || err.message}\n` + - `╰────────────◊\n` + - `💡 Eta bot side er problem — pastebin e code ta e upload hoyni, tai rawUrl toiri hoyni.`, - threadID - ); - } - - try { - const res = await axios.post(`${API_BASE}/miraistore/upload`, { rawUrl, rawCode: data, framework: "goat", kind }); - - // "Already exists" (same name+author+type+version) and protected-name - // blocks come back as res.data.error — but they aren't really paste/API - // failures, so give them their own clear message instead of the generic - // "Store API Error" wording below. - if (res.data?.error === "Already exists" || res.data?.error === "Not allowed") { - if (pid) api.unsendMessage(pid); - return api.sendMessage( - `⚠️ ${res.data.error === "Not allowed" ? "Upload Blocked!" : "Already Exists in Store!"}\n` + - `╭─‣ Name : ${displayName}\n` + - (res.data.id ? `├‣ ID : ${res.data.id}\n` : "") + - `╰────────────◊\n` + - `💡 ${res.data.message}`, - threadID - ); - } - - // Server hard-requires rawUrl now — give this its own clear message too - // instead of falling through to the generic "Store API Error" wording, - // since this specific case means the payload itself was incomplete. - if (res.data?.error === "rawUrl required") { - if (pid) api.unsendMessage(pid); - return api.sendMessage( - `⚠️ rawUrl Missing!\n` + - `╭─‣ Name : ${displayName}\n` + - `╰────────────◊\n` + - `💡 Store API ke rawUrl pathano hoyni. Eta bot side er bug — report koro.`, - threadID - ); - } - - if (res.data?.error) { - if (pid) api.unsendMessage(pid); - return api.sendMessage( - `⚠️ Paste Hoyeche, Kintu Store API Error!\n` + - `╭─‣ Paste Link : ${rawUrl}\n` + - `├‣ Error : ${res.data.error}\n` + - `╰────────────◊\n` + - `💡 Code ta pastebin e successfully upload hoyeche (link kaj korbe), kintu MiraiStore backend register korte parenai. Backend/API side check koro.`, - threadID - ); - } - - const author = data.match(/author\s*:\s*["'`](.*?)["'`]/)?.[1] - || data.match(/credits\s*:\s*["'`](.*?)["'`]/)?.[1] - || "Unknown"; - const version = data.match(/version\s*:\s*["'`](.*?)["'`]/)?.[1] || "N/A"; - const category = data.match(/category\s*:\s*["'`](.*?)["'`]/)?.[1] || "Uncategorized"; - - // Distinguish: brand new entry vs overwritten (newer version) entry vs - // stored-separately (older version) entry — each gets its own header - // and surfaces the server's message so the user knows exactly what happened. - let header = "✅ Upload Successful!"; - let note = ""; - if (res.data.olderVersion) { - header = "⚠️ Older Version — Stored As New Entry!"; - note = `💡 ${res.data.message}\n`; - } else if (res.data.updated) { - header = "🔄 Updated Existing Entry (Overwritten)!"; - note = `💡 ${res.data.message}\n`; - } - - const msg = - `${header}\n` + - `╭─‣ Name : ${displayName}\n` + - `├‣ Type : ${res.data.type || `goat-${kind}`}\n` + - `├‣ Version : ${version}\n` + - `├‣ Author : ${author}\n` + - `├‣ Category : ${category}\n` + - `├‣ ID : ${res.data.id}\n` + - `╰────────────◊\n` + - note + - `⭔ Upload : ${new Date().toDateString()}`; - if (pid) { try { await api.editMessage(msg, pid); } catch (_) { api.sendMessage(msg, threadID); } } - else api.sendMessage(msg, threadID); - } catch (err) { - if (pid) api.unsendMessage(pid); - api.sendMessage( - `⚠️ Paste Hoyeche, Kintu Store API Call Fail Korlo!\n` + - `╭─‣ Paste Link : ${rawUrl}\n` + - `├‣ Error : ${err.response?.data?.error || err.message}\n` + - `╰────────────◊\n` + - `💡 Code ta pastebin e ache (link kaj korbe), kintu MiraiStore backend e request e i pouchayni thik moto. Backend/network check koro.`, - threadID - ); - } -} - -module.exports = { - config: { - name: "goatstore", - aliases: ["gs", "cmdstore", "commandstore"], - version: "7.2.0", - author: "rX & EryXenX", - countDown: 3, - role: 2, - shortDescription: "GoatBot Store — Search, Install, Upload, AutoSync", - longDescription: "Browse, install, upload, and autosync GoatBot commands and events from the MiraiStore API.", - category: "system", - guide: { - en: - "{pn} — Menu / Notifications\n" + - "{pn} n — Today's updates\n" + - "{pn} list [page] — Command list\n" + - "{pn} list event [page] — Event list\n" + - "{pn} — Search\n" + - "{pn} install — Install\n" + - "{pn} event install — Force as event\n" + - "{pn} like — Like\n" + - "{pn} trending — Trending\n" + - "{pn} upload — Upload command\n" + - "{pn} upload event — Upload event\n" + - "{pn} sync — Manual sync\n" + - "{pn} delete — Delete" - }, - autoSync: true - }, - - onLoad: function () { - setTimeout(() => { checkSelfUpdate().catch(() => {}); }, 6000); - if (module.exports.config.autoSync) { - const ONE_DAY = 1000 * 60 * 60 * 24; - setTimeout(() => { - runAutoSync().catch(() => {}); - setInterval(() => { runAutoSync().catch(() => {}); }, ONE_DAY); - }, 8000); - } - }, - - onReply: async function ({ api, event, Reply }) { - const { threadID, body, senderID } = event; - const { mode, query, listType, page, totalPages, limit, senderID: origSender } = Reply; - if (senderID !== origSender) return; - const match = body.match(/^page (\d+)$/i); - if (!match) return; - const newPage = parseInt(match[1]); - if (newPage < 1 || newPage > totalPages) - return api.sendMessage(`❌ Page must be between 1 and ${totalPages}.`, threadID); - api.unsendMessage(Reply.messageID).catch(() => {}); - if (mode === "list") await sendListPage(api, threadID, senderID, listType, newPage, limit); - else await sendSearchPage(api, threadID, senderID, query, newPage, limit); - }, - - onReaction: async function ({ api, event, Reaction }) { - const { threadID, userID } = event; - const { mode, query, listType, page, totalPages, limit, senderID } = Reaction; - if (userID !== senderID) return; - if (page >= totalPages) return api.sendMessage("✅ Already on the last page.", threadID); - api.unsendMessage(Reaction.messageID).catch(() => {}); - if (mode === "list") await sendListPage(api, threadID, senderID, listType, page + 1, limit); - else await sendSearchPage(api, threadID, senderID, query, page + 1, limit); - }, - - onStart: async function ({ api, event, args }) { - const { threadID, senderID } = event; - const sub = args[0]?.toLowerCase() || null; - - if (!sub) { - const [updates, selfUpdate] = await Promise.all([getTodayUpdates(), checkSelfUpdate()]); - - if (selfUpdate?.hasUpdate && !userSeenNoti.get(`upd_${selfUpdate.latestVersion}_${senderID}`)) { - userSeenNoti.set(`upd_${selfUpdate.latestVersion}_${senderID}`, true); - return api.sendMessage( - `🆙 [ GOATSTORE UPDATE AVAILABLE ]\n` + - `━━━━━━━━━━━━━━━━━━\n` + - `Current version : v${selfUpdate.currentVersion}\n` + - `New version : v${selfUpdate.latestVersion}\n` + - `Store ID : ${selfUpdate.latestId}\n` + - `━━━━━━━━━━━━━━━━━━\n` + - `💡 !gs install ${selfUpdate.latestId}\n\n` + - `(Type "!gs" again to see the menu)`, - threadID - ); - } - - if (updates.length && !userSeenNoti.get(senderID)) { - let n = `🔔 [ NOTIFICATION ]\nToday ${updates.length} GoatBot update(s)!\n━━━━━━━━━━━━━━━━━━\n`; - updates.forEach(f => n += ` ‣ ${f.name} (ID: ${f.id})\n`); - n += `\n(Type "!gs n" for details or "!gs" again for menu)`; - userSeenNoti.set(senderID, true); - return api.sendMessage(n, threadID); - } - - return api.sendMessage( - `📦 GoatBot Store\n\nUsage:\n` + - `• !gs \n` + - `• !gs n\n` + - `• !gs list [page]\n` + - `• !gs list event [page]\n` + - `• !gs install \n` + - `• !gs event install \n` + - `• !gs like \n` + - `• !gs trending\n` + - `• !gs upload \n` + - `• !gs upload event \n` + - `• !gs sync\n` + - `• !gs delete `, - threadID - ); - } - - if (sub === "n" || sub === "notification") { - const [updates, selfUpdate] = await Promise.all([getTodayUpdates(), checkSelfUpdate()]); - let msg = ""; - if (selfUpdate?.hasUpdate) - msg += - `🆙 [ GOATSTORE SELF UPDATE ]\n` + - `━━━━━━━━━━━━━━━━━━\n` + - `Current : v${selfUpdate.currentVersion}\n` + - `Latest : v${selfUpdate.latestVersion}\n` + - `ID : ${selfUpdate.latestId}\n` + - `━━━━━━━━━━━━━━━━━━\n` + - `💡 !gs install ${selfUpdate.latestId}\n\n`; - if (!updates.length && !selfUpdate?.hasUpdate) - return api.sendMessage("📅 No GoatBot updates today.", threadID); - if (updates.length) { - msg += `📂 Today's GoatBot Updates\n━━━━━━━━━━━━━━━━━━\n`; - updates.forEach(cmd => - msg += `╭─‣ ${cmd.name}\n├‣ ID: ${cmd.id}\n├‣ Type: ${cmd.type || "N/A"}\n├‣ Author: ${cmd.author}\n╰────────────◊\n\n` - ); - } - return api.sendMessage(msg.trim(), threadID); - } - - if (sub === "sync") { - api.sendMessage("🔄 Starting manual sync...", threadID); - try { - await runAutoSync(); - api.sendMessage("✅ Sync complete.", threadID); - } catch (err) { - api.sendMessage(`❌ Sync failed: ${err.message}`, threadID); - } - return; - } - - if (sub === "list" || sub === "ls") { - const isEvent = args[1]?.toLowerCase() === "event"; - const page = Math.max(1, Number(isEvent ? args[2] : args[1]) || 1); - return sendListPage(api, threadID, senderID, isEvent ? "goat-event" : "goat-command", page, 10); - } - - if (sub === "event") { - const action = args[1]?.toLowerCase(); - - if (action === "install") { - const id = args[2]; - if (!id) return api.sendMessage("❌ Usage: !gs event install ", threadID); - return doInstall(api, threadID, id, "event"); - } - - if (!action) { - try { - const res = await axios.get(`${API_BASE}/miraistore/list?limit=20&type=goat-event`); - const events = res.data.commands || []; - if (!events.length) return api.sendMessage("❌ No GoatBot events found in store.", threadID); - let msg = `📂 GoatBot Store Events (${res.data.total})\n\n`; - events.forEach(cmd => { - msg += `╭─‣ ${cmd.name}\n├‣ ID : ${cmd.id}\n├‣ Author : ${cmd.author}\n╰────────────◊\n\n`; - }); - msg += `💡 Use: !gs event install `; - return api.sendMessage(msg.trim(), threadID); - } catch (_) { return api.sendMessage("❌ Event list API error.", threadID); } - } - - try { - const res = await axios.get(`${API_BASE}/miraistore/search?q=${encodeURIComponent(action)}&limit=5&type=goat-event`); - const events = res.data.commands || []; - if (!events.length) return api.sendMessage(`❌ No GoatBot event found: "${action}"`, threadID); - let msg = `📂 GoatBot Events matching "${action}"\n\n`; - events.forEach(cmd => { - msg += `╭─‣ ${cmd.name}\n├‣ ID : ${cmd.id}\n├‣ Author : ${cmd.author}\n├‣ Version : ${cmd.version || "N/A"}\n╰────────────◊\n\n`; - }); - msg += `💡 Use: !gs event install `; - return api.sendMessage(msg.trim(), threadID); - } catch (_) { return api.sendMessage("❌ Event search API error.", threadID); } - } - - if (sub === "install") { - const id = args[1]; - if (!id) return api.sendMessage("❌ Usage: !gs install ", threadID); - return doInstall(api, threadID, id, null); - } - - if (sub === "like") { - const id = args[1]; - if (!id) return api.sendMessage("❌ Usage: !gs like ", threadID); - try { - const res = await axios.post(`${API_BASE}/miraistore/like/${id}`, { userID: senderID }); - if (res.data?.message) return api.sendMessage("⚠️ Already liked.", threadID); - return api.sendMessage(`❤️ Liked! Total Likes: ${res.data.likes}`, threadID); - } catch (_) { return api.sendMessage("❌ Like API error.", threadID); } - } - - if (sub === "trend" || sub === "trending") { - try { - const res = await axios.get(`${API_BASE}/miraistore/trending?limit=5`); - const list = (res.data || []).filter(c => ["goat-command", "goat-event"].includes(c.type)); - if (!list.length) return api.sendMessage("❌ No GoatBot trending files.", threadID); - let msg = `🔥 Top GoatBot Trending 🔥\n\n`; - list.forEach((cmd, i) => { - msg += - `╭─‣ ${cmd.name}${i === 0 ? " 🏆" : ""}\n` + - `├‣ Type : ${cmd.type === "goat-event" ? "🎯 Event" : "⚡ Command"}\n` + - `├‣ Likes : ❤️ ${cmd.likes}\n` + - `├‣ Views : 👁️ ${cmd.views}\n` + - `├‣ ID : ${cmd.id}\n` + - `╰────────────◊\n\n`; - }); - return api.sendMessage(msg.trim(), threadID); - } catch (_) { return api.sendMessage("❌ Trending API error.", threadID); } - } - - if (sub === "upload") { - const isEvent = args[1]?.toLowerCase() === "event"; - const fileName = isEvent ? args[2] : args[1]; - const kind = isEvent ? "event" : "command"; - if (!fileName) - return api.sendMessage(`📁 Usage:\n• !gs upload \n• !gs upload event `, threadID); - const baseDir = process.cwd(); - const dirs = kind === "event" - ? [path.join(baseDir, "scripts", "events")] - : [path.join(baseDir, "scripts", "cmds"), path.join(baseDir, "scripts", "events")]; - let filePath = null; - for (const dir of dirs) { - if (fs.existsSync(path.join(dir, fileName))) { filePath = path.join(dir, fileName); break; } - if (fs.existsSync(path.join(dir, fileName + ".js"))) { filePath = path.join(dir, fileName + ".js"); break; } - } - if (!filePath) return api.sendMessage(`❌ File not found: "${fileName}"`, threadID); - return uploadFile(api, threadID, filePath, kind); - } - - if (sub === "delete") { - const id = args[1], secret = args[2]; - if (!id || !secret) return api.sendMessage("❌ Usage: !gs delete ", threadID); - try { - const res = await axios.post(`${API_BASE}/miraistore/delete/${id}`, { secret }); - if (res.data?.error) return api.sendMessage(`❌ ${res.data.error}`, threadID); - return api.sendMessage(`🗑️ Deleted! ID: ${id}`, threadID); - } catch (_) { return api.sendMessage("❌ Delete API error.", threadID); } - } - - const query = args.join(" "); - try { - const res = await axios.get(`${API_BASE}/miraistore/search?q=${encodeURIComponent(query)}`); - const data = res.data; - if (!data || data.message) return api.sendMessage("❌ Not found.", threadID); - - if (!isNaN(query) && !Array.isArray(data) && !data.commands) { - if (!String(data.type || "").startsWith("goat-")) - return api.sendMessage( - `⚠️ ID ${query} is not a GoatBot file.\n├‣ Type : ${data.type || "unknown"}\n╰── Only goat-command / goat-event shown here.`, - threadID - ); - return api.sendMessage( - `${data.type === "goat-event" ? "🎯 GoatBot Event" : "⚡ GoatBot Command"}\n` + - `╭─‣ Name : ${data.name}\n` + - `├‣ Author : ${data.author}\n` + - `├‣ Version : ${data.version || "N/A"}\n` + - `├‣ Category : ${data.category}\n` + - `├‣ Views : 👁️ ${data.views}\n` + - `├‣ Likes : ❤️ ${data.likes}\n` + - `├‣ Installs : ⬇️ ${data.installs}\n` + - `├‣ ID : ${data.id}\n` + - `╰────────────◊\n` + - `⭔ Description: ${data.description || "No description"}\n` + - `⭔ Upload : ${new Date(data.uploadDate || Date.now()).toDateString()}\n` + - `🌐 URL : ${data.rawUrl}`, - threadID - ); - } - - await sendSearchPage(api, threadID, senderID, query, 1); - } catch (_) { return api.sendMessage("❌ Search API error.", threadID); } - } -}; \ No newline at end of file diff --git a/scripts/cmds/group_Emoji.js b/scripts/cmds/group_Emoji.js deleted file mode 100644 index 157258b7..00000000 --- a/scripts/cmds/group_Emoji.js +++ /dev/null @@ -1,31 +0,0 @@ -module.exports = { - config: { - name: "emoji", - version: "1.1.0", - author: "Mohammad Akash", - countDown: 0, - role: 0, - shortDescription: "Change group emoji 😘", - longDescription: "Messenger গ্রুপের ইমোজি (Quick Reaction) পরিবর্তন করো মাত্র এক কমান্ডে!", - category: "box chat", - guide: "{pn} 😘" - }, - - onStart: async function ({ api, event, args }) { - const emoji = args.join(" "); - - // ⚠️ যদি কোনো ইমোজি না দেয় - if (!emoji) { - return api.sendMessage("❌ | দয়া করে একটি ইমোজি দিন! উদাহরণ: /emoji 😘", event.threadID, event.messageID); - } - - try { - // ✅ গ্রুপ ইমোজি পরিবর্তন - await api.changeThreadEmoji(emoji, event.threadID); - return api.sendMessage(`✅ | গ্রুপ ইমোজি সফলভাবে পরিবর্তন হয়েছে ${emoji} এ!`, event.threadID, event.messageID); - } catch (err) { - console.error(err); - return api.sendMessage("⚠️ | ইমোজি পরিবর্তনে সমস্যা হয়েছে, আবার চেষ্টা করুন!", event.threadID, event.messageID); - } - } -}; diff --git a/scripts/cmds/group_refresh.js b/scripts/cmds/group_refresh.js deleted file mode 100644 index 66902084..00000000 --- a/scripts/cmds/group_refresh.js +++ /dev/null @@ -1,5032 +0,0 @@ -exports.config = { - name: "group_refresh", - version: "1.2", - author: "MOHAMMAD AKASH", - countDown: 0, - role: 2, - shortDescription: "Group refresh frog pattern", - category: "fun" -}; - -const last = {}; -const cool = 10000; - -const frogText = `➖ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -🌪️ -`; - -exports.onStart = async function(){}; - -exports.onChat = async function({event, api}){ - const t = event.threadID; - const now = Date.now(); - - if(event.senderID === api.getCurrentUserID()) return; - - if(last[t] && now - last[t] < cool) return; - - const msg = (event.body || "").toLowerCase().trim(); - - if(msg === "group refresh"){ - for(let i = 0; i < 5; i++){ - api.sendMessage(frogText, t); - } - - last[t] = now; - } -}; diff --git a/scripts/cmds/groupimage.js b/scripts/cmds/groupimage.js deleted file mode 100644 index e31e64c0..00000000 --- a/scripts/cmds/groupimage.js +++ /dev/null @@ -1,50 +0,0 @@ -const fs = require("fs"); -const axios = require("axios"); - -module.exports = { - config: { - name: "groupimage", - version: "1.1.0", - author: "Mohammad Akash", - countDown: 0, - role: 1, // অ্যাডমিন বা মডারেটরদের জন্য (চাওলে 0 করো) - shortDescription: "Change group photo", - longDescription: "রিপ্লাই দেওয়া ছবিটাকে গ্রুপ প্রোফাইল ছবিতে সেট করবে", - category: "box", - guide: "{pn} (একটা ছবিতে রিপ্লাই দাও)" - }, - - onStart: async function ({ api, event }) { - try { - // ✅ প্রথমে চেক করবো রিপ্লাই আছে কিনা - if (event.type !== "message_reply") { - return api.sendMessage("❌ দয়া করে একটি ছবিতে রিপ্লাই দাও!", event.threadID, event.messageID); - } - - // ✅ অ্যাটাচমেন্ট আছে কিনা - const attachments = event.messageReply.attachments; - if (!attachments || attachments.length === 0) { - return api.sendMessage("❌ রিপ্লাই করা মেসেজে কোনো ছবি পাওয়া যায়নি!", event.threadID, event.messageID); - } - - // ✅ একাধিক ছবি দেওয়া থাকলে - if (attachments.length > 1) { - return api.sendMessage("⚠️ শুধু একটি ছবিতে রিপ্লাই দাও!", event.threadID, event.messageID); - } - - // ✅ ডাউনলোড ও সেট করা - const imageURL = attachments[0].url; - const pathImg = __dirname + "/cache/groupimage.png"; - const getData = (await axios.get(imageURL, { responseType: "arraybuffer" })).data; - - fs.writeFileSync(pathImg, Buffer.from(getData, "utf-8")); - await api.changeGroupImage(fs.createReadStream(pathImg), event.threadID); - fs.unlinkSync(pathImg); - - return api.sendMessage("✅ | গ্রুপ প্রোফাইল ছবি সফলভাবে পরিবর্তন হয়েছে!", event.threadID, event.messageID); - } catch (error) { - console.error(error); - return api.sendMessage("⚠️ | ছবিটি সেট করা যায়নি, আবার চেষ্টা করো!", event.threadID, event.messageID); - } - } -}; diff --git a/scripts/cmds/groupname.js b/scripts/cmds/groupname.js deleted file mode 100644 index 181abae8..00000000 --- a/scripts/cmds/groupname.js +++ /dev/null @@ -1,33 +0,0 @@ -module.exports = { - config: { - name: "groupname", - version: "1.1.0", - author: "Mohammad Akash", - countDown: 0, - role: 1, // শুধু গ্রুপ অ্যাডমিন বা বট অ্যাডমিন (চাওলে 0 করো) - shortDescription: "Change group name", - longDescription: "তুমি যেই নাম দেবে সেটাই গ্রুপের নতুন নাম হবে।", - category: "box", - guide: "{pn} [new name]" - }, - - onStart: async function ({ api, event, args }) { - const name = args.join(" "); - - if (!name) { - return api.sendMessage( - "❌ | দয়া করে নতুন গ্রুপ নাম লিখো!\n\n📝 উদাহরণঃ /groupname Dark Army 💀", - event.threadID, - event.messageID - ); - } - - try { - await api.setTitle(name, event.threadID); - api.sendMessage(`✅ | গ্রুপের নাম পরিবর্তন হয়েছে:\n➡️ ${name}`, event.threadID, event.messageID); - } catch (err) { - console.error(err); - api.sendMessage("⚠️ | নাম পরিবর্তন করা যায়নি! নিশ্চিত হও বটের পর্যাপ্ত পারমিশন আছে কিনা।", event.threadID, event.messageID); - } - } -}; diff --git a/scripts/cmds/grouptag.js b/scripts/cmds/grouptag.js deleted file mode 100644 index 5a3554e3..00000000 --- a/scripts/cmds/grouptag.js +++ /dev/null @@ -1,245 +0,0 @@ -module.exports = { - config: { - name: "grouptag", - aliases: ["grtag"], - version: "1.5", - author: "NTKhang", - countDown: 5, - role: 0, - description: { - vi: "Tag thành viên theo nhóm", - en: "Tag members by group" - }, - category: "info", - guide: { - vi: " {pn} add <@tags>: dùng để thêm nhóm tag mới hoặc thêm thành viên vào nhóm tag đã có" - + "\n Ví dụ:" - + "\n {pn} add TEAM1 @tag1 @tag2" - + "\n\n {pn} del <@tags>: dùng để xóa các thành viên được tag khỏi nhóm tag " - + "\n Ví dụ:" - + "\n {pn} del TEAM1 @tag1 @tag2" - + "\n\n {pn} remove : dùng để xóa nhóm tag" - + "\n Ví dụ:" - + "\n {pn} remove TEAM1" - + "\n\n {pn} tag : dùng để tag nhóm tag" - + "\n\n {pn} rename | : dùng để đổi tên nhóm tag" - + "\n\n {pn} [list | all]: dùng để xem danh sách các nhóm tag trong nhóm chat của bạn" - + "\n\n {pn} info : dùng để xem thông tin của nhóm tag", - en: " {pn} add <@tags>: use to add new group tag or add members to group tag" - + "\n Example:" - + "\n {pn} add TEAM1 @tag1 @tag2" - + "\n\n {pn} del <@tags>: use to remove members from group tag" - + "\n Example:" - + "\n {pn} del TEAM1 @tag1 @tag2" - + "\n\n {pn} remove : use to remove group tag" - + "\n Example:" - + "\n {pn} remove TEAM1" - + "\n\n {pn} tag : use to tag group tag" - + "\n\n {pn} rename | : use to rename group tag" - + "\n\n {pn} [list | all]: use to view list of group tag in your group chat" - + "\n\n {pn} info : use to view info of group tag" - } - }, - - langs: { - vi: { - noGroupTagName: "Vui lòng nhập tên nhóm tag", - noMention: "Bạn chưa tag thành viên nào để thêm vào nhóm tag", - addedSuccess: "Đã thêm các thành viên sau vào nhóm tag \"%1\":\n%2", - addedSuccess2: "Đã thêm nhóm tag \"%1\" với các thành viên sau:\n%2", - existedInGroupTag: "Các thành viên sau:\n%1\nđã có trong nhóm tag \"%2\" từ trước", - notExistedInGroupTag: "Các thành viên sau:\n%1\nkhông có trong nhóm tag \"%2\"", - noExistedGroupTag: "Nhóm tag \"%1\" không tồn tại trong box chat của bạn", - noExistedGroupTag2: "Box chat của bạn chưa thêm nhóm tag nào", - noMentionDel: "Vui lòng tag thành viên muốn xóa khỏi nhóm tag \"%1\"", - deletedSuccess: "Đã xóa các thành viên sau:\n%1\nkhỏi nhóm tag \"%2\"", - deletedSuccess2: "Đã xóa nhóm tag \"%1\"", - tagged: "Tag nhóm \"%1\":\n%2", - noGroupTagName2: "Vui lòng nhập tên nhóm tag cũ và tên mới, cách nhau bằng dấu \"|\"", - renamedSuccess: "Đã đổi tên nhóm tag \"%1\" thành \"%2\"", - infoGroupTag: "📑 | Tên nhóm: %1\n👥 | Số thành viên: %2\n👨‍👩‍👧‍👦 | Danh sách thành viên:\n %3" - }, - en: { - noGroupTagName: "Please enter group tag name", - noMention: "You haven't tagged any member to add to group tag", - addedSuccess: "Added members to group tag \"%1\":\n%2", - addedSuccess2: "Added group tag \"%1\" with members:\n%2", - existedInGroupTag: "Members:\n%1\nalready existed in group tag \"%2\"", - notExistedInGroupTag: "Members:\n%1\ndoesn't exist in group tag \"%2\"", - noExistedGroupTag: "Group tag \"%1\" doesn't exist in your group chat", - noExistedGroupTag2: "Your group chat hasn't added any group tag", - noMentionDel: "Please tag members to remove from group tag \"%1\"", - deletedSuccess: "Deleted members:\n%1\nfrom group tag \"%2\"", - deletedSuccess2: "Deleted group tag \"%1\"", - tagged: "Tag group \"%1\":\n%2", - noGroupTagName2: "Please enter old group tag name and new group tag name, separated by \"|\"", - renamedSuccess: "Renamed group tag \"%1\" to \"%2\"", - infoGroupTag: "📑 | Group name: %1\n👥 | Number of members: %2\n👨‍👩‍👧‍👦 | List of members:\n %3" - } - }, - - onStart: async function ({ message, event, args, threadsData, getLang }) { - const { threadID, mentions } = event; - for (const uid in mentions) - mentions[uid] = mentions[uid].replace("@", ""); - const groupTags = await threadsData.get(threadID, "data.groupTags", []); - - switch (args[0]) { - case "add": { - const mentionsID = Object.keys(event.mentions); - const content = (args.slice(1) || []).join(" "); - const groupTagName = content.slice(0, content.indexOf(event.mentions[mentionsID[0]]) - 1).trim(); - if (!groupTagName) - return message.reply(getLang("noGroupTagName")); - if (mentionsID.length === 0) - return message.reply(getLang("noMention")); - - const oldGroupTag = groupTags.find(tag => tag.name.toLowerCase() === groupTagName.toLowerCase()); - if (oldGroupTag) { - const usersIDExist = []; - const usersIDNotExist = []; - for (const uid in mentions) { - if (oldGroupTag.users.hasOwnProperty(uid)) { - usersIDExist.push(uid); - } - else { - oldGroupTag.users[uid] = mentions[uid]; - usersIDNotExist.push(uid); - } - } - await threadsData.set(threadID, groupTags, "data.groupTags"); - - let msg = ""; - if (usersIDNotExist.length > 0) - msg += getLang("addedSuccess", oldGroupTag.name, usersIDNotExist.map(uid => mentions[uid]).join("\n")) + "\n"; - if (usersIDExist.length > 0) - msg += getLang("existedInGroupTag", usersIDExist.map(uid => mentions[uid]).join("\n")); - message.reply(msg); - } - else { - const newGroupTag = { - name: groupTagName, - users: mentions - }; - groupTags.push(newGroupTag); - await threadsData.set(threadID, groupTags, "data.groupTags"); - message.reply(getLang("addedSuccess2", groupTagName, Object.values(mentions).join("\n"))); - } - break; - } - case "list": - case "all": { - if (args[1]) { - const groupTagName = args.slice(1).join(" "); - if (!groupTagName) - return message.reply(getLang("noGroupTagName")); - const groupTag = groupTags.find(tag => tag.name.toLowerCase() === groupTagName.toLowerCase()); - if (!groupTag) - return message.reply(getLang("noExistedGroupTag", groupTagName)); - return showInfoGroupTag(message, groupTag, getLang); - } - const msg = groupTags.reduce((msg, group) => msg + `\n\n${group.name}:\n ${Object.values(group.users).map(name => name).join("\n ")}`, ""); - message.reply(msg || getLang("noExistedGroupTag2")); - break; - } - case "info": { - const groupTagName = args.slice(1).join(" "); - if (!groupTagName) - return message.reply(getLang("noGroupTagName")); - const groupTag = groupTags.find(tag => tag.name.toLowerCase() === groupTagName.toLowerCase()); - if (!groupTag) - return message.reply(getLang("noExistedGroupTag", groupTagName)); - return showInfoGroupTag(message, groupTag, getLang); - } - case "del": { - const content = (args.slice(1) || []).join(" "); - const mentionsID = Object.keys(event.mentions); - const groupTagName = content.slice(0, content.indexOf(mentions[mentionsID[0]]) - 1).trim(); - if (!groupTagName) - return message.reply(getLang("noGroupTagName")); - if (mentionsID.length === 0) - return message.reply(getLang("noMention", groupTagName)); - const oldGroupTag = groupTags.find(tag => tag.name.toLowerCase() === groupTagName.toLowerCase()); - if (!oldGroupTag) - return message.reply(getLang("noExistedGroupTag", groupTagName)); - const usersIDExist = []; - const usersIDNotExist = []; - for (const uid in mentions) { - if (oldGroupTag.users.hasOwnProperty(uid)) { - delete oldGroupTag.users[uid]; - usersIDExist.push(uid); - } - else { - usersIDNotExist.push(uid); - } - } - await threadsData.set(threadID, groupTags, "data.groupTags"); - - let msg = ""; - if (usersIDNotExist.length > 0) - msg += getLang("notExistedInGroupTag", usersIDNotExist.map(uid => mentions[uid]).join("\n"), groupTagName) + "\n"; - if (usersIDExist.length > 0) - msg += getLang("deletedSuccess", usersIDExist.map(uid => mentions[uid]).join("\n")); - message.reply(msg); - break; - } - case "remove": - case "rm": { - const content = (args.slice(1) || []).join(" "); - const groupTagName = content.trim(); - if (!groupTagName) - return message.reply(getLang("noGroupTagName")); - const index = groupTags.findIndex(group => group.name.toLowerCase() === groupTagName.toLowerCase()); - if (index === -1) - return message.reply(getLang("noExistedGroupTag", groupTagName)); - groupTags.splice(index, 1); - await threadsData.set(threadID, groupTags, "data.groupTags"); - message.reply(getLang("deletedSuccess2", groupTagName)); - break; - } - case "rename": { - const content = (args.slice(1) || []).join(" "); - const [oldGroupTagName, newGroupTagName] = content.split("|").map(str => str.trim()); - if (!oldGroupTagName || !newGroupTagName) - return message.reply(getLang("noGroupTagName2")); - const oldGroupTag = groupTags.find(tag => tag.name.toLowerCase() === oldGroupTagName.toLowerCase()); - if (!oldGroupTag) - return message.reply(getLang("noExistedGroupTag", oldGroupTagName)); - oldGroupTag.name = newGroupTagName; - await threadsData.set(threadID, groupTags, "data.groupTags"); - message.reply(getLang("renamedSuccess", oldGroupTagName, newGroupTagName)); - break; - } - case "tag": - default: { - const content = (args.slice(args[0] === "tag" ? 1 : 0) || []).join(" "); - const groupTagName = content.trim(); - if (!groupTagName) - return message.reply(getLang("noGroupTagName")); - const oldGroupTag = groupTags.find(tag => tag.name.toLowerCase() === groupTagName.toLowerCase()); - if (!oldGroupTag) - return message.reply(getLang("noExistedGroupTag", groupTagName)); - const { users } = oldGroupTag; - const mentions = []; - let msg = ""; - for (const uid in users) { - const userName = users[uid]; - mentions.push({ - id: uid, - tag: userName - }); - msg += `${userName}\n`; - } - message.reply({ - body: getLang("tagged", groupTagName, msg), - mentions - }); - break; - } - } - } -}; - -function showInfoGroupTag(message, groupTag, getLang) { - message.reply(getLang("infoGroupTag", groupTag.name, Object.keys(groupTag.users).length, Object.keys(groupTag.users).map(uid => groupTag.users[uid]).join("\n "))); -} \ No newline at end of file diff --git a/scripts/cmds/guessnumber.js b/scripts/cmds/guessnumber.js deleted file mode 100644 index 211e22d1..00000000 --- a/scripts/cmds/guessnumber.js +++ /dev/null @@ -1,654 +0,0 @@ -const { randomString, getTime, convertTime } = global.utils; -const { createCanvas } = require('canvas'); -const rows = [ - { - col: 4, - row: 10, - rewardPoint: 1 - }, - { - col: 5, - row: 12, - rewardPoint: 2 - }, - { - col: 6, - row: 15, - rewardPoint: 3 - } -]; - -module.exports = { - config: { - name: "guessnumber", - aliases: ["guessnum"], - version: "1.1", - author: "NTKhang", - countDown: 5, - role: 0, - description: { - vi: "Game đoán số", - en: "Guess number game" - }, - category: "game", - guide: { - vi: " {pn} [4 | 5 | 6] [single | multi]: tạo một bàn chơi mới, với:" - + "\n 4 5 6 là số chữ số của số cần đoán, mặc định là 4." - + "\n single | multi là chế độ chơi, single là 1 người chơi, multi là nhiều người chơi, mặc định là single." - + "\n Ví dụ:" - + "\n {pn}" - + "\n {pn} 4 single" - + "\n" - + "\n Cách chơi: người chơi trả lời tin nhắn của bot theo quy tắc sau:" - + "\n Bạn có " + rows.map(item => `${item.row} lần (${item.col} số)`).join(", ") + "." - + "\n Sau mỗi lần đoán, bạn sẽ nhận được thêm gợi ý là số lượng chữ số đúng (hiển thị bên trái) và số lượng chữ số đúng vị trí (hiển thị bên phải)." - + "\n Lưu ý: Số được hình thành với các chữ số từ 0 đến 9, mỗi chữ số xuất hiện duy nhất một lần và số có thể đứng đầu là 0." - + "\n\n {pn} rank : xem bảng xếp hạng." - + "\n {pn} info [ | <@tag> | | <để trống>]: xem thông tin xếp hạng của bạn hoặc người khác." - + "\n {pn} reset: reset bảng xếp hạng (chỉ admin bot).", - en: " {pn} [4 | 5 | 6] [single | multi]: create a new game, with:" - + "\n 4 5 6 is the number of digits of the number to guess, default is 4." - + "\n single | multi is the game mode, single is 1 player, multi is multi player, default is single." - + "\n Example:" - + "\n {pn}" - + "\n {pn} 4 single" - + "\n" - + "\n How to play: the player replies to the message of the bot with the following rules:" - + "\n You have " + rows.map(item => `${item.row} times (${item.col} numbers)`).join(", ") + "." - + "\n After each guess, you will get additional hints of the number of correct digits (shown on the left) and the number of correct digits (shown on the right)." - + "\n Note: The number is formed with digits from 0 to 9, each digit appears only once and the number can start with 0." - + "\n\n {pn} rank : view the ranking." - + "\n {pn} info [ | <@tag> | | ]: view your or other's ranking information." - + "\n {pn} reset: reset the ranking (only admin bot)." - } - }, - - langs: { - vi: { - charts: "🏆 | Bảng xếp hạng:\n%1", - pageInfo: "Trang %1/%2", - noScore: "⭕ | Hiện tại chưa có ai ghi điểm.", - noPermissionReset: "⚠️ | Bạn không có quyền reset bảng xếp hạng.", - notFoundUser: "⚠️ | Không tìm thấy người dùng có id %1 trong bảng xếp hạng.", - userRankInfo: "🏆 | Thông tin xếp hạng:\nTên: %1\nĐiểm: %2\nSố lần chơi: %3\nSố lần thắng: %4\n%5\nSố lần thua: %6\nTỉ lệ thắng: %7%\nTổng thời gian chơi: %8", - digits: "%1 chữ số: %2", - resetRankSuccess: "✅ | Reset bảng xếp hạng thành công.", - invalidCol: "⚠️ | Vui lòng nhập số chữ số của số cần đoán là 4, 5 hoặc 6", - invalidMode: "⚠️ | Vui lòng nhập chế độ chơi là single hoặc multi", - created: "✅ | Tạo bàn chơi thành công.", - gameName: "GAME ĐOÁN SỐ", - gameGuide: "⏳ | Cách chơi:\nBạn có %1 lần đoán.\nSau mỗi lần đoán, bạn sẽ nhận được thêm gợi ý là số lượng chữ số đúng (hiển thị bên trái) và số lượng chữ số đúng vị trí (hiển thị bên phải).", - gameNote: "📄 | Lưu ý:\nSố được hình thành với các chữ số từ 0 đến 9, mỗi chữ số xuất hiện duy nhất một lần và số có thể đứng đầu là 0.", - replyToPlayGame: "🎮 | Phản hồi tin nhắn hình ảnh bên dưới kèm theo %1 số bạn đoán để chơi game.", - invalidNumbers: "⚠️ | Vui lòng nhập %1 số bạn muốn đoán", - win: "🎉 | Chúc mừng bạn đã đoán đúng số %1 sau %2 lần đoán và nhận được %3 điểm thưởng.", - loss: "🤦‍♂️ | Bạn đã thua, số đúng là %1." - }, - en: { - charts: "🏆 | Ranking:\n%1", - pageInfo: "Page %1/%2", - noScore: "⭕ | There is no one who has scored.", - noPermissionReset: "⚠️ | You do not have permission to reset the ranking.", - notFoundUser: "⚠️ | Could not find user with id %1 in the ranking.", - userRankInfo: "🏆 | Ranking information:\nName: %1\nScore: %2\nNumber of games: %3\nNumber of wins: %4\n%5\nNumber of losses: %6\nWin rate: %7%\nTotal play time: %8", - digits: "%1 digits: %2", - resetRankSuccess: "✅ | Reset the ranking successfully.", - invalidCol: "⚠️ | Please enter the number of digits of the number to guess is 4, 5 or 6", - invalidMode: "⚠️ | Please enter the game mode is single or multi", - created: "✅ | Create game successfully.", - gameName: "GUESS NUMBER GAME", - gameGuide: "⏳ | How to play:\nYou have %1 guesses.\nAfter each guess, you will get additional hints of the number of correct digits (shown on the left) and the number of correct digits (shown on the right).", - gameNote: "📄 | Note:\nThe number is formed with digits from 0 to 9, each digit appears only once and the number can start with 0.", - replyToPlayGame: "🎮 | Reply to the message below with the image of %1 numbers you guess to play the game.", - invalidNumbers: "⚠️ | Please enter %1 numbers you want to guess", - win: "🎉 | Congratulations you guessed the number %1 after %2 guesses and received %3 bonus points.", - loss: "🤦‍♂️ | You lost, the correct number is %1." - } - }, - - onStart: async function ({ message, event, getLang, commandName, args, globalData, usersData, role }) { - if (args[0] == "rank") { - const rankGuessNumber = await globalData.get("rankGuessNumber", "data", []); - if (!rankGuessNumber.length) - return message.reply(getLang("noScore")); - - const page = parseInt(args[1]) || 1; - const maxUserOnePage = 30; - - let rankGuessNumberHandle = await Promise.all(rankGuessNumber.slice((page - 1) * maxUserOnePage, page * maxUserOnePage).map(async item => { - const userName = await usersData.getName(item.id); - return { - ...item, - userName, - winNumber: item.wins?.length || 0, - lossNumber: item.losses?.length || 0 - }; - })); - - rankGuessNumberHandle = rankGuessNumberHandle.sort((a, b) => b.winNumber - a.winNumber); - const medals = ["🥇", "🥈", "🥉"]; - const rankGuessNumberText = rankGuessNumberHandle.map((item, index) => { - const medal = medals[index] || index + 1; - return `${medal} ${item.userName} - ${item.winNumber} wins - ${item.lossNumber} losses`; - }).join("\n"); - - return message.reply(getLang("charts", rankGuessNumberText || getLang("noScore")) + "\n" + getLang("pageInfo", page, Math.ceil(rankGuessNumber.length / maxUserOnePage))); - } - else if (args[0] == "info") { - const rankGuessNumber = await globalData.get("rankGuessNumber", "data", []); - let targetID; - if (Object.keys(event.mentions).length) - targetID = Object.keys(event.mentions)[0]; - else if (event.messageReply) - targetID = event.messageReply.senderID; - else if (!isNaN(args[1])) - targetID = args[1]; - else - targetID = event.senderID; - - const userDataGuessNumber = rankGuessNumber.find(item => item.id == targetID); - if (!userDataGuessNumber) - return message.reply(getLang("notFoundUser", targetID)); - - const userName = await usersData.getName(targetID); - const pointsReceived = userDataGuessNumber.points; - const winNumber = userDataGuessNumber.wins?.length || 0; - const playNumber = winNumber + (userDataGuessNumber.losses?.length || 0); - const lossNumber = userDataGuessNumber.losses?.length || 0; - const winRate = (winNumber / playNumber * 100).toFixed(2); - const winInfo = {}; - for (const item of userDataGuessNumber.wins || []) - winInfo[item.col] = winInfo[item.col] ? winInfo[item.col] + 1 : 1; - const playTime = convertTime(userDataGuessNumber.wins.reduce((a, b) => a + b.timeSuccess, 0) + userDataGuessNumber.losses.reduce((a, b) => a + b.timeSuccess, 0)); - return message.reply(getLang("userRankInfo", userName, pointsReceived, playNumber, winNumber, Object.keys(winInfo).map(item => ` + ${getLang("digits", item, winInfo[item])}`).join("\n"), lossNumber, winRate, playTime)); - } - else if (args[0] == "reset") { - if (role < 2) - return message.reply(getLang("noPermissionReset")); - await globalData.set("rankGuessNumber", [], "data"); - return message.reply(getLang("resetRankSuccess")); - } - - const col = parseInt(args.join(" ").match(/(\d+)/)?.[1] || 4); - const levelOfDifficult = rows.find(item => item.col == col); - if (!levelOfDifficult) - return message.reply(getLang("invalidCol")); - const mode = args.join(" ").match(/(single|multi|-s|-m)/)?.[1] || "single"; - const row = levelOfDifficult.row || 10; - - const options = { - col, - row, - timeStart: parseInt(getTime("x")), - numbers: [], - tryNumber: 0, - ctx: null, - canvas: null, - answer: randomString(col, true, "0123456789"), - gameName: getLang("gameName"), - gameGuide: getLang("gameGuide", row), - gameNote: getLang("gameNote") - }; - - const gameData = guessNumberGame(options); - gameData.mode = mode; - - const messageData = message.reply(`${getLang("created")}\n\n${getLang("gameGuide", row)}\n\n${getLang("gameNote")}\n\n${getLang("replyToPlayGame", col)}`); - gameData.messageData = messageData; - - message.reply({ - attachment: gameData.imageStream - }, (err, info) => { - global.GoatBot.onReply.set(info.messageID, { - commandName, - messageID: info.messageID, - author: event.senderID, - gameData - }); - }); - }, - - onReply: async ({ message, Reply, event, getLang, commandName, globalData }) => { - const { gameData: oldGameData } = Reply; - if (event.senderID != Reply.author && oldGameData.mode == "single") - return; - - const numbers = (event.body || "").split("").map(item => item.trim()).filter(item => item != "" && !isNaN(item)); - if (numbers.length != oldGameData.col) - return message.reply(getLang("invalidNumbers", oldGameData.col)); - global.GoatBot.onReply.delete(Reply.messageID); - - oldGameData.numbers = numbers; - const gameData = guessNumberGame(oldGameData); - - if (gameData.isWin == null) { - message.reply({ - attachment: gameData.imageStream - }, (err, info) => { - message.unsend(Reply.messageID); - global.GoatBot.onReply.set(info.messageID, { - commandName, - messageID: info.messageID, - author: event.senderID, - gameData - }); - }); - } - else { - const rankGuessNumber = await globalData.get("rankGuessNumber", "data", []); - const rewardPoint = rows.find(item => item.col == gameData.col)?.rewardPoint || 0; - const messageText = gameData.isWin ? - getLang("win", gameData.answer, gameData.tryNumber - 1, rewardPoint) : - getLang("loss", gameData.answer); - message.unsend((await oldGameData.messageData).messageID); - message.unsend(Reply.messageID); - message.reply({ - body: messageText, - attachment: gameData.imageStream - }); - - if (gameData.isWin != null) { - const userIndex = rankGuessNumber.findIndex(item => item.id == event.senderID); - const data = { - tryNumber: gameData.tryNumber - 1, - timeSuccess: parseInt(getTime("x") - oldGameData.timeStart), - date: getTime(), - col: gameData.col - }; - - if (gameData.isWin == true) { - if (userIndex == -1) - rankGuessNumber.push({ - id: event.senderID, - wins: [data], - losses: [], - points: rewardPoint - }); - else { - rankGuessNumber[userIndex].wins.push(data); - rankGuessNumber[userIndex].points += rewardPoint; - } - } - else { - delete data.tryNumber; - if (userIndex == -1) - rankGuessNumber.push({ - id: event.senderID, - wins: [], - losses: [data], - points: 0 - }); - else - rankGuessNumber[userIndex].losses.push(data); - } - await globalData.set("rankGuessNumber", rankGuessNumber, "data"); - } - } - } -}; - - -function wrapTextGetHeight(ctx, text, maxWidth, lineHeight, margin = 0) { - const lines = text.split('\n'); - let height = 0; - let count = 0; - for (let i = 0; i < lines.length; i++) { - let line = ''; - const words = lines[i].split(' '); - for (let n = 0; n < words.length; n++) { - const textLine = line + words[n] + ' '; - const textWidth = ctx.measureText(textLine).width; - if (textWidth > maxWidth && n > 0) { - line = words[n] + ' '; - height += lineHeight; - count++; - } - else { - line = textLine; - } - } - height += lineHeight; - count++; - } - return height + margin * count; -} - -function wrapText(ctx, text, x, y, maxWidth, lineHeight) { - const yStart = y; - const lines = text.split('\n'); - for (let i = 0; i < lines.length; i++) { - let line = ''; - const words = lines[i].split(' '); - for (let n = 0; n < words.length; n++) { - const textLine = line + words[n] + ' '; - const metrics = ctx.measureText(textLine); - const textWidth = metrics.width; - if (textWidth > maxWidth && n > 0) { - ctx.fillText(line, x, y); - line = words[n] + ' '; - y += lineHeight; - } - else { - line = textLine; - } - } - ctx.fillText(line, x, y); - y += lineHeight; - } - return y - yStart; -} - -function drawBorderSquareRadius(ctx, x, y, width, height, radius = 5, lineWidth = 1, strokeStyle = '#000', fill) { - ctx.save(); - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); - if (fill) { - ctx.fillStyle = strokeStyle; - ctx.fill(); - } - else { - ctx.strokeStyle = strokeStyle; - ctx.lineWidth = lineWidth; - ctx.stroke(); - } - ctx.restore(); -} - -function drawWrappedText(ctx, text, startY, wrapWidth, lineHeight, boldFirstLine, margin, marginText) { - const splitText = text.split('\n'); - let y = startY; - for (let i = 0; i < splitText.length; i++) { - if (i === 0 && boldFirstLine) - ctx.font = `bold ${ctx.font}`; - else - ctx.font = ctx.font.replace('bold ', ''); - const height = wrapText(ctx, splitText[i], margin / 2, y, wrapWidth, lineHeight); - y += height + marginText; - } - return y; -} - - -function drawBorderSquareRadius(ctx, x, y, width, height, radius = 5, lineWidth = 1, strokeStyle = '#000', fill) { - ctx.save(); - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); - if (fill) { - ctx.fillStyle = strokeStyle; - ctx.fill(); - } - else { - ctx.strokeStyle = strokeStyle; - ctx.lineWidth = lineWidth; - ctx.stroke(); - } - ctx.restore(); -} - -function drawWrappedText(ctx, text, startY, wrapWidth, lineHeight, boldFirstLine, margin, marginText) { - const splitText = text.split('\n'); - let y = startY; - for (let i = 0; i < splitText.length; i++) { - if (i === 0 && boldFirstLine) - ctx.font = `bold ${ctx.font}`; - else - ctx.font = ctx.font.replace('bold ', ''); - const height = wrapText(ctx, splitText[i], margin / 2, y, wrapWidth, lineHeight); - y += height + marginText; - } - return y; -} - -function getPositionOfSquare(x, y, sizeOfOneSquare, distance, marginX, marginY, lineWidth, heightGameName) { - const xOutSide = marginX + x * (sizeOfOneSquare + distance) + lineWidth / 2; - const yOutSide = marginY + y * (sizeOfOneSquare + distance) + lineWidth / 2 + heightGameName; - const xInSide = xOutSide + lineWidth; - const yInSide = yOutSide + lineWidth; - - return { - xOutSide, - yOutSide, - xInSide, - yInSide - }; -} - -function guessNumberGame(options) { - let { numbers, ctx, canvas, tryNumber, row, ctxNumbers, canvasNumbers, ctxHightLight, canvasHightLight } = options; - const { col, answer, gameName, gameGuide, gameNote } = options; - tryNumber--; - if (Array.isArray(numbers)) - numbers = numbers.map(item => item.toString().trim()); - if (typeof numbers == 'string') - numbers = numbers.split('').map(item => item.trim()); - - if (numbers.length) - options.allGuesss ? options.allGuesss.push(numbers) : options.allGuesss = [numbers]; - - row = row || 10; - - const heightGameName = 40; - const yGameName = 150; - const sizeOfOneSquare = 100; - const lineWidth = 6; - const radius = 10; - const distance = 10; - const marginX = 150; - const marginY = 100; - const backgroundColor = '#F0F2F5'; - - const fontGameGuide = '35px "Arial"'; - const fontGameName = 'bold 50px "Arial"'; - const fontNumbers = 'bold 60px "Arial"'; - const fontSuggest = 'bold 40px "Arial"'; - const fontResultWin = 'bold 150px "Times New Roman"'; - const fontResultLose = 'bold 150px "Arial"'; - const marginText = 2.9; - const lineHeightGuideText = 38; - - if (!ctx && !canvas) { - const xCanvas = col * sizeOfOneSquare + (col - 1) * distance + marginX * 2; - canvas = createCanvas(1, 1); - ctx = canvas.getContext('2d'); - ctx.font = fontGameGuide; - - const heightGameGuide = wrapTextGetHeight(ctx, gameGuide, xCanvas - marginX, lineHeightGuideText, marginText); - const heightGameNote = wrapTextGetHeight(ctx, gameNote, xCanvas - marginX, lineHeightGuideText, marginText); - const marginGuideNote = 10; - - canvas = createCanvas( - col * sizeOfOneSquare + (col - 1) * distance + marginX * 2, - heightGameName + row * sizeOfOneSquare + (row - 1) * distance + marginY * 2 + heightGameGuide + heightGameNote + marginGuideNote - ); - ctx = canvas.getContext('2d'); - ctx.fillStyle = backgroundColor; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - // draw game name - ctx.font = fontGameName; - ctx.fillStyle = '#404040'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText(gameName, canvas.width / 2, yGameName / 2); - - // draw guide - ctx.font = fontGameGuide; - ctx.fillStyle = '#404040'; - ctx.textAlign = 'left'; - const yGuide = heightGameName + marginY / 2 + row * (sizeOfOneSquare + distance) + marginY / 2 + lineHeightGuideText * 2; - - // draw note - const yNote = drawWrappedText(ctx, gameGuide, yGuide, canvas.width - marginX, lineHeightGuideText, true, marginX, marginText); - - drawWrappedText(ctx, gameNote, yNote + 10, canvas.width - marginX, lineHeightGuideText, true, marginX, marginText); - - // draw all squares - for (let i = 0; i < col; i++) { - for (let j = 0; j < row; j++) { - const { xOutSide, yOutSide, xInSide, yInSide } = getPositionOfSquare(i, j, sizeOfOneSquare, distance, marginX, marginY, lineWidth, heightGameName); - drawBorderSquareRadius( - ctx, - xOutSide, - yOutSide, - sizeOfOneSquare, - sizeOfOneSquare, - radius, - lineWidth, - '#919191', - true - ); - - drawBorderSquareRadius( - ctx, - xInSide, - yInSide, - sizeOfOneSquare - lineWidth * 2, - sizeOfOneSquare - lineWidth * 2, - radius / 2, - lineWidth, - backgroundColor, - true - ); - } - } - } - - if (!canvasHightLight) { - // if there's no canvasHightLight, then of course ctxHightLight, canvasNumbers and ctxNumbers doesn't either - canvasHightLight = createCanvas(canvas.width, canvas.height); - ctxHightLight = canvasHightLight.getContext('2d'); - canvasNumbers = createCanvas(canvas.width, canvas.height); - ctxNumbers = canvasNumbers.getContext('2d'); - } - - // draw numbers - let isWin = null; - if (numbers.length) { - ctxNumbers.font = fontNumbers; - ctxNumbers.fillStyle = '#f0f0f0'; - ctxNumbers.textAlign = 'center'; - ctxNumbers.textBaseline = 'middle'; - for (let i = 0; i < col; i++) { - const { xOutSide, yOutSide, xInSide, yInSide } = getPositionOfSquare(i, tryNumber, sizeOfOneSquare, distance, marginX, marginY, lineWidth, heightGameName); - // draw background of square - drawBorderSquareRadius( - ctx, - xInSide, - yInSide, - sizeOfOneSquare - lineWidth * 2, - sizeOfOneSquare - lineWidth * 2, - radius / 2, - lineWidth, - '#a3a3a3', - true - ); - // draw number - const x = xOutSide + sizeOfOneSquare / 2; - const y = yOutSide + sizeOfOneSquare / 2; - ctxNumbers.fillText(numbers[i], x, y); - - // yellow || green - if ( - answer.includes(numbers[i]) // yellow (correct number) - || numbers[i] === answer[i] // green (correct number and position) - ) { - drawBorderSquareRadius( - ctxHightLight, - xOutSide, - yOutSide, - sizeOfOneSquare, - sizeOfOneSquare, - radius, - lineWidth, - numbers[i] == answer[i] ? '#417642' : '#A48502', - true - ); - drawBorderSquareRadius( - ctxHightLight, - xInSide, - yInSide, - sizeOfOneSquare - lineWidth * 2, - sizeOfOneSquare - lineWidth * 2, - radius / 2, - lineWidth, - numbers[i] == answer[i] ? '#57AC58' : '#E9BE00', - true - ); - } - } - - // After each guess, you will get additional hints of the number of correct digits (shown on the left) and the number of correct digits (shown on the right). - let numberRight = 0; - let numberRightPosition = 0; - answer.split('').forEach((item, index) => { - if (numbers.includes(item)) - numberRight++; - if (item == numbers[index]) - numberRightPosition++; - }); - - ctx.font = fontSuggest; - ctx.fillText(numberRight, marginX / 2, marginY + sizeOfOneSquare / 2 + heightGameName + tryNumber * (sizeOfOneSquare + distance)); - ctx.fillText(numberRightPosition, marginX + col * (sizeOfOneSquare) + distance * (col - 1) + marginX / 2, marginY + sizeOfOneSquare / 2 + heightGameName + tryNumber * (sizeOfOneSquare + distance)); - - if ( - numberRight == answer.length && numberRightPosition == answer.length - || tryNumber + 1 == row - ) { - isWin = numberRight == answer.length && numberRightPosition == answer.length; - ctx.save(); - ctx.drawImage(canvasHightLight, 0, 0); - ctx.drawImage(canvasNumbers, 0, 0); - - ctx.font = isWin ? fontResultWin : fontResultLose; - ctx.fillStyle = isWin ? '#005900' : '#590000'; - // rotate -45 degree - ctx.globalAlpha = 0.4; - ctx.translate(canvas.width / 2, marginY + heightGameName + (row * (sizeOfOneSquare + distance)) / 2); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.rotate(-45 * Math.PI / 180); - ctx.fillText(isWin ? 'YOU WIN' : answer.split('').join(' '), 0, 0); - ctx.restore(); - } - else { - ctx.drawImage(canvasNumbers, 0, 0); - } - } - - tryNumber++; - - const imageStream = canvas.createPNGStream(); - imageStream.path = `guessNumber${Date.now()}.png`; - - return { - ...options, - imageStream, - ctx, - canvas, - tryNumber: tryNumber + 1, - isWin, - ctxHightLight, - canvasHightLight, - ctxNumbers, - canvasNumbers - }; -} diff --git a/scripts/cmds/hack.js b/scripts/cmds/hack.js deleted file mode 100644 index 48cd2700..00000000 --- a/scripts/cmds/hack.js +++ /dev/null @@ -1,119 +0,0 @@ -const fs = require("fs-extra"); -const axios = require("axios"); -const { loadImage, createCanvas } = require("canvas"); - -module.exports = { - config: { - name: "hack", - version: "1.0.0", - author: "NAZRUL (Converted by Akash)", - countDown: 0, - role: 0, - shortDescription: "Fake FB hack generator 😅", - longDescription: "Creates a fake hacking style image using target profile photo and name.", - category: "fun", - guide: { - en: "{pn} @mention বা reply দিয়ে ব্যবহার করো" - } - }, - - // ✏️ টেক্সট লাইন ভাঙার হেল্পার ফাংশন - wrapText(ctx, text, maxWidth) { - return new Promise(resolve => { - if (ctx.measureText(text).width < maxWidth) return resolve([text]); - if (ctx.measureText("W").width > maxWidth) return resolve(null); - - const words = text.split(" "); - const lines = []; - let line = ""; - - while (words.length > 0) { - let split = false; - while (ctx.measureText(words[0]).width >= maxWidth) { - const temp = words[0]; - words[0] = temp.slice(0, -1); - if (split) { - words[1] = temp.slice(-1) + words[1]; - } else { - split = true; - words.splice(1, 0, temp.slice(-1)); - } - } - - if (ctx.measureText(line + words[0]).width < maxWidth) { - line += words.shift() + " "; - } else { - lines.push(line.trim()); - line = ""; - } - - if (words.length === 0) lines.push(line.trim()); - } - - resolve(lines); - }); - }, - - // 🎯 মূল কমান্ড - onStart: async function ({ event, message, usersData }) { - try { - const mentionID = Object.keys(event.mentions)[0] || event.senderID; - const userName = await usersData.getName(mentionID); - - // ব্যাকগ্রাউন্ড লিংক (তুমি চাইলে নিজেও কাস্টম দিতে পারো) - const backgrounds = [ - "https://drive.google.com/uc?id=1_S9eqbx8CxMMxUdOfATIDXwaKWMC-8ox&export=download" - ]; - const bgLink = backgrounds[Math.floor(Math.random() * backgrounds.length)]; - - // ক্যাশ ফোল্ডার তৈরি - const bgPath = __dirname + "/cache/hack_bg.png"; - const avatarPath = __dirname + "/cache/hack_avatar.png"; - - // প্রোফাইল ছবি নামানো - const avatarData = ( - await axios.get( - `https://graph.facebook.com/${mentionID}/picture?width=720&height=720&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662`, - { responseType: "arraybuffer" } - ) - ).data; - fs.writeFileSync(avatarPath, Buffer.from(avatarData, "utf-8")); - - // ব্যাকগ্রাউন্ড নামানো - const bgData = (await axios.get(bgLink, { responseType: "arraybuffer" })).data; - fs.writeFileSync(bgPath, Buffer.from(bgData, "utf-8")); - - // ক্যানভাসে আঁকা - const background = await loadImage(bgPath); - const avatar = await loadImage(avatarPath); - const canvas = createCanvas(background.width, background.height); - const ctx = canvas.getContext("2d"); - - ctx.drawImage(background, 0, 0, canvas.width, canvas.height); - ctx.font = "400 23px Arial"; - ctx.fillStyle = "#1878F3"; - ctx.textAlign = "start"; - - const wrappedText = await this.wrapText(ctx, userName, 1160); - ctx.fillText(wrappedText.join("\n"), 136, 335); - - ctx.beginPath(); - ctx.drawImage(avatar, 57, 290, 66, 68); - - const finalBuffer = canvas.toBuffer(); - fs.writeFileSync(bgPath, finalBuffer); - - await message.reply({ - body: "😎 হ্যাক সম্পূর্ণ!", - attachment: fs.createReadStream(bgPath) - }); - - // ক্যাশ পরিষ্কার করা - fs.unlinkSync(bgPath); - fs.unlinkSync(avatarPath); - } catch (err) { - console.error(err); - message.reply("❌ কিছু ভুল হয়েছে!"); - } - } -}; diff --git a/scripts/cmds/help.js b/scripts/cmds/help.js deleted file mode 100644 index 76eddbb0..00000000 --- a/scripts/cmds/help.js +++ /dev/null @@ -1,145 +0,0 @@ -const fs = require("fs-extra"); -const path = require("path"); -const https = require("https"); - -module.exports = { - config: { - name: "help", - aliases: ["menu", "commands"], - version: "6.4", - author: "EryXenX", - shortDescription: "Show all commands", - longDescription: "Show all commands in clean UI", - category: "system", - guide: "{pn}help [command name]" - }, - - onStart: async function ({ message, args, prefix }) { - const allCommands = global.GoatBot.commands; - - const fancyFont = (str) => - str.replace(/[A-Za-z]/g, (c) => { - const map = { - A:"𝐀",B:"𝐁",C:"𝐂",D:"𝐃",E:"𝐄",F:"𝐅",G:"𝐆",H:"𝐇", - I:"𝐈",J:"𝐉",K:"𝐊",L:"𝐋",M:"𝐌",N:"𝐍",O:"𝐎",P:"𝐏", - Q:"𝐐",R:"𝐑",S:"𝐒",T:"𝐓",U:"𝐔",V:"𝐕",W:"𝐖",X:"𝐗", - Y:"𝐘",Z:"𝐙", - a:"𝐚",b:"𝐛",c:"𝐜",d:"𝐝",e:"𝐞",f:"𝐟",g:"𝐠",h:"𝐡", - i:"𝐢",j:"𝐣",k:"𝐤",l:"𝐥",m:"𝐦",n:"𝐧",o:"𝐨",p:"𝐩", - q:"𝐪",r:"𝐫",s:"𝐬",t:"𝐭",u:"𝐮",v:"𝐯",w:"𝐰",x:"𝐱", - y:"𝐲",z:"𝐳" - }; - return map[c] || c; - }); - - const categoryFont = (str) => - str.split("").map(c => { - const map = { - A:"𝐀",B:"𝐁",C:"𝐂",D:"𝐃",E:"𝐄",F:"𝐅",G:"𝐆",H:"𝐇", - I:"𝐈",J:"𝐉",K:"𝐊",L:"𝐋",M:"𝐌",N:"𝐍",O:"𝐎",P:"𝐏", - Q:"𝐐",R:"𝐑",S:"𝐒",T:"𝐓",U:"𝐔",V:"𝐕",W:"𝐖",X:"𝐗", - Y:"𝐘",Z:"𝐙" - }; - return map[c] || c; - }).join(""); - - const cleanCategoryName = (text) => text ? text.toLowerCase() : "others"; - - if (args[0]) { - const cmdName = args[0].toLowerCase(); - const cmd = - allCommands.get(cmdName) || - [...allCommands.values()].find(c => c.config.aliases?.includes(cmdName)); - - if (!cmd) - return message.reply( -`❌ ${fancyFont(`Command '${cmdName}' not found!`)} -➤ Try ${prefix}help to see full list` - ); - - const usage = typeof cmd.config.guide === "string" - ? cmd.config.guide.replace("{pn}", cmd.config.name) - : cmd.config.name; - - const infoMsg = -`┏━━━━━━━━━━━━━┓ - 🧩 𝐂𝐌𝐃 𝐈𝐍𝐅𝐎 -┗━━━━━━━━━━━━━┛ - ✦ Name : ${cmd.config.name} - ✦ Aliases : ${cmd.config.aliases?.join(", ") || "None"} - ✦ Category : ${categoryFont((cmd.config.category || "Others").toUpperCase())} - ✦ Version : v${cmd.config.version || "1.0"} - ✦ Author : ${cmd.config.author || "Unknown"} - ✦ Usage : ${prefix}${usage} -━━━━━━━━━━━━━━━ - 📝 ${(cmd.config.longDescription || cmd.config.shortDescription || "No description")}`; - - return message.reply(infoMsg); - } - - const categories = {}; - - for (const [name, cmd] of allCommands) { - const cat = cleanCategoryName(cmd.config.category); - if (!categories[cat]) categories[cat] = []; - categories[cat].push(name); - } - - let msg = -`╭─ 𝐂𝐎𝐌𝐌𝐀𝐍𝐃𝐒 𝐌𝐄𝐍𝐔 -├ Prefix : ${prefix} -├ Total : ${allCommands.size} -├ Author : EryXenX\n`; - - for (const cat of Object.keys(categories).sort()) { - const catTitle = categoryFont(cat.toUpperCase()); - msg += `\n┌─ ${catTitle} ─┐\n`; - for (const cmdName of categories[cat].sort()) { - msg += `│ ⎙ ${fancyFont(cmdName)}\n`; - } - msg += `└─────────────┘\n`; - } - - msg += `\n╰─ Use: ${prefix}help `; - - const gifURLs = [ - "https://i.imgur.com/Xw6JTfn.gif", - "https://i.imgur.com/mW0yjZb.gif", - "https://i.imgur.com/KQBcxOV.gif" - ]; - - const randomGifURL = gifURLs[Math.floor(Math.random() * gifURLs.length)]; - const gifFolder = path.join(__dirname, "cache"); - - if (!fs.existsSync(gifFolder)) - fs.mkdirSync(gifFolder, { recursive: true }); - - const gifName = path.basename(randomGifURL); - const gifPath = path.join(gifFolder, gifName); - - if (!fs.existsSync(gifPath)) - await downloadGif(randomGifURL, gifPath); - - return message.reply({ - body: msg, - attachment: fs.createReadStream(gifPath) - }); - } -}; - -function downloadGif(url, dest) { - return new Promise((resolve, reject) => { - const file = fs.createWriteStream(dest); - https.get(url, (res) => { - if (res.statusCode !== 200) { - fs.unlink(dest, () => {}); - return reject(); - } - res.pipe(file); - file.on("finish", () => file.close(resolve)); - }).on("error", (err) => { - fs.unlink(dest, () => {}); - reject(err); - }); - }); -} diff --git a/scripts/cmds/ignoreonlyad.js b/scripts/cmds/ignoreonlyad.js deleted file mode 100644 index 06818084..00000000 --- a/scripts/cmds/ignoreonlyad.js +++ /dev/null @@ -1,90 +0,0 @@ -const ignoreList = global.GoatBot.config.adminOnly.ignoreCommand; -const fs = require("fs-extra"); - -module.exports = { - config: { - name: "ignoreonlyad", - aliases: ["ignoreadonly", "ignoreonlyadmin", "ignoreadminonly"], - version: "1.2", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Bỏ qua lệnh trong adminonly (khi bật adminonly, các lệnh được thêm từ lệnh này người dùng vẫn có thể sử dụng)", - en: "Ignore command in adminonly (when turn on adminonly, user can use command added from this command)" - }, - category: "owner", - guide: { - vi: " {pn} add : Thêm lệnh vào danh sách bỏ qua" - + "\n {pn} del : Xóa lệnh khỏi danh sách bỏ qua" - + "\n {pn} list: Xem danh sách lệnh bỏ qua", - en: " {pn} add : Add command to ignore list" - + "\n {pn} del : Remove command from ignore list" - + "\n {pn} list: View ignore list" - } - }, - - langs: { - vi: { - missingCommandNameToAdd: "⚠️ Vui lòng nhập tên lệnh bạn muốn thêm vào danh sách bỏ qua", - missingCommandNameToDelete: "⚠️ Vui lòng nhập tên lệnh bạn muốn xóa khỏi danh sách bỏ qua", - commandNotFound: "❌ Không tìm thấy lệnh \"%1\" trong danh sách lệnh của bot", - commandAlreadyInList: "❌ Lệnh \"%1\" đã có trong danh sách bỏ qua", - commandAdded: "✅ Đã thêm lệnh \"%1\" vào danh sách bỏ qua", - commandNotInList: "❌ Lệnh \"%1\" không có trong danh sách bỏ qua", - commandDeleted: "✅ Đã xóa lệnh \"%1\" khỏi danh sách bỏ qua", - ignoreList: "📑 Danh sách lệnh bỏ qua trong adminonly:\n%1" - }, - en: { - missingCommandNameToAdd: "⚠️ Please enter the command name you want to add to the ignore list", - missingCommandNameToDelete: "⚠️ Please enter the command name you want to delete from the ignore list", - commandNotFound: "❌ Command \"%1\" not found in bot's command list", - commandAlreadyInList: "❌ Command \"%1\" already in ignore list", - commandAdded: "✅ Added command \"%1\" to ignore list", - commandNotInList: "❌ Command \"%1\" not in ignore list", - commandDeleted: "✅ Removed command \"%1\" from ignore list", - ignoreList: "📑 Ignore list in adminonly:\n%1" - } - }, - - onStart: async function ({ args, message, getLang }) { - switch (args[0]) { - case "add": { - if (!args[1]) - return message.reply(getLang("missingCommandNameToAdd")); - const commandName = args[1].toLowerCase(); - const command = global.GoatBot.commands.get(commandName); - if (!command) - return message.reply(getLang("commandNotFound", commandName)); - if (ignoreList.includes(commandName)) - return message.reply(getLang("commandAlreadyInList", commandName)); - ignoreList.push(commandName); - fs.writeFileSync(global.client.dirConfig, JSON.stringify(global.GoatBot.config, null, 2)); - return message.reply(getLang("commandAdded", commandName)); - } - case "del": - case "delete": - case "remove": - case "rm": - case "-d": { - if (!args[1]) - return message.reply(getLang("missingCommandNameToDelete")); - const commandName = args[1].toLowerCase(); - const command = global.GoatBot.commands.get(commandName); - if (!command) - return message.reply(getLang("commandNotFound", commandName)); - if (!ignoreList.includes(commandName)) - return message.reply(getLang("commandNotInList", commandName)); - ignoreList.splice(ignoreList.indexOf(commandName), 1); - fs.writeFileSync(global.client.dirConfig, JSON.stringify(global.GoatBot.config, null, 2)); - return message.reply(getLang("commandDeleted", commandName)); - } - case "list": { - return message.reply(getLang("ignoreList", ignoreList.join(", "))); - } - default: { - return message.SyntaxError(); - } - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/ignoreonlyadbox.js b/scripts/cmds/ignoreonlyadbox.js deleted file mode 100644 index be83422f..00000000 --- a/scripts/cmds/ignoreonlyadbox.js +++ /dev/null @@ -1,89 +0,0 @@ -module.exports = { - config: { - name: "ignoreonlyadbox", - aliases: ["ignoreadboxonly", "ignoreadminboxonly"], - version: "1.2", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Bỏ qua lệnh trong adminonly (khi bật adminonly, các lệnh được thêm từ lệnh này người dùng vẫn có thể sử dụng)", - en: "Ignore command in adminonly (when turn on adminonly, user can use command added from this command)" - }, - category: "owner", - guide: { - vi: " {pn} add : Thêm lệnh vào danh sách bỏ qua" - + "\n {pn} del : Xóa lệnh khỏi danh sách bỏ qua" - + "\n {pn} list: Xem danh sách lệnh bỏ qua", - en: " {pn} add : Add command to ignore list" - + "\n {pn} del : Remove command from ignore list" - + "\n {pn} list: View ignore list" - } - }, - - langs: { - vi: { - missingCommandNameToAdd: "⚠️ Vui lòng nhập tên lệnh bạn muốn thêm vào danh sách bỏ qua", - missingCommandNameToDelete: "⚠️ Vui lòng nhập tên lệnh bạn muốn xóa khỏi danh sách bỏ qua", - commandNotFound: "❌ Không tìm thấy lệnh \"%1\" trong danh sách lệnh của bot", - commandAlreadyInList: "❌ Lệnh \"%1\" đã có trong danh sách bỏ qua", - commandAdded: "✅ Đã thêm lệnh \"%1\" vào danh sách bỏ qua", - commandNotInList: "❌ Lệnh \"%1\" không có trong danh sách bỏ qua", - commandDeleted: "✅ Đã xóa lệnh \"%1\" khỏi danh sách bỏ qua", - ignoreList: "📑 Danh sách lệnh bỏ qua trong nhóm bạn:\n%1" - }, - en: { - missingCommandNameToAdd: "⚠️ Please enter the command name you want to add to the ignore list", - missingCommandNameToDelete: "⚠️ Please enter the command name you want to delete from the ignore list", - commandNotFound: "❌ Command \"%1\" not found in bot's command list", - commandAlreadyInList: "❌ Command \"%1\" already in ignore list", - commandAdded: "✅ Added command \"%1\" to ignore list", - commandNotInList: "❌ Command \"%1\" not in ignore list", - commandDeleted: "✅ Removed command \"%1\" from ignore list", - ignoreList: "📑 Ignore list in your group:\n%1" - } - }, - - onStart: async function ({ args, message, threadsData, getLang, event }) { - const ignoreList = await threadsData.get(event.threadID, "data.ignoreCommanToOnlyAdminBox", []); - switch (args[0]) { - case "add": { - if (!args[1]) - return message.reply(getLang("missingCommandNameToAdd")); - const commandName = args[1].toLowerCase(); - const command = global.GoatBot.commands.get(commandName); - if (!command) - return message.reply(getLang("commandNotFound", commandName)); - if (ignoreList.includes(commandName)) - return message.reply(getLang("commandAlreadyInList", commandName)); - ignoreList.push(commandName); - await threadsData.set(event.threadID, ignoreList, "data.ignoreCommanToOnlyAdminBox"); - return message.reply(getLang("commandAdded", commandName)); - } - case "del": - case "delete": - case "remove": - case "rm": - case "-d": { - if (!args[1]) - return message.reply(getLang("missingCommandNameToDelete")); - const commandName = args[1].toLowerCase(); - const command = global.GoatBot.commands.get(commandName); - if (!command) - return message.reply(getLang("commandNotFound", commandName)); - - if (!ignoreList.includes(commandName)) - return message.reply(getLang("commandNotInList", commandName)); - ignoreList.splice(ignoreList.indexOf(commandName), 1); - await threadsData.set(event.threadID, ignoreList, "data.ignoreCommanToOnlyAdminBox"); - return message.reply(getLang("commandDeleted", commandName)); - } - case "list": { - return message.reply(getLang("ignoreList", ignoreList.join(", "))); - } - default: { - return message.SyntaxError(); - } - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/imagen3.js b/scripts/cmds/imagen3.js deleted file mode 100644 index c09788b8..00000000 --- a/scripts/cmds/imagen3.js +++ /dev/null @@ -1,50 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -module.exports = { - config: { - name: "imagen3", - aliases: [], - version: "1.0", - author: "nexo_here", - countDown: 10, - role: 0, - shortDescription: "Generate image using Imagen 3", - longDescription: "Generate AI image using Imagen 3", - category: "ai-image", - guide: { - en: "{pn} [prompt]\nExample: {pn} a samurai standing in sunset" - } - }, - - onStart: async function ({ args, message, event, api }) { - const prompt = args.join(" "); - if (!prompt) { - return message.reply("❌ Please provide a prompt.\nExample: imagen3 a samurai standing in sunset"); - } - - // React while loading - api.setMessageReaction("⏳", event.messageID, () => {}, true); - - const url = `https://renzweb.onrender.com/api/imagen3?prompt=${encodeURIComponent(prompt)}`; - - try { - const response = await axios.get(url, { responseType: "arraybuffer" }); - - const fileName = `${Date.now()}_imagen3.jpg`; - const filePath = path.join(__dirname, "cache", fileName); - fs.writeFileSync(filePath, Buffer.from(response.data, "binary")); - - message.reply({ attachment: fs.createReadStream(filePath) }, () => { - fs.unlinkSync(filePath); // Delete after send - api.setMessageReaction("✅", event.messageID, () => {}, true); - }); - - } catch (error) { - console.error("Error generating image:", error.message); - message.reply("❌ Failed to generate image."); - api.setMessageReaction("❌", event.messageID, () => {}, true); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/imgbb.js b/scripts/cmds/imgbb.js deleted file mode 100644 index 4f4f9b50..00000000 --- a/scripts/cmds/imgbb.js +++ /dev/null @@ -1,55 +0,0 @@ -const axios = require("axios"); -const FormData = require("form-data"); - -const IMGBB_API_KEY = "a0bcf5603cef298e99236e6f0bab90b2"; - -module.exports = { - config: { - name: "imgbb", - version: "2.0", - author: "EryXenX", - category: "tools", - shortDescription: "Upload replied image to ImgBB and get link", - longDescription: "Reply to an image with this command to upload it to ImgBB and receive a direct link.", - guide: "{pn}imgbb (reply to an image)" - }, - - onStart: async function ({ api, event }) { - try { - const attachments = event.messageReply?.attachments; - - if (!attachments || attachments.length === 0) { - return api.sendMessage("❌ Please reply to an image.", event.threadID, event.messageID); - } - - if (attachments[0].type !== "photo") { - return api.sendMessage("❌ Only photo attachments are supported.", event.threadID, event.messageID); - } - - const imageUrl = attachments[0].url; - const imageResponse = await axios.get(imageUrl, { responseType: "arraybuffer" }); - const imageBuffer = Buffer.from(imageResponse.data); - - const form = new FormData(); - form.append("image", imageBuffer.toString("base64")); - form.append("key", IMGBB_API_KEY); - - const uploadResponse = await axios.post("https://api.imgbb.com/1/upload", form, { - headers: form.getHeaders() - }); - - const result = uploadResponse.data; - - if (result.success) { - const { url } = result.data; - return api.sendMessage(url, event.threadID, event.messageID); - } else { - return api.sendMessage("❌ Upload failed. Please try again.", event.threadID, event.messageID); - } - - } catch (error) { - console.error("ImgBB Error:", error.message); - return api.sendMessage("❌ Something went wrong. Please try again.", event.threadID, event.messageID); - } - } -}; diff --git a/scripts/cmds/imggen.js b/scripts/cmds/imggen.js deleted file mode 100644 index 3b11de09..00000000 --- a/scripts/cmds/imggen.js +++ /dev/null @@ -1,49 +0,0 @@ -const axios = require("axios"); -const fs = require("fs-extra"); - -module.exports = { - config: { - name: "imgen", - aliases: ["imggen", "imagine"], - version: "1.0", - author: "nexo_here", - countDown: 10, - role: 0, - shortDescription: "Generate AI image using imgen API", - longDescription: "Use this command to generate images from a prompt using the imgen endpoint.", - category: "AI-IMAGE", - guide: { - en: "{pn} \nExample: {pn} A dragon flying over a castle" - } - }, - - onStart: async function ({ api, event, args }) { - const prompt = args.join(" "); - if (!prompt) { - return api.sendMessage("❌ | Please provide a prompt.\nExample: .imgen A dragon flying over a castle", event.threadID, event.messageID); - } - - const msg = await api.sendMessage("🧠 | Generating image, please wait...", event.threadID); - - try { - const response = await axios({ - method: "GET", - url: "https://www.arch2devs.ct.ws/api/imgen", - params: { prompt }, - responseType: "arraybuffer" - }); - - const imagePath = __dirname + `/cache/imgen_${event.senderID}.png`; - fs.writeFileSync(imagePath, Buffer.from(response.data, "binary")); - - api.sendMessage({ - body: `✅ | Prompt: ${prompt}`, - attachment: fs.createReadStream(imagePath) - }, event.threadID, () => fs.unlinkSync(imagePath), msg.messageID); - - } catch (err) { - console.error(err); - api.sendMessage("❌ | Failed to generate image. The server might be overloaded. Try again later.", event.threadID, msg.messageID); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/imgur.js b/scripts/cmds/imgur.js deleted file mode 100644 index e9dd7823..00000000 --- a/scripts/cmds/imgur.js +++ /dev/null @@ -1,53 +0,0 @@ -const axios = require('axios'); // ✅ Axios সরাসরি import করা হয়েছে - -module.exports = { - config: { - name: "imgur", - version: "1.0.2", - author: "MOHAMMAD AKASH", - role: 0, - shortDescription: "Upload image/video/GIF to Imgur and get direct links", - longDescription: "Reply to any image, video, or GIF to upload it to Imgur and get the link.", - category: "other", - guide: "[reply with any media file]", - cooldowns: 0 - }, - - onStart: async function ({ api, event }) { - // Get API link from JSON - let Shaon; - try { - const apis = await axios.get('https://raw.githubusercontent.com/shaonproject/Shaon/main/api.json'); - Shaon = apis.data.imgur; - } catch { - return api.sendMessage("❌ Failed to fetch Imgur API link!", event.threadID, event.messageID); - } - - const reply = event.messageReply; - if (!reply || !reply.attachments || reply.attachments.length === 0) { - return api.sendMessage( - "Please reply to the image or video with the command Imgur...!✅", - event.threadID, - event.messageID - ); - } - - const links = []; - - for (const attachment of reply.attachments) { - try { - const url = encodeURIComponent(attachment.url); - const upload = await axios.get(`${Shaon}/imgur?link=${url}`); - links.push(upload.data.uploaded.image || "❌ No link received"); - } catch (e) { - links.push("❌ Failed to upload"); - } - } - - const messageToSend = links.length === 1 - ? links[0] - : `✅ Uploaded files Imgur links:\n\n${links.join("\n")}`; - - return api.sendMessage(messageToSend, event.threadID, event.messageID); - } -}; diff --git a/scripts/cmds/install.js b/scripts/cmds/install.js deleted file mode 100644 index 50abe7a5..00000000 --- a/scripts/cmds/install.js +++ /dev/null @@ -1,196 +0,0 @@ -const axios = require("axios"); -const fs = require("fs-extra"); -const path = require("path"); -const cheerio = require("cheerio"); - -const { configCommands } = global.GoatBot; -const { log } = global.utils; - -function getDomain(url) { - const regex = /^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:/\n]+)/im; - const match = url.match(regex); - return match ? match[1] : null; -} - -function isURL(str) { - try { - new URL(str); - return true; - } catch { - return false; - } -} - -function extractUrlFromText(text) { - const match = text.match(/https?:\/\/[^\s]+/i); - return match ? match[0] : null; -} - -async function fetchCodeFromUrl(url) { - const domain = getDomain(url); - let fixedUrl = url; - - if (domain === "pastebin.com" && !url.includes("/raw/")) { - fixedUrl = url.replace("pastebin.com/", "pastebin.com/raw/"); - } - - if (domain === "github.com" && url.includes("/blob/")) { - fixedUrl = url - .replace("github.com", "raw.githubusercontent.com") - .replace("/blob/", "/"); - } - - try { - const res = await axios.get(fixedUrl); - let code = res.data; - - if (domain === "savetext.net") { - const $ = cheerio.load(code); - code = $("#content").text().trim(); - } - - return code; - } catch { - return null; - } -} - -function extractCommandName(code) { - const nameMatch = code.match(/name\s*:\s*["']([^"']+)["']/); - return nameMatch ? nameMatch[1].trim() + ".js" : null; -} - -module.exports = { - config: { - name: "install", - version: "3.0", - author: "Rx Abdullah", - countDown: 3, - role: 2, - hasPrefix: false, - description: "Install command via reply / code / url", - category: "owner" - }, - - onStart: async function ({ args, message, event, api }) { - - let rawCode = ""; - let fileName = ""; - - if (event.messageReply?.body) { - const replyText = event.messageReply.body.trim(); - const url = extractUrlFromText(replyText); - - if (url) { - rawCode = await fetchCodeFromUrl(url); - if (!rawCode) return message.reply( - "✖ Failed to fetch code from URL.\n" + - "Please check the link and try again." - ); - } else { - rawCode = replyText; - } - - fileName = extractCommandName(rawCode); - } - - else if (args[0] && isURL(args[0])) { - rawCode = await fetchCodeFromUrl(args[0]); - if (!rawCode) return message.reply( - "✖ Invalid or unreachable URL.\n" + - "Make sure the link is accessible and try again." - ); - - fileName = extractCommandName(rawCode); - } - - else if (args.length >= 2 && args[0].endsWith(".js")) { - fileName = args[0]; - rawCode = args.slice(1).join(" "); - } - - if (!rawCode) - return message.reply( - "⚠ No code provided.\n\n" + - "Usage:\n" + - "• Reply to a message containing code or URL\n" + - "• Provide a raw URL directly\n" + - "• install " - ); - - if (!fileName) - return message.reply( - "✖ Could not detect command name.\n" + - "Make sure the code has a valid name field." - ); - - const filePath = path.join(process.cwd(), "scripts", "cmds", fileName); - - if (fs.existsSync(filePath)) { - return message.reply( - `⚠ ${fileName} already exists.\n\n` + - "React to this message to overwrite and reinstall.", - (err, info) => { - global.GoatBot.onReaction.set(info.messageID, { - commandName: "install", - author: event.senderID, - data: { rawCode, fileName } - }); - } - ); - } - - fs.writeFileSync(filePath, rawCode); - - const load = global.utils.loadScripts( - "cmds", - fileName.replace(".js", ""), - log, - configCommands, - api - ); - - if (load.status === "success") { - return message.reply( - `✅ Installed: ${fileName}\n` + - `📌 Status: Loaded & Ready` - ); - } else { - return message.reply( - `✖ Installation failed: ${fileName}\n` + - `⚠ Error: ${load.error?.message || "Unknown error"}` - ); - } - }, - - onReaction: async function ({ Reaction, event, message, api }) { - - if (event.userID !== Reaction.author) return; - - const { rawCode, fileName } = Reaction.data; - - const filePath = path.join(process.cwd(), "scripts", "cmds", fileName); - - fs.writeFileSync(filePath, rawCode); - - const load = global.utils.loadScripts( - "cmds", - fileName.replace(".js", ""), - log, - configCommands, - api - ); - - if (load.status === "success") { - message.reply( - `✅ Overwritten & Reloaded: ${fileName}\n` + - `📌 Status: Loaded & Ready` - ); - } else { - message.reply( - `✖ Overwrite failed: ${fileName}\n` + - `⚠ Error: ${load.error?.message || "Unknown error"}` - ); - } - } -}; diff --git a/scripts/cmds/jail.js b/scripts/cmds/jail.js deleted file mode 100644 index 28c0e804..00000000 --- a/scripts/cmds/jail.js +++ /dev/null @@ -1,73 +0,0 @@ -const fs = require("fs-extra"); -const axios = require("axios"); -const { loadImage, createCanvas } = require("canvas"); - -const JAIL_URL = "https://i.ibb.co.com/84f1gzcJ/pngtree-jail-prison-bars-vector-png-image-6665843.png"; - -module.exports = { - config: { - name: "jail", - version: "1.0.0", - author: "EryXenX", - countDown: 5, - role: 0, - description: { - en: "Put someone behind jail bars", - bn: "কাউকে জেলের গ্রিলের পেছনে বসাও", - hi: "Kisi ko jail ke peeche daalo", - tl: "Ilagay ang isa sa likod ng rehas ng bilangguan", - ar: "ضع شخصاً خلف قضبان السجن" - }, - category: "fun", - guide: { en: "{pn} @mention or reply to a message" } - }, - - langs: { - en: { noMention: "❌ | Mention someone or reply to a message!", error: "❌ | Failed to generate. Try again." }, - bn: { noMention: "❌ | কাউকে mention করুন বা reply করুন!", error: "❌ | তৈরি করতে সমস্যা হয়েছে।" }, - hi: { noMention: "❌ | Kisi ko mention karein ya reply karein!", error: "❌ | Banana fail hua." }, - tl: { noMention: "❌ | Mag-mention ng isa o mag-reply!", error: "❌ | Hindi nagawa." }, - ar: { noMention: "❌ | أشر إلى شخص أو رد على رسالة!", error: "❌ | فشل الإنشاء." } - }, - - onStart: async function ({ event, message, getLang }) { - try { - const mentionID = Object.keys(event.mentions)[0] || (event.messageReply ? event.messageReply.senderID : null); - if (!mentionID) return message.reply(getLang("noMention")); - - const ts = Date.now(); - const jailPath = __dirname + "/cache/jail_base_" + ts + ".png"; - const avatarPath = __dirname + "/cache/jail_avt_" + ts + ".jpg"; - const outputPath = __dirname + "/cache/jail_out_" + ts + ".jpg"; - - const [jailRes, avatarRes] = await Promise.all([ - axios.get(JAIL_URL, { responseType: "arraybuffer" }), - axios.get("https://graph.facebook.com/" + mentionID + "/picture?height=720&width=720&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662", { responseType: "arraybuffer" }) - ]); - - fs.writeFileSync(jailPath, Buffer.from(jailRes.data)); - fs.writeFileSync(avatarPath, Buffer.from(avatarRes.data)); - - const jailImg = await loadImage(jailPath); - const avatarImg = await loadImage(avatarPath); - - const W = jailImg.width; - const H = jailImg.height; - const canvas = createCanvas(W, H); - const ctx = canvas.getContext("2d"); - - ctx.drawImage(avatarImg, 0, 0, W, H); - ctx.drawImage(jailImg, 0, 0, W, H); - - fs.writeFileSync(outputPath, canvas.toBuffer("image/jpeg", { quality: 0.92 })); - - await message.reply({ body: "🔒 You are in jail!", attachment: fs.createReadStream(outputPath) }); - - [jailPath, avatarPath, outputPath].forEach(p => { try { fs.unlinkSync(p); } catch (_) {} }); - - } catch (err) { - console.error("Jail Error:", err); - message.reply(getLang("error")); - } - } -}; diff --git a/scripts/cmds/join.js b/scripts/cmds/join.js deleted file mode 100644 index baa8d33c..00000000 --- a/scripts/cmds/join.js +++ /dev/null @@ -1,113 +0,0 @@ -module.exports = { - config: { - name: "join", - aliases: ["boxlist", "allbox"], - version: "1.5.0", - author: "MOHAMMAD AKASH", - role: 2, - shortDescription: "Paginated active group list & add yourself", - category: "system", - countDown: 10 - }, - - onStart: async function ({ api, event }) { - const { threadID, messageID, senderID } = event; - const perPage = 10; - - try { - // সর্বোচ্চ 50 থ্রেড ফেচ করা - const allThreads = await api.getThreadList(50, null, ["INBOX"]); - - // শুধু ACTIVE গ্রুপ - const groups = allThreads.filter(t => t.isGroup && t.isSubscribed); - if (!groups.length) - return api.sendMessage("⚠️ Bot is not currently in any group.", threadID, messageID); - - const page = 1; - const start = (page - 1) * perPage; - const end = start + perPage; - const currentGroups = groups.slice(start, end); - - let msg = `📦 | 𝙱𝙾𝚇 𝙻𝙸𝚂𝚃 (𝙿𝙰𝙶𝙴 ${page})\n\n`; - currentGroups.forEach((g, i) => { - msg += `${start + i + 1}. ${g.name || "Unnamed Group"}\n`; - msg += `🆔 ${g.threadID}\n\n`; - }); - - msg += "↩️ Rᴇᴘʟʏ Wɪᴛʜ: ᴀᴅᴅ 1 | ᴀᴅᴅ 2 5\n➡️ Oʀ ᴘᴀɢᴇ 2 ... Tᴏ sᴇᴇ Mᴏʀᴇ Gʀᴏᴜᴘs"; - - api.sendMessage(msg.trim(), threadID, (err, info) => { - global.GoatBot.onReply.set(info.messageID, { - commandName: this.config.name, - author: senderID, - groups, - page, - perPage - }); - }, messageID); - - } catch (e) { - console.error(e); - api.sendMessage("❌ Failed to fetch active group list.", threadID, messageID); - } - }, - - onReply: async function ({ api, event, Reply }) { - if (event.senderID !== Reply.author) return; - - const args = event.body.trim().toLowerCase().split(/\s+/); - const perPage = Reply.perPage || 10; - - // PAGE কমান্ড - if (args[0] === "page") { - const pageNum = parseInt(args[1]); - if (isNaN(pageNum) || pageNum < 1) return api.sendMessage("❌ Invalid page number", event.threadID); - - const start = (pageNum - 1) * perPage; - const end = start + perPage; - const currentGroups = Reply.groups.slice(start, end); - - if (!currentGroups.length) return api.sendMessage("⚠️ No more groups", event.threadID); - - let msg = `📦 | 𝙱𝙾𝚇 𝙻𝙸𝚂𝚃 (𝙿𝙰𝙶𝙴 ${pageNum})\n\n`; - currentGroups.forEach((g, i) => { - msg += `${start + i + 1}. ${g.name || "Unnamed Group"}\n`; - msg += `🆔 ${g.threadID}\n\n`; - }); - msg += `↩️ Rᴇᴘʟʏ Wɪᴛʜ: Aᴅᴅ 1 | Aᴅᴅ 2 5\n➡️ Oʀ Pᴀɢᴇ ${pageNum + 1} ... to see more groups`; - - api.sendMessage(msg.trim(), event.threadID, (err, info) => { - global.GoatBot.onReply.set(info.messageID, { - commandName: Reply.commandName, - author: Reply.author, - groups: Reply.groups, - page: pageNum, - perPage - }); - }); - return; - } - - // ADD কমান্ড - if (args[0] === "add") { - const addUserToGroup = async (uid, tid, name) => { - try { - await api.addUserToGroup(uid, tid); - await api.sendMessage(`✅ Aᴅᴅᴇᴅ Yᴏᴜ Tᴏ: ${name}`, event.threadID); - } catch { - await api.sendMessage(`❌ Fᴀɪʟᴅ Tᴏ Aᴅᴅ Yᴏᴜ ᴛᴏ: ${name}`, event.threadID); - } - }; - - for (let i = 1; i < args.length; i++) { - const index = parseInt(args[i]) - 1; - if (isNaN(index) || index < 0 || index >= Reply.groups.length) { - await api.sendMessage(`❌ Iɴᴠᴀʟɪᴅ Nᴜᴍʙᴇʀ: ${args[i]}`, event.threadID); - continue; - } - const g = Reply.groups[index]; - await addUserToGroup(event.senderID, g.threadID, g.name || "Unnamed Group"); - } - } - } -}; diff --git a/scripts/cmds/jsontomongodb.js b/scripts/cmds/jsontomongodb.js deleted file mode 100644 index 1de6e1a6..00000000 --- a/scripts/cmds/jsontomongodb.js +++ /dev/null @@ -1,265 +0,0 @@ -const fs = require("fs-extra"); - -module.exports = { - config: { - name: "jsontomongodb", - aliases: ["jsontomongo"], - version: "1.5", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Đồng bộ dữ liệu từ json sang mongodb", - en: "Synchronize data from json to mongodb" - }, - category: "owner", - guide: { - vi: " {pn} : Sẽ đồng bộ dữ liệu từ data json được lưu trong thư mục database/data sang mongodb\n\n Lưu ý: Nếu dữ liệu đã tồn tại trong mongodb thì sẽ được cập nhật lại", - en: " {pn} : Will synchronize data from json data stored in the database/data folder to mongodb\n\n Note: If the data already exists in mongodb, it will be updated" - } - }, - - langs: { - vi: { - invalidDatabase: "❌ Vui lòng chuyển database sang mongodb trong config sau đó khởi động lại bot để sử dụng lệnh này", - missingFile: "❌ Bạn chưa sao chép dữ liệu file %1 vào thư mục database/data", - formatInvalid: "❌ Định dạng dữ liệu không hợp lệ", - error: "❌ Đã có lỗi xảy ra:\n%1: %2", - successThread: "✅ Đã đồng bộ dữ liệu nhóm từ json sang mongodb thành công!", - successUser: "✅ Đã đồng bộ dữ liệu người dùng từ json sang mongodb thành công!", - successDashboard: "✅ Đã đồng bộ dữ liệu dashboard từ json sang mongodb thành công!", - successGlobal: "✅ Đã đồng bộ dữ liệu global từ json sang mongodb thành công!" - }, - en: { - invalidDatabase: "❌ Please switch database to mongodb in config then restart the bot to use this command", - missingFile: "❌ You haven't copied the data file %1 into the database/data folder", - formatInvalid: "❌ Data format is invalid", - error: "❌ An error occurred:\n%1: %2", - successThread: "✅ Successfully synchronized thread data from json to mongodb!", - successUser: "✅ Successfully synchronized user data from json to mongodb!", - successDashboard: "✅ Successfully synchronized dashboard data from json to mongodb!", - successGlobal: "✅ Successfully synchronized global data from json to mongodb!" - } - }, - - onStart: async function ({ args, message, threadModel, userModel, dashBoardModel, globalModel, getLang }) { - if (global.GoatBot.config.database.type !== "mongodb") - return message.reply(getLang("invalidDatabase")); - - switch (args[0]) { - case "thread": { - return syncThreadData(message, threadModel, getLang); - } - case "user": { - return syncUserData(message, userModel, getLang); - } - case "dashboard": { - return syncDashBoardData(message, dashBoardModel, getLang); - } - case "global": { - return syncGlobalData(message, globalModel, getLang); - } - case "all": { - await syncThreadData(message, threadModel, getLang); - await syncUserData(message, userModel, getLang); - await syncDashBoardData(message, dashBoardModel, getLang); - await syncGlobalData(message, globalModel, getLang); - return; - } - default: - return message.SyntaxError(); - } - } -}; - -async function syncThreadData(message, threadModel, getLang) { - let oldThreadsData; - const pathThreadData = `${process.cwd()}/database/data/threadsData.json`; - if (!fs.existsSync(pathThreadData)) - return message.reply(getLang("missingFile", pathThreadData.split("/").pop())); - - try { - oldThreadsData = require(pathThreadData); - delete require.cache[require.resolve(pathThreadData)]; - } - catch (err) { - return message.reply(getLang("formatInvalid")); - } - - try { - const bulkOperations = []; - - for (const thread of oldThreadsData) { - const threadIndex = global.db.allThreadData.findIndex(item => item.threadID == thread.threadID); - if (threadIndex === -1) { - bulkOperations.push({ - insertOne: { - document: thread - } - }); - } - else { - bulkOperations.push({ - updateOne: { - filter: { threadID: thread.threadID }, - update: thread - } - }); - } - } - - if (bulkOperations.length > 0) { - await threadModel.bulkWrite(bulkOperations); - global.db.allThreadData = await threadModel.find({}).lean(); - } - - return message.reply(getLang("successThread")); - } - catch (err) { - return message.reply(getLang("error", err.name, err.message)); - } -} - -async function syncUserData(message, userModel, getLang) { - let oldUsersData; - const pathUsersData = `${process.cwd()}/database/data/usersData.json`; - if (!fs.existsSync(pathUsersData)) - return message.reply(getLang("missingFile", pathUsersData.split("/").pop())); - - try { - oldUsersData = require(pathUsersData); - delete require.cache[require.resolve(pathUsersData)]; - } - catch (err) { - return message.reply(getLang("formatInvalid")); - } - - try { - const bulkOperations = []; - - for (const user of oldUsersData) { - const userIndex = global.db.allUserData.findIndex(item => item.userID == user.userID); - if (userIndex === -1) { - bulkOperations.push({ - insertOne: { - document: user - } - }); - } - else { - bulkOperations.push({ - updateOne: { - filter: { userID: user.userID }, - update: user - } - }); - } - } - - if (bulkOperations.length > 0) { - await userModel.bulkWrite(bulkOperations); - global.db.allUserData = await userModel.find({}).lean(); - } - - return message.reply(getLang("successUser")); - } - catch (err) { - return message.reply(getLang("error", err.name, err.message)); - } -} - -async function syncDashBoardData(message, dashBoardModel, getLang) { - let oldDashBoardData; - const pathDashBoardData = `${process.cwd()}/database/data/dashBoardData.json`; - if (!fs.existsSync(pathDashBoardData)) - return message.reply(getLang("missingFile", pathDashBoardData.split("/").pop())); - - try { - oldDashBoardData = require(pathDashBoardData); - delete require.cache[require.resolve(pathDashBoardData)]; - } - catch (err) { - return message.reply(getLang("formatInvalid")); - } - - try { - const bulkOperations = []; - - for (const dashboard of oldDashBoardData) { - const dashboardIndex = global.db.allDashBoardData.findIndex(item => item.email == dashboard.email); - if (dashboardIndex === -1) { - bulkOperations.push({ - insertOne: { - document: dashboard - } - }); - } - else { - bulkOperations.push({ - updateOne: { - filter: { email: dashboard.email }, - update: dashboard - } - }); - } - } - - if (bulkOperations.length > 0) { - await dashBoardModel.bulkWrite(bulkOperations); - global.db.allDashBoardData = await dashBoardModel.find({}).lean(); - } - - return message.reply(getLang("successDashboard")); - } - catch (err) { - return message.reply(getLang("error", err.name, err.message)); - } -} - -async function syncGlobalData(message, globalModel, getLang) { - let oldGlobalData; - const pathGlobalData = `${process.cwd()}/database/data/globalData.json`; - if (!fs.existsSync(pathGlobalData)) - return message.reply(getLang("missingFile", pathGlobalData.split("/").pop())); - - try { - oldGlobalData = require(pathGlobalData); - delete require.cache[require.resolve(pathGlobalData)]; - } - catch (err) { - return message.reply(getLang("formatInvalid")); - } - - try { - const bulkOperations = []; - - for (const global_ of oldGlobalData) { - const globalIndex = global.db.allGlobalData.findIndex(item => item.key == global_.key); - if (globalIndex === -1) { - bulkOperations.push({ - insertOne: { - document: global_ - } - }); - } - else { - bulkOperations.push({ - updateOne: { - filter: { key: global_.key }, - update: global_ - } - }); - } - } - - if (bulkOperations.length > 0) { - await globalModel.bulkWrite(bulkOperations); - global.db.allGlobalData = await globalModel.find({}).lean(); - } - - return message.reply(getLang("successGlobal")); - } - catch (err) { - return message.reply(getLang("error", err.name, err.message)); - } -} diff --git a/scripts/cmds/jsontosqlite.js b/scripts/cmds/jsontosqlite.js deleted file mode 100644 index 65f080b8..00000000 --- a/scripts/cmds/jsontosqlite.js +++ /dev/null @@ -1,225 +0,0 @@ -const fs = require("fs-extra"); -const { sequelize } = global.db; - -module.exports = { - config: { - name: "jsontosqlite", - version: "1.4", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Đồng bộ dữ liệu từ json sang sqlite", - en: "Synchronize data from json to sqlite" - }, - category: "owner", - guide: { - vi: " {pn} : Sẽ đồng bộ dữ liệu từ data json được lưu trong thư mục database/data sang sqlite\n\n Lưu ý: Nếu dữ liệu đã tồn tại trong sqlite thì sẽ được cập nhật lại", - en: " {pn} : Will synchronize data from json data stored in the database/data folder to sqlite\n\n Note: If the data already exists in sqlite, it will be updated" - } - }, - - langs: { - vi: { - invalidDatabase: "❌ Vui lòng chuyển database sang sqlite trong config sau đó khởi động lại bot để sử dụng lệnh này", - missingFile: "❌ Bạn chưa sao chép dữ liệu file %1 vào thư mục database/data", - formatInvalid: "❌ Định dạng dữ liệu không hợp lệ", - error: "❌ Đã có lỗi xảy ra:\n%1: %2", - successThread: "✅ Đã đồng bộ dữ liệu nhóm từ json sang sqlite thành công!", - successUser: "✅ Đã đồng bộ dữ liệu người dùng từ json sang sqlite thành công!", - successDashboard: "✅ Đã đồng bộ dữ liệu dashboard từ json sang sqlite thành công!", - successGlobal: "✅ Đã đồng bộ dữ liệu global từ json sang sqlite thành công!" - }, - en: { - invalidDatabase: "❌ Please switch database to sqlite in config then restart the bot to use this command", - missingFile: "❌ You haven't copied the data file %1 into the database/data folder", - formatInvalid: "❌ Data format is invalid", - error: "❌ An error occurred:\n%1: %2", - successThread: "✅ Successfully synchronized thread data from json to sqlite!", - successUser: "✅ Successfully synchronized user data from json to sqlite!", - successDashboard: "✅ Successfully synchronized dashboard data from json to sqlite!", - successGlobal: "✅ Successfully synchronized global data from json to sqlite!" - } - }, - - onStart: async function ({ args, message, threadModel, userModel, dashBoardModel, globalModel, getLang }) { - if (global.GoatBot.config.database.type !== "sqlite") - return message.reply(getLang("invalidDatabase")); - - switch (args[0]) { - case "thread": { - return await syncThreadData(message, threadModel, getLang); - } - case "user": { - return await syncUserData(message, userModel, getLang); - } - case "dashboard": { - return await syncDashBoardData(message, dashBoardModel, getLang); - } - case "global": { - return await syncGlobalData(message, globalModel, getLang); - } - case "all": { - await syncThreadData(message, threadModel, getLang); - await syncUserData(message, userModel, getLang); - await syncDashBoardData(message, dashBoardModel, getLang); - await syncGlobalData(message, globalModel, getLang); - return; - } - default: - return message.SyntaxError(); - } - } -}; - -async function syncThreadData(message, threadModel, getLang) { - let oldThreadsData; - const pathThreadData = `${process.cwd()}/database/data/threadsData.json`; - if (!fs.existsSync(pathThreadData)) - return message.reply(getLang("missingFile", pathThreadData.split("/").pop())); - - try { - oldThreadsData = require(pathThreadData); - delete require.cache[require.resolve(pathThreadData)]; - } - catch (err) { - return message.reply(getLang("formatInvalid")); - } - - try { - await sequelize.transaction(async (transaction) => { - for (const thread of oldThreadsData) { - const threadIndex = global.db.allThreadData.findIndex(item => item.threadID == thread.threadID); - - if (threadIndex === -1) { - await threadModel.create(thread, { transaction }); - } - else { - await threadModel.update(thread, { where: { threadID: thread.threadID }, transaction }); - } - } - }); - - const allThreadData = await threadModel.findAll(); - global.db.allThreadData = allThreadData.map(thread => thread.get({ plain: true })); - - return message.reply(getLang("successThread")); - } - catch (err) { - return message.reply(getLang("error", err.name, err.message)); - } -} - -async function syncUserData(message, userModel, getLang) { - let oldUsersData; - const pathUserData = `${process.cwd()}/database/data/usersData.json`; - if (!fs.existsSync(pathUserData)) - return message.reply(getLang("missingFile", pathUserData.split("/").pop())); - - try { - oldUsersData = require(pathUserData); - delete require.cache[require.resolve(pathUserData)]; - } - catch (err) { - return message.reply(getLang("formatInvalid")); - } - - try { - await sequelize.transaction(async (transaction) => { - for (const user of oldUsersData) { - const userIndex = global.db.allUserData.findIndex(item => item.userID == user.userID); - - if (userIndex === -1) { - await userModel.create(user, { transaction }); - } - else { - await userModel.update(user, { where: { userID: user.userID }, transaction }); - } - } - }); - - const allUserData = await userModel.findAll(); - global.db.allUserData = allUserData.map(user => user.get({ plain: true })); - - return message.reply(getLang("successUser")); - } - catch (err) { - return message.reply(getLang("error", err.name, err.message)); - } -} - -async function syncDashBoardData(message, dashBoardModel, getLang) { - let oldDashBoardData; - const pathDashBoardData = `${process.cwd()}/database/data/dashBoardData.json`; - if (!fs.existsSync(pathDashBoardData)) - return message.reply(getLang("missingFile", pathDashBoardData.split("/").pop())); - - try { - oldDashBoardData = require(pathDashBoardData); - delete require.cache[require.resolve(pathDashBoardData)]; - } - catch (err) { - return message.reply(getLang("formatInvalid")); - } - - try { - await sequelize.transaction(async (transaction) => { - for (const dashboard of oldDashBoardData) { - const dashboardIndex = global.db.dashBoardData.findIndex(item => item.email == dashboard.email); - - if (dashboardIndex === -1) { - await dashBoardModel.create(dashboard, { transaction }); - } - else { - await dashBoardModel.update(dashboard, { where: { email: dashboard.email }, transaction }); - } - } - }); - - const allDashBoardData = await dashBoardModel.findAll(); - global.db.dashBoardData = allDashBoardData.map(dashboard => dashboard.get({ plain: true })); - - return message.reply(getLang("successDashboard")); - } - catch (err) { - return message.reply(getLang("error", err.name, err.message)); - } -} - -async function syncGlobalData(message, globalModel, getLang) { - let oldGlobalData; - const pathGlobalData = `${process.cwd()}/database/data/globalData.json`; - if (!fs.existsSync(pathGlobalData)) - return message.reply(getLang("missingFile", pathGlobalData.split("/").pop())); - - try { - oldGlobalData = require(pathGlobalData); - delete require.cache[require.resolve(pathGlobalData)]; - } - catch (err) { - return message.reply(getLang("formatInvalid")); - } - - try { - await sequelize.transaction(async (transaction) => { - for (const global_ of oldGlobalData) { - const globalIndex = global.db.allGlobalData.findIndex(item => item.key == global_.key); - - if (globalIndex === -1) { - await globalModel.create(global_, { transaction }); - } - else { - await globalModel.update(global_, { where: { key: global_.key }, transaction }); - } - } - }); - - const allGlobalData = await globalModel.findAll(); - global.db.allGlobalData = allGlobalData.map(global_ => global_.get({ plain: true })); - - return message.reply(getLang("successGlobal")); - } - catch (err) { - return message.reply(getLang("error", err.name, err.message)); - } -} diff --git a/scripts/cmds/kick.js b/scripts/cmds/kick.js deleted file mode 100644 index 7c7c21f4..00000000 --- a/scripts/cmds/kick.js +++ /dev/null @@ -1,68 +0,0 @@ -module.exports = { - config: { - name: "kick", - version: "1.3", - author: "NTKhang", - countDown: 5, - role: 1, - description: { - vi: "Kick thành viên khỏi box chat", - en: "Kick member out of chat box" - }, - category: "owner", - guide: { - vi: " {pn} @tags: dùng để kick những người được tag", - en: " {pn} @tags: use to kick members who are tagged" - } - }, - - langs: { - vi: { - needAdmin: "Vui lòng thêm quản trị viên cho bot trước khi sử dụng tính năng này" - }, - en: { - needAdmin: "Please add admin for bot before using this feature" - }, - tl: { - needAdmin: "Mangyaring magdagdag ng admin para sa bot bago gamitin ang feature na ito" - }, - hi: { - needAdmin: "Is feature ka upyog karne se pehle bot ke liye admin add karein" - }, - ar: { - needAdmin: "الرجاء إضافة مسؤول للبوت قبل استخدام هذه الميزة" - }, - bn: { - needAdmin: "এই ফিচার ব্যবহার করার আগে bot এ admin যোগ করুন" - } - }, - - onStart: async function ({ message, event, args, threadsData, api, getLang }) { - const adminIDs = await threadsData.get(event.threadID, "adminIDs"); - if (!adminIDs.includes(api.getCurrentUserID())) - return message.reply(getLang("needAdmin")); - async function kickAndCheckError(uid) { - try { - await api.removeUserFromGroup(uid, event.threadID); - } - catch (e) { - message.reply(getLang("needAdmin")); - return "ERROR"; - } - } - if (!args[0]) { - if (!event.messageReply) - return message.SyntaxError(); - await kickAndCheckError(event.messageReply.senderID); - } - else { - const uids = Object.keys(event.mentions); - if (uids.length === 0) - return message.SyntaxError(); - if (await kickAndCheckError(uids.shift()) === "ERROR") - return; - for (const uid of uids) - api.removeUserFromGroup(uid, event.threadID); - } - } -}; diff --git a/scripts/cmds/kickall.js b/scripts/cmds/kickall.js deleted file mode 100644 index 9d334f7d..00000000 --- a/scripts/cmds/kickall.js +++ /dev/null @@ -1,48 +0,0 @@ -module.exports = { - config: { - name: "kickall", - version: "1.0", - author: "NEXXO", - role: 2, - shortDescription: { - en: "Kick everyone from the group" - }, - category: "owner", - guide: { - en: "{prefix}kickall" - } - }, - - onStart: async function ({ api, event }) { - const threadID = event.threadID; - const senderID = event.senderID; - - try { - const threadInfo = await api.getThreadInfo(threadID); - - if (!threadInfo.adminIDs.some(item => item.id === api.getCurrentUserID())) { - return api.sendMessage("❌ Bot must be an admin to kick members.", threadID); - } - - const membersToKick = threadInfo.participantIDs.filter(id => id !== senderID && id !== api.getCurrentUserID()); - - if (membersToKick.length === 0) { - return api.sendMessage("❌ No members to kick.", threadID); - } - - api.sendMessage(`⚠️ Kicking ${membersToKick.length} members...`, threadID, async () => { - for (const userID of membersToKick) { - try { - await api.removeUserFromGroup(userID, threadID); - } catch (e) { - console.log(`❌ Failed to kick ${userID}: ${e.message}`); - } - } - }); - } catch (e) { - console.error(e); - api.sendMessage("❌ An error occurred while trying to kick members.", threadID); - } - - } -}; diff --git a/scripts/cmds/kiss.js b/scripts/cmds/kiss.js deleted file mode 100644 index 2d407b31..00000000 --- a/scripts/cmds/kiss.js +++ /dev/null @@ -1,77 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -const mahmud = async () => { - const base = await axios.get( - "https://raw.githubusercontent.com/mahmudx7/HINATA/main/baseApiUrl.json" - ); - return base.data.mahmud; -}; - -/** - * @author MahMUD - * @author: do not delete it - */ - -module.exports = { - config: { - name: "kiss", - version: "1.7", - author: "MahMUD", - countDown: 5, - role: 0, - longDescription: "Generate anime-style kiss image", - category: "love", - guide: "{pn} @mention" - }, - - onStart: async function ({ message, event, api }) { - try { - const obfuscatedAuthor = String.fromCharCode(77, 97, 104, 77, 85, 68); - if (module.exports.config.author.trim() !== obfuscatedAuthor) { - return api.sendMessage( - "❌ | You are not authorized to change the author name.", - event.threadID, - event.messageID - ); - } - - const mention = Object.keys(event.mentions); - if (mention.length === 0) { - return message.reply("Please mention someone to kiss 💋"); - } - - const senderID = event.senderID; - const targetID = mention[0]; - - const base = await mahmud(); - const apiURL = `${base}/api/kiss`; - - const response = await axios.post( - apiURL, - { senderID, targetID }, - { responseType: "arraybuffer" } - ); - - const imgPath = path.join( - __dirname, - `kiss_${senderID}_${targetID}.png` - ); - fs.writeFileSync(imgPath, Buffer.from(response.data, "binary")); - - message.reply({ - body: "💋 Here’s your kiss image!", - attachment: fs.createReadStream(imgPath) - }); - - setTimeout(() => { - if (fs.existsSync(imgPath)) fs.unlinkSync(imgPath); - }, 10000); - - } catch (err) { - console.error("Error in kiss command:", err.message || err); - message.reply("🥹 error, contact MahMUD."); - } - } -}; diff --git a/scripts/cmds/kiss2.js b/scripts/cmds/kiss2.js deleted file mode 100644 index 7f636962..00000000 --- a/scripts/cmds/kiss2.js +++ /dev/null @@ -1,74 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -const baseApiUrl = async () => { - const base = await axios.get( - "https://raw.githubusercontent.com/mahmudx7/HINATA/main/baseApiUrl.json" - ); - return base.data.mahmud; -}; - -/** -* @author MahMUD -* @author: do not delete it -*/ - -module.exports = { - config: { - name: "kiss2", - aliases: ["k2"], - version: "1.7", - author: "MahMUD", - role: 0, - category: "fun", - cooldown: 8, - guide: "kiss2 [mention/reply/UID]", - }, - - onStart: async function ({ api, event, args }) { - const obfuscatedAuthor = String.fromCharCode(77, 97, 104, 77, 85, 68); - if (module.exports.config.author !== obfuscatedAuthor) { - return api.sendMessage("You are not authorized to change the author name.", event.threadID, event.messageID); - } - - const { threadID, messageID, messageReply, mentions, senderID } = event; - const type = args[0]; - - if (!type) return api.sendMessage("Use: fun slap @tag", threadID, messageID); - - let id = senderID; - let id2; - - if (messageReply) { - id2 = messageReply.senderID; - } else if (Object.keys(mentions).length > 0) { - id2 = Object.keys(mentions)[0]; - } else if (args[1]) { - id2 = args[1]; - } else { - return api.sendMessage("Mention, reply, or provide UID of the target.", threadID, messageID); - } - - try { - const url = `${await baseApiUrl()}/api/dig?type=kiss&user=${id}&user2=${id2}`; - - const response = await axios.get(url, { responseType: "arraybuffer" }); - const filePath = path.join(__dirname, `kiss_${id2}.png`); - fs.writeFileSync(filePath, response.data); - - api.sendMessage( - { - attachment: fs.createReadStream(filePath), - body: `Effect kiss successful 💋` - }, - threadID, - () => fs.unlinkSync(filePath), - messageID - ); - } catch (err) { - console.error(err); - api.sendMessage(`🥹error, contact MahMUD.`, threadID, messageID); - } - } -}; diff --git a/scripts/cmds/leaderboard.js b/scripts/cmds/leaderboard.js deleted file mode 100644 index 3022271e..00000000 --- a/scripts/cmds/leaderboard.js +++ /dev/null @@ -1,198 +0,0 @@ -const fs = require("fs-extra"); -const path = require("path"); -const { createCanvas, loadImage } = require("canvas"); -const axios = require("axios"); - -module.exports.config = { - name: "leaderboard", - aliases: ["lb", "top"], - version: "6.0", - author: "MOHAMMAD AKASH", - countDown: 10, - role: 0, - shortDescription: "Top 10 richest users", - category: "economy" -}; - -function formatBalance(num) { - if (num >= 1e9) return (num / 1e9).toFixed(1) + "B"; - if (num >= 1e6) return (num / 1e6).toFixed(1) + "M"; - if (num >= 1e3) return (num / 1e3).toFixed(1) + "K"; - return String(num); -} - -function roundRect(ctx, x, y, w, h, r, fill = false, stroke = false) { - ctx.beginPath(); - ctx.moveTo(x + r, y); - ctx.lineTo(x + w - r, y); - ctx.quadraticCurveTo(x + w, y, x + w, y + r); - ctx.lineTo(x + w, y + h - r); - ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); - ctx.lineTo(x + r, y + h); - ctx.quadraticCurveTo(x, y + h, x, y + h - r); - ctx.lineTo(x, y + r); - ctx.quadraticCurveTo(x, y, x + r, y); - ctx.closePath(); - if (fill) ctx.fill(); - if (stroke) ctx.stroke(); -} - -async function loadAvatar(uid, cacheDir) { - const tmpPath = path.join(cacheDir, `av_${uid}_${Date.now()}.png`); - try { - const imageUrl = `https://graph.facebook.com/${uid}/picture?height=200&width=200&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662`; - const response = await axios.get(imageUrl, { responseType: "arraybuffer" }); - await fs.writeFile(tmpPath, response.data); - const img = await loadImage(tmpPath); - await fs.remove(tmpPath); - return img; - } catch (e) { - if (await fs.pathExists(tmpPath)) await fs.remove(tmpPath); - return null; - } -} - -function drawAvatar(ctx, avatar, name, ax, ay, size, isTop3) { - ctx.save(); - ctx.beginPath(); - ctx.arc(ax + size / 2, ay + size / 2, size / 2, 0, Math.PI * 2); - ctx.clip(); - - if (avatar) { - ctx.drawImage(avatar, ax, ay, size, size); - } else { - const colors = ["#1565c0", "#6a1b9a", "#00695c", "#bf360c", "#4e342e", "#37474f"]; - ctx.fillStyle = colors[name.charCodeAt(0) % colors.length]; - ctx.fillRect(ax, ay, size, size); - ctx.font = `bold ${Math.floor(size * 0.45)}px Arial`; - ctx.fillStyle = "#ffffff"; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillText((name || "?")[0].toUpperCase(), ax + size / 2, ay + size / 2 + 2); - } - - ctx.restore(); - ctx.strokeStyle = isTop3 ? "#ffd700" : "rgba(255,255,255,0.3)"; - ctx.lineWidth = isTop3 ? 2.5 : 1.5; - ctx.beginPath(); - ctx.arc(ax + size / 2, ay + size / 2, size / 2 + 2, 0, Math.PI * 2); - ctx.stroke(); -} - -module.exports.onStart = async function ({ api, event, usersData }) { - const { threadID, messageID } = event; - - const allUsers = await usersData.getAll(); - const sorted = Object.entries(allUsers) - .map(([uid, data]) => ({ - uid, - name: data.name || "Unknown", - money: data?.data?.money ?? 0 - })) - .sort((a, b) => b.money - a.money) - .slice(0, 10); - - const cacheDir = path.join(__dirname, "cache"); - await fs.ensureDir(cacheDir); - - const avatars = []; - for (const user of sorted) { - const img = await loadAvatar(user.uid, cacheDir); - avatars.push(img); - } - - const width = 800; - const rowH = 72; - const headerH = 130; - const footerH = 50; - const height = headerH + rowH * sorted.length + footerH; - - const canvas = createCanvas(width, height); - const ctx = canvas.getContext("2d"); - - const bgGrad = ctx.createLinearGradient(0, 0, width, height); - bgGrad.addColorStop(0, "#0a2a4a"); - bgGrad.addColorStop(1, "#0f4c81"); - ctx.fillStyle = bgGrad; - roundRect(ctx, 0, 0, width, height, 20, true); - - ctx.fillStyle = "rgba(255,255,255,0.04)"; - for (let i = 0; i < 5; i++) { - ctx.beginPath(); - ctx.arc(width - 40 + i * 25, 30 + i * 20, 90 + i * 35, 0, Math.PI * 2); - ctx.fill(); - } - - ctx.textAlign = "center"; - ctx.font = "bold 38px Arial"; - ctx.fillStyle = "#ffffff"; - ctx.fillText("🏆 LEADERBOARD", width / 2, 55); - - ctx.font = "18px Arial"; - ctx.fillStyle = "rgba(255,255,255,0.6)"; - ctx.fillText("GOAT NATIONAL BANK — TOP 10", width / 2, 85); - - ctx.strokeStyle = "rgba(255,255,255,0.15)"; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(40, 105); - ctx.lineTo(width - 40, 105); - ctx.stroke(); - - const medals = ["🥇", "🥈", "🥉"]; - const avatarSize = 46; - - for (let i = 0; i < sorted.length; i++) { - const user = sorted[i]; - const avatar = avatars[i]; - const y = headerH + i * rowH; - const isTop3 = i < 3; - - if (isTop3) { - const rowGrad = ctx.createLinearGradient(30, y, width - 30, y); - rowGrad.addColorStop(0, "rgba(255,215,0,0.12)"); - rowGrad.addColorStop(1, "rgba(255,215,0,0.02)"); - ctx.fillStyle = rowGrad; - } else { - ctx.fillStyle = i % 2 === 0 ? "rgba(255,255,255,0.05)" : "rgba(255,255,255,0.02)"; - } - roundRect(ctx, 30, y + 6, width - 60, rowH - 10, 12, true); - - ctx.textAlign = "center"; - ctx.textBaseline = "alphabetic"; - ctx.font = isTop3 ? "bold 26px Arial" : "bold 20px Arial"; - ctx.fillStyle = isTop3 ? "#ffd700" : "rgba(255,255,255,0.5)"; - ctx.fillText(isTop3 ? medals[i] : `#${i + 1}`, 72, y + rowH / 2 + 8); - - const ax = 100; - const ay = y + rowH / 2 - avatarSize / 2; - drawAvatar(ctx, avatar, user.name, ax, ay, avatarSize, isTop3); - - ctx.textAlign = "left"; - ctx.textBaseline = "alphabetic"; - let displayName = user.name; - ctx.font = isTop3 ? "bold 22px Arial" : "bold 19px Arial"; - ctx.fillStyle = "#ffffff"; - while (ctx.measureText(displayName).width > 390 && displayName.length > 1) { - displayName = displayName.slice(0, -1); - } - if (displayName !== user.name) displayName += "…"; - ctx.fillText(displayName, 162, y + rowH / 2 + 8); - - ctx.textAlign = "right"; - ctx.font = isTop3 ? "bold 22px Arial" : "bold 19px Arial"; - ctx.fillStyle = isTop3 ? "#ffd700" : "#4fc3f7"; - ctx.fillText("$" + formatBalance(user.money), width - 50, y + rowH / 2 + 8); - } - - ctx.textAlign = "center"; - ctx.font = "15px Arial"; - ctx.fillStyle = "rgba(255,255,255,0.3)"; - ctx.fillText("GOAT BOT • Economy System", width / 2, height - 18); - - const filePath = path.join(cacheDir, `leaderboard_${Date.now()}.png`); - await fs.writeFile(filePath, canvas.toBuffer("image/png")); - - await api.sendMessage({ attachment: fs.createReadStream(filePath) }, threadID, messageID); - setTimeout(() => fs.remove(filePath), 10000); -}; diff --git a/scripts/cmds/liner.js b/scripts/cmds/liner.js deleted file mode 100644 index 3d79f07c..00000000 --- a/scripts/cmds/liner.js +++ /dev/null @@ -1,60 +0,0 @@ -const axios = require('axios'); - -async function liner(api, event, args, message) { - try { - const prompt = args.join(" ").trim(); - - if (!prompt) { - return message.reply("Please provide a prompt."); - } - - const response = await getLinerResponse(prompt); - - if (response && response.answer) { - message.reply(response.answer, (r, s) => { - global.GoatBot.onReply.set(s.messageID, { - commandName: module.exports.config.name, - uid: event.senderID - }); - }); - } else { - message.reply("No response from Liner."); - } - } catch (error) { - console.error("Error:", error); - message.reply("An error occurred while processing your request."); - } -} - -async function getLinerResponse(prompt) { - try { - const url = `https://liner-ai.vercel.app/kshitiz?prompt=${encodeURIComponent(prompt)}`; - const response = await axios.get(url); - return response.data; - } catch (error) { - console.error("Error from Liner API:", error.message); - throw error; - } -} - -module.exports = { - config: { - name: "liner", - version: "1.0", - author: "nexo_here", - role: 0, - longDescription: "Liner AI assistant.", - category: "ai", - guide: { - en: "{p}liner [prompt]" - } - }, - - handleCommand: liner, - onStart: function ({ api, message, event, args }) { - return liner(api, event, args, message); - }, - onReply: function ({ api, message, event, args }) { - return liner(api, event, args, message); - } -}; \ No newline at end of file diff --git a/scripts/cmds/loadconfig.js b/scripts/cmds/loadconfig.js deleted file mode 100644 index cb1f4897..00000000 --- a/scripts/cmds/loadconfig.js +++ /dev/null @@ -1,33 +0,0 @@ -const fs = require("fs-extra"); - -module.exports = { - config: { - name: "loadconfig", - aliases: ["loadcf"], - version: "1.4", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Load lại config của bot", - en: "Reload config of bot" - }, - category: "owner", - guide: "{pn}" - }, - - langs: { - vi: { - success: "Config đã được load lại thành công" - }, - en: { - success: "Config has been reloaded successfully" - } - }, - - onStart: async function ({ message, getLang }) { - global.GoatBot.config = fs.readJsonSync(global.client.dirConfig); - global.GoatBot.configCommands = fs.readJsonSync(global.client.dirConfigCommands); - message.reply(getLang("success")); - } -}; \ No newline at end of file diff --git a/scripts/cmds/manga.js b/scripts/cmds/manga.js deleted file mode 100644 index 861fbfd3..00000000 --- a/scripts/cmds/manga.js +++ /dev/null @@ -1,78 +0,0 @@ -const axios = require("axios"); - -module.exports = { - config: { - name: "manga", - aliases: ["man", "ani-manga"], - version: "1.0", - author: "nexo_here", - countDown: 0, - role: 0, - description: "Search Manga info using AniList API", - category: "anime", - guide: { - en: "{pn} [manga name] — get manga info from AniList" - } - }, - - onStart: async function ({ api, event, args }) { - const query = args.join(" "); - if (!query) return api.sendMessage("🔍 | Please provide a manga name.", event.threadID); - - const anilistQuery = ` - query ($search: String) { - Media(search: $search, type: MANGA) { - title { - romaji - english - native - } - description(asHtml: false) - status - chapters - volumes - averageScore - genres - siteUrl - coverImage { - large - } - } - } - `; - - const variables = { - search: query - }; - - try { - const res = await axios.post("https://graphql.anilist.co", { - query: anilistQuery, - variables: variables - }); - - const manga = res.data.data.Media; - - const title = manga.title.english || manga.title.romaji || manga.title.native; - const desc = manga.description?.replace(/
/g, "\n").replace(/<\/?[^>]+(>|$)/g, "").substring(0, 300) || "No description available."; - const msg = `📖 ${title}\n\n📌 Status: ${manga.status}\n📚 Chapters: ${manga.chapters || "?"}\n📘 Volumes: ${manga.volumes || "?"}\n⭐ Score: ${manga.averageScore || "?"}/100\n🎭 Genres: ${manga.genres.join(", ")}\n\n📝 Description:\n${desc}...\n\n🔗 ${manga.siteUrl}`; - - const cover = manga.coverImage.large; - - // Download and send image with message - const img = (await axios.get(cover, { responseType: "arraybuffer" })).data; - const imgPath = __dirname + "/manga.jpg"; - const fs = require("fs"); - fs.writeFileSync(imgPath, Buffer.from(img, "utf-8")); - - api.sendMessage({ - body: msg, - attachment: fs.createReadStream(imgPath) - }, event.threadID, () => fs.unlinkSync(imgPath)); - - } catch (e) { - console.error(e); - api.sendMessage("❌ | Couldn't fetch manga info. Try again or check the name.", event.threadID); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/math.js b/scripts/cmds/math.js deleted file mode 100644 index a12794f1..00000000 --- a/scripts/cmds/math.js +++ /dev/null @@ -1,133 +0,0 @@ -const axios = require("axios"); - -// SAME API as slot.js -const API_URL = "https://balance-bot-api.onrender.com"; - -// Get balance -async function getBalance(userID) { - try { - const res = await axios.get(`${API_URL}/api/balance/${userID}`); - return res.data.balance || 100; - } catch { - return 100; - } -} - -// Win -async function winGame(userID, amount) { - try { - const res = await axios.post(`${API_URL}/api/balance/win`, { userID, amount }); - return res.data.success ? res.data.balance : null; - } catch { - return null; - } -} - -// Lose -async function loseGame(userID, amount) { - try { - const res = await axios.post(`${API_URL}/api/balance/lose`, { userID, amount }); - return res.data.success ? res.data.balance : null; - } catch { - return null; - } -} - -// Format balance -function formatBalance(num) { - return num.toLocaleString("en-US") + " $"; -} - -// Generate math -function generateMath() { - const a = Math.floor(Math.random() * 20) + 1; - const b = Math.floor(Math.random() * 20) + 1; - const ops = ["+", "-", "×"]; - const op = ops[Math.floor(Math.random() * ops.length)]; - - let answer; - if (op === "+") answer = a + b; - if (op === "-") answer = a - b; - if (op === "×") answer = a * b; - - return { question: `${a} ${op} ${b}`, answer }; -} - -module.exports = { - config: { - name: "math", - version: "1.0", - author: "MOHAMMAD AKASH", - role: 0, - category: "economy", - shortDescription: "Math Game (Reply Based)" - }, - - onStart: async function ({ api, event }) { - const { threadID, senderID, messageID } = event; - - const balance = await getBalance(senderID); - if (balance < 30) { - return api.sendMessage( - `❌ Insufficient Balance\n💳 Balance: ${formatBalance(balance)}`, - threadID, - messageID - ); - } - - const math = generateMath(); - - api.sendMessage( -`✦ Mᴀᴛʜ Gᴀᴍᴇ ✦ - -Solve this: - -${math.question} = ? - -✍️ Reply with the answer`, - threadID, - (err, info) => { - if (err) return; - - global.GoatBot.onReply.set(info.messageID, { - commandName: "math", - author: senderID, - answer: math.answer, - messageID: info.messageID - }); - - // Auto timeout (20s) - setTimeout(() => { - global.GoatBot.onReply.delete(info.messageID); - api.unsendMessage(info.messageID).catch(() => {}); - }, 20000); - }, - messageID // ✅ reply to command - ); - }, - - onReply: async function ({ api, event, Reply }) { - const { senderID, body, threadID } = event; - if (senderID !== Reply.author) return; - - const userAns = Number(body.trim()); - if (isNaN(userAns)) return; - - await api.unsendMessage(Reply.messageID); - global.GoatBot.onReply.delete(Reply.messageID); - - if (userAns === Reply.answer) { - const newBal = await winGame(senderID, 200); - return api.sendMessage( - `✅ Correct Answer!\n🎉 +200 $\n💳 Balance: ${formatBalance(newBal)}`, - threadID - ); - } else { - const newBal = await loseGame(senderID, 50); - return api.sendMessage( - `❌ Wrong Answer!\nCorrect: ${Reply.answer}\n−50 $\n💳 Balance: ${formatBalance(newBal)}`, - threadID - ); - } - } -}; diff --git a/scripts/cmds/mia.js b/scripts/cmds/mia.js deleted file mode 100644 index 14b3015f..00000000 --- a/scripts/cmds/mia.js +++ /dev/null @@ -1,96 +0,0 @@ -const axios = require("axios"); -const fs = require("fs-extra"); -const canvas = require("canvas"); - -module.exports = { - config: { - name: "mia", - aliases: ["mia khalifa"], - author: "Otineeeeyyyy",//fixed by Denish and updated - countDown: 5, - role: 0, - category: "fun", - }, - - wrapText: async (ctx, text, maxWidth) => { - return new Promise((resolve) => { - if (ctx.measureText(text).width < maxWidth) return resolve([text]); - if (ctx.measureText("W").width > maxWidth) return resolve(null); - - const words = text.split(" "); - const lines = []; - let line = ""; - - while (words.length > 0) { - let split = false; - while (ctx.measureText(words[0]).width >= maxWidth) { - const temp = words[0]; - words[0] = temp.slice(0, -1); - if (split) words[1] = `${temp.slice(-1)}${words[1]}`; - else { - split = true; - words.splice(1, 0, temp.slice(-1)); - } - } - - if (ctx.measureText(`${line}${words[0]}`).width < maxWidth) { - line += `${words.shift()} `; - } else { - lines.push(line.trim()); - line = ""; - } - - if (words.length === 0) lines.push(line.trim()); - } - resolve(lines); - }); - }, - - onStart: async function ({ api, event, args }) { - const { loadImage, createCanvas } = require("canvas"); - let { threadID, messageID } = event; - - const text = args.join(" "); - if (!text) return api.sendMessage("Enter text!", threadID, messageID); - - const imageURL = "https://i.ibb.co/4gDpt4Tx/img-1765026096438.jpg"; - const pathImg = __dirname + "/cache/mia.png"; - - try { - const res = await axios.get(imageURL, { responseType: "arraybuffer" }); - fs.writeFileSync(pathImg, Buffer.from(res.data)); - } catch (err) { - return api.sendMessage("❌ Failed to download image!", threadID, messageID); - } - - const baseImage = await loadImage(pathImg); - const canvasImg = createCanvas(baseImage.width, baseImage.height); - const ctx = canvasImg.getContext("2d"); - - // Draw image - ctx.drawImage(baseImage, 0, 0, canvasImg.width, canvasImg.height); - - // 🔥 FIXED TEXT SETTINGS - ctx.font = "300 32px Arial"; // thin + smaller - ctx.fillStyle = "#000000"; - ctx.textAlign = "start"; - - // Wrap text - const lines = await this.wrapText(ctx, text, 600); - - // 🔥 MOVE TEXT DOWN (was 120, changed to 160) - const startY = 160; - - ctx.fillText(lines.join("\n"), 50, startY); - - // Save final - fs.writeFileSync(pathImg, canvasImg.toBuffer()); - - return api.sendMessage( - { attachment: fs.createReadStream(pathImg) }, - threadID, - () => fs.unlinkSync(pathImg), - messageID - ); - }, -}; diff --git a/scripts/cmds/mp3.js b/scripts/cmds/mp3.js deleted file mode 100644 index 5dcec7cb..00000000 --- a/scripts/cmds/mp3.js +++ /dev/null @@ -1,49 +0,0 @@ -const fs = require("fs-extra"); -const path = require("path"); -const axios = require("axios"); - -module.exports = { - config: { - name: "convertmp3", - aliases: ["mp3", "convertmp3"], - version: "1.0.0", - role: 0, - author: "MOHAMMAD AKASH", - shortDescription: "Convert video to MP3 🎧", - longDescription: "Download video from URL and convert to MP3.", - category: "media", - guide: "{p}convertmp3 " - }, - - onStart: async function({ api, args, event }) { - const { threadID, messageID } = event; - - try { - // 🔗 Get video URL from args or replied message - const url = args.join(" ") || event.messageReply?.attachments?.[0]?.url; - if (!url) return api.sendMessage("⚠️ ᴘʟᴇᴀsᴇ ᴘʀᴏᴠɪᴅᴇ ᴀ ᴠɪᴅᴇᴏ ᴜʀʟ!", threadID, messageID); - - // ⏳ Font ABC style message - api.sendMessage("Mᴘ3 ᴘʀᴏᴄᴇssɪɴɢ ᴘʟᴇᴀsᴇ ᴡᴀɪᴛ ⏳", threadID, messageID); - - // 📥 Download video - const { data } = await axios.get(url, { responseType: "arraybuffer" }); - - // 💾 Save as MP3 - const filePath = path.join(__dirname, "/cache/video.mp3"); - fs.writeFileSync(filePath, Buffer.from(data)); - - // 🔊 Send back as attachment - api.sendMessage({ - body: "Mᴘ3 ʀᴇᴀᴅʏ ✅", - attachment: fs.createReadStream(filePath) - }, threadID, async () => { - fs.unlinkSync(filePath); // Delete after sending - }, messageID); - - } catch (err) { - console.log(err); - api.sendMessage("⚠️ Fᴀɪʟᴇᴅ ᴛᴏ ᴄᴏɴᴠᴇʀᴛ ᴠɪᴅᴇᴏ!", threadID, messageID); - } - } -}; diff --git a/scripts/cmds/mygirl.js b/scripts/cmds/mygirl.js deleted file mode 100644 index fa49a981..00000000 --- a/scripts/cmds/mygirl.js +++ /dev/null @@ -1,50 +0,0 @@ -const fs = require("fs"); -const axios = require("axios"); - -const baseApiUrl = async () => { - const base = await axios.get("https://raw.githubusercontent.com/mahmudx7/HINATA/main/baseApiUrl.json"); - return base.data.mahmud; -}; - -module.exports.config = { - name: "mygirl", - version: "1.7", - role: 0, - author: "MahMUD", - category: "fun", - cooldowns: 5 -}; - -module.exports.onStart = async ({ event, api, args }) => { - const obfuscatedAuthor = String.fromCharCode(77, 97, 104, 77, 85, 68); - if (module.exports.config.author !== obfuscatedAuthor) { - return api.sendMessage("You are not authorized to change the author name.", event.threadID, event.messageID); - } - try { - const { threadID, messageID, senderID } = event; - const mention = Object.keys(event.mentions)[0] || (event.messageReply && event.messageReply.senderID); - - if (!mention) - return api.sendMessage("Please tag or reply to 1 person", threadID, messageID); - - const user1 = senderID; - const user2 = mention; - - const baseUrl = await baseApiUrl(); - const apiUrl = `${baseUrl}/api/myboy?user1=${user1}&user2=${user2}`; - - const response = await axios.get(apiUrl, { responseType: "arraybuffer" }); - - const imgPath = __dirname + `/cache/mygirl_${user1}_${user2}.png`; - fs.writeFileSync(imgPath, Buffer.from(response.data, "binary")); - - api.sendMessage({ - body: `𝐓𝐇𝐀𝐓'𝐒 𝐌𝐀𝐇 𝐆𝐈𝐑𝐋 🖤`, - attachment: fs.createReadStream(imgPath) - }, threadID, () => fs.unlinkSync(imgPath), messageID); - - } catch (error) { - console.error(error); - api.sendMessage("🥹error, contact MahMUD.", event.threadID, event.messageID); - } -}; diff --git a/scripts/cmds/needgf.js b/scripts/cmds/needgf.js deleted file mode 100644 index 301de5bf..00000000 --- a/scripts/cmds/needgf.js +++ /dev/null @@ -1,68 +0,0 @@ -const axios = require("axios"); -const fs = require("fs-extra"); -const path = require("path"); -const https = require("https"); - -function decode(b64) { - return Buffer.from(b64, "base64").toString("utf-8"); -} - -async function downloadImage(url, filePath) { - return new Promise((resolve, reject) => { - const file = fs.createWriteStream(filePath); - https.get(url, res => { - if (res.statusCode !== 200) - return reject(new Error(`Image fetch failed with status: ${res.statusCode}`)); - res.pipe(file); - file.on("finish", () => file.close(resolve)); - }).on("error", err => { - fs.unlink(filePath, () => reject(err)); - }); - }); -} - -const encodedUrl = "aHR0cHM6Ly9yYXNpbi1hcGlzLm9ucmVuZGVyLmNvbQ=="; -const encodedKey = "cnNfaGVpNTJjbTgtbzRvai11Y2ZjLTR2N2MtZzE="; - -module.exports = { - config: { - name: "needgf", - version: "3.0.1", - author: "MOHAMMAD AKASH", - countDown: 10, - role: 0, - shortDescription: "তোর Gf এর প্রোফাইল পিক দেখায় 😍", - longDescription: "সিঙ্গেলদের জন্য বিশেষ কমান্ড 💔 প্রতি বার নতুন সুন্দরী মেয়ের প্রোফাইল 😚", - category: "fun", - }, - - onStart: async function ({ message, event }) { - try { - const apiUrl = decode(encodedUrl); - const apiKey = decode(encodedKey); - const fullUrl = `${apiUrl}/api/rasin/gf?apikey=${apiKey}`; - - const res = await axios.get(fullUrl); - const imgUrl = res.data?.data?.url; - - if (!imgUrl) - return message.reply("⚠️ ছবি পাওয়া যায়নি ভাই 😭 আবার চেষ্টা করো!"); - - const imgPath = path.join(__dirname, "tmp", `${event.senderID}_gf.jpg`); - await downloadImage(imgUrl, imgPath); - - const replyMsg = `🌸✨ আপনার ভাগ্য জেগেছে ভাই!\nএমন সুন্দরী গফ সবাই পায় না 💕\n👇 নিচে দেখুন আপনার গফের প্রোফাইল 😚`; - - await message.reply({ - body: replyMsg, - attachment: fs.createReadStream(imgPath) - }); - - fs.unlinkSync(imgPath); - - } catch (err) { - console.error("❌ Error:", err.message); - message.reply("⚠️ কিছু একটা গন্ডগোল হইছে ভাই 😭 পরে আবার চেষ্টা করো!"); - } - } -}; diff --git a/scripts/cmds/newcommand.eg.js b/scripts/cmds/newcommand.eg.js deleted file mode 100644 index c8b75ee6..00000000 --- a/scripts/cmds/newcommand.eg.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @Vietnamese - * Trước tiên bạn cần có kiến thức về javascript như biến, hàm, vòng lặp, mảng, object, promise, async/await,... bạn có thể tìm hiểu thêm tại đây: https://developer.mozilla.org/en-US/docs/Web/JavaScript hoặc tại đây: https://www.w3schools.com/js/ - * Tiếp theo là kiến thức về Nodejs như require, module.exports, ... bạn có thể tìm hiểu thêm tại đây: https://nodejs.org/en/docs/ - * Và kiến thức về api không chính thức của facebook như api.sendMessage, api.changeNickname,... bạn có thể tìm hiểu thêm tại đây: https://github.com/ntkhang03/fb-chat-api/blob/master/DOCS.md - * Nếu tên file kết thúc bằng `.eg.js` thì nó sẽ không được load vào bot, nếu muốn load vào bot thì đổi phần mở rộng của file thành `.js` - */ - -/** - * @English - * First you need to have knowledge of javascript such as variables, functions, loops, arrays, objects, promise, async/await, ... you can learn more at here: https://developer.mozilla.org/en-US/docs/Web/JavaScript or here: https://www.w3schools.com/js/ - * Next is knowledge of Nodejs such as require, module.exports, ... you can learn more at here: https://nodejs.org/en/docs/ - * And knowledge of unofficial facebook api such as api.sendMessage, api.changeNickname,... you can learn more at here: https://github.com/ntkhang03/fb-chat-api/blob/master/DOCS.md - * If the file name ends with `.eg.js` then it will not be loaded into the bot, if you want to load it into the bot then change the extension of the file to `.js` - */ - -module.exports = { - config: { - name: "commandName", // Name of command, it must be unique to identify with other commands - version: "1.1", // Version of command - author: "NTKhang", // Author of command - countDown: 5, // Time to wait before executing command again (seconds) - role: 0, // Role of user to use this command (0: normal user, 1: admin box chat, 2: owner bot) - shortDescription: { - vi: "đây là mô tả ngắn của lệnh", - en: "this is short description of command" - }, // Short description of command - description: { - vi: "đây là mô tả dài của lệnh", - en: "this is long description of command" - }, // Long description of command - category: "categoryName", // Category of command - guide: { - vi: "đây là hướng dẫn sử dụng của lệnh", - en: "this is guide of command" - } // Guide of command - }, - - langs: { - vi: { - hello: "xin chào", - helloWithName: "xin chào, id facebook của bạn là %1" - }, // Vietnamese language - en: { - hello: "hello world", - helloWithName: "hello, your facebook id is %1" - } // English language - }, - - // onStart is a function that will be executed when the command is executed - onStart: async function ({ api, args, message, event, threadsData, usersData, dashBoardData, globalData, threadModel, userModel, dashBoardModel, globalModel, role, commandName, getLang }) { - // YOUR CODE HERE, use console.log() to see all properties in variables above - - - // getLang is a function to get language of command - - // getLang without parameter is a function to get language of command without parameter - message.reply(getLang("hello")); - // getLang with parameter is a function to get language of command with parameter (delete // in line below to test) - // message.reply(getLang("hello", event.senderID)); - - } -}; \ No newline at end of file diff --git a/scripts/cmds/nokia.js b/scripts/cmds/nokia.js deleted file mode 100644 index 7900a885..00000000 --- a/scripts/cmds/nokia.js +++ /dev/null @@ -1,55 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -module.exports = { - config: { - name: "nokia", - version: "1.0", - author: "Helal", - countDown: 10, - role: 0, - shortDescription: { - en: "Apply Nokia screen effect to profile photo" - }, - description: { - en: "Creates a Nokia-style image using your or mentioned user's avatar" - }, - category: "fun", - guide: { - en: "{p}nokia [@mention or reply]\n\nDefault: Your profile picture" - } - }, - - onStart: async function ({ api, event, usersData, message }) { - const { senderID, mentions, type, messageReply } = event; - - let uid; - if (Object.keys(mentions).length > 0) { - uid = Object.keys(mentions)[0]; - } else if (type === "message_reply") { - uid = messageReply.senderID; - } else { - uid = senderID; - } - - const avatarURL = `https://graph.facebook.com/${uid}/picture?height=512&width=512&access_token=350685531728|62f8ce9f74b12f84c123cc23437a4a32`; - - try { - const res = await axios.get(`https://api.popcat.xyz/v2/nokia?image=${encodeURIComponent(avatarURL)}`, { - responseType: "arraybuffer" - }); - - const imagePath = path.join(__dirname, "cache", `nokia_${uid}.jpg`); - fs.writeFileSync(imagePath, res.data); - - message.reply({ - body: `📱 | Here's your Nokia screen effect!`, - attachment: fs.createReadStream(imagePath) - }, () => fs.unlinkSync(imagePath)); - } catch (err) { - console.error(err); - message.reply("❌ | Failed to generate Nokia image."); - } - } -}; diff --git a/scripts/cmds/notification.js b/scripts/cmds/notification.js deleted file mode 100644 index cb9ad05f..00000000 --- a/scripts/cmds/notification.js +++ /dev/null @@ -1,138 +0,0 @@ -const { getStreamsFromAttachment } = global.utils; - -module.exports = { - config: { - name: "notification", - aliases: ["notify", "noti"], - version: "1.8", - author: "NTKhang Fixed By EryXenX", - countDown: 5, - role: 2, - description: { - vi: "Gửi thông báo từ admin đến all box", - en: "Send notification from admin to all box" - }, - category: "owner", - guide: { - en: "{pn} " - }, - envConfig: { - delayPerGroup: 250 - } - }, - - langs: { - vi: { - missingMessage: "Vui lòng nhập tin nhắn bạn muốn gửi đến tất cả các nhóm", - sendingNotification: "📡 Đang gửi thông báo đến %1 nhóm...\n⏳ Vui lòng chờ...", - sentNotification: "📊 Kết quả thông báo\n─────────────────────\n✅ Thành công : %1 nhóm", - errorSendingNotification: "❌ Thất bại : %1 nhóm\n%2" - }, - en: { - missingMessage: "Please enter the message you want to send to all groups", - sendingNotification: "📡 Sending notification to %1 groups...\n⏳ Please wait...", - sentNotification: "📊 Notification Report\n─────────────────────\n✅ Success : %1 groups", - errorSendingNotification: "❌ Failed : %1 groups\n%2" - }, - bn: { - missingMessage: "অনুগ্রহ করে সব গ্রুপে পাঠাতে চান এমন message লিখুন", - sendingNotification: "📡 %1 টি গ্রুপে নোটিফিকেশন পাঠানো হচ্ছে...\n⏳ অপেক্ষা করুন...", - sentNotification: "📊 নোটিফিকেশন রিপোর্ট\n─────────────────────\n✅ সফল : %1 টি গ্রুপ", - errorSendingNotification: "❌ ব্যর্থ : %1 টি গ্রুপ\n%2" - }, - tl: { - missingMessage: "Mangyaring ilagay ang mensaheng gusto mong ipadala sa lahat ng grupo", - sendingNotification: "📡 Nagpapadala ng notification sa %1 grupo...\n⏳ Mangyaring maghintay...", - sentNotification: "📊 Ulat ng Notification\n─────────────────────\n✅ Tagumpay : %1 grupo", - errorSendingNotification: "❌ Nabigo : %1 grupo\n%2" - }, - hi: { - missingMessage: "Kripya wo message dalein jo aap sabhi groups mein bhejna chahte hain", - sendingNotification: "📡 %1 groups mein notification bheja ja raha hai...\n⏳ Kripya prateeksha karein...", - sentNotification: "📊 Notification Report\n─────────────────────\n✅ Safal : %1 groups", - errorSendingNotification: "❌ Asafal : %1 groups\n%2" - }, - ar: { - missingMessage: "الرجاء إدخال الرسالة التي تريد إرسالها لجميع المجموعات", - sendingNotification: "📡 جاري إرسال الإشعار إلى %1 مجموعة...\n⏳ يرجى الانتظار...", - sentNotification: "📊 تقرير الإشعار\n─────────────────────\n✅ نجاح : %1 مجموعة", - errorSendingNotification: "❌ فشل : %1 مجموعة\n%2" - } - }, - - onStart: async function ({ message, api, event, args, commandName, envCommands, threadsData, usersData, getLang }) { - const { delayPerGroup } = envCommands[commandName]; - if (!args[0]) - return message.reply(getLang("missingMessage")); - - const senderID = event.senderID; - const senderName = await usersData.get(senderID, "name") || "Admin"; - - const attachmentStreams = await getStreamsFromAttachment( - [ - ...event.attachments, - ...(event.messageReply?.attachments || []) - ].filter(item => ["photo", "png", "animated_image", "video", "audio"].includes(item.type)) - ); - - const msgText = args.join(" "); - const body = `📢 ADMIN NOTIFICATION\n─────────────────────\n ${msgText}\n─────────────────────\n👤 ${senderName}`; - - const formSend = { - body, - mentions: [ - { - tag: senderName, - id: senderID - } - ] - }; - if (attachmentStreams && attachmentStreams.length > 0) - formSend.attachment = attachmentStreams; - - const allThreadID = (await threadsData.getAll()).filter(t => t.isGroup && t.members.find(m => m.userID == api.getCurrentUserID())?.inGroup); - message.reply(getLang("sendingNotification", allThreadID.length)); - - let sendSucces = 0; - const sendError = []; - const wattingSend = []; - - for (const thread of allThreadID) { - const tid = thread.threadID; - try { - wattingSend.push({ - threadID: tid, - pending: api.sendMessage(formSend, tid) - }); - await new Promise(resolve => setTimeout(resolve, delayPerGroup)); - } - catch (e) { - sendError.push({ threadIDs: [tid], errorDescription: e?.error || e?.message || String(e) }); - } - } - - for (const sended of wattingSend) { - try { - await sended.pending; - sendSucces++; - } - catch (e) { - const errorDescription = e?.error || e?.message || String(e); - if (!sendError.some(item => item.errorDescription == errorDescription)) - sendError.push({ - threadIDs: [sended.threadID], - errorDescription - }); - else - sendError.find(item => item.errorDescription == errorDescription).threadIDs.push(sended.threadID); - } - } - - let msg = ""; - if (sendSucces > 0) - msg += getLang("sentNotification", sendSucces) + "\n"; - if (sendError.length > 0) - msg += getLang("errorSendingNotification", sendError.reduce((a, b) => a + b.threadIDs.length, 0), sendError.reduce((a, b) => a + `\n • ${b.errorDescription}\n └ ${b.threadIDs.join(", ")}`, "")); - message.reply(msg); - } -}; \ No newline at end of file diff --git a/scripts/cmds/out.js b/scripts/cmds/out.js deleted file mode 100644 index f027a4f4..00000000 --- a/scripts/cmds/out.js +++ /dev/null @@ -1,28 +0,0 @@ -module.exports = { - config: { - name: "out", - version: "2.0", - author: "MOHAMMAD AKASH", - countDown: 5, - role: 2, - shortDescription: "বটকে গ্রুপ থেকে বের করে দেওয়া", - longDescription: "এই কমান্ডের মাধ্যমে বটকে বর্তমান বা নির্দিষ্ট গ্রুপ থেকে বের করে দেওয়া হয়।", - category: "owner", - guide: { - en: "{pn} [threadID (optional)]", - }, - }, - - onStart: async function ({ api, event, args }) { - const botID = api.getCurrentUserID(); - const targetThread = args[0] || event.threadID; - - try { - await api.sendMessage("👋 আলবিদা সবাই! আমি এখন গ্রুপ থেকে বের হচ্ছি...", targetThread); - await api.removeUserFromGroup(botID, targetThread); - } catch (error) { - console.error(error); - return api.sendMessage("❌ বের হতে পারলাম না! হয়তো আমি অ্যাডমিন না বা কোনো সমস্যা হয়েছে।", event.threadID); - } - }, -}; diff --git a/scripts/cmds/owner.js b/scripts/cmds/owner.js deleted file mode 100644 index 18e44e2c..00000000 --- a/scripts/cmds/owner.js +++ /dev/null @@ -1,57 +0,0 @@ -const fs = require("fs-extra"); -const request = require("request"); -const path = require("path"); - -module.exports = { - config: { - name: "owner", - version: "1.3.0", - author: "Mᴏʜᴀᴍᴍᴀᴅ Aᴋᴀsʜ", - role: 0, - shortDescription: "Owner information with image", - category: "Information", - guide: { - en: "owner" - } - }, - - onStart: async function ({ api, event }) { - const ownerText = -`╭─ 👑 Oᴡɴᴇʀ Iɴғᴏ 👑 ─╮ -│ 👤 Nᴀᴍᴇ : Mᴏʜᴀᴍᴍᴀᴅ Aᴋᴀsʜ -│ 🧸 Nɪᴄᴋ : Aᴋᴀsʜ -│ 🎂 Aɢᴇ : 18+ -│ 💘 Rᴇʟᴀᴛɪᴏɴ : Sɪɴɢʟᴇ -│ 🎓 Pʀᴏғᴇssɪᴏɴ : Sᴛᴜᴅᴇɴᴛ -│ 📚 Eᴅᴜᴄᴀᴛɪᴏɴ : Iɴᴛᴇʀ 2ɴᴅ Yᴇᴀʀ -│ 🏡 Lᴏᴄᴀᴛɪᴏɴ : 𝐃𝐡𝐚𝐤𝐚 - 𝐆𝐚𝐳𝐢𝐩𝐮𝐫 -├─ 🔗 Cᴏɴᴛᴀᴄᴛ ─╮ -│ 📘 Facebook : fb.com/akashx404 -│ 💬 Messenger: m.me/akashx404 -│ 📞 WhatsApp : wa.me/01933165880 -╰────────────────╯`; - - const cacheDir = path.join(__dirname, "cache"); - const imgPath = path.join(cacheDir, "owner.jpg"); - - if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir); - - const imgLink = "https://i.imgur.com/1G4ZhU7.jpeg"; - - const send = () => { - api.sendMessage( - { - body: ownerText, - attachment: fs.createReadStream(imgPath) - }, - event.threadID, - () => fs.unlinkSync(imgPath), - event.messageID - ); - }; - - request(encodeURI(imgLink)) - .pipe(fs.createWriteStream(imgPath)) - .on("close", send); - } -}; diff --git a/scripts/cmds/pair.js b/scripts/cmds/pair.js deleted file mode 100644 index 4c816d7c..00000000 --- a/scripts/cmds/pair.js +++ /dev/null @@ -1,159 +0,0 @@ -const fs = require("fs-extra"); -const axios = require("axios"); -const { loadImage, createCanvas } = require("canvas"); - -module.exports = { - config: { - name: "pair", - version: "1.0.0", - author: "EryXenX", - countDown: 5, - role: 0, - description: { - en: "Find today's random couple in the group", - bn: "আজকের random জুটি খোঁজো", - hi: "Aaj ka random pair dhundho", - tl: "Hanapin ang random na pares ngayon", - ar: "ابحث عن زوج اليوم العشوائي" - }, - category: "fun", - guide: { en: "{pn}" } - }, - - langs: { - en: { - noMembers: "❌ | Not enough members in this group!", - error: "❌ | Failed to generate. Try again.", - result: "💕 Today's Couple 💕\n\n👤 %1\n💑 &\n👤 %2\n\n❤️ Compatibility: %3%\n\n🔁 New pair tomorrow!" - }, - bn: { - noMembers: "❌ | গ্রুপে যথেষ্ট সদস্য নেই!", - error: "❌ | তৈরি করতে সমস্যা হয়েছে।", - result: "💕 আজকের জুটি 💕\n\n👤 %1\n💑 &\n👤 %2\n\n❤️ মিল: %3%\n\n🔁 কাল নতুন জুটি!" - }, - hi: { - noMembers: "❌ | Group mein kaafi members nahi hain!", - error: "❌ | Banana fail hua.", - result: "💕 Aaj ka Pair 💕\n\n👤 %1\n💑 &\n👤 %2\n\n❤️ Compatibility: %3%\n\n🔁 Kal naya pair!" - }, - tl: { - noMembers: "❌ | Hindi sapat ang mga miyembro sa grupo!", - error: "❌ | Hindi nagawa.", - result: "💕 Pares Ngayon 💕\n\n👤 %1\n💑 &\n👤 %2\n\n❤️ Compatibility: %3%\n\n🔁 Bagong pares bukas!" - }, - ar: { - noMembers: "❌ | لا يوجد أعضاء كافيون في المجموعة!", - error: "❌ | فشل الإنشاء.", - result: "💕 زوج اليوم 💕\n\n👤 %1\n💑 &\n👤 %2\n\n❤️ التوافق: %3%\n\n🔁 زوج جديد غداً!" - } - }, - - onStart: async function ({ event, message, getLang, threadsData, usersData, api }) { - try { - const { threadID, senderID } = event; - const threadInfo = await api.getThreadInfo(threadID); - const members = threadInfo.participantIDs.filter(id => id !== api.getCurrentUserID() && id !== senderID); - - if (members.length < 1) return message.reply(getLang("noMembers")); - - const id2 = members[Math.floor(Math.random() * members.length)]; - const compatibility = Math.floor(Math.random() * 51) + 50; - const pair = { id1: senderID, id2, compatibility }; - - const [user1, user2] = await Promise.all([ - usersData.get(pair.id1), - usersData.get(pair.id2) - ]); - const name1 = user1.name || "Unknown"; - const name2 = user2.name || "Unknown"; - - const ts = Date.now(); - const outputPath = __dirname + "/cache/pair_out_" + ts + ".jpg"; - - const [res1, res2] = await Promise.all([ - axios.get("https://graph.facebook.com/" + pair.id1 + "/picture?height=720&width=720&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662", { responseType: "arraybuffer" }), - axios.get("https://graph.facebook.com/" + pair.id2 + "/picture?height=720&width=720&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662", { responseType: "arraybuffer" }) - ]); - - const avt1Path = __dirname + "/cache/pair_avt1_" + ts + ".jpg"; - const avt2Path = __dirname + "/cache/pair_avt2_" + ts + ".jpg"; - fs.writeFileSync(avt1Path, Buffer.from(res1.data)); - fs.writeFileSync(avt2Path, Buffer.from(res2.data)); - - const [img1, img2] = await Promise.all([loadImage(avt1Path), loadImage(avt2Path)]); - - const W = 800, H = 400; - const canvas = createCanvas(W, H); - const ctx = canvas.getContext("2d"); - - const grad = ctx.createLinearGradient(0, 0, W, H); - grad.addColorStop(0, "#ff6b6b"); - grad.addColorStop(0.5, "#ee0979"); - grad.addColorStop(1, "#ff6b6b"); - ctx.fillStyle = grad; - ctx.fillRect(0, 0, W, H); - - const r = 150; - - ctx.save(); - ctx.beginPath(); - ctx.arc(r + 30, H / 2, r, 0, Math.PI * 2); - ctx.closePath(); - ctx.clip(); - ctx.drawImage(img1, 30, H / 2 - r, r * 2, r * 2); - ctx.restore(); - - ctx.save(); - ctx.beginPath(); - ctx.arc(W - r - 30, H / 2, r, 0, Math.PI * 2); - ctx.closePath(); - ctx.clip(); - ctx.drawImage(img2, W - r * 2 - 30, H / 2 - r, r * 2, r * 2); - ctx.restore(); - - ctx.strokeStyle = "white"; - ctx.lineWidth = 5; - ctx.beginPath(); - ctx.arc(r + 30, H / 2, r, 0, Math.PI * 2); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(W - r - 30, H / 2, r, 0, Math.PI * 2); - ctx.stroke(); - - ctx.font = "bold 60px serif"; - ctx.fillStyle = "white"; - ctx.textAlign = "center"; - ctx.fillText("❤️", W / 2, H / 2 + 20); - - const barW = 200, barH = 22; - const barX = W / 2 - barW / 2; - const barY = H - 55; - ctx.fillStyle = "rgba(255,255,255,0.3)"; - ctx.beginPath(); - ctx.roundRect(barX, barY, barW, barH, 11); - ctx.fill(); - - ctx.fillStyle = "white"; - ctx.beginPath(); - ctx.roundRect(barX, barY, barW * (pair.compatibility / 100), barH, 11); - ctx.fill(); - - ctx.font = "bold 16px sans-serif"; - ctx.fillStyle = "white"; - ctx.textAlign = "center"; - ctx.fillText(pair.compatibility + "% Compatible", W / 2, barY - 8); - - fs.writeFileSync(outputPath, canvas.toBuffer("image/jpeg", { quality: 0.92 })); - - const body = getLang("result", name1, name2, pair.compatibility); - - await message.reply({ body, attachment: fs.createReadStream(outputPath) }); - - [avt1Path, avt2Path, outputPath].forEach(p => { try { fs.unlinkSync(p); } catch (_) {} }); - - } catch (err) { - console.error("Pair Error:", err); - message.reply(getLang("error")); - } - } -}; diff --git a/scripts/cmds/pending.js b/scripts/cmds/pending.js deleted file mode 100644 index ce5633f4..00000000 --- a/scripts/cmds/pending.js +++ /dev/null @@ -1,179 +0,0 @@ -"use strict"; - -module.exports = { - config: { - name: "pending", - version: "1.0.9", - author: "EryXenX", - aliases: [], - role: 2, - shortDescription: "Manage bot's waiting groups", - longDescription: "Approve or cancel pending groups", - category: "owner", - countDown: 10 - }, - - languages: { - en: { - invaildNumber: "%1 is not a valid number", - cancelSuccess: "❌ Cancelled %1 thread(s)", - approveSuccess: "✅ Approved %1 thread(s)", - cantGetPendingList: "⚠️ Can't get pending list", - returnListClean: "No pending group found" - }, - bn: { - invaildNumber: "%1 সঠিক নাম্বার নয়", - cancelSuccess: "❌ %1 টি থ্রেড বাতিল করা হয়েছে", - approveSuccess: "✅ %1 টি থ্রেড অনুমোদন করা হয়েছে", - cantGetPendingList: "⚠️ পেন্ডিং লিস্ট আনা যাচ্ছে না", - returnListClean: "কোনো পেন্ডিং গ্রুপ পাওয়া যায়নি" - }, - hi: { - invaildNumber: "%1 एक मान्य नंबर नहीं है", - cancelSuccess: "❌ %1 थ्रेड रद्द किए गए", - approveSuccess: "✅ %1 थ्रेड स्वीकृत किए गए", - cantGetPendingList: "⚠️ पेंडिंग लिस्ट प्राप्त नहीं हो सकी", - returnListClean: "कोई पेंडिंग ग्रुप नहीं मिला" - }, - tl: { - invaildNumber: "%1 ay hindi wastong numero", - cancelSuccess: "❌ %1 thread(s) ang kinansela", - approveSuccess: "✅ %1 thread(s) ang na-approve", - cantGetPendingList: "⚠️ Hindi makuha ang pending list", - returnListClean: "Walang nahanap na pending group" - }, - ar: { - invaildNumber: "%1 ليس رقمًا صالحًا", - cancelSuccess: "❌ تم إلغاء %1 محادثة", - approveSuccess: "✅ تمت الموافقة على %1 محادثة", - cantGetPendingList: "⚠️ لا يمكن الحصول على قائمة الانتظار", - returnListClean: "لم يتم العثور على أي مجموعة معلقة" - } - }, - - _getText(key, ...args) { - const lang = global.GoatBot?.config?.language || "en"; - const text = (this.languages[lang] && this.languages[lang][key]) || this.languages.en[key] || key; - return args.length - ? text.replace("%1", args[0]).replace("%2", args[1] || "") - : text; - }, - - onStart: async function ({ api, event }) { - const { threadID, messageID, senderID } = event; - - let pendingList = []; - - try { - const other = await api.getThreadList(100, null, ["OTHER"]); - const pending = await api.getThreadList(100, null, ["PENDING"]); - - pendingList = [...other, ...pending].filter( - g => g.isGroup && g.isSubscribed - ); - } catch { - return api.sendMessage( - this._getText("cantGetPendingList"), - threadID, - messageID - ); - } - - if (!pendingList.length) - return api.sendMessage( - this._getText("returnListClean"), - threadID, - messageID - ); - - const prefix = global.GoatBot?.config?.prefix || "!"; - - let msg = ""; - pendingList.forEach((g, i) => { - msg += `${i + 1}️⃣ ${g.name}\n🆔 ${g.threadID}\n\n`; - }); - - const finalMsg = -`📋 Pending Groups (${pendingList.length}) -━━━━━━━━━━━━━━━━━━━ - -${msg}━━━━━━━━━━━━━━━━━━━ -✅ Approve » ${prefix}pending 1 2 -❌ Cancel » ${prefix}pending c 1 2`; - - return api.sendMessage(finalMsg, threadID, (err, info) => { - global.GoatBot.onReply.set(info.messageID, { - commandName: this.config.name, - author: senderID, - pending: pendingList - }); - }, messageID); - }, - - onReply: async function ({ event, Reply, api }) { - const { author, pending } = Reply; - - if (String(event.senderID) !== String(author)) return; - - const input = event.body.trim().toLowerCase().split(/\s+/); - const botID = api.getCurrentUserID(); - const prefix = global.GoatBot?.config?.prefix || "!"; - let count = 0; - - if (input[0] === "c" || input[0] === "cancel") { - for (let i = 1; i < input.length; i++) { - const idx = parseInt(input[i]); - - if (isNaN(idx) || idx <= 0 || idx > pending.length) - return api.sendMessage( - this._getText("invaildNumber", input[i]), - event.threadID - ); - - await api.removeUserFromGroup(botID, pending[idx - 1].threadID); - count++; - } - - return api.sendMessage( - this._getText("cancelSuccess", count), - event.threadID - ); - } - - for (const v of input) { - const idx = parseInt(v); - - if (isNaN(idx) || idx <= 0 || idx > pending.length) - return api.sendMessage( - this._getText("invaildNumber", v), - event.threadID - ); - - const tID = pending[idx - 1].threadID; - - await api.sendMessage( -`🎉 GROUP APPROVED - -👋 Hello everyone! -🤖 I am now active in this group. - -⚙️ Prefix: ${prefix} -📜 Type ${prefix}help to see all commands - -🚀 Bot is ready to assist you!`, - tID - ); - - const nickNameBot = global.GoatBot?.config?.nickNameBot; - if (nickNameBot) - await api.changeNickname(nickNameBot, tID, botID); - - count++; - } - - return api.sendMessage( - this._getText("approveSuccess", count), - event.threadID - ); - } -}; \ No newline at end of file diff --git a/scripts/cmds/pet.js b/scripts/cmds/pet.js deleted file mode 100644 index 4055994f..00000000 --- a/scripts/cmds/pet.js +++ /dev/null @@ -1,46 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -module.exports = { - config: { - name: "pet", - version: "1.0", - author: "nexo", - countDown: 5, - role: 0, - shortDescription: "Pet a user", - longDescription: "Generates a pet image/video for a tagged user", - category: "fun", - guide: "{p}pet @user" - }, - - onStart: async function ({ message, event, usersData }) { - const mentions = Object.keys(event.mentions); - if (mentions.length === 0) return message.reply("❌ Please tag a user."); - - const userid = mentions[0]; - const apiUrl = `https://betadash-api-swordslush-production.up.railway.app/pet?userid=${userid}`; - - try { - const res = await axios.get(apiUrl, { responseType: "arraybuffer" }); - const contentType = res.headers["content-type"]; - const ext = contentType.includes("gif") ? "gif" : contentType.includes("mp4") ? "mp4" : "jpg"; - const filePath = path.join(__dirname, "cache", `pet_${userid}.${ext}`); - - fs.writeFileSync(filePath, res.data); - - const name = await usersData.getName(userid); - - await message.reply({ - body: `🐾 You petted ${name}!`, - attachment: fs.createReadStream(filePath) - }); - - fs.unlinkSync(filePath); - } catch (err) { - console.error("❌ Pet command error:", err); - message.reply("⚠️ Failed to generate pet image/video."); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/pin.js b/scripts/cmds/pin.js deleted file mode 100644 index ff20f1f1..00000000 --- a/scripts/cmds/pin.js +++ /dev/null @@ -1,57 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -module.exports = { - config: { - name: "pinterest", - aliases: ["pin", "pint"], - version: "1.0", - author: "nexo_here", - countDown: 2, - role: 0, - description: "Search Pinterest and get image results", - category: "image", - guide: { - en: "{pn} [keyword] — Get Pinterest image results\nExample: {pn} Naruto" - } - }, - - onStart: async function ({ api, event, args }) { - const query = args.join(" "); - if (!query) return api.sendMessage("❗ Please provide a search keyword.\nExample: pinterest Naruto", event.threadID, event.messageID); - - try { - const count = 5; - const url = `https://betadash-api-swordslush-production.up.railway.app/pinterest?search=${encodeURIComponent(query)}&count=${count}`; - const res = await axios.get(url); - - const imageList = res.data?.data; - if (!Array.isArray(imageList) || imageList.length === 0) { - return api.sendMessage("❌ No results found!", event.threadID, event.messageID); - } - - const attachments = []; - - for (let i = 0; i < imageList.length; i++) { - const imageRes = await axios.get(imageList[i], { responseType: "arraybuffer" }); - const imagePath = path.join(__dirname, `pin_${i}.jpg`); - fs.writeFileSync(imagePath, imageRes.data); - attachments.push(fs.createReadStream(imagePath)); - } - - api.sendMessage({ - body: `🔍 Pinterest results for: "${query}"`, - attachment: attachments - }, event.threadID, () => { - for (let i = 0; i < attachments.length; i++) { - fs.unlinkSync(path.join(__dirname, `pin_${i}.jpg`)); - } - }, event.messageID); - - } catch (err) { - console.error(err); - api.sendMessage("🚫 Error fetching from Pinterest API.", event.threadID, event.messageID); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/poli.js b/scripts/cmds/poli.js deleted file mode 100644 index 4db83d66..00000000 --- a/scripts/cmds/poli.js +++ /dev/null @@ -1,66 +0,0 @@ -const axios = require("axios"); -const fs = require("fs"); -const path = require("path"); - -const baseApiUrl = async () => { - const base = await axios.get("https://raw.githubusercontent.com/mahmudx7/HINATA/main/baseApiUrl.json"); - return base.data.mahmud; -}; - -module.exports = { - config: { - name: "poli", - author: "MahMUD", - version: "1.7", - cooldowns: 10, - role: 0, - category: "ai-image", - guide: { - en: "{p}poli " - } - }, - - onStart: async function ({ message, args, api, event }) { - if (args.length === 0) { - return api.sendMessage("❌ | Please provide a prompt.", event.threadID, event.messageID); - } - - const prompt = args.join(" "); - const cacheDir = path.join(__dirname, "cache"); - if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir); - - api.sendMessage("𝐖𝐚𝐢𝐭 𝐤𝐨𝐫𝐨 𝐣𝐚𝐧 <😘", event.threadID, event.messageID); - - try { - const styles = ["ultra detailed", "4k resolution", "realistic lighting", "artstation", "digital painting"]; - const imagePaths = []; - - for (let i = 0; i < 4; i++) { - const enhancedPrompt = `${prompt}, ${styles[i % styles.length]}`; - - const response = await axios.post(`${await baseApiUrl()}/api/poli/generate`, { - prompt: enhancedPrompt - }, { - responseType: "arraybuffer", - headers: { - "author": module.exports.config.author - } - }); - - const filePath = path.join(cacheDir, `generated_${Date.now()}_${i}.png`); - fs.writeFileSync(filePath, response.data); - imagePaths.push(filePath); - } - - const attachments = imagePaths.map(p => fs.createReadStream(p)); - message.reply({ - body: "✅ | Here are images generated from your prompt:", - attachment: attachments - }); - - } catch (error) { - console.error("Image generation error:", error); - message.reply("❌ | Couldn't generate images. Try again later."); - } - } -}; diff --git a/scripts/cmds/post.js b/scripts/cmds/post.js deleted file mode 100644 index 92574c26..00000000 --- a/scripts/cmds/post.js +++ /dev/null @@ -1,222 +0,0 @@ -const fs = require("fs-extra"); -const axios = require("axios"); -const { loadImage, createCanvas } = require("canvas"); - -module.exports = { - config: { - name: "post", - aliases: ["prankpost"], - version: "1.0.0", - author: "EryXenX", - countDown: 5, - role: 0, - description: { - en: "Fake Facebook post prank", - bn: "ফেক ফেসবুক পোস্ট প্র্যাংক" - }, - category: "fun", - guide: { en: "{pn} or {pn} @mention or reply with {pn} " } - }, - - langs: { - en: { noText: "❌ | Write something to post!", error: "❌ | Failed to generate. Try again." }, - bn: { noText: "❌ | পোস্টে কিছু লিখো!", error: "❌ | তৈরি করতে সমস্যা হয়েছে।" }, - hi: { noText: "❌ | Post mein kuch likho!", error: "❌ | Banana fail hua." }, - tl: { noText: "❌ | Maglagay ng text sa post!", error: "❌ | Hindi nagawa." }, - ar: { noText: "❌ | اكتب شيئاً لنشره!", error: "❌ | فشل الإنشاء." } - }, - - onStart: async function ({ event, message, getLang, usersData, args }) { - try { - const mentionID = Object.keys(event.mentions)[0] - || (event.messageReply ? event.messageReply.senderID : null); - - const posterID = mentionID || event.senderID; - - let postText = args.join(" "); - if (mentionID) { - const mentionTag = Object.values(event.mentions)[0]; - postText = postText.replace("@" + mentionTag, "").trim(); - } - if (!postText) return message.reply(getLang("noText")); - - const posterName = await usersData.getName(posterID).catch(() => "Unknown"); - - const ts = Date.now(); - const avatarPath = __dirname + "/cache/post_avt_" + ts + ".jpg"; - const outputPath = __dirname + "/cache/post_out_" + ts + ".jpg"; - - const avatarRes = await axios.get("https://graph.facebook.com/" + posterID + "/picture?height=720&width=720&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662", { responseType: "arraybuffer" }); - fs.writeFileSync(avatarPath, Buffer.from(avatarRes.data)); - const avatarImg = await loadImage(avatarPath); - - const W = 720; - const padding = 28; - const lineHeight = 38; - const textStartY = 145; - const textLines = wrapText(postText, 36); - const textBlockHeight = textLines.length * lineHeight; - const H = textStartY + textBlockHeight + 170; - - const canvas = createCanvas(W, H); - const ctx = canvas.getContext("2d"); - - ctx.fillStyle = "#ffffff"; - ctx.fillRect(0, 0, W, H); - - const avatarSize = 56; - const avatarX = padding; - const avatarY = padding; - - ctx.save(); - ctx.beginPath(); - ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2); - ctx.closePath(); - ctx.clip(); - drawCoverImage(ctx, avatarImg, avatarX, avatarY, avatarSize, avatarSize); - ctx.restore(); - - ctx.fillStyle = "#050505"; - ctx.font = "bold 26px Sans"; - ctx.textAlign = "left"; - ctx.fillText(posterName, avatarX + avatarSize + 16, avatarY + 26); - - ctx.fillStyle = "#65676b"; - ctx.font = "20px Sans"; - ctx.fillText("Just now · 🌐", avatarX + avatarSize + 16, avatarY + 52); - - ctx.fillStyle = "#65676b"; - ctx.font = "bold 30px Sans"; - ctx.fillText("⋯", W - 60, avatarY + 36); - - let textY = textStartY; - ctx.fillStyle = "#050505"; - ctx.font = "28px Sans"; - for (const line of textLines) { - ctx.fillText(line, padding, textY); - textY += lineHeight; - } - - const statsY = textY + 6; - ctx.strokeStyle = "#e4e6eb"; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(padding, statsY); - ctx.lineTo(W - padding, statsY); - ctx.stroke(); - - const likeCount = randomCount(); - const commentCount = randomCount(); - const shareCount = randomCount(); - - ctx.fillStyle = "#1877f2"; - ctx.beginPath(); - ctx.arc(padding + 12, statsY + 30, 12, 0, Math.PI * 2); - ctx.fill(); - drawThumbIcon(ctx, padding + 12, statsY + 30, "#ffffff"); - - ctx.fillStyle = "#65676b"; - ctx.font = "22px Sans"; - ctx.fillText(likeCount, padding + 32, statsY + 38); - - ctx.textAlign = "right"; - ctx.fillText(commentCount + " comments · " + shareCount + " shares", W - padding, statsY + 38); - ctx.textAlign = "left"; - - const statsLineY = statsY + 58; - ctx.beginPath(); - ctx.moveTo(padding, statsLineY); - ctx.lineTo(W - padding, statsLineY); - ctx.stroke(); - - ctx.fillStyle = "#65676b"; - ctx.font = "bold 24px Sans"; - const actionsY = statsLineY + 42; - ctx.fillText("👍 Like", padding + 10, actionsY); - ctx.fillText("💬 Comment", W / 2 - 60, actionsY); - ctx.fillText("↗ Share", W - padding - 110, actionsY); - - fs.writeFileSync(outputPath, canvas.toBuffer("image/jpeg", { quality: 0.92 })); - - await message.reply({ attachment: fs.createReadStream(outputPath) }); - - [avatarPath, outputPath].forEach(p => { try { fs.unlinkSync(p); } catch (_) {} }); - - } catch (err) { - console.error("Post Error:", err); - message.reply(getLang("error")); - } - } -}; - -function wrapText(text, maxCharsPerLine) { - const words = text.split(" "); - const lines = []; - let current = ""; - for (let word of words) { - while (word.length > maxCharsPerLine) { - if (current) { - lines.push(current.trim()); - current = ""; - } - lines.push(word.slice(0, maxCharsPerLine)); - word = word.slice(maxCharsPerLine); - } - if ((current + " " + word).trim().length > maxCharsPerLine) { - lines.push(current.trim()); - current = word; - } else { - current += " " + word; - } - } - if (current.trim()) lines.push(current.trim()); - return lines; -} - -function randomCount() { - const pools = [ - () => (Math.random() * 9 + 1).toFixed(1) + "K", - () => (Math.random() * 90 + 10).toFixed(1) + "K", - () => Math.floor(Math.random() * 900 + 100).toString(), - () => (Math.random() * 9 + 1).toFixed(0) + "0K" - ]; - const pick = pools[Math.floor(Math.random() * pools.length)]; - return pick(); -} - -function drawCoverImage(ctx, img, x, y, w, h) { - const scale = Math.max(w / img.width, h / img.height); - const dw = img.width * scale; - const dh = img.height * scale; - const dx = x + (w - dw) / 2; - const dy = y + (h - dh) / 2; - ctx.drawImage(img, dx, dy, dw, dh); -} - -function drawThumbIcon(ctx, cx, cy, color) { - ctx.save(); - ctx.translate(cx, cy); - ctx.scale(0.55, 0.55); - ctx.fillStyle = color; - ctx.beginPath(); - ctx.moveTo(-9, -2); - ctx.lineTo(-9, 11); - ctx.lineTo(-4, 11); - ctx.lineTo(-4, -2); - ctx.closePath(); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(-3, -2); - ctx.lineTo(-3, 11); - ctx.lineTo(7, 11); - ctx.bezierCurveTo(9, 11, 10, 10, 10, 8); - ctx.lineTo(12, 0); - ctx.bezierCurveTo(12.5, -2, 11, -4, 9, -4); - ctx.lineTo(2, -4); - ctx.lineTo(3, -10); - ctx.bezierCurveTo(3.3, -12.5, 1, -14, -0.5, -12.5); - ctx.lineTo(-3, -7); - ctx.closePath(); - ctx.fill(); - ctx.restore(); -} \ No newline at end of file diff --git a/scripts/cmds/pp.js b/scripts/cmds/pp.js deleted file mode 100644 index 6ba99500..00000000 --- a/scripts/cmds/pp.js +++ /dev/null @@ -1,63 +0,0 @@ -const fs = require("fs-extra"); -const axios = require("axios"); -const path = require("path"); - -module.exports = { - config: { - name: "pp", - version: "1.1.0", - author: "EryXenX", - countDown: 3, - role: 0, - shortDescription: "View Facebook profile picture 📸", - longDescription: "View profile picture of any user via reply, mention, link, or UID.", - category: "media", - guide: { - en: "{pn} [reply / @mention / profile link / UID]" - } - }, - - onStart: async function ({ api, event, args, usersData }) { - const cacheDir = path.join(__dirname, "cache"); - const cachePath = path.join(cacheDir, `profile_${Date.now()}.png`); - - try { - await fs.ensureDir(cacheDir); - - let uid; - - if (event.type === "message_reply") { - uid = event.messageReply.senderID; - } else if (Object.keys(event.mentions || {}).length > 0) { - uid = Object.keys(event.mentions)[0]; - } else if (args[0] && args[0].includes("facebook.com")) { - uid = await api.getUID(args[0]); - } else if (args[0] && /^\d+$/.test(args[0])) { - uid = args[0]; - } else { - uid = event.senderID; - } - - const name = await usersData.getName(uid).catch(() => "Unknown User"); - - const imageUrl = `https://graph.facebook.com/${uid}/picture?height=1500&width=1500&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662`; - - const response = await axios.get(imageUrl, { responseType: "arraybuffer" }); - await fs.writeFile(cachePath, response.data); - - await api.sendMessage( - { - body: `📸 Profile picture of ${name}`, - attachment: fs.createReadStream(cachePath) - }, - event.threadID, - () => fs.remove(cachePath), - event.messageID - ); - - } catch (err) { - console.error("[pp]", err.message); - api.sendMessage("⚠️ Failed to fetch profile picture. Please try again.", event.threadID, event.messageID); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/prefix.js b/scripts/cmds/prefix.js deleted file mode 100644 index 2e43363b..00000000 --- a/scripts/cmds/prefix.js +++ /dev/null @@ -1,139 +0,0 @@ -const fs = require("fs-extra"); -const { utils } = global; - -module.exports = { - config: { - name: "prefix", - version: "1.4", - author: "NTKhang", - countDown: 5, - role: 0, - description: "Thay đổi dấu lệnh của bot trong box chat của bạn hoặc cả hệ thống bot (chỉ admin bot)", - category: "config", - guide: { - vi: " {pn} : thay đổi prefix mới trong box chat của bạn" - + "\n Ví dụ:" - + "\n {pn} #" - + "\n\n {pn} -g: thay đổi prefix mới trong hệ thống bot (chỉ admin bot)" - + "\n Ví dụ:" - + "\n {pn} # -g" - + "\n\n {pn} reset: thay đổi prefix trong box chat của bạn về mặc định", - en: " {pn} : change new prefix in your box chat" - + "\n Example:" - + "\n {pn} #" - + "\n\n {pn} -g: change new prefix in system bot (only admin bot)" - + "\n Example:" - + "\n {pn} # -g" - + "\n\n {pn} reset: change prefix in your box chat to default" - } - }, - - langs: { - vi: { - reset: "Đã reset prefix của bạn về mặc định: %1", - onlyAdmin: "Chỉ admin mới có thể thay đổi prefix hệ thống bot", - confirmGlobal: "Vui lòng thả cảm xúc bất kỳ vào tin nhắn này để xác nhận thay đổi prefix của toàn bộ hệ thống bot", - confirmThisThread: "Vui lòng thả cảm xúc bất kỳ vào tin nhắn này để xác nhận thay đổi prefix trong nhóm chat của bạn", - successGlobal: "Đã thay đổi prefix hệ thống bot thành: %1", - successThisThread: "Đã thay đổi prefix trong nhóm chat của bạn thành: %1", - myPrefix: "🌐 Prefix của hệ thống: %1\n🛸 Prefix của nhóm bạn: %2" - }, - en: { - reset: "Your prefix has been reset to default: %1", - onlyAdmin: "Only admin can change prefix of system bot", - confirmGlobal: "Please react to this message to confirm change prefix of system bot", - confirmThisThread: "Please react to this message to confirm change prefix in your box chat", - successGlobal: "Changed prefix of system bot to: %1", - successThisThread: "Changed prefix in your box chat to: %1", - myPrefix: "Hey senpai! ~_~\n🌐 Global prefix: %1\n🛸 Your group chat prefix: %2" - }, - tl: { - reset: "Ang iyong prefix ay na-reset sa default: %1", - onlyAdmin: "Ang admin lamang ang maaaring magbago ng prefix ng system bot", - confirmGlobal: "Mangyaring mag-react sa mensaheng ito para kumpirmahin ang pagbabago ng prefix ng system bot", - confirmThisThread: "Mangyaring mag-react sa mensaheng ito para kumpirmahin ang pagbabago ng prefix sa iyong box chat", - successGlobal: "Binago ang prefix ng system bot sa: %1", - successThisThread: "Binago ang prefix sa iyong box chat sa: %1", - myPrefix: "Hey senpai! ~_~\n🌐 Global prefix: %1\n🛸 Prefix ng iyong group chat: %2" - }, - hi: { - reset: "Aapka prefix default par reset kar diya gaya: %1", - onlyAdmin: "Sirf admin hi system bot ka prefix badal sakta hai", - confirmGlobal: "System bot ka prefix badlne ki pushthi ke liye is message par react karein", - confirmThisThread: "Aapke box chat mein prefix badlne ki pushthi ke liye is message par react karein", - successGlobal: "System bot ka prefix badal diya gaya: %1", - successThisThread: "Aapke box chat ka prefix badal diya gaya: %1", - myPrefix: "Hey senpai! ~_~\n🌐 Global prefix: %1\n🛸 Aapke group chat ka prefix: %2" - }, - ar: { - reset: "تمت إعادة تعيين بادئتك إلى الافتراضي: %1", - onlyAdmin: "فقط المسؤول يمكنه تغيير بادئة بوت النظام", - confirmGlobal: "الرجاء التفاعل مع هذه الرسالة لتأكيد تغيير بادئة بوت النظام", - confirmThisThread: "الرجاء التفاعل مع هذه الرسالة لتأكيد تغيير البادئة في محادثتك", - successGlobal: "تم تغيير بادئة بوت النظام إلى: %1", - successThisThread: "تم تغيير البادئة في محادثتك إلى: %1", - myPrefix: "Hey senpai! ~_~\n🌐 البادئة العامة: %1\n🛸 بادئة مجموعتك: %2" - }, - bn: { - reset: "আপনার prefix default এ রিসেট করা হয়েছে: %1", - onlyAdmin: "শুধুমাত্র admin system bot এর prefix পরিবর্তন করতে পারবে", - confirmGlobal: "System bot এর prefix পরিবর্তন নিশ্চিত করতে এই message এ react করুন", - confirmThisThread: "আপনার box chat এ prefix পরিবর্তন নিশ্চিত করতে এই message এ react করুন", - successGlobal: "System bot এর prefix পরিবর্তন হয়েছে: %1", - successThisThread: "আপনার box chat এর prefix পরিবর্তন হয়েছে: %1", - myPrefix: "Hey senpai! ~_~\n🌐 Global prefix: %1\n🛸 আপনার group chat prefix: %2" - } - }, - - onStart: async function ({ message, role, args, commandName, event, threadsData, getLang }) { - if (!args[0]) - return message.SyntaxError(); - - if (args[0] == 'reset') { - await threadsData.set(event.threadID, null, "data.prefix"); - return message.reply(getLang("reset", global.GoatBot.config.prefix)); - } - - const newPrefix = args[0]; - const formSet = { - commandName, - author: event.senderID, - newPrefix - }; - - if (args[1] === "-g") - if (role < 2) - return message.reply(getLang("onlyAdmin")); - else - formSet.setGlobal = true; - else - formSet.setGlobal = false; - - return message.reply(args[1] === "-g" ? getLang("confirmGlobal") : getLang("confirmThisThread"), (err, info) => { - formSet.messageID = info.messageID; - global.GoatBot.onReaction.set(info.messageID, formSet); - }); - }, - - onReaction: async function ({ message, threadsData, event, Reaction, getLang }) { - const { author, newPrefix, setGlobal } = Reaction; - if (event.userID !== author) - return; - if (setGlobal) { - global.GoatBot.config.prefix = newPrefix; - fs.writeFileSync(global.client.dirConfig, JSON.stringify(global.GoatBot.config, null, 2)); - return message.reply(getLang("successGlobal", newPrefix)); - } - else { - await threadsData.set(event.threadID, newPrefix, "data.prefix"); - return message.reply(getLang("successThisThread", newPrefix)); - } - }, - - onChat: async function ({ event, message, getLang }) { - if (event.body && event.body.toLowerCase() === "prefix") - return () => { - return message.reply(getLang("myPrefix", global.GoatBot.config.prefix, utils.getPrefix(event.threadID))); - }; - } -}; diff --git a/scripts/cmds/prompt.js b/scripts/cmds/prompt.js deleted file mode 100644 index 0fe21cc9..00000000 --- a/scripts/cmds/prompt.js +++ /dev/null @@ -1,73 +0,0 @@ -const axios = require("axios"); - -const configUrl = "https://raw.githubusercontent.com/aryannix/stuffs/master/raw/apis.json"; - -module.exports = { - config: { - name: "prompt", - aliases: ["p"], - version: "0.0.1", - role: 0, - author: "ArYAN", - category: "AI", - cooldowns: 5, - guide: { en: "Reply to an image to generate Midjourney prompt" } - }, - - onStart: async ({ api, event }) => { - const { threadID, messageID, messageReply } = event; - - let baseApi; - try { - const configRes = await axios.get(configUrl); - baseApi = configRes.data && configRes.data.api; - if (!baseApi) throw new Error("Configuration Error: Missing API in GitHub JSON."); - } catch (error) { - return api.sendMessage("❌ Failed to fetch API configuration from GitHub.", threadID, messageID); - } - - if ( - !messageReply || - !messageReply.attachments || - messageReply.attachments.length === 0 || - !messageReply.attachments[0].url - ) { - return api.sendMessage("Please reply to an image.", threadID, messageID); - } - - try { - api.setMessageReaction("⏰", messageID, () => {}, true); - - const imageUrl = messageReply.attachments[0].url; - const apiUrl = `${baseApi}/promptv2`; - - const apiResponse = await axios.get(apiUrl, { - params: { imageUrl } - }); - - const result = apiResponse.data; - - if (!result.success) { - throw new Error(result.message || "Prompt API failed."); - } - - const promptText = result.prompt || "No prompt returned."; - - await api.sendMessage( - { body: `${promptText}` }, - threadID, - messageID - ); - - api.setMessageReaction("✅", messageID, () => {}, true); - } catch (e) { - api.setMessageReaction("❌", messageID, () => {}, true); - - let msg = "Error while generating prompt."; - if (e.response?.data?.error) msg = e.response.data.error; - else if (e.message) msg = e.message; - - api.sendMessage(msg, threadID, messageID); - } - } -}; diff --git a/scripts/cmds/protect.js b/scripts/cmds/protect.js deleted file mode 100644 index 1040085b..00000000 --- a/scripts/cmds/protect.js +++ /dev/null @@ -1,106 +0,0 @@ -module.exports = { - config: { - name: "protect", - version: "1.2", - author: "MOHAMMAD AKASH", - role: 1, - shortDescription: "Lock group name, nickname, theme, emoji", - category: "group", - guide: "{pn} on/off" - }, - - onStart: async ({ api, event, message, threadsData, args }) => { - const { threadID } = event; - - if (!args[0]) return message.reply("⚠️ Usage: /protect on | /protect off"); - - if (args[0] === "on") { - const info = await api.getThreadInfo(threadID); - - const protectData = { - enable: true, - name: info.threadName || "", - emoji: info.emoji || "", - color: info.color || "", - nickname: {} - }; - - // Safely handle members - const members = info.members || []; - members.forEach(u => { - protectData.nickname[u.userID] = u.nickname || ""; - }); - - await threadsData.set(threadID, protectData, "data.protect"); - - return message.reply( - "🛡 𝗣𝗥𝗢𝗧𝗘𝗖𝗧 𝗘𝗡𝗔𝗕𝗟𝗘𝗗\n✨ Name, Nickname, Theme & Emoji are now LOCKED!" - ); - } - - if (args[0] === "off") { - await threadsData.set(threadID, {}, "data.protect"); - return message.reply( - "🔓 𝗣𝗥𝗢𝗧𝗘𝗖𝗧 𝗗𝗜𝗦𝗔𝗕𝗟𝗘𝗗\n💥 All locks are now OFF!" - ); - } - }, - - onEvent: async ({ api, event, threadsData }) => { - const { threadID, author, logMessageType, logMessageData } = event; - const protectData = await threadsData.get(threadID, "data.protect"); - if (!protectData?.enable) return; - - const info = await api.getThreadInfo(threadID); - const isAdmin = info.adminIDs.some(e => e.id === author); - const isBot = api.getCurrentUserID() === author; - - if (!isAdmin && !isBot) { - // NAME - if (logMessageType === "log:thread-name") { - api.setTitle(protectData.name, threadID); - } - - // EMOJI - if (logMessageType === "log:thread-icon") { - api.changeThreadEmoji(protectData.emoji, threadID); - } - - // COLOR/THEME - if (logMessageType === "log:thread-color") { - api.changeThreadColor(protectData.color, threadID); - } - - // NICKNAME - if (logMessageType === "log:user-nickname") { - const { participant_id } = logMessageData; - api.changeNickname( - protectData.nickname[participant_id] || "", - threadID, - participant_id - ); - } - } - - // ADMIN changed → update saved data - if (isAdmin) { - if (logMessageType === "log:thread-name") { - await threadsData.set(threadID, logMessageData.name || "", "data.protect.name"); - } - if (logMessageType === "log:thread-icon") { - await threadsData.set(threadID, logMessageData.thread_icon || "", "data.protect.emoji"); - } - if (logMessageType === "log:thread-color") { - await threadsData.set(threadID, logMessageData.theme_id || "", "data.protect.color"); - } - if (logMessageType === "log:user-nickname") { - const { participant_id, nickname } = logMessageData; - await threadsData.set( - threadID, - nickname || "", - `data.protect.nickname.${participant_id}` - ); - } - } - } -}; diff --git a/scripts/cmds/qrgen.js b/scripts/cmds/qrgen.js deleted file mode 100644 index b6c69294..00000000 --- a/scripts/cmds/qrgen.js +++ /dev/null @@ -1,75 +0,0 @@ -const QRCode = require('qrcode'); -const fs = require('fs-extra'); -const path = require('path'); - -function extractData(args) { - let data = args.join(" ").trim(); - if (!data) { - data = "https://example.com"; // ডিফল্ট ডাটা যদি কিছু না দেয় - } - return data; -} - -module.exports = { - config: { - name: "qrgen", - aliases: ["qrcode"], - version: "1.0", - author: "MOHAMMAD AKASH", - countDown: 5, - role: 0, - longDescription: "Generate a QR code from text, link, or any information.", - category: "utility", - guide: { - en: "{pn} [text or link]" - } - }, - - onStart: async function({ message, args, event }) { - const qrData = extractData(args); - - if (!qrData) { - return message.reply("❌ Please provide text, link, or information to generate QR code."); - } - - message.reaction("⏳", event.messageID); - let tempFilePath; - - try { - const cacheDir = path.join(__dirname, 'cache'); - if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true }); - - tempFilePath = path.join(cacheDir, `qr_code_${Date.now()}.png`); - - // QR কোড জেনারেট করে ফাইলে সেভ করা - await QRCode.toFile(tempFilePath, qrData, { - color: { - dark: '#000', // কালো - light: '#FFF' // সাদা - }, - scale: 8 // সাইজ অ্যাডজাস্ট - }); - - message.reaction("✅", event.messageID); - await message.reply({ - body: "✅ Yᴏᴜʀ QR ᴄᴏᴅᴇ ʜᴀs ʙᴇᴇɴ ɢᴇɴᴇʀᴀᴛᴇᴅ!", - attachment: fs.createReadStream(tempFilePath) - }); - - } catch (error) { - message.reaction("❌", event.messageID); - - let errorMessage = "An error occurred during QR code generation."; - if (error.message) { - errorMessage = `❌ ${error.message}`; - } - - console.error("QRGen Command Error:", error); - message.reply(`❌ ${errorMessage}`); - } finally { - if (tempFilePath && fs.existsSync(tempFilePath)) { - fs.unlinkSync(tempFilePath); - } - } - } -}; diff --git a/scripts/cmds/quiz.js b/scripts/cmds/quiz.js deleted file mode 100644 index 59fb59ab..00000000 --- a/scripts/cmds/quiz.js +++ /dev/null @@ -1,142 +0,0 @@ -const axios = require("axios"); - -module.exports.config = { - name: "quiz", - version: "2.0", - author: "EryXenX", - role: 0, - category: "economy", - countDown: 10, - shortDescription: "Answer quiz questions to earn money", - guide: "{prefix}quiz" -}; - -const usedQuestions = new Map(); - -function decodeHTML(str) { - return str - .replace(/&/g, "&") - .replace(/</g, "<") - .replace(/>/g, ">") - .replace(/"/g, '"') - .replace(/'/g, "'") - .replace(/“/g, "\u201C") - .replace(/”/g, "\u201D") - .replace(/‘/g, "\u2018") - .replace(/’/g, "\u2019"); -} - -function shuffle(arr) { - for (let i = arr.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [arr[i], arr[j]] = [arr[j], arr[i]]; - } - return arr; -} - -async function fetchQuestion(senderID) { - const used = usedQuestions.get(senderID) || new Set(); - - for (let attempt = 0; attempt < 5; attempt++) { - const res = await axios.get("https://opentdb.com/api.php?amount=5&type=multiple"); - const results = res.data?.results; - if (!results) continue; - - for (const item of results) { - const question = decodeHTML(item.question); - if (used.has(question)) continue; - - const correct = decodeHTML(item.correct_answer); - const wrong = item.incorrect_answers.map(decodeHTML); - const allOptions = shuffle([correct, ...wrong]); - const labels = ["A", "B", "C", "D"]; - const answerLabel = labels[allOptions.indexOf(correct)]; - const options = allOptions.map((opt, i) => `${labels[i]}. ${opt}`); - - used.add(question); - if (used.size > 200) { - const first = used.values().next().value; - used.delete(first); - } - usedQuestions.set(senderID, used); - - return { question, options, answer: answerLabel }; - } - } - - return null; -} - -module.exports.onStart = async function ({ api, event, usersData }) { - const { senderID, threadID, messageID } = event; - - let quizData; - try { - quizData = await fetchQuestion(senderID); - } catch (e) { - return api.sendMessage("❌ Failed to fetch question. Try again later.", threadID, messageID); - } - - if (!quizData) - return api.sendMessage("❌ Could not get a new question. Try again later.", threadID, messageID); - - const msg = -`📝 QUIZ TIME! - -❓ ${quizData.question} - -${quizData.options.join("\n")} - -⏱ Reply with A, B, C or D -✅ Correct → +500$ -❌ Wrong → -50$`; - - api.sendMessage(msg, threadID, (err, info) => { - if (err) return; - global.GoatBot.onReply.set(info.messageID, { - commandName: "quiz", - messageID: info.messageID, - answer: quizData.answer, - senderID - }); - - setTimeout(() => { - if (global.GoatBot.onReply.has(info.messageID)) { - global.GoatBot.onReply.delete(info.messageID); - api.unsendMessage(info.messageID); - } - }, 60000); - }, messageID); -}; - -module.exports.onReply = async function ({ api, event, usersData, Reply }) { - const { senderID, threadID, messageID, body } = event; - const { answer } = Reply; - - const userAnswer = body.trim().toUpperCase(); - - if (!["A", "B", "C", "D"].includes(userAnswer)) - return api.sendMessage("⚠ Please reply with only A, B, C or D.", threadID, messageID); - - global.GoatBot.onReply.delete(Reply.messageID); - - const userData = await usersData.get(senderID); - let balance = userData?.data?.money ?? 100; - - if (userAnswer === answer) { - balance += 500; - await usersData.set(senderID, { data: { ...userData.data, money: balance } }); - api.sendMessage( - `✅ Correct! The answer was ${answer}\n💵 Won +500$\n💰 Balance: ${balance}$`, - threadID, messageID - ); - } else { - balance = Math.max(0, balance - 50); - await usersData.set(senderID, { data: { ...userData.data, money: balance } }); - api.unsendMessage(Reply.messageID); - api.sendMessage( - `❌ Wrong! The correct answer was ${answer}\n💸 Lost -50$\n💰 Balance: ${balance}$`, - threadID, messageID - ); - } -}; \ No newline at end of file diff --git a/scripts/cmds/rank.js b/scripts/cmds/rank.js deleted file mode 100644 index 4cd71311..00000000 --- a/scripts/cmds/rank.js +++ /dev/null @@ -1,971 +0,0 @@ -const Canvas = require("canvas"); -const { uploadZippyshare } = global.utils; - -const defaultFontName = "BeVietnamPro-SemiBold"; -const defaultPathFontName = `${__dirname}/assets/font/BeVietnamPro-SemiBold.ttf`; -const { randomString } = global.utils; -const percentage = total => total / 100; - -Canvas.registerFont(`${__dirname}/assets/font/BeVietnamPro-Bold.ttf`, { - family: "BeVietnamPro-Bold" -}); -Canvas.registerFont(defaultPathFontName, { - family: defaultFontName -}); - -let deltaNext; -const expToLevel = (exp, deltaNextLevel = deltaNext) => Math.floor((1 + Math.sqrt(1 + 8 * exp / deltaNextLevel)) / 2); -const levelToExp = (level, deltaNextLevel = deltaNext) => Math.floor(((Math.pow(level, 2) - level) * deltaNextLevel) / 2); -global.client.makeRankCard = makeRankCard; - -module.exports = { - config: { - name: "rank", - version: "1.7", - author: "NTKhang", - countDown: 5, - role: 0, - description: { - vi: "Xem level của bạn hoặc người được tag. Có thể tag nhiều người", - en: "View your level or the level of the tagged person. You can tag many people" - }, - category: "game", - guide: { - vi: " {pn} [để trống | @tags]", - en: " {pn} [empty | @tags]" - }, - envConfig: { - deltaNext: 5 - } - }, - - onStart: async function ({ message, event, usersData, threadsData, commandName, envCommands, api }) { - deltaNext = envCommands[commandName].deltaNext; - let targetUsers; - const arrayMentions = Object.keys(event.mentions); - - if (arrayMentions.length == 0) - targetUsers = [event.senderID]; - else - targetUsers = arrayMentions; - - const rankCards = await Promise.all(targetUsers.map(async userID => { - const rankCard = await makeRankCard(userID, usersData, threadsData, event.threadID, deltaNext, api); - rankCard.path = `${randomString(10)}.png`; - return rankCard; - })); - - return message.reply({ - attachment: rankCards - }); - }, - - onChat: async function ({ usersData, event }) { - let { exp } = await usersData.get(event.senderID); - if (isNaN(exp) || typeof exp != "number") - exp = 0; - try { - await usersData.set(event.senderID, { - exp: exp + 1 - }); - } - catch (e) { } - } -}; - -const defaultDesignCard = { - widthCard: 2000, - heightCard: 500, - main_color: "#474747", - sub_color: "rgba(255, 255, 255, 0.5)", - alpha_subcard: 0.9, - exp_color: "#e1e1e1", - expNextLevel_color: "#3f3f3f", - text_color: "#000000" -}; - -async function makeRankCard(userID, usersData, threadsData, threadID, deltaNext, api = global.GoatBot.fcaApi) { - const { exp } = await usersData.get(userID); - const levelUser = expToLevel(exp, deltaNext); - - const expNextLevel = levelToExp(levelUser + 1, deltaNext) - levelToExp(levelUser, deltaNext); - const currentExp = expNextLevel - (levelToExp(levelUser + 1, deltaNext) - exp); - - const allUser = await usersData.getAll(); - allUser.sort((a, b) => b.exp - a.exp); - const rank = allUser.findIndex(user => user.userID == userID) + 1; - - const customRankCard = await threadsData.get(threadID, "data.customRankCard") || {}; - const dataLevel = { - exp: currentExp, - expNextLevel, - name: allUser[rank - 1].name, - rank: `#${rank}/${allUser.length}`, - level: levelUser, - avatar: await usersData.getAvatarUrl(userID) - }; - - const configRankCard = { - ...defaultDesignCard, - ...customRankCard - }; - - const checkImagKey = [ - "main_color", - "sub_color", - "line_color", - "exp_color", - "expNextLevel_color" - ]; - - for (const key of checkImagKey) { - if (!isNaN(configRankCard[key])) - configRankCard[key] = await api.resolvePhotoUrl(configRankCard[key]); - } - - const image = new RankCard({ - ...configRankCard, - ...dataLevel - }); - return await image.buildCard(); -} - - -class RankCard { - /** - * Create a new RankCard - * @param {Object} options - Options for the RankCard: - * @param {String} options.main_color - The main color of the card - * @param {String} options.sub_color - The sub color of the card - * @param {Number} options.alpha_subcard - The alpha of the sub card - * @param {String} options.exp_color - The color of the exp bar - * @param {String} options.expNextLevel_color - The color of the expNextLevel bar - * @param {String} options.text_color - The color of the text - * @param {String} options.name_color - The color of the name - * @param {String} options.level_color - The color of the level - * @param {String} options.rank_color - The color of the rank - * @param {String} options.line_color - The color of the line - * @param {String} options.exp_text_color - The color of the exp text - * @param {Number} options.exp - The exp of the user - * @param {Number} options.expNextLevel - The expNextLevel of the user - * @param {String} options.name - The name of the user - * @param {Number} options.level - The level of the user - * @param {Number} options.rank - The rank of the user - * @param {String} options.avatar - The avatar of the user - * @param {Number} options.widthCard - The width of the card - * @param {Number} options.heightCard - The height of the card - * @param {String} options.fontName - The font name of the card - * @param {String} options.textSize - The value will be added to the font size of all text, default is 0 - * - * @example - * const fs = require("fs-extra"); - * const card = new RankCard() - * .setWidthCard(2000) - * .setHeightCard(500) - * .setMainColor("#474747") - * .setSubColor("rgba(255, 255, 255, 0.5)") - * .setAlphaSubCard(0.9) - * .setExpColor("#e1e1e1") - * .setExpBarColor("#3f3f3f") - * .setTextColor("#000000"); - * - * rank.buildCard() - * .then(buffer => { - * fs.writeFileSync("rank.png", buffer); - * }) - * .catch(err => { - * console.log(err); - * }); - * - * // or - * const card = new RankCard({ - * widthCard: 2000, - * heightCard: 500, - * main_color: "#474747", - * sub_color: "rgba(255, 255, 255, 0.5)", - * alpha_subcard: 0.9, - * exp_color: "#e1e1e1", - * expNextLevel_color: "#3f3f3f", - * text_color: "#000000" - * }); - * - * rank.buildCard() - * .then(buffer => { - * fs.writeFileSync("rank.png", buffer); - * }) - * .catch(err => { - * console.log(err); - * }); - */ - constructor(options) { - this.widthCard = 2000; - this.heightCard = 500; - this.main_color = "#474747"; - this.sub_color = "rgba(255, 255, 255, 0.5)"; - this.alpha_subcard = 0.9; - this.exp_color = "#e1e1e1"; - this.expNextLevel_color = "#3f3f3f"; - this.text_color = "#000000"; - this.fontName = "BeVietnamPro-Bold"; - this.textSize = 0; - - for (const key in options) - this[key] = options[key]; - } - - /** - * @param {string} path - * @param {string} name - * @description Register a new font - * @returns {RankCard} - * @example - * .registerFont("path/to/font.ttf", "FontName"); - */ - registerFont(path, name) { - Canvas.registerFont(path, { - family: name - }); - return this; - } - - /** - * @param {string} fontName - * @description Set the font name - * @returns {RankCard} - * @example - * .setFontName("BeVietnamPro-SemiBold"); - * .setFontName("BeVietnamPro-Bold"); - * .setFontName("Arial"); - * .setFontName("Arial Italic"); - */ - setFontName(fontName) { - this.fontName = fontName; - return this; - } - - /** - * @param {size} size - * @description increase the size of all the text by {size} units - * @returns {RankCard} - * @example - * .increaseTextSize(10); - * .increaseTextSize(20); - */ - increaseTextSize(size) { - if (isNaN(size)) - throw new Error("Size must be a number"); - if (size < 0) - throw new Error("Size must be greater than 0"); - this.textSize = size; - return this; - } - - /** - * @param {number} size - * @description decrease the size of all the text by {size} units - * @returns {RankCard} - * @example - * .decreaseTextSize(10); - * .decreaseTextSize(20); - */ - decreaseTextSize(size) { - if (isNaN(size)) - throw new Error("Size must be a number"); - if (size < 0) - throw new Error("Size must be greater than 0"); - this.textSize = -size; - return this; - } - - /** - * @param {number} widthCard - * @description Width of the card - * @returns {RankCard} - * @example - * .setWidthCard(2000); - */ - setWidthCard(widthCard) { - if (isNaN(widthCard)) - throw new Error("Width card must be a number"); - if (widthCard < 0) - throw new Error("Width card must be greater than 0"); - this.widthCard = Number(widthCard); - return this; - } - - /** - * @param {number} heightCard - * @description Height of the card - * @returns {RankCard} - * @example - * .setHeightCard(500); - */ - setHeightCard(heightCard) { - if (isNaN(heightCard)) - throw new Error("Height card must be a number"); - if (heightCard < 0) - throw new Error("Height card must be greater than 0"); - this.heightCard = Number(heightCard); - return this; - } - - /** - * @param {number} alpha_subcard - * @description Alpha of the sub card is a number between 0 and 1 - * @returns {RankCard} - * @example - * .setAlphaSubCard(0.5) - * 0.5 = 50% opacity - * 0.9 = 90% opacity - * 1 = 100% opacity - * 0 = 0% opacity - */ - setAlphaSubCard(alpha_subcard) { - if (isNaN(alpha_subcard)) - throw new Error("Alpha subcard must be a number"); - if (alpha_subcard < 0 || alpha_subcard > 1) - throw new Error("Alpha subcard must be between 0 and 1"); - this.alpha_subcard = Number(alpha_subcard); - return this; - } - - /** - * @param {string|string[]} main_color - * @description Color of the main card (background) is a string or array that can be a `hex color`, `rgb`, `rgba`, `image url`. If it's an array it will be a `gradient` color - * @returns {RankCard} - * @example - * .setMainColor("#474747"); - * .setMainColor("rgb(255, 255, 255)"); - * .setMainColor("rgba(255, 255, 255, 0.5)"); - * .setMainColor("https://example.com/image.png"); - */ - setMainColor(main_color) { - if (typeof main_color !== "string" && !Array.isArray(main_color)) - throw new Error("Main color must be a string or array"); - checkFormatColor(main_color); - this.main_color = main_color; - return this; - } - - /** - * @param {string|string[]} sub_color - * @description Color of the sub card is a string or array that can be a `hex color`, `rgb`, `rgba`, `image url`. If it's an array it will be a `gradient` color - * @returns {RankCard} - * @example - * .setSubColor("rgba(255, 255, 255, 0.5)") - * .setSubColor("#474747") - * .setSubColor("rgb(255, 255, 255)") - * .setSubColor("https://example.com/image.png") - */ - setSubColor(sub_color) { - if (typeof sub_color !== "string" && !Array.isArray(sub_color)) - throw new Error("Sub color must be a string or array"); - checkFormatColor(sub_color); - this.sub_color = sub_color; - return this; - } - - /** - * @param {string|string[]} exp_color - * @description Color of the exp bar is a string or array that can be a `hex color`, `rgb` or `rgba`. If it's an array it will be a `gradient` color - * @returns {RankCard} - * @example - * .setExpColor("#474747") - * .setExpColor("rgb(255, 255, 255)") - * .setExpColor("rgba(255, 255, 255, 0.5)") - */ - setExpColor(exp_color) { - if (typeof exp_color !== "string" && !Array.isArray(exp_color)) - throw new Error("Exp color must be a string or array"); - checkFormatColor(exp_color); - this.exp_color = exp_color; - return this; - } - - /** - * @param {string|string[]} expNextLevel_color - * @description Color of the exp bar next level is a string or array that can be a `hex color`, `rgb` or `rgba`. If it's an array it will be a `gradient` color - * @returns {RankCard} - * @example - * .setExpBarColor("#474747") - * .setExpBarColor("rgb(255, 255, 255)") - * .setExpBarColor("rgba(255, 255, 255, 0.5)") - */ - setExpBarColor(expNextLevel_color) { - if (typeof expNextLevel_color !== "string" && !Array.isArray(expNextLevel_color)) - throw new Error("Exp next level color must be a string"); - checkFormatColor(expNextLevel_color); - this.expNextLevel_color = expNextLevel_color; - return this; - } - - /** - * @param {string|string[]} text_color - * @description Color of the all text is a string or array that can be a `hex color`, `rgb` or `rgba`. If it's an array it will be a `gradient` color - * @returns {RankCard} - * @example - * .setTextColor("#474747") - * .setTextColor("rgb(255, 255, 255)") - * .setTextColor("rgba(255, 255, 255, 0.5)") - */ - setTextColor(text_color) { - if (typeof text_color !== "string" && !Array.isArray(text_color)) - throw new Error("Text color must be a string or an array of string"); - checkFormatColor(text_color, false); - this.text_color = text_color; - return this; - } - - /** - * @param {string|string[]} name_color - * @description Color of the name is a string or array that can be a `hex color`, `rgb` or `rgba`. If it's an array it will be a `gradient` color - * @returns {RankCard} - * @example - * .setNameColor("#474747") - * .setNameColor("rgb(255, 255, 255)") - * .setNameColor("rgba(255, 255, 255, 0.5)") - * .setNameColor(["#474747", "#474747"]) - * .setNameColor(['rgb(133, 255, 189)', 'rgb(255, 251, 125)']) - */ - setNameColor(name_color) { - if (typeof name_color !== "string" && !Array.isArray(name_color)) - throw new Error("Name color must be a string or an array of string"); - checkFormatColor(name_color, false); - this.name_color = name_color; - return this; - } - - /** - * @param {string|string[]} level_color - * @description Color of the level text is a string or array that can be a `hex color`, `rgb` or `rgba`. If it's an array it will be a `gradient` color - * @returns {RankCard} - * @example - * .setLevelColor("#474747") - * .setLevelColor("rgb(255, 255, 255)") - * .setLevelColor("rgba(255, 255, 255, 0.5)") - * .setLevelColor(["#474747", "#474747"]) - * .setLevelColor(['rgb(133, 255, 189)', 'rgb(255, 251, 125)']) - */ - setLevelColor(level_color) { - if (typeof level_color !== "string" && !Array.isArray(level_color)) - throw new Error("Level color must be a string or an array of string"); - checkFormatColor(level_color, false); - this.level_color = level_color; - return this; - } - - /** - * @param {string|string[]} exp_text_color - * @description Color of the exp text is a string or array that can be a `hex color`, `rgb` or `rgba`. If it's an array it will be a `gradient` color - * @returns {RankCard} - * @example - * .setExpTextColor("#474747") - * .setExpTextColor("rgb(255, 255, 255)") - * .setExpTextColor("rgba(255, 255, 255, 0.5)") - * .setExpTextColor(["#474747", "#474747"]) - * .setExpTextColor(['rgb(133, 255, 189)', 'rgb(255, 251, 125)']) - * - */ - setExpTextColor(exp_text_color) { - if (typeof exp_text_color !== "string" && !Array.isArray(exp_text_color)) - throw new Error("Exp text color must be a string or an array of string"); - checkFormatColor(exp_text_color, false); - this.exp_text_color = exp_text_color; - return this; - } - - /** - * @param {string|string[]} rank_color - * @description Color of the rank is a string or array that can be a `hex color`, `rgb` or `rgba`. If it's an array it will be a `gradient` color - * @returns {RankCard} - */ - setRankColor(rank_color) { - if (typeof rank_color !== "string" && !Array.isArray(rank_color)) - throw new Error("Rank color must be a string or an array of string"); - checkFormatColor(rank_color, false); - this.rank_color = rank_color; - return this; - } - - - /** - * @param {string|string[]} line_color - * @description Color of the line is a string or array that can be a `hex color`, `rgb`, `rgba` or url of image. If it's an array it will be a `gradient` color - * @returns {RankCard} - * @example - * .setLineColor("#474747") - * .setLineColor("rgb(255, 255, 255)") - * .setLineColor("rgba(255, 255, 255, 0.5)") - * .setLineColor(['#00DBDE', '#FC00FF']) - * .setLineColor(['rgb(133, 255, 189)', 'rgb(255, 251, 125)']) - * .setLineColor(['rgba(133, 255, 189, 0.5)', 'rgba(255, 251, 125, 0.5)']) - */ - setLineColor(line_color) { - if (typeof line_color !== "string" && !Array.isArray(line_color)) - throw new Error("Line color must be a string or an array of string"); - this.line_color = line_color; - return this; - } - - /** - * @param {number} exp - * @description Exp of the user - * @returns {RankCard} - */ - setExp(exp) { - this.exp = exp; - return this; - } - - /** - * @param {number} expNextLevel - * @description Exp next level of the user - * @returns {RankCard} - */ - setExpNextLevel(expNextLevel) { - this.expNextLevel = expNextLevel; - return this; - } - - /** - * @param {number} level - * @description Level of the user - * @returns {RankCard} - */ - setLevel(level) { - this.level = level; - return this; - } - - /** - * @param {string} rank - * @description Rank of the user - * @returns {RankCard} - * @example - * .setRank("#1/100") - */ - setRank(rank) { - this.rank = rank; - return this; - } - - /** - * @param {string} name - * @description Name of the user - * @returns {RankCard} - */ - setName(name) { - this.name = name; - return this; - } - - /** - * @param {string} avatar - * @description url or path of the avatar - * @returns {RankCard} - */ - setAvatar(avatar) { - this.avatar = avatar; - return this; - } - - - async buildCard() { - let { - widthCard, - heightCard - } = this; - const { - main_color, - sub_color, - alpha_subcard, - exp_color, - expNextLevel_color, - text_color, - name_color, - level_color, - rank_color, - line_color, - exp_text_color, - exp, - expNextLevel, - name, - level, - rank, - avatar - } = this; - - widthCard = Number(widthCard); - heightCard = Number(heightCard); - - const canvas = Canvas.createCanvas(widthCard, heightCard); - const ctx = canvas.getContext("2d"); - - /* - +-----------------------+ - | DRAW SUBCARD | - +-----------------------+ - */ - - const alignRim = 3 * percentage(widthCard); - const Alpha = parseFloat(alpha_subcard || 0); - - ctx.globalAlpha = Alpha; - await checkColorOrImageAndDraw(alignRim, alignRim, widthCard - alignRim * 2, heightCard - alignRim * 2, ctx, sub_color, 20, alpha_subcard); - ctx.globalAlpha = 1; - - ctx.globalCompositeOperation = "destination-out"; - - const xyAvatar = heightCard / 2; - const resizeAvatar = 60 * percentage(heightCard); - - // Kẽ đường ngang ở giữa - // Draw a horizontal line in the middle - const widthLineBetween = 58 * percentage(widthCard); - const heightLineBetween = 2 * percentage(heightCard); - - const angleLineCenter = 40; - const edge = heightCard / 2 * Math.tan(angleLineCenter * Math.PI / 180); - - if (line_color) { - if (!isUrl(line_color)) { - ctx.fillStyle = ctx.strokeStyle = checkGradientColor(ctx, - Array.isArray(line_color) ? line_color : [line_color], - xyAvatar - resizeAvatar / 2 - heightLineBetween, - 0, - xyAvatar + resizeAvatar / 2 + widthLineBetween + edge, - 0 - ); - ctx.globalCompositeOperation = "source-over"; - } - else { - ctx.save(); - const img = Canvas.loadImage(line_color); - ctx.globalCompositeOperation = "source-over"; - - ctx.beginPath(); - ctx.arc(xyAvatar, xyAvatar, resizeAvatar / 2 + heightLineBetween, 0, 2 * Math.PI); - ctx.fill(); - - ctx.rect(xyAvatar + resizeAvatar / 2, heightCard / 2 - heightLineBetween / 2, widthLineBetween, heightLineBetween); - ctx.fill(); - - ctx.translate(xyAvatar + resizeAvatar / 2 + widthLineBetween + edge, 0); - ctx.rotate(angleLineCenter * Math.PI / 180); - ctx.rect(0, 0, heightLineBetween, 1000); - ctx.fill(); - ctx.rotate(-angleLineCenter * Math.PI / 180); - ctx.translate(-xyAvatar - resizeAvatar / 2 - widthLineBetween - edge, 0); - - ctx.clip(); - ctx.drawImage(await img, 0, 0, widthCard, heightCard); - ctx.restore(); - } - } - ctx.beginPath(); - if (!isUrl(line_color)) - ctx.rect(xyAvatar + resizeAvatar / 2, heightCard / 2 - heightLineBetween / 2, widthLineBetween, heightLineBetween); - ctx.fill(); - - // Kẽ đường chéo ở cuối - // Draw a slant at the end - ctx.beginPath(); - if (!isUrl(line_color)) { - ctx.moveTo(xyAvatar + resizeAvatar / 2 + widthLineBetween + edge, 0); - ctx.lineTo(xyAvatar + resizeAvatar / 2 + widthLineBetween - edge, heightCard); - ctx.lineWidth = heightLineBetween; - ctx.stroke(); - } - - // Xóa nền vị trí đặt avatar - // Remove background of avatar placement - ctx.beginPath(); - if (!isUrl(line_color)) - ctx.arc(xyAvatar, xyAvatar, resizeAvatar / 2 + heightLineBetween, 0, 2 * Math.PI); - ctx.fill(); - ctx.globalCompositeOperation = "destination-out"; - - // Xóa xung quanh sub card - // Remove around sub card - ctx.fillRect(0, 0, widthCard, alignRim); - ctx.fillRect(0, heightCard - alignRim, widthCard, alignRim); - - // Xóa nền tại vị trí đặt thanh Exp - // Remove the background at the location where the Exp bar is located - const radius = 6 * percentage(heightCard); - const xStartExp = (25 + 1.5) * percentage(widthCard), - yStartExp = 67 * percentage(heightCard), - widthExp = 40.5 * percentage(widthCard), - heightExp = radius * 2; - ctx.globalCompositeOperation = "source-over"; - centerImage(ctx, await Canvas.loadImage(avatar), xyAvatar, xyAvatar, resizeAvatar, resizeAvatar); - - // Vẽ thanh Exp - // Draw Exp bar - if (!isUrl(expNextLevel_color)) { - ctx.beginPath(); - ctx.fillStyle = checkGradientColor(ctx, expNextLevel_color, xStartExp, yStartExp, xStartExp + widthExp, yStartExp); - ctx.arc(xStartExp, yStartExp + radius, radius, 1.5 * Math.PI, 0.5 * Math.PI, true); - ctx.fill(); - ctx.fillRect(xStartExp, yStartExp, widthExp, heightExp); - ctx.arc(xStartExp + widthExp, yStartExp + radius, radius, 1.5 * Math.PI, 0.5 * Math.PI, false); - ctx.fill(); - } - else { - ctx.save(); - ctx.beginPath(); - - ctx.moveTo(xStartExp, yStartExp); - ctx.lineTo(xStartExp + widthExp, yStartExp); - ctx.arcTo(xStartExp + widthExp + radius, yStartExp, xStartExp + widthExp + radius, yStartExp + radius, radius); - ctx.lineTo(xStartExp + widthExp + radius, yStartExp + heightExp - radius); - ctx.arcTo(xStartExp + widthExp + radius, yStartExp + heightExp, xStartExp + widthExp, yStartExp + heightExp, radius); - ctx.lineTo(xStartExp, yStartExp + heightExp); - ctx.arcTo(xStartExp, yStartExp + heightExp, xStartExp - radius, yStartExp + heightExp - radius, radius); - ctx.lineTo(xStartExp - radius, yStartExp + radius); - ctx.arcTo(xStartExp, yStartExp, xStartExp, yStartExp, radius); - - ctx.closePath(); - ctx.clip(); - - ctx.drawImage(await Canvas.loadImage(expNextLevel_color), xStartExp, yStartExp, widthExp + radius, heightExp); - ctx.restore(); - } - - - // Exp hiện tại - // Current Exp - const widthExpCurrent = (100 / expNextLevel * exp) * percentage(widthExp); - if (!isUrl(exp_color)) { - ctx.fillStyle = checkGradientColor(ctx, exp_color, xStartExp, yStartExp, xStartExp + widthExp, yStartExp); - ctx.beginPath(); - ctx.arc(xStartExp, yStartExp + radius, radius, 1.5 * Math.PI, 0.5 * Math.PI, true); - ctx.fill(); - - ctx.fillRect(xStartExp, yStartExp, widthExpCurrent, heightExp); - - ctx.beginPath(); - ctx.arc(xStartExp + widthExpCurrent - 1, yStartExp + radius, radius, 1.5 * Math.PI, 0.5 * Math.PI); - ctx.fill(); - } - else { - const imgExp = await Canvas.loadImage(exp_color); - ctx.save(); - ctx.beginPath(); - ctx.moveTo(xStartExp, yStartExp); - ctx.lineTo(xStartExp + widthExpCurrent, yStartExp); - ctx.arc(xStartExp + widthExpCurrent, yStartExp + radius, radius, 1.5 * Math.PI, 0.5 * Math.PI, false); - ctx.lineTo(xStartExp + widthExpCurrent + radius, yStartExp + heightExp - radius); - ctx.arcTo(xStartExp + widthExpCurrent + radius, yStartExp + heightExp, xStartExp + widthExpCurrent, yStartExp + heightExp, radius); - ctx.lineTo(xStartExp, yStartExp + heightExp); - ctx.arc(xStartExp, yStartExp + radius, radius, 1.5 * Math.PI, 0.5 * Math.PI, true); - ctx.lineTo(xStartExp - radius, yStartExp + radius); - ctx.arc(xStartExp, yStartExp + radius, radius, 1.5 * Math.PI, 0.5 * Math.PI, true); - ctx.closePath(); - ctx.clip(); - ctx.drawImage(imgExp, xStartExp - radius, yStartExp, widthExp + radius * 2, heightExp); - ctx.restore(); - } - - const maxSizeFont_Name = 4 * percentage(widthCard) + this.textSize; - const maxSizeFont_Exp = 2 * percentage(widthCard) + this.textSize; - const maxSizeFont_Level = 3.25 * percentage(widthCard) + this.textSize; - const maxSizeFont_Rank = 4 * percentage(widthCard) + this.textSize; - - ctx.textAlign = "end"; - - // Vẽ chữ Rank - // Draw rank text - ctx.font = autoSizeFont(18.4 * percentage(widthCard), maxSizeFont_Rank, rank, ctx, this.fontName); - const metricsRank = ctx.measureText(rank); - ctx.fillStyle = checkGradientColor(ctx, rank_color || text_color, - 94 * percentage(widthCard) - metricsRank.width, - 76 * percentage(heightCard) + metricsRank.emHeightDescent, - 94 * percentage(widthCard), - 76 * percentage(heightCard) - metricsRank.actualBoundingBoxAscent - ); - ctx.fillText(rank, 94 * percentage(widthCard), 76 * percentage(heightCard)); - - // Draw Level text - const textLevel = `Lv ${level}`; - ctx.font = autoSizeFont(9.8 * percentage(widthCard), maxSizeFont_Level, textLevel, ctx, this.fontName); - const metricsLevel = ctx.measureText(textLevel); - const xStartLevel = 94 * percentage(widthCard); - const yStartLevel = 32 * percentage(heightCard); - ctx.fillStyle = checkGradientColor(ctx, level_color || text_color, - xStartLevel - ctx.measureText(textLevel).width, - yStartLevel + metricsLevel.emHeightDescent, - xStartLevel, - yStartLevel - metricsLevel.actualBoundingBoxAscent - ); - ctx.fillText(textLevel, xStartLevel, yStartLevel); - ctx.font = autoSizeFont(52.1 * percentage(widthCard), maxSizeFont_Name, name, ctx, this.fontName); - ctx.textAlign = "center"; - - // Draw Name - const metricsName = ctx.measureText(name); - ctx.fillStyle = checkGradientColor(ctx, name_color || text_color, - 47.5 * percentage(widthCard) - metricsName.width / 2, - 40 * percentage(heightCard) + metricsName.emHeightDescent, - 47.5 * percentage(widthCard) + metricsName.width / 2, - 40 * percentage(heightCard) - metricsName.actualBoundingBoxAscent - ); - ctx.fillText(name, 47.5 * percentage(widthCard), 40 * percentage(heightCard)); - - // Draw Exp text - const textExp = `Exp ${exp}/${expNextLevel}`; - ctx.font = autoSizeFont(49 * percentage(widthCard), maxSizeFont_Exp, textExp, ctx, this.fontName); - const metricsExp = ctx.measureText(textExp); - ctx.fillStyle = checkGradientColor(ctx, exp_text_color || text_color, - 47.5 * percentage(widthCard) - metricsExp.width / 2, - 61.4 * percentage(heightCard) + metricsExp.emHeightDescent, - 47.5 * percentage(widthCard) + metricsExp.width / 2, - 61.4 * percentage(heightCard) - metricsExp.actualBoundingBoxAscent - ); - ctx.fillText(textExp, 47.5 * percentage(widthCard), 61.4 * percentage(heightCard)); - - - /* - +------------------------------------+ - | DRAW MAINCARD (BACKGROUND) | - +------------------------------------+ - */ - ctx.globalCompositeOperation = "destination-over"; - if (main_color.match?.(/^https?:\/\//) || Buffer.isBuffer(main_color)) { - ctx.beginPath(); - ctx.moveTo(radius, 0); - ctx.lineTo(widthCard - radius, 0); - ctx.quadraticCurveTo(widthCard, 0, widthCard, radius); - ctx.lineTo(widthCard, heightCard - radius); - ctx.quadraticCurveTo(widthCard, heightCard, widthCard - radius, heightCard); - ctx.lineTo(radius, heightCard); - ctx.quadraticCurveTo(0, heightCard, 0, heightCard - radius); - ctx.lineTo(0, radius); - ctx.quadraticCurveTo(0, 0, radius, 0); - ctx.closePath(); - ctx.clip(); - ctx.drawImage(await Canvas.loadImage(main_color), 0, 0, widthCard, heightCard); - } - else { - ctx.fillStyle = checkGradientColor(ctx, main_color, 0, 0, widthCard, heightCard); - drawSquareRounded(ctx, 0, 0, widthCard, heightCard, radius, main_color); - } - // return canvas.toBuffer(); - // return stream - return canvas.createPNGStream(); - } -} - -async function checkColorOrImageAndDraw(xStart, yStart, width, height, ctx, colorOrImage, r) { - if (!colorOrImage.match?.(/^https?:\/\//)) { - if (Array.isArray(colorOrImage)) { - const gradient = ctx.createLinearGradient(xStart, yStart, xStart + width, yStart + height); - colorOrImage.forEach((color, index) => { - gradient.addColorStop(index / (colorOrImage.length - 1), color); - }); - ctx.fillStyle = gradient; - } - drawSquareRounded(ctx, xStart, yStart, width, height, r, colorOrImage); - } - else { - const imageLoad = await Canvas.loadImage(colorOrImage); - ctx.save(); - roundedImage(xStart, yStart, width, height, r, ctx); - ctx.clip(); - ctx.drawImage(imageLoad, xStart, yStart, width, height); - ctx.restore(); - } -} - -function drawSquareRounded(ctx, x, y, w, h, r, color, defaultGlobalCompositeOperation, notChangeColor) { - ctx.save(); - if (defaultGlobalCompositeOperation) - ctx.globalCompositeOperation = "source-over"; - if (w < 2 * r) - r = w / 2; - if (h < 2 * r) - r = h / 2; - 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(); - if (!notChangeColor) - ctx.fillStyle = color; - ctx.fill(); - ctx.restore(); -} - -function roundedImage(x, y, width, height, radius, ctx) { - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); -} - -function centerImage(ctx, img, xCenter, yCenter, w, h) { - const x = xCenter - w / 2; - const y = yCenter - h / 2; - ctx.save(); - ctx.beginPath(); - ctx.arc(xCenter, yCenter, w / 2, 0, 2 * Math.PI); - ctx.clip(); - ctx.closePath(); - ctx.drawImage(img, x, y, w, h); - ctx.restore(); -} - -function autoSizeFont(maxWidthText, maxSizeFont, text, ctx, fontName) { - let sizeFont = 0; - // eslint-disable-next-line no-constant-condition - while (true) { - sizeFont += 1; - ctx.font = sizeFont + "px " + fontName; - const widthText = ctx.measureText(text).width; - if (widthText > maxWidthText || sizeFont > maxSizeFont) break; - } - return sizeFont + "px " + fontName; -} - -function checkGradientColor(ctx, color, x1, y1, x2, y2) { - if (Array.isArray(color)) { - const gradient = ctx.createLinearGradient(x1, y1, x2, y2); - color.forEach((c, index) => { - gradient.addColorStop(index / (color.length - 1), c); - }); - return gradient; - } - else { - return color; - } -} - -function isUrl(string) { - try { - new URL(string); - return true; - } - catch (err) { - return false; - } -} - -function checkFormatColor(color, enableUrl = true) { - if ( - !/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(color) && - !/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color) && - !/^rgba\((\d{1,3}), (\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color) && - (enableUrl ? !isUrl(color) : true) && - !Array.isArray(color) - ) - throw new Error(`The color format must be a hex, rgb, rgba ${enableUrl ? ", url image" : ""} or an array of colors`); -} diff --git a/scripts/cmds/rankup.js b/scripts/cmds/rankup.js deleted file mode 100644 index e6a2f6d8..00000000 --- a/scripts/cmds/rankup.js +++ /dev/null @@ -1,101 +0,0 @@ -const deltaNext = global.GoatBot.configCommands.envCommands.rank.deltaNext; -const expToLevel = exp => Math.floor((1 + Math.sqrt(1 + 8 * exp / deltaNext)) / 2); -const { drive } = global.utils; - -module.exports = { - config: { - name: "rankup", - version: "1.4", - author: "NTKhang", - countDown: 5, - role: 0, - description: { - vi: "Bật/tắt thông báo level up", - en: "Turn on/off level up notification" - }, - category: "rank", - guide: { - en: "{pn} [on | off]" - }, - envConfig: { - deltaNext: 5 - } - }, - - langs: { - vi: { - syntaxError: "Sai cú pháp, chỉ có thể dùng {pn} on hoặc {pn} off", - turnedOn: "Đã bật thông báo level up", - turnedOff: "Đã tắt thông báo level up", - notiMessage: "🎉🎉 chúc mừng bạn đạt level %1" - }, - en: { - syntaxError: "Syntax error, only use {pn} on or {pn} off", - turnedOn: "Turned on level up notification", - turnedOff: "Turned off level up notification", - notiMessage: "🎉🎉 Congratulations on reaching level %1" - } - }, - - onStart: async function ({ message, event, threadsData, args, getLang }) { - if (!["on", "off"].includes(args[0])) - return message.reply(getLang("syntaxError")); - await threadsData.set(event.threadID, args[0] == "on", "settings.sendRankupMessage"); - return message.reply(args[0] == "on" ? getLang("turnedOn") : getLang("turnedOff")); - }, - - onChat: async function ({ threadsData, usersData, event, message, getLang }) { - const threadData = await threadsData.get(event.threadID); - const sendRankupMessage = threadData.settings.sendRankupMessage; - if (!sendRankupMessage) - return; - const { exp } = await usersData.get(event.senderID); - const currentLevel = expToLevel(exp); - if (currentLevel > expToLevel(exp - 1)) { - let customMessage = await threadsData.get(event.threadID, "data.rankup.message"); - let isTag = false; - let userData; - const formMessage = {}; - - if (customMessage) { - userData = await usersData.get(event.senderID); - customMessage = customMessage - // .replace(/{userName}/g, userData.name) - .replace(/{oldRank}/g, currentLevel - 1) - .replace(/{currentRank}/g, currentLevel); - if (customMessage.includes("{userNameTag}")) { - isTag = true; - customMessage = customMessage.replace(/{userNameTag}/g, `@${userData.name}`); - } - else { - customMessage = customMessage.replace(/{userName}/g, userData.name); - } - - formMessage.body = customMessage; - } - else { - formMessage.body = getLang("notiMessage", currentLevel); - } - - if (threadData.data.rankup?.attachments?.length > 0) { - const files = threadData.data.rankup.attachments; - const attachments = files.reduce((acc, file) => { - acc.push(drive.getFile(file, "stream")); - return acc; - }, []); - formMessage.attachment = (await Promise.allSettled(attachments)) - .filter(({ status }) => status == "fulfilled") - .map(({ value }) => value); - } - - if (isTag) { - formMessage.mentions = [{ - tag: `@${userData.name}`, - id: event.senderID - }]; - } - - message.reply(formMessage); - } - } -}; diff --git a/scripts/cmds/rbg.js b/scripts/cmds/rbg.js deleted file mode 100644 index db5a17a8..00000000 --- a/scripts/cmds/rbg.js +++ /dev/null @@ -1,47 +0,0 @@ -const axios = require("axios"); - -const apiKey = "66e0cfbb-62b8-4829-90c7-c78cacc72ae2"; - -module.exports = { - config: { - name: "rbg", - version: "1.0", - author: "nexo_here", - category: "image", - shortDescription: "Remove background from image", - longDescription: "Removes background from replied or attached image using removebgv3 API", - guide: "{pn} (reply to image)" - }, - - onStart: async function({ api, event, args }) { - try { - let imageUrl = ""; - - if (event.type === "message_reply" && event.messageReply && event.messageReply.attachments && event.messageReply.attachments.length) { - imageUrl = event.messageReply.attachments[0].url; - } - else if (event.attachments && event.attachments.length) { - imageUrl = event.attachments[0].url; - } - else { - return api.sendMessage("❌ Please reply to or attach an image.", event.threadID, event.messageID); - } - - const apiUrl = `https://kaiz-apis.gleeze.com/api/removebgv3?url=${encodeURIComponent(imageUrl)}&stream=true&apikey=${apiKey}`; - - const response = await axios({ - method: "GET", - url: apiUrl, - responseType: "stream" - }); - - return api.sendMessage({ - attachment: response.data - }, event.threadID, event.messageID); - - } catch (error) { - console.error("rbg command error:", error); - return api.sendMessage("❌ Failed to remove background.", event.threadID, event.messageID); - } - } -}; \ No newline at end of file diff --git a/scripts/cmds/refresh.js b/scripts/cmds/refresh.js deleted file mode 100644 index da4b170d..00000000 --- a/scripts/cmds/refresh.js +++ /dev/null @@ -1,118 +0,0 @@ -module.exports = { - config: { - name: "refresh", - version: "1.2", - author: "NTKhang", - countDown: 60, - role: 0, - description: { - vi: "làm mới thông tin nhóm chat hoặc người dùng", - en: "refresh information of group chat or user" - }, - category: "box chat", - guide: { - vi: " {pn} [thread | group]: làm mới thông tin nhóm chat của bạn" - + "\n {pn} group : làm mới thông tin nhóm chat theo ID" - + "\n\n {pn} user: làm mới thông tin người dùng của bạn" - + "\n {pn} user [ | @tag]: làm mới thông tin người dùng theo ID", - en: " {pn} [thread | group]: refresh information of your group chat" - + "\n {pn} group : refresh information of group chat by ID" - + "\n\n {pn} user: refresh information of your user" - + "\n {pn} user [ | @tag]: refresh information of user by ID" - } - }, - - langs: { - vi: { - refreshMyThreadSuccess: "✅ | Đã làm mới thông tin nhóm chat của bạn thành công!", - refreshThreadTargetSuccess: "✅ | Đã làm mới thông tin nhóm chat %1 thành công!", - errorRefreshMyThread: "❌ | Đã xảy ra lỗi không thể làm mới thông tin nhóm chat của bạn", - errorRefreshThreadTarget: "❌ | Đã xảy ra lỗi không thể làm mới thông tin nhóm chat %1", - refreshMyUserSuccess: "✅ | Đã làm mới thông tin người dùng của bạn thành công!", - refreshUserTargetSuccess: "✅ | Đã làm mới thông tin người dùng %1 thành công!", - errorRefreshMyUser: "❌ | Đã xảy ra lỗi không thể làm mới thông tin người dùng của bạn", - errorRefreshUserTarget: "❌ | Đã xảy ra lỗi không thể làm mới thông tin người dùng %1" - }, - en: { - refreshMyThreadSuccess: "✅ | Refresh information of your group chat successfully!", - refreshThreadTargetSuccess: "✅ | Refresh information of group chat %1 successfully!", - errorRefreshMyThread: "❌ | Error when refresh information of your group chat", - errorRefreshThreadTarget: "❌ | Error when refresh information of group chat %1", - refreshMyUserSuccess: "✅ | Refresh information of your user successfully!", - refreshUserTargetSuccess: "✅ | Refresh information of user %1 successfully!", - errorRefreshMyUser: "❌ | Error when refresh information of your user", - errorRefreshUserTarget: "❌ | Error when refresh information of user %1" - }, - tl: { - refreshMyThreadSuccess: "✅ | Matagumpay na na-refresh ang impormasyon ng iyong group chat!", - refreshThreadTargetSuccess: "✅ | Matagumpay na na-refresh ang impormasyon ng group chat %1!", - errorRefreshMyThread: "❌ | Error habang nire-refresh ang impormasyon ng iyong group chat", - errorRefreshThreadTarget: "❌ | Error habang nire-refresh ang impormasyon ng group chat %1", - refreshMyUserSuccess: "✅ | Matagumpay na na-refresh ang iyong impormasyon ng user!", - refreshUserTargetSuccess: "✅ | Matagumpay na na-refresh ang impormasyon ng user %1!", - errorRefreshMyUser: "❌ | Error habang nire-refresh ang iyong impormasyon ng user", - errorRefreshUserTarget: "❌ | Error habang nire-refresh ang impormasyon ng user %1" - }, - hi: { - refreshMyThreadSuccess: "✅ | Aapke group chat ki jankari successfully refresh ho gayi!", - refreshThreadTargetSuccess: "✅ | Group chat %1 ki jankari successfully refresh ho gayi!", - errorRefreshMyThread: "❌ | Aapke group chat ki jankari refresh karne mein error aaya", - errorRefreshThreadTarget: "❌ | Group chat %1 ki jankari refresh karne mein error aaya", - refreshMyUserSuccess: "✅ | Aapki user jankari successfully refresh ho gayi!", - refreshUserTargetSuccess: "✅ | User %1 ki jankari successfully refresh ho gayi!", - errorRefreshMyUser: "❌ | Aapki user jankari refresh karne mein error aaya", - errorRefreshUserTarget: "❌ | User %1 ki jankari refresh karne mein error aaya" - }, - ar: { - refreshMyThreadSuccess: "✅ | تم تحديث معلومات مجموعتك بنجاح!", - refreshThreadTargetSuccess: "✅ | تم تحديث معلومات المجموعة %1 بنجاح!", - errorRefreshMyThread: "❌ | خطأ أثناء تحديث معلومات مجموعتك", - errorRefreshThreadTarget: "❌ | خطأ أثناء تحديث معلومات المجموعة %1", - refreshMyUserSuccess: "✅ | تم تحديث معلومات مستخدمك بنجاح!", - refreshUserTargetSuccess: "✅ | تم تحديث معلومات المستخدم %1 بنجاح!", - errorRefreshMyUser: "❌ | خطأ أثناء تحديث معلومات مستخدمك", - errorRefreshUserTarget: "❌ | خطأ أثناء تحديث معلومات المستخدم %1" - }, - bn: { - refreshMyThreadSuccess: "✅ | আপনার group chat এর তথ্য সফলভাবে refresh হয়েছে!", - refreshThreadTargetSuccess: "✅ | Group chat %1 এর তথ্য সফলভাবে refresh হয়েছে!", - errorRefreshMyThread: "❌ | আপনার group chat এর তথ্য refresh করতে error হয়েছে", - errorRefreshThreadTarget: "❌ | Group chat %1 এর তথ্য refresh করতে error হয়েছে", - refreshMyUserSuccess: "✅ | আপনার user তথ্য সফলভাবে refresh হয়েছে!", - refreshUserTargetSuccess: "✅ | User %1 এর তথ্য সফলভাবে refresh হয়েছে!", - errorRefreshMyUser: "❌ | আপনার user তথ্য refresh করতে error হয়েছে", - errorRefreshUserTarget: "❌ | User %1 এর তথ্য refresh করতে error হয়েছে" - } - }, - - onStart: async function ({ args, threadsData, message, event, usersData, getLang }) { - if (args[0] == "group" || args[0] == "thread") { - const targetID = args[1] || event.threadID; - try { - await threadsData.refreshInfo(targetID); - return message.reply(targetID == event.threadID ? getLang("refreshMyThreadSuccess") : getLang("refreshThreadTargetSuccess", targetID)); - } - catch (error) { - return message.reply(targetID == event.threadID ? getLang("errorRefreshMyThread") : getLang("errorRefreshThreadTarget", targetID)); - } - } - else if (args[0] == "user") { - let targetID = event.senderID; - if (args[1]) { - if (Object.keys(event.mentions).length) - targetID = Object.keys(event.mentions)[0]; - else - targetID = args[1]; - } - try { - await usersData.refreshInfo(targetID); - return message.reply(targetID == event.senderID ? getLang("refreshMyUserSuccess") : getLang("refreshUserTargetSuccess", targetID)); - } - catch (error) { - return message.reply(targetID == event.senderID ? getLang("errorRefreshMyUser") : getLang("errorRefreshUserTarget", targetID)); - } - } - else - message.SyntaxError(); - } -}; \ No newline at end of file diff --git a/scripts/cmds/remini.js b/scripts/cmds/remini.js deleted file mode 100644 index d904a9c8..00000000 --- a/scripts/cmds/remini.js +++ /dev/null @@ -1,75 +0,0 @@ -const axios = require("axios"); - -const mahmud = async () => { - const base = await axios.get("https://raw.githubusercontent.com/mahmudx7/HINATA/main/baseApiUrl.json"); - return base.data.mahmud; -}; - -/** -* @author MahMUD -* @author: do not delete it -*/ - -module.exports = { - config: { - name: "remini", - version: "1.7", - author: "MahMUD", - countDown: 10, - role: 0, - category: "AI", - description: "Enhance or restore image quality using Remini AI.", - guide: { - en: "{pn} [url] or reply with image" - } - }, - - onStart: async function ({ message, event, args }) { - - const obfuscatedAuthor = String.fromCharCode(77, 97, 104, 77, 85, 68); - if (module.exports.config.author !== obfuscatedAuthor) { - return api.sendMessage("You are not authorized to change the author name.", event.threadID, event.messageID); - } - const startTime = Date.now(); - let imgUrl; - - if (event.messageReply?.attachments?.[0]?.type === "photo") { - imgUrl = event.messageReply.attachments[0].url; - } - - else if (args[0]) { - imgUrl = args.join(" "); - } - - if (!imgUrl) { - return message.reply("Baby, Please reply to an image or provide an image URL"); - } - - const waitMsg = await message.reply("Remini images loading...wait baby <😘"); - message.reaction("😘", event.messageID); - - try { - - const apiUrl = `${await mahmud()}/api/remini?imgUrl=${encodeURIComponent(imgUrl)}`; - - const res = await axios.get(apiUrl, { responseType: "stream" }); - if (waitMsg?.messageID) message.unsend(waitMsg.messageID); - - message.reaction("✅", event.messageID); - - const processTime = ((Date.now() - startTime) / 1000).toFixed(2); - - message.reply({ - body: `✅ | Here's your Remini image baby`, - attachment: res.data - }); - - } catch (error) { - - if (waitMsg?.messageID) message.unsend(waitMsg.messageID); - - message.reaction("❎", event.messageID); - message.reply(`🥹error baby, contact MahMUD.`); - } - } -}; diff --git a/scripts/cmds/restart.js b/scripts/cmds/restart.js deleted file mode 100644 index e2ec56ac..00000000 --- a/scripts/cmds/restart.js +++ /dev/null @@ -1,57 +0,0 @@ -const fs = require("fs-extra"); - -module.exports = { - config: { - name: "restart", - version: "1.1", - author: "NTKhang", - countDown: 5, - role: 2, - description: { - vi: "Khởi động lại bot", - en: "Restart bot" - }, - category: "Owner", - guide: { - vi: " {pn}: Khởi động lại bot", - en: " {pn}: Restart bot" - } - }, - - langs: { - vi: { - restartting: "🔄 | Đang khởi động lại bot..." - }, - en: { - restartting: "🔄 | Restarting bot..." - }, - tl: { - restartting: "🔄 | Nire-restart ang bot..." - }, - hi: { - restartting: "🔄 | Bot restart ho raha hai..." - }, - ar: { - restartting: "🔄 | جارٍ إعادة تشغيل البوت..." - }, - bn: { - restartting: "🔄 | Bot পুনরায় চালু হচ্ছে..." - } - }, - - onLoad: function ({ api }) { - const pathFile = `${__dirname}/tmp/restart.txt`; - if (fs.existsSync(pathFile)) { - const [tid, time] = fs.readFileSync(pathFile, "utf-8").split(" "); - api.sendMessage(`✅ | Bot restarted\n⏰ | Time: ${(Date.now() - time) / 1000}s`, tid); - fs.unlinkSync(pathFile); - } - }, - - onStart: async function ({ message, event, getLang }) { - const pathFile = `${__dirname}/tmp/restart.txt`; - fs.writeFileSync(pathFile, `${event.threadID} ${Date.now()}`); - await message.reply(getLang("restartting")); - process.exit(2); - } -}; \ No newline at end of file diff --git a/scripts/cmds/rules.js b/scripts/cmds/rules.js deleted file mode 100644 index 90cf72f3..00000000 --- a/scripts/cmds/rules.js +++ /dev/null @@ -1,344 +0,0 @@ -const { getPrboxx } = global.utils; - -module.exports = { - config: { - name: "rules", - version: "1.6", - author: "NTKhang", - countDown: 5, - role: 0, - description: { - vi: "Tạo/xem/thêm/sửa/đổi vị trí/xóa nội quy nhóm của bạn", - en: "Create/view/add/edit/change position/delete group rules of you" - }, - category: "box chat", - guide: { - vi: " {pn} [add | -a] : thêm nội quy cho nhóm." - + "\n {pn}: xem nội quy của nhóm." - + "\n {pn} [edit | -e] : chỉnh sửa lại nội quy thứ n." - + "\n {pn} [move | -m] hoán đổi vị trí của nội quy thứ với nhau." - + "\n {pn} [delete | -d] : xóa nội quy theo số thứ tự thứ n." - + "\n {pn} [remove | -r]: xóa tất cả nội quy của nhóm." - + "\n" - + "\n Ví dụ:" - + "\n {pn} add không spam" - + "\n {pn} move 1 3" - + "\n {pn} -e 1 không spam tin nhắn trong nhóm" - + "\n {pn} -r", - en: " {pn} [add | -a] : add rule for group." - + "\n {pn}: view group rules." - + "\n {pn} [edit | -e] : edit rule number n." - + "\n {pn} [move | -m] swap position of rule number and ." - + "\n {pn} [delete | -d] : delete rule number n." - + "\n {pn} [remove | -r]: delete all rules of group." - + "\n" - + "\n Example:" - + "\n {pn} add don't spam" - + "\n {pn} move 1 3" - + "\n {pn} -e 1 don't spam message in group" - + "\n {pn} -r" - } - }, - - langs: { - vi: { - yourRules: "Nội quy của nhóm bạn\n%1", - noRules: "Hiện tại nhóm bạn chưa có bất kỳ nội quy nào, để thêm nội quy cho nhóm hãy sử dụng `%1rules add`", - noPermissionAdd: "Chỉ quản trị viên mới có thể thêm nội quy cho nhóm", - noContent: "Vui lòng nhập nội dung cho nội quy bạn muốn thêm", - success: "Đã thêm nội quy mới cho nhóm thành công", - noPermissionEdit: "Chỉ quản trị viên mới có thể chỉnh sửa nội quy nhóm", - invalidNumber: "Vui lòng nhập số thứ tự của quy định bạn muốn chỉnh sửa", - rulesNotExist: "Không tồn tại nội quy thứ %1", - numberRules: "Hiện tại nhóm bạn chỉ có %1 nội quy được đặt ra", - noContentEdit: "Vui lòng nhập nội dung bạn muốn thay đổi cho nội quy thứ %1", - successEdit: "Đã chỉnh sửa nội quy thứ %1 thành: %2", - noPermissionMove: "Chỉ quản trị viên mới có thể đổi vị trí nội quy của nhóm", - invalidNumberMove: "Vui lòng nhập số thứ tự của 2 nội quy nhóm bạn muốn chuyển đổi vị trí với nhau", - sameNumberMove: "Không thể chuyển đổi vị trí của 2 nội quy giống nhau", - rulesNotExistMove2: "Không tồn tại nội quy thứ %1 và %2", - successMove: "Đã chuyển đổi vị trí của 2 nội quy thứ %1 và %2 thành công", - noPermissionDelete: "Chỉ quản trị viên mới có thể xóa nội quy của nhóm", - invalidNumberDelete: "Vui lòng nhập số thứ tự của nội quy bạn muốn xóa", - rulesNotExistDelete: "Không tồn tại nội quy thứ %1", - successDelete: "Đã xóa nội quy thứ %1 của nhóm, nội dung: %2", - noPermissionRemove: "Chỉ có quản trị viên nhóm mới có thể xoá bỏ tất cả nội quy của nhóm", - confirmRemove: "⚠️ Thả cảm xúc bất kỳ vào tin nhắn này để xác nhận xóa toàn bộ nội quy của nhóm", - successRemove: "Đã xóa toàn bộ nội quy của nhóm thành công", - invalidNumberView: "Vui lòng nhập số thứ tự của nội quy bạn muốn xem" - }, - en: { - yourRules: "Your group rules\n%1", - noRules: "Your group has no rules, to add rules for group use `%1rules add`", - noPermissionAdd: "Only admins can add rules for group", - noContent: "Please enter the content for the rule you want to add", - success: "Added new rule for group successfully", - noPermissionEdit: "Only admins can edit group rules", - invalidNumber: "Please enter the number of the rule you want to edit", - rulesNotExist: "Rule number %1 does not exist", - numberRules: "Your group only has %1 rules", - noContentEdit: "Please enter the content you want to change for rule number %1", - successEdit: "Edited rule number %1 to: %2", - noPermissionMove: "Only admins can move group rules", - invalidNumberMove: "Please enter the number of 2 group rules you want to swap", - sameNumberMove: "Cannot swap position of 2 same rules", - rulesNotExistMove2: "Rule number %1 and %2 does not exist", - successMove: "Swapped position of rule number %1 and %2 successfully", - noPermissionDelete: "Only admins can delete group rules", - invalidNumberDelete: "Please enter the number of the rule you want to delete", - rulesNotExistDelete: "Rule number %1 does not exist", - successDelete: "Deleted rule number %1 of group, content: %2", - noPermissionRemove: "Only group admins can remove all group rules", - confirmRemove: "⚠️ React to this message with any emoji to confirm remove all group rules", - successRemove: "Removed all group rules successfully", - invalidNumberView: "Please enter the number of the rule you want to view" - }, - tl: { - yourRules: "Mga panuntunan ng iyong grupo\n%1", - noRules: "Walang panuntunan ang iyong grupo, para magdagdag gumamit ng `%1rules add`", - noPermissionAdd: "Ang mga admin lamang ang maaaring magdagdag ng panuntunan sa grupo", - noContent: "Mangyaring ilagay ang nilalaman ng panuntunan na gusto mong idagdag", - success: "Matagumpay na naidagdag ang bagong panuntunan sa grupo", - noPermissionEdit: "Ang mga admin lamang ang maaaring mag-edit ng mga panuntunan ng grupo", - invalidNumber: "Mangyaring ilagay ang numero ng panuntunan na gusto mong i-edit", - rulesNotExist: "Ang panuntunan bilang %1 ay hindi umiiral", - numberRules: "Ang iyong grupo ay mayroon lamang %1 panuntunan", - noContentEdit: "Mangyaring ilagay ang nilalaman na gusto mong baguhin para sa panuntunan bilang %1", - successEdit: "Na-edit ang panuntunan bilang %1 sa: %2", - noPermissionMove: "Ang mga admin lamang ang maaaring ilipat ang mga panuntunan ng grupo", - invalidNumberMove: "Mangyaring ilagay ang numero ng 2 panuntunan ng grupo na gusto mong palitan ng posisyon", - sameNumberMove: "Hindi maaaring palitan ang posisyon ng 2 parehong panuntunan", - rulesNotExistMove2: "Ang panuntunan bilang %1 at %2 ay hindi umiiral", - successMove: "Matagumpay na napalitan ang posisyon ng panuntunan bilang %1 at %2", - noPermissionDelete: "Ang mga admin lamang ang maaaring magtanggal ng mga panuntunan ng grupo", - invalidNumberDelete: "Mangyaring ilagay ang numero ng panuntunan na gusto mong tanggalin", - rulesNotExistDelete: "Ang panuntunan bilang %1 ay hindi umiiral", - successDelete: "Natanggal ang panuntunan bilang %1 ng grupo, nilalaman: %2", - noPermissionRemove: "Ang mga admin ng grupo lamang ang maaaring alisin ang lahat ng panuntunan", - confirmRemove: "⚠️ Mag-react sa mensaheng ito ng kahit anong emoji para kumpirmahin ang pag-alis ng lahat ng panuntunan ng grupo", - successRemove: "Matagumpay na naalis ang lahat ng panuntunan ng grupo", - invalidNumberView: "Mangyaring ilagay ang numero ng panuntunan na gusto mong tingnan" - }, - hi: { - yourRules: "Aapke group ke rules\n%1", - noRules: "Aapke group mein koi rule nahi hai, rule add karne ke liye `%1rules add` use karein", - noPermissionAdd: "Sirf admin hi group mein rules add kar sakte hain", - noContent: "Kripya jo rule add karna hai uska content dalein", - success: "Group mein naya rule successfully add kar diya gaya", - noPermissionEdit: "Sirf admin hi group rules edit kar sakte hain", - invalidNumber: "Kripya jo rule edit karna hai uska number dalein", - rulesNotExist: "Rule number %1 exist nahi karta", - numberRules: "Aapke group mein sirf %1 rules hain", - noContentEdit: "Kripya rule number %1 ke liye badlna chahte hain wo content dalein", - successEdit: "Rule number %1 edit hokar ho gaya: %2", - noPermissionMove: "Sirf admin hi group rules ko move kar sakte hain", - invalidNumberMove: "Kripya 2 group rules ke number dalein jo aap swap karna chahte hain", - sameNumberMove: "2 same rules ki position swap nahi ho sakti", - rulesNotExistMove2: "Rule number %1 aur %2 exist nahi karte", - successMove: "Rule number %1 aur %2 ki position successfully swap ho gayi", - noPermissionDelete: "Sirf admin hi group rules delete kar sakte hain", - invalidNumberDelete: "Kripya jo rule delete karna hai uska number dalein", - rulesNotExistDelete: "Rule number %1 exist nahi karta", - successDelete: "Group ka rule number %1 delete ho gaya, content: %2", - noPermissionRemove: "Sirf group admin hi saare group rules hata sakte hain", - confirmRemove: "⚠️ Saare group rules hatane ki pushthi ke liye is message par koi bhi emoji se react karein", - successRemove: "Saare group rules successfully hata diye gaye", - invalidNumberView: "Kripya jo rule dekhna hai uska number dalein" - }, - ar: { - yourRules: "قواعد مجموعتك\n%1", - noRules: "مجموعتك ليس لديها قواعد، لإضافة قواعد استخدم `%1rules add`", - noPermissionAdd: "فقط المسؤولون يمكنهم إضافة قواعد للمجموعة", - noContent: "الرجاء إدخال محتوى القاعدة التي تريد إضافتها", - success: "تمت إضافة قاعدة جديدة للمجموعة بنجاح", - noPermissionEdit: "فقط المسؤولون يمكنهم تعديل قواعد المجموعة", - invalidNumber: "الرجاء إدخال رقم القاعدة التي تريد تعديلها", - rulesNotExist: "القاعدة رقم %1 غير موجودة", - numberRules: "مجموعتك لديها فقط %1 قاعدة", - noContentEdit: "الرجاء إدخال المحتوى الذي تريد تغييره للقاعدة رقم %1", - successEdit: "تم تعديل القاعدة رقم %1 إلى: %2", - noPermissionMove: "فقط المسؤولون يمكنهم نقل قواعد المجموعة", - invalidNumberMove: "الرجاء إدخال رقم القاعدتين اللتين تريد تبادل موضعيهما", - sameNumberMove: "لا يمكن تبادل موضع نفس القاعدتين", - rulesNotExistMove2: "القاعدة رقم %1 و%2 غير موجودة", - successMove: "تم تبادل موضع القاعدة رقم %1 و%2 بنجاح", - noPermissionDelete: "فقط المسؤولون يمكنهم حذف قواعد المجموعة", - invalidNumberDelete: "الرجاء إدخال رقم القاعدة التي تريد حذفها", - rulesNotExistDelete: "القاعدة رقم %1 غير موجودة", - successDelete: "تم حذف القاعدة رقم %1 من المجموعة، المحتوى: %2", - noPermissionRemove: "فقط مسؤولو المجموعة يمكنهم إزالة جميع القواعد", - confirmRemove: "⚠️ تفاعل مع هذه الرسالة بأي إيموجي لتأكيد إزالة جميع قواعد المجموعة", - successRemove: "تمت إزالة جميع قواعد المجموعة بنجاح", - invalidNumberView: "الرجاء إدخال رقم القاعدة التي تريد عرضها" - }, - bn: { - yourRules: "আপনার group এর নিয়মাবলী\n%1", - noRules: "আপনার group এ কোনো নিয়ম নেই, নিয়ম যোগ করতে `%1rules add` ব্যবহার করুন", - noPermissionAdd: "শুধুমাত্র admin গ্রুপে নিয়ম যোগ করতে পারবে", - noContent: "অনুগ্রহ করে যোগ করতে চান এমন নিয়মের বিষয়বস্তু লিখুন", - success: "গ্রুপে নতুন নিয়ম সফলভাবে যোগ হয়েছে", - noPermissionEdit: "শুধুমাত্র admin গ্রুপের নিয়ম সম্পাদনা করতে পারবে", - invalidNumber: "অনুগ্রহ করে সম্পাদনা করতে চান এমন নিয়মের নম্বর দিন", - rulesNotExist: "নিয়ম নম্বর %1 বিদ্যমান নেই", - numberRules: "আপনার group এ মাত্র %1 টি নিয়ম আছে", - noContentEdit: "অনুগ্রহ করে নিয়ম নম্বর %1 এর জন্য পরিবর্তন করতে চান এমন বিষয়বস্তু লিখুন", - successEdit: "নিয়ম নম্বর %1 পরিবর্তন হয়েছে: %2", - noPermissionMove: "শুধুমাত্র admin গ্রুপের নিয়ম সরাতে পারবে", - invalidNumberMove: "অনুগ্রহ করে যে ২টি নিয়ম swap করতে চান তাদের নম্বর দিন", - sameNumberMove: "একই নিয়মের অবস্থান swap করা যাবে না", - rulesNotExistMove2: "নিয়ম নম্বর %1 এবং %2 বিদ্যমান নেই", - successMove: "নিয়ম নম্বর %1 এবং %2 এর অবস্থান সফলভাবে swap হয়েছে", - noPermissionDelete: "শুধুমাত্র admin গ্রুপের নিয়ম মুছতে পারবে", - invalidNumberDelete: "অনুগ্রহ করে মুছতে চান এমন নিয়মের নম্বর দিন", - rulesNotExistDelete: "নিয়ম নম্বর %1 বিদ্যমান নেই", - successDelete: "গ্রুপের নিয়ম নম্বর %1 মুছা হয়েছে, বিষয়বস্তু: %2", - noPermissionRemove: "শুধুমাত্র গ্রুপ admin সব নিয়ম সরাতে পারবে", - confirmRemove: "⚠️ সব গ্রুপের নিয়ম সরানো নিশ্চিত করতে যেকোনো emoji দিয়ে এই message এ react করুন", - successRemove: "সব গ্রুপের নিয়ম সফলভাবে সরানো হয়েছে", - invalidNumberView: "অনুগ্রহ করে দেখতে চান এমন নিয়মের নম্বর দিন" - } - }, - - onStart: async function ({ role, args, message, event, threadsData, getLang, commandName }) { - const { threadID, senderID } = event; - - const type = args[0]; - const rulesOfThread = await threadsData.get(threadID, "data.rules", []); - const totalRules = rulesOfThread.length; - - if (!type) { - let i = 1; - const msg = rulesOfThread.reduce((text, rules) => text += `${i++}. ${rules}\n`, ""); - message.reply(msg ? getLang("yourRules", msg) : getLang("noRules", getPrefix(threadID)), (err, info) => { - global.GoatBot.onReply.set(info.messageID, { - commandName, - author: senderID, - rulesOfThread, - messageID: info.messageID - }); - }); - } - else if (["add", "-a"].includes(type)) { - if (role < 1) - return message.reply(getLang("noPermissionAdd")); - if (!args[1]) - return message.reply(getLang("noContent")); - rulesOfThread.push(args.slice(1).join(" ")); - try { - await threadsData.set(threadID, rulesOfThread, "data.rules"); - message.reply(getLang("success")); - } - catch (err) { - message.err(err); - } - } - else if (["edit", "-e"].includes(type)) { - if (role < 1) - return message.reply(getLang("noPermissionEdit")); - const stt = parseInt(args[1]); - if (stt === NaN) - return message.reply(getLang("invalidNumber")); - if (!rulesOfThread[stt - 1]) - return message.reply(`${getLang("rulesNotExist", stt)}, ${totalRules == 0 ? getLang("noRules") : getLang("numberRules", totalRules)}`); - if (!args[2]) - return message.reply(getLang("noContentEdit", stt)); - const newContent = args.slice(2).join(" "); - rulesOfThread[stt - 1] = newContent; - try { - await threadsData.set(threadID, rulesOfThread, "data.rules"); - message.reply(getLang("successEdit", stt, newContent)); - } - catch (err) { - message.err(err); - } - } - else if (["move", "-m"].includes(type)) { - if (role < 1) - return message.reply(getLang("noPermissionMove")); - const num1 = parseInt(args[1]); - const num2 = parseInt(args[2]); - if (isNaN(num1) || isNaN(num2)) - return message.reply(getLang("invalidNumberMove")); - if (!rulesOfThread[num1 - 1] || !rulesOfThread[num2 - 1]) { - let msg = !rulesOfThread[num1 - 1] ? - !rulesOfThread[num2 - 1] ? - message.reply(getLang("rulesNotExistMove2", num1, num2)) : - message.reply(getLang("rulesNotExistMove", num1)) : - message.reply(getLang("rulesNotExistMove", num2)); - msg += `, ${totalRules == 0 ? getLang("noRules") : getLang("numberRules", totalRules)}`; - return message.reply(msg); - } - if (num1 == num2) - return message.reply(getLang("sameNumberMove")); - - // swap - [rulesOfThread[num1 - 1], rulesOfThread[num2 - 1]] = [rulesOfThread[num2 - 1], rulesOfThread[num1 - 1]]; - try { - await threadsData.set(threadID, rulesOfThread, "data.rules"); - message.reply(getLang("successMove", num1, num2)); - } - catch (err) { - message.err(err); - } - } - else if (["delete", "del", "-d"].includes(type)) { - if (role < 1) - return message.reply(getLang("noPermissionDelete")); - if (!args[1] || isNaN(args[1])) - return message.reply(getLang("invalidNumberDelete")); - const rulesDel = rulesOfThread[parseInt(args[1]) - 1]; - if (!rulesDel) - return message.reply(`${getLang("rulesNotExistDelete", args[1])}, ${totalRules == 0 ? getLang("noRules") : getLang("numberRules", totalRules)}`); - rulesOfThread.splice(parseInt(args[1]) - 1, 1); - await threadsData.set(threadID, rulesOfThread, "data.rules"); - message.reply(getLang("successDelete", args[1], rulesDel)); - } - else if (["remove", "reset", "-r", "-rm"].includes(type)) { - if (role < 1) - return message.reply(getLang("noPermissionRemove")); - message.reply(getLang("confirmRemove"), (err, info) => { - global.GoatBot.onReaction.set(info.messageID, { - commandName: "rules", - messageID: info.messageID, - author: senderID - }); - }); - } - else if (!isNaN(type)) { - let msg = ""; - for (const stt of args) { - const rules = rulesOfThread[parseInt(stt) - 1]; - if (rules) - msg += `${stt}. ${rules}\n`; - } - if (msg == "") - return message.reply(`${getLang("rulesNotExist", type)}, ${totalRules == 0 ? getLang("noRules") : getLang("numberRules", totalRules)}`); - message.reply(msg); - } - else { - message.SyntaxError(); - } - }, - - onReply: async function ({ message, event, getLang, Reply }) { - const { author, rulesOfThread } = Reply; - if (author != event.senderID) - return; - const num = parseInt(event.body || ""); - if (isNaN(num) || num < 1) - return message.reply(getLang("invalidNumberView")); - const totalRules = rulesOfThread.length; - if (num > totalRules) - return message.reply(`${getLang("rulesNotExist", num)}, ${totalRules == 0 ? getLang("noRules") : getLang("numberRules", totalRules)}`); - message.reply(`${num}. ${rulesOfThread[num - 1]}`, () => message.unsend(Reply.messageID)); - }, - - onReaction: async ({ threadsData, message, Reaction, event, getLang }) => { - const { author } = Reaction; - const { threadID, userID } = event; - if (author != userID) - return; - await threadsData.set(threadID, [], "data.rules"); - message.reply(getLang("successRemove")); - } -}; diff --git a/scripts/cmds/say.js b/scripts/cmds/say.js deleted file mode 100644 index e2844079..00000000 --- a/scripts/cmds/say.js +++ /dev/null @@ -1,42 +0,0 @@ -const fs = require("fs-extra"); -const path = require("path"); -const axios = require("axios"); - -module.exports = { - config: { - name: "say", - version: "2.0.0", - author: "MOHAMMAD AKASH", - countDown: 5, - role: 0, - shortDescription: "Google TTS দিয়ে ভয়েসে টেক্সট বলা", - longDescription: "যেকোনো টেক্সটকে বাংলায় Google Translate এর ভয়েসে রূপান্তর করে পাঠাবে।", - category: "media", - guide: { - en: "{p}say " - } - }, - - onStart: async function ({ api, event, args }) { - try { - const text = args.join(" ") || (event.messageReply?.body ?? null); - if (!text) return api.sendMessage("❌ দয়া করে কিছু লিখুন যেটা ভয়েসে বলতে হবে।", event.threadID, event.messageID); - - const filePath = path.join(__dirname, "cache", `${event.senderID}.mp3`); - const url = `https://translate.google.com/translate_tts?ie=UTF-8&q=${encodeURIComponent(text)}&tl=bn&client=tw-ob`; - - // 🔽 MP3 ফাইল ডাউনলোড - const response = await axios.get(url, { responseType: "arraybuffer" }); - fs.writeFileSync(filePath, Buffer.from(response.data, "utf-8")); - - // 🎧 পাঠানো - await api.sendMessage({ attachment: fs.createReadStream(filePath) }, event.threadID, () => { - fs.unlinkSync(filePath); // 🧹 ফাইল মুছে ফেলা - }); - - } catch (error) { - console.error("Say command error:", error); - api.sendMessage("❌ কিছু সমস্যা হয়েছে। পরে আবার চেষ্টা করুন!", event.threadID); - } - } -}; diff --git a/scripts/cmds/sdxl.js b/scripts/cmds/sdxl.js deleted file mode 100644 index a62f1d1d..00000000 --- a/scripts/cmds/sdxl.js +++ /dev/null @@ -1,61 +0,0 @@ -const axios = require("axios"); -const fs = require("fs-extra"); - -module.exports = { - config: { - name: "sdxl", - aliases: [], - version: "1.0", - author: "nexo_here", - countDown: 10, - role: 0, - shortDescription: "Generate image with SDXL Light", - longDescription: "Generate AI image using SDXL Light API with various styles", - category: "AI-IMAGE", - guide: { - en: "{pn} |