Skip to content

Commit fca0d85

Browse files
committed
make some cmd options optional + add more cmds
1 parent 8396de5 commit fca0d85

File tree

9 files changed

+316
-19
lines changed

9 files changed

+316
-19
lines changed

src/classes/Command.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ type Role = keyof typeof roles;
77
export default class Command {
88
public name: string;
99
public description: string;
10-
public integration_types: number[];
11-
public contexts: number[];
10+
public integration_types?: number[] | [0];
11+
public contexts?: number[] | [0];
1212
public options: ApplicationCommandOptionData[];
13-
public default_member_permissions: Permissions | null;
13+
public default_member_permissions?: Permissions | null;
1414
public botPermissions: PermissionResolvable[];
1515
public requiredRoles: Role[];
1616
public cooldown: number;

src/commands/info/bot.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ const bot = require("../../../package.json");
77
const command: Command = {
88
name: "bot",
99
description: "Different information about the bot.",
10-
integration_types: [0, 1],
11-
contexts: [0, 1, 2],
1210
options: [],
13-
default_member_permissions: null,
1411
botPermissions: [],
1512
requiredRoles: [],
1613
cooldown: 5,

src/commands/info/help.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import { getDirs } from "../../util/functions";
99
const command: Command = {
1010
name: "help",
1111
description: "Displays a list of all my commands.",
12-
integration_types: [0, 1],
13-
contexts: [0, 1, 2],
1412
options: [
1513
{
1614
type: 3,
@@ -19,7 +17,6 @@ const command: Command = {
1917
required: false,
2018
},
2119
],
22-
default_member_permissions: null,
2320
botPermissions: [],
2421
requiredRoles: [],
2522
cooldown: 5,

src/commands/info/ping.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ import { emojis as emoji } from "../../../config.json";
77
const command: Command = {
88
name: "ping",
99
description: "Check the bot's latency.",
10-
integration_types: [0, 1],
11-
contexts: [0, 1, 2],
1210
options: [],
13-
default_member_permissions: null,
1411
botPermissions: [],
1512
requiredRoles: [],
1613
cooldown: 10,

src/commands/is-a-dev/check.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import Command from "../../classes/Command";
2+
import ExtendedClient from "../../classes/ExtendedClient";
3+
import { ChatInputCommandInteraction, ColorResolvable } from "discord.js";
4+
5+
import axios from "axios";
6+
import { emojis as emoji } from "../../../config.json";
7+
8+
const command: Command = {
9+
name: "check",
10+
description: "Check if a subdomain is available.",
11+
options: [
12+
{
13+
type: 3,
14+
name: "subdomain",
15+
description: "The subdomain you want to check.",
16+
max_length: 244,
17+
required: true
18+
}
19+
],
20+
botPermissions: [],
21+
requiredRoles: [],
22+
cooldown: 5,
23+
enabled: true,
24+
deferReply: true,
25+
ephemeral: false,
26+
async execute(
27+
interaction: ChatInputCommandInteraction,
28+
client: ExtendedClient,
29+
Discord: typeof import("discord.js")
30+
) {
31+
try {
32+
const subdomain = interaction.options.get("subdomain").value as string;
33+
34+
const hostnameRegex =
35+
/^(?=.{1,253}$)(?:(?:[_a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)\.)+[a-zA-Z]{2,63}$/;
36+
37+
if (!hostnameRegex.test(`${subdomain}.is-a.dev`)) {
38+
const invalidSubdomain = new Discord.EmbedBuilder()
39+
.setColor(client.config.embeds.error as ColorResolvable)
40+
.setDescription(`${emoji.cross} \`${subdomain}\` is not a valid subdomain.`);
41+
42+
await interaction.editReply({ embeds: [invalidSubdomain] });
43+
return;
44+
}
45+
46+
const res = (await axios.get("https://raw.is-a.dev/v2.json")).data;
47+
const subdomainData = res.find((entry: any) => entry.subdomain === subdomain);
48+
49+
if (!subdomainData) {
50+
const available = new Discord.EmbedBuilder()
51+
.setColor(client.config.embeds.default as ColorResolvable)
52+
.setDescription(`${emoji.tick} \`${subdomain}.is-a.dev\` is available!`);
53+
54+
const registerButton: any = new Discord.ActionRowBuilder().addComponents(
55+
new Discord.ButtonBuilder()
56+
.setStyle(Discord.ButtonStyle.Link)
57+
.setLabel("Register")
58+
.setURL(`https://wdh.gg/XEMAmxc`)
59+
);
60+
61+
await interaction.editReply({ embeds: [available], components: [registerButton] });
62+
return;
63+
} else if (subdomainData.internal) {
64+
const internal = new Discord.EmbedBuilder()
65+
.setColor(client.config.embeds.error as ColorResolvable)
66+
.setDescription(`${emoji.cross} \`${subdomain}.is-a.dev\` is being used internally.`);
67+
68+
await interaction.editReply({ embeds: [internal] });
69+
return;
70+
} else if (subdomainData.reserved) {
71+
const reserved = new Discord.EmbedBuilder()
72+
.setColor(client.config.embeds.error as ColorResolvable)
73+
.setDescription(`${emoji.cross} \`${subdomain}.is-a.dev\` is reserved.`);
74+
75+
await interaction.editReply({ embeds: [reserved] });
76+
return;
77+
} else {
78+
const notAvailable = new Discord.EmbedBuilder()
79+
.setColor(client.config.embeds.error as ColorResolvable)
80+
.setDescription(`${emoji.cross} \`${subdomain}.is-a.dev\` is not available.`);
81+
82+
await interaction.editReply({ embeds: [notAvailable] });
83+
return;
84+
}
85+
} catch (err) {
86+
client.logCommandError(err, interaction, Discord);
87+
}
88+
}
89+
};
90+
91+
export = command;

src/commands/is-a-dev/redirect-config.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import { emojis as emoji } from "../../../config.json";
88
const command: Command = {
99
name: "redirect-config",
1010
description: "Get a is-a.dev subdomain's redirect configuration.",
11-
integration_types: [0, 1],
12-
contexts: [0, 1, 2],
1311
options: [
1412
{
1513
type: 3,
@@ -20,7 +18,6 @@ const command: Command = {
2018
autocomplete: true,
2119
},
2220
],
23-
default_member_permissions: null,
2421
botPermissions: [],
2522
requiredRoles: [],
2623
cooldown: 5,

src/commands/is-a-dev/whois.ts

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import Command from "../../classes/Command";
2+
import ExtendedClient from "../../classes/ExtendedClient";
3+
import { AutocompleteInteraction, ChatInputCommandInteraction, ColorResolvable } from "discord.js";
4+
5+
import axios from "axios";
6+
import { emojis as emoji } from "../../../config.json";
7+
8+
const command: Command = {
9+
name: "whois",
10+
description: "Run a WHOIS lookup on an is-a.dev subdomain.",
11+
options: [
12+
{
13+
type: 3,
14+
name: "subdomain",
15+
description: "The subdomain you want to find information about.",
16+
max_length: 253 - ".is-a.dev".length,
17+
required: true,
18+
autocomplete: true
19+
}
20+
],
21+
botPermissions: [],
22+
requiredRoles: [],
23+
cooldown: 5,
24+
enabled: true,
25+
deferReply: true,
26+
ephemeral: false,
27+
async execute(
28+
interaction: ChatInputCommandInteraction,
29+
client: ExtendedClient,
30+
Discord: typeof import("discord.js")
31+
) {
32+
try {
33+
const subdomain = interaction.options.get("subdomain").value as string;
34+
35+
const res = (await axios.get("https://raw.is-a.dev/v2.json")).data;
36+
const data = res.find((entry: any) => entry.subdomain === subdomain);
37+
38+
if (!data) {
39+
const noResult = new Discord.EmbedBuilder()
40+
.setColor(client.config.embeds.error as ColorResolvable)
41+
.setDescription(`${emoji.cross} \`${subdomain}.is-a.dev\` does not exist.`);
42+
43+
await interaction.editReply({ embeds: [noResult] });
44+
return;
45+
}
46+
47+
const whoisResult = new Discord.EmbedBuilder()
48+
.setColor(client.config.embeds.default as ColorResolvable)
49+
.setAuthor({
50+
name: data.owner?.username,
51+
iconURL: `https://github.com/${data.owner?.username}.png`,
52+
url: `https://github.com/${data.owner?.username}`
53+
})
54+
.setTitle(`${subdomain}.is-a.dev`);
55+
56+
if (!data.internal && !data.reserved) {
57+
if (!subdomain.includes("_")) {
58+
whoisResult.setURL(`https://${subdomain}.is-a.dev`);
59+
}
60+
61+
const owner = [];
62+
const records = [];
63+
const redirectConfig = [];
64+
65+
if (data.owner) {
66+
for (const [key, value] of Object.entries(data.owner)) {
67+
if (key !== "username") {
68+
owner.push(`${key}: \`${value}\``);
69+
}
70+
}
71+
}
72+
73+
if (owner.length > 0) {
74+
whoisResult.addFields({ name: "Owner Information", value: owner.join("\n") });
75+
}
76+
77+
for (const key of Object.keys(data.records).sort()) {
78+
switch (key) {
79+
case "A":
80+
case "AAAA":
81+
case "MX":
82+
case "NS":
83+
records.push(`**${key}**: \`${data.records[key].join("`, `")}\``);
84+
break;
85+
case "CAA":
86+
records.push(
87+
`**${key}**: \`${data.records.CAA.map(
88+
(entry: any) => `${entry.flag} ${entry.tag} "${entry.value}"`
89+
).join("`, `")}\``
90+
);
91+
break;
92+
case "DS":
93+
records.push(
94+
`**${key}**: \`${data.records.DS.map(
95+
(entry: any) =>
96+
`${entry.key_tag} ${entry.algorithm} ${entry.digest_type} ${entry.digest}`
97+
).join("`, `")}\``
98+
);
99+
break;
100+
case "SRV":
101+
records.push(
102+
`**${key}**: \`${data.records.SRV.map(
103+
(entry: any) => `${entry.priority} ${entry.weight} ${entry.port} ${entry.target}`
104+
).join("`, `")}\``
105+
);
106+
break;
107+
case "TXT":
108+
const txtRecords = Array.isArray(data.records.TXT)
109+
? (data.records.TXT as string[])
110+
: [data.records.TXT as string];
111+
records.push(`**${key}**: \`${txtRecords.join("`, `")}\``);
112+
break;
113+
case "URL":
114+
records.push(`**${key}**: ${data.records.URL}`);
115+
redirectConfig.push(
116+
`Redirect Paths: ${data?.redirect_config?.redirect_paths ? emoji.tick : emoji.cross}`
117+
);
118+
break;
119+
default:
120+
if (Array.isArray(data.records[key])) {
121+
const recordsToPush = [];
122+
123+
for (const record of data.records[key]) {
124+
if (typeof record === "string") {
125+
records.push(`**${key}**: \`${record}\``);
126+
} else {
127+
recordsToPush.push(
128+
`\`${Object.entries(record)
129+
.map(([k, v]) => `${k}: ${v}`)
130+
.join(", ")}\``
131+
); // `key1: value1, key2: value2`
132+
}
133+
}
134+
135+
if (recordsToPush.length > 0) {
136+
records.push(`**${key}**: ${recordsToPush.join(", ")}`);
137+
}
138+
} else {
139+
records.push(`**${key}**: \`${data.records[key]}\``);
140+
}
141+
break;
142+
}
143+
}
144+
145+
if (records.length > 0 && !data.reserved && !data.internal) {
146+
whoisResult.setDescription(`### DNS Records\n${records.join("\n")}`);
147+
}
148+
149+
if (data?.redirect_config?.custom_paths) {
150+
redirectConfig.push(
151+
`Custom Paths: \n${Object.entries(data.redirect_config.custom_paths)
152+
.map(([path, url]) => `- **\`${path}\`**: ${url}`)
153+
.join("\n")}`
154+
);
155+
}
156+
157+
if (data.records.A || data.records.AAAA || data.records.CNAME) {
158+
whoisResult.addFields({
159+
name: "Proxied",
160+
value: data.proxied ? emoji.tick : emoji.cross,
161+
inline: true
162+
});
163+
}
164+
165+
if (redirectConfig.length > 0) {
166+
whoisResult.addFields({
167+
name: "Redirect Config",
168+
value: redirectConfig.join("\n"),
169+
inline: true
170+
});
171+
}
172+
} else {
173+
if (data.internal) {
174+
whoisResult.addFields({
175+
name: "Status",
176+
value: "Internal Use",
177+
inline: true
178+
});
179+
} else if (data.reserved) {
180+
whoisResult.addFields({
181+
name: "Status",
182+
value: "Reserved",
183+
inline: true
184+
});
185+
}
186+
}
187+
188+
await interaction.editReply({ embeds: [whoisResult] });
189+
} catch (err) {
190+
client.logCommandError(err, interaction, Discord);
191+
}
192+
},
193+
autocomplete: async (interaction: AutocompleteInteraction, client: ExtendedClient) => {
194+
const option = interaction.options.getFocused(true);
195+
196+
if (option.name === "subdomain") {
197+
// Fetch all subdomains
198+
const res = (await axios.get("https://raw.is-a.dev/v2.json")).data;
199+
200+
// Filter subdomains
201+
const filteredSubdomains = res.filter((entry: any) => entry.subdomain.startsWith(option.value));
202+
203+
// Map subdomains to choices
204+
const choices = filteredSubdomains
205+
.map((entry: any) => {
206+
return {
207+
name: entry.subdomain,
208+
value: entry.subdomain
209+
};
210+
})
211+
.slice(0, 25);
212+
213+
await interaction.respond(choices);
214+
}
215+
}
216+
};
217+
218+
export = command;

src/commands/is-a-dev/zone-updated.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ import { emojis as emoji } from "../../../config.json";
88
const command: Command = {
99
name: "zone-updated",
1010
description: "Get the last time the is-a.dev zone was updated.",
11-
integration_types: [0, 1],
12-
contexts: [0, 1, 2],
1311
options: [],
14-
default_member_permissions: null,
1512
botPermissions: [],
1613
requiredRoles: [],
1714
cooldown: 5,

0 commit comments

Comments
 (0)