This commit is contained in:
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
24
.gitea/workflows/build.yml
Normal file
24
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Gitea Auto Deploy
|
||||
run-name: ${{ gitea.actor }} pushed code 🚀
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
Deploy-Container:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out latest code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Stop and remove old containers
|
||||
run: |
|
||||
docker compose down || true
|
||||
|
||||
- name: Remove unused Docker resources
|
||||
run: |
|
||||
docker system prune -a --volumes -f
|
||||
|
||||
- name: Build and restart containers
|
||||
run: |
|
||||
docker compose up -d
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.env
|
||||
node_modules
|
||||
.history
|
||||
16
Dockerfile
Normal file
16
Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
FROM base AS deps
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
COPY package*.json package-lock*.json ./
|
||||
RUN npm ci
|
||||
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY --from=deps /app/package.json ./package.json
|
||||
COPY package.json package-lock.json tsconfig.json .env ./
|
||||
COPY src ./src
|
||||
|
||||
CMD ["npm", "run", "start"]
|
||||
11
docker-compose.yml
Normal file
11
docker-compose.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
firefly-bot:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: firefly-bot
|
||||
networks:
|
||||
- bot-network
|
||||
|
||||
networks:
|
||||
bot-network:
|
||||
1390
package-lock.json
generated
Normal file
1390
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
package.json
Normal file
29
package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "firefly-bot",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "tsx watch src/index.ts",
|
||||
"start": "tsx src/index.ts",
|
||||
"build": "tsc"
|
||||
},
|
||||
"_moduleAliases": {
|
||||
"@": "src"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^1.9.0",
|
||||
"discord.js": "^14.19.3",
|
||||
"dotenv": "^16.5.0",
|
||||
"module-alias": "^2.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"tsx": "^4.19.4",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
2
src/commands/index.ts
Normal file
2
src/commands/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./prefix";
|
||||
export * from "./slash";
|
||||
10
src/commands/prefix/fetchApk/fetch.ts
Normal file
10
src/commands/prefix/fetchApk/fetch.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Client, Message } from "discord.js";
|
||||
import { fetchApk } from "@/services/fetchApk";
|
||||
|
||||
export const name = "apk";
|
||||
export const description = "Fetch apk";
|
||||
|
||||
export async function execute(client: Client, message: Message, args: string[]) {
|
||||
const version = args[0];
|
||||
await fetchApk(version, message);
|
||||
}
|
||||
24
src/commands/prefix/index.ts
Normal file
24
src/commands/prefix/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Client, Collection } from "discord.js";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import type { prefixCommand } from "@/types/discord.js";
|
||||
|
||||
export function ActiveAllPrefixCommands(client: Client) {
|
||||
const commandFolder = path.join(__dirname);
|
||||
client.prefix = new Collection<string, prefixCommand>();
|
||||
for (const folder of fs.readdirSync(commandFolder)) {
|
||||
const folderPath = path.join(commandFolder, folder);
|
||||
if (!fs.statSync(folderPath).isDirectory()) continue;
|
||||
|
||||
const commandFiles = fs.readdirSync(folderPath).filter(file => file.endsWith(".ts"));
|
||||
for (const file of commandFiles) {
|
||||
const command = require(path.join(folderPath, file));
|
||||
if (!command.name || typeof command.execute !== "function") {
|
||||
console.log(`Prefix command at ${path.join(folderPath, file)} is missing a name or execute function`);
|
||||
continue;
|
||||
}
|
||||
// console.log(`Prefix command ${command.name} loaded`);
|
||||
client.prefix.set(command.name, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/commands/prefix/ping/ping.ts
Normal file
10
src/commands/prefix/ping/ping.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Message, Client } from "discord.js";
|
||||
|
||||
export const name = "ping";
|
||||
export const description = "Kiểm tra thời gian phản hồi của bot";
|
||||
|
||||
export async function execute(client: Client, message: Message, args: string[]) {
|
||||
const sent = await message.reply('Pong!');
|
||||
const latency = sent.createdTimestamp - message.createdTimestamp;
|
||||
await sent.edit(`Pong! \`${latency}ms\``);
|
||||
}
|
||||
13
src/commands/slash/fetchApk/fetch.js
Normal file
13
src/commands/slash/fetchApk/fetch.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Client, ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { fetchApk } from "@/services/fetchApk";
|
||||
export const data = new SlashCommandBuilder()
|
||||
.setName("apk")
|
||||
.setDescription("Fetch apk")
|
||||
.addStringOption((option) => option
|
||||
.setName("version")
|
||||
.setDescription("Version of apk")
|
||||
.setRequired(true));
|
||||
export async function execute(client, interaction) {
|
||||
const version = interaction.options.getString("version");
|
||||
await fetchApk(version, interaction);
|
||||
}
|
||||
17
src/commands/slash/fetchApk/fetch.ts
Normal file
17
src/commands/slash/fetchApk/fetch.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Client, ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { fetchApk } from "@/services/fetchApk";
|
||||
|
||||
export const data = new SlashCommandBuilder()
|
||||
.setName("apk")
|
||||
.setDescription("Fetch apk")
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("version")
|
||||
.setDescription("Version of apk")
|
||||
.setRequired(true)
|
||||
)
|
||||
|
||||
export async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
const version = interaction.options.getString("version");
|
||||
await fetchApk(version, interaction);
|
||||
}
|
||||
47
src/commands/slash/index.js
Normal file
47
src/commands/slash/index.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Client, Collection, REST, Routes } from "discord.js";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { config } from "@/config";
|
||||
export async function ActiveAllSlashCommands(client) {
|
||||
client.slash = new Collection();
|
||||
const commandFolder = path.join(__dirname);
|
||||
for (const folder of fs.readdirSync(commandFolder)) {
|
||||
const folderPath = path.join(commandFolder, folder);
|
||||
if (!fs.statSync(folderPath).isDirectory())
|
||||
continue;
|
||||
const commandFiles = fs.readdirSync(folderPath).filter(file => file.endsWith(".ts"));
|
||||
for (const file of commandFiles) {
|
||||
const command = require(path.join(folderPath, file));
|
||||
if (!command.data?.name || typeof command.execute !== "function") {
|
||||
console.log(`Slash command at ${path.join(folderPath, file)} is missing a name or execute function`);
|
||||
continue;
|
||||
}
|
||||
client.slash.set(command.data.name, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
const rest = new REST({ version: '10' }).setToken(config.DISCORD_TOKEN);
|
||||
export async function registerCommandsAll() {
|
||||
const commands = [];
|
||||
const commandFolder = path.join(__dirname);
|
||||
for (const folder of fs.readdirSync(commandFolder)) {
|
||||
const folderPath = path.join(commandFolder, folder);
|
||||
if (!fs.statSync(folderPath).isDirectory())
|
||||
continue;
|
||||
const commandFiles = fs.readdirSync(folderPath).filter(file => file.endsWith(".ts"));
|
||||
for (const file of commandFiles) {
|
||||
const command = require(path.join(folderPath, file));
|
||||
if (!command.data?.name || typeof command.execute !== "function")
|
||||
continue;
|
||||
commands.push(command.data.toJSON());
|
||||
}
|
||||
}
|
||||
try {
|
||||
console.log('Started refreshing application (/) commands.');
|
||||
const data = await rest.put(Routes.applicationCommands(config.DISCORD_CLIENT_ID), { body: commands });
|
||||
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
53
src/commands/slash/index.ts
Normal file
53
src/commands/slash/index.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import type { slashCommand } from "@/types/discord.js";
|
||||
import { Client, Collection, REST, Routes, type RESTPostAPIChatInputApplicationCommandsJSONBody } from "discord.js";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { config } from "@/config";
|
||||
|
||||
export async function ActiveAllSlashCommands(client: Client) {
|
||||
client.slash = new Collection<string, slashCommand>();
|
||||
|
||||
const commandFolder = path.join(__dirname);
|
||||
for (const folder of fs.readdirSync(commandFolder)) {
|
||||
const folderPath = path.join(commandFolder, folder);
|
||||
if (!fs.statSync(folderPath).isDirectory()) continue;
|
||||
|
||||
const commandFiles = fs.readdirSync(folderPath).filter(file => file.endsWith(".ts"));
|
||||
for (const file of commandFiles) {
|
||||
const command = require(path.join(folderPath, file));
|
||||
if (!command.data?.name || typeof command.execute !== "function") {
|
||||
console.log(`Slash command at ${path.join(folderPath, file)} is missing a name or execute function`);
|
||||
continue;
|
||||
}
|
||||
// console.log(`Slash command ${command.data.name} loaded`);
|
||||
client.slash.set(command.data.name, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
const rest = new REST({ version: '10' }).setToken(config.DISCORD_TOKEN);
|
||||
|
||||
export async function registerCommandsAll() {
|
||||
const commands: RESTPostAPIChatInputApplicationCommandsJSONBody[] = [];
|
||||
const commandFolder = path.join(__dirname);
|
||||
|
||||
for (const folder of fs.readdirSync(commandFolder)) {
|
||||
const folderPath = path.join(commandFolder, folder);
|
||||
if (!fs.statSync(folderPath).isDirectory()) continue;
|
||||
|
||||
const commandFiles = fs.readdirSync(folderPath).filter(file => file.endsWith(".ts"));
|
||||
for (const file of commandFiles) {
|
||||
const command = require(path.join(folderPath, file));
|
||||
if (!command.data?.name || typeof command.execute !== "function") continue;
|
||||
commands.push(command.data.toJSON());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('Started refreshing application (/) commands.');
|
||||
const data = await rest.put(Routes.applicationCommands(config.DISCORD_CLIENT_ID),
|
||||
{ body: commands }) as RESTPostAPIChatInputApplicationCommandsJSONBody[];
|
||||
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
7
src/commands/slash/ping/ping.js
Normal file
7
src/commands/slash/ping/ping.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { ChatInputCommandInteraction, Client, SlashCommandBuilder } from "discord.js";
|
||||
export const data = new SlashCommandBuilder()
|
||||
.setName("ping")
|
||||
.setDescription("Replies with pong!");
|
||||
export async function execute(client, interaction) {
|
||||
await interaction.reply("Pong!");
|
||||
}
|
||||
10
src/commands/slash/ping/ping.ts
Normal file
10
src/commands/slash/ping/ping.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ChatInputCommandInteraction, Client, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
export const data = new SlashCommandBuilder()
|
||||
.setName("ping")
|
||||
.setDescription("Replies with pong!")
|
||||
|
||||
|
||||
export async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
await interaction.reply("Pong!");
|
||||
}
|
||||
18
src/config.ts
Normal file
18
src/config.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const { DISCORD_TOKEN, DISCORD_CLIENT_ID, DISCORD_GUILD_ID, DISCORD_PREFIX } = process.env;
|
||||
|
||||
if (!DISCORD_TOKEN || !DISCORD_CLIENT_ID || !DISCORD_GUILD_ID || !DISCORD_PREFIX) {
|
||||
throw new Error("Missing environment variables");
|
||||
}
|
||||
|
||||
export const config = {
|
||||
DISCORD_TOKEN,
|
||||
DISCORD_CLIENT_ID,
|
||||
DISCORD_GUILD_ID,
|
||||
DISCORD_PREFIX,
|
||||
};
|
||||
|
||||
26
src/events/index.ts
Normal file
26
src/events/index.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Client } from "discord.js";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
|
||||
export default function ActiveAllEvents(client: Client) {
|
||||
const eventFolder = path.join(__dirname);
|
||||
for (const folder of fs.readdirSync(eventFolder)) {
|
||||
const folderPath = path.join(eventFolder, folder);
|
||||
if (!fs.statSync(folderPath).isDirectory()) continue;
|
||||
|
||||
const eventFiles = fs.readdirSync(folderPath).filter(file => file.endsWith(".ts"));
|
||||
for (const file of eventFiles) {
|
||||
const event = require(path.join(folderPath, file));
|
||||
if (!event.name || typeof event.execute !== "function") {
|
||||
console.log(`Event at ${path.join(folderPath, file)} is missing a name or execute function`);
|
||||
continue;
|
||||
}
|
||||
// console.log(`Event ${event.name} loaded`);
|
||||
if (event.once) {
|
||||
client.once(event.name, (...args) => event.execute(client, ...args));
|
||||
} else {
|
||||
client.on(event.name, (...args) => event.execute(client, ...args));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/events/interaction/interaction.ts
Normal file
31
src/events/interaction/interaction.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ChatInputCommandInteraction, Client, Events, MessageFlags } from "discord.js";
|
||||
|
||||
export const name = Events.InteractionCreate;
|
||||
|
||||
export const once = false;
|
||||
|
||||
export async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
const command = client.slash.get(interaction.commandName);
|
||||
if (!command) {
|
||||
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await command.execute(client, interaction);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (interaction.replied || interaction.deferred) {
|
||||
await interaction.followUp({
|
||||
content: "An error occurred while executing this command.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
} else {
|
||||
await interaction.reply({
|
||||
content: "An error occurred while executing this command.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
30
src/events/messageCreate/message.ts
Normal file
30
src/events/messageCreate/message.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { config } from "@/config";
|
||||
import { Client, Events, Message } from "discord.js";
|
||||
|
||||
export const name = Events.MessageCreate;
|
||||
|
||||
export const once = false;
|
||||
|
||||
export async function execute(client: Client, message: Message) {
|
||||
if (message.author.bot) return;
|
||||
|
||||
const prefix = config.DISCORD_PREFIX;
|
||||
if (!message.content.startsWith(prefix)) return;
|
||||
|
||||
const args = message.content.slice(prefix.length).trim().split(/ +/);
|
||||
const commandName = args.shift()?.toLowerCase();
|
||||
|
||||
if (!commandName) return;
|
||||
|
||||
const command = client.prefix.get(commandName);
|
||||
|
||||
if (command) {
|
||||
try {
|
||||
await command.execute(client, message, args);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await message.reply('Đã xảy ra lỗi khi thực hiện lệnh này!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
src/events/ready/ready.js
Normal file
33
src/events/ready/ready.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { ActivityType, Client, Events, PresenceUpdateStatus } from "discord.js";
|
||||
export const name = Events.ClientReady;
|
||||
export const once = true;
|
||||
export async function execute(client) {
|
||||
if (!client.user || !client.application)
|
||||
return;
|
||||
console.log(`🚩Logged in as ${client.user.tag}!`);
|
||||
const activityTypeMap = {
|
||||
'PLAYING': ActivityType.Playing,
|
||||
'WATCHING': ActivityType.Watching,
|
||||
'LISTENING': ActivityType.Listening,
|
||||
'STREAMING': ActivityType.Streaming,
|
||||
'COMPETING': ActivityType.Competing
|
||||
};
|
||||
const statusMap = {
|
||||
'online': PresenceUpdateStatus.Online,
|
||||
'idle': PresenceUpdateStatus.Idle,
|
||||
'dnd': PresenceUpdateStatus.DoNotDisturb,
|
||||
'invisible': PresenceUpdateStatus.Invisible
|
||||
};
|
||||
const statusType = process.env.BOT_STATUS || 'online';
|
||||
const activityType = process.env.ACTIVITY_TYPE || 'PLAYING';
|
||||
const activityName = process.env.ACTIVITY_NAME || 'Shoudo sakusen jikkou!';
|
||||
client.user.setPresence({
|
||||
status: statusMap[statusType],
|
||||
activities: [{
|
||||
name: activityName,
|
||||
type: activityTypeMap[activityType]
|
||||
}]
|
||||
});
|
||||
console.log(`🗿Bot status set to: ${statusType}`);
|
||||
console.log(`👨🎤Activity set to: ${activityType} ${activityName}`);
|
||||
}
|
||||
41
src/events/ready/ready.ts
Normal file
41
src/events/ready/ready.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ActivityType, Client, Events, PresenceUpdateStatus } from "discord.js";
|
||||
// import { registerCommandsAll } from "@/commands";
|
||||
|
||||
export const name = Events.ClientReady;
|
||||
|
||||
export const once = true;
|
||||
|
||||
export async function execute(client: Client) {
|
||||
if (!client.user || !client.application) return;
|
||||
console.log(`🚩Logged in as ${client.user.tag}!`);
|
||||
// await registerCommandsAll()
|
||||
const activityTypeMap = {
|
||||
'PLAYING': ActivityType.Playing,
|
||||
'WATCHING': ActivityType.Watching,
|
||||
'LISTENING': ActivityType.Listening,
|
||||
'STREAMING': ActivityType.Streaming,
|
||||
'COMPETING': ActivityType.Competing
|
||||
} as const;
|
||||
|
||||
const statusMap = {
|
||||
'online': PresenceUpdateStatus.Online,
|
||||
'idle': PresenceUpdateStatus.Idle,
|
||||
'dnd': PresenceUpdateStatus.DoNotDisturb,
|
||||
'invisible': PresenceUpdateStatus.Invisible
|
||||
} as const;
|
||||
|
||||
const statusType = (process.env.BOT_STATUS as keyof typeof statusMap) || 'online';
|
||||
const activityType = (process.env.ACTIVITY_TYPE as keyof typeof activityTypeMap) || 'PLAYING';
|
||||
const activityName = process.env.ACTIVITY_NAME || 'Shoudo sakusen jikkou!';
|
||||
|
||||
client.user.setPresence({
|
||||
status: statusMap[statusType],
|
||||
activities: [{
|
||||
name: activityName,
|
||||
type: activityTypeMap[activityType]
|
||||
}]
|
||||
});
|
||||
|
||||
console.log(`🗿Bot status set to: ${statusType}`);
|
||||
console.log(`👨🎤Activity set to: ${activityType} ${activityName}`)
|
||||
}
|
||||
31
src/index.ts
Normal file
31
src/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'module-alias/register'
|
||||
import { Client, GatewayIntentBits, Partials } from "discord.js";
|
||||
import { config } from "./config";
|
||||
import ActiveAllEvents from "./events";
|
||||
import { ActiveAllPrefixCommands, ActiveAllSlashCommands } from "./commands";
|
||||
|
||||
console.log("🔥 Starting bot...")
|
||||
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.MessageContent,
|
||||
GatewayIntentBits.GuildMembers,
|
||||
GatewayIntentBits.GuildPresences,
|
||||
],
|
||||
partials: [
|
||||
Partials.Channel,
|
||||
Partials.Message,
|
||||
Partials.User,
|
||||
Partials.GuildMember
|
||||
]
|
||||
});
|
||||
|
||||
ActiveAllPrefixCommands(client);
|
||||
ActiveAllSlashCommands(client);
|
||||
ActiveAllEvents(client);
|
||||
|
||||
(async () => {
|
||||
await client.login(config.DISCORD_TOKEN);
|
||||
})();
|
||||
67
src/services/fetchApk.ts
Normal file
67
src/services/fetchApk.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import axios from "axios";
|
||||
import { ChatInputCommandInteraction, EmbedBuilder, Message, MessageFlags } from "discord.js";
|
||||
|
||||
export async function fetchApk(version: string | undefined | null, req: ChatInputCommandInteraction | Message) {
|
||||
if (!version) {
|
||||
await req.reply({
|
||||
content: '❌ Please provide a version.',
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const t = Math.floor(Date.now() / 1000);
|
||||
const url = 'https://globaldp-beta-cn01.bhsr.com/query_dispatch';
|
||||
|
||||
const params = {
|
||||
version: `CNBETAAndroid${version.replace(/\s+/g, "")}`,
|
||||
t: t.toString(),
|
||||
language_type: '3',
|
||||
platform_type: '3',
|
||||
channel_id: '1',
|
||||
sub_channel_id: '1',
|
||||
is_new_format: '1',
|
||||
};
|
||||
let sent;
|
||||
if (req instanceof Message) {
|
||||
sent = await req.reply({
|
||||
content: '🔄 Fetching APK...',
|
||||
flags: MessageFlags.SuppressNotifications
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.get(url, { params });
|
||||
const decoded = Buffer.from(response.data, 'base64').toString('utf-8');
|
||||
const match = decoded.match(/https?:\/\/[^\s\\"]+/);
|
||||
|
||||
if (match) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("📦 APK Download Link")
|
||||
.setDescription(`[Click here to download](${match[0]})`)
|
||||
.addFields({ name: "Version", value: version })
|
||||
.setColor("Random")
|
||||
.setTimestamp();
|
||||
|
||||
await req.reply({ embeds: [embed] });
|
||||
} else {
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("❌ No URL Found")
|
||||
.setDescription("No valid APK URL was found in the response.")
|
||||
.setColor("Red");
|
||||
|
||||
await req.reply({ embeds: [embed] });
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("❌ Error:", error.message);
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("🚨 Error")
|
||||
.setDescription("An error occurred while fetching the APK.")
|
||||
.setColor("Red");
|
||||
|
||||
await req.reply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
if (sent) {
|
||||
await sent.delete();
|
||||
}
|
||||
}
|
||||
19
src/types/discord.js.d.ts
vendored
Normal file
19
src/types/discord.js.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Collection, Client as BaseClient, SlashCommandBuilder, CommandInteraction } from "discord.js";
|
||||
|
||||
export interface slashCommand {
|
||||
data: SlashCommandBuilder;
|
||||
execute: (client: BaseClient, interaction: CommandInteraction) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface prefixCommand {
|
||||
name: string;
|
||||
description: string;
|
||||
execute: (client: Client, message: Message, args: string[]) => Promise<void>;
|
||||
}
|
||||
|
||||
declare module "discord.js" {
|
||||
interface Client {
|
||||
slash: Collection<string, slashCommand>;
|
||||
prefix: Collection<string, prefixCommand>;
|
||||
}
|
||||
}
|
||||
35
tsconfig.json
Normal file
35
tsconfig.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": false,
|
||||
"removeComments": true,
|
||||
"resolveJsonModule": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strictNullChecks": true,
|
||||
"verbatimModuleSyntax": false,
|
||||
"baseUrl": "src",
|
||||
"module": "CommonJS",
|
||||
"moduleDetection": "force",
|
||||
"isolatedModules": true,
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
// "noEmit": true,
|
||||
"outDir": "dist",
|
||||
"lib": ["es2022"],
|
||||
"paths": {
|
||||
"@/*": ["*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user