initial rewrite in rust & moderation commands
Signed-off-by: seth <getchoo@tuta.io>
This commit is contained in:
parent
b17e357b75
commit
45403e9d9b
53 changed files with 3297 additions and 2820 deletions
12
src/commands/general/joke.rs
Normal file
12
src/commands/general/joke.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use crate::api::dadjoke;
|
||||
use crate::Context;
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
|
||||
#[poise::command(slash_command, prefix_command)]
|
||||
pub async fn joke(ctx: Context<'_>) -> Result<()> {
|
||||
let joke = dadjoke::get_joke().await?;
|
||||
|
||||
ctx.reply(joke).await?;
|
||||
Ok(())
|
||||
}
|
25
src/commands/general/members.rs
Normal file
25
src/commands/general/members.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use crate::{consts, Context};
|
||||
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
|
||||
#[poise::command(slash_command, prefix_command)]
|
||||
pub async fn members(ctx: Context<'_>) -> Result<()> {
|
||||
let guild = ctx.guild().ok_or_else(|| eyre!("Couldn't fetch guild!"))?;
|
||||
|
||||
let count = guild.member_count;
|
||||
let online = if let Some(count) = guild.approximate_presence_count {
|
||||
count.to_string()
|
||||
} else {
|
||||
"Undefined".to_string()
|
||||
};
|
||||
|
||||
ctx.send(|m| {
|
||||
m.embed(|e| {
|
||||
e.title(format!("{count} total members!"))
|
||||
.description(format!("{online} online members"))
|
||||
.color(consts::COLORS["blue"])
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
13
src/commands/general/mod.rs
Normal file
13
src/commands/general/mod.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
mod joke;
|
||||
mod members;
|
||||
mod modrinth;
|
||||
mod rory;
|
||||
mod say;
|
||||
mod stars;
|
||||
|
||||
pub use joke::joke;
|
||||
pub use members::members;
|
||||
pub use modrinth::modrinth;
|
||||
pub use rory::rory;
|
||||
pub use say::say;
|
||||
pub use stars::stars;
|
8
src/commands/general/modrinth.rs
Normal file
8
src/commands/general/modrinth.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use crate::Context;
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
|
||||
#[poise::command(slash_command, prefix_command)]
|
||||
pub async fn modrinth(ctx: Context<'_>) -> Result<()> {
|
||||
todo!()
|
||||
}
|
21
src/commands/general/rory.rs
Normal file
21
src/commands/general/rory.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use crate::api::rory::get_rory;
|
||||
use crate::Context;
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
|
||||
#[poise::command(slash_command, prefix_command)]
|
||||
pub async fn rory(ctx: Context<'_>, id: Option<u64>) -> Result<()> {
|
||||
let resp = get_rory(id).await?;
|
||||
|
||||
ctx.send(|m| {
|
||||
m.embed(|e| {
|
||||
e.title("Rory :3")
|
||||
.url(&resp.url)
|
||||
.image(resp.url)
|
||||
.footer(|f| f.text(format!("ID {}", resp.id)))
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
43
src/commands/general/say.rs
Normal file
43
src/commands/general/say.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use crate::Context;
|
||||
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
|
||||
#[poise::command(slash_command, prefix_command, ephemeral)]
|
||||
pub async fn say(ctx: Context<'_>, content: String) -> Result<()> {
|
||||
let guild = ctx.guild().ok_or_else(|| eyre!("Couldn't get guild!"))?;
|
||||
let channel = ctx
|
||||
.guild_channel()
|
||||
.await
|
||||
.ok_or_else(|| eyre!("Couldn't get channel!"))?;
|
||||
|
||||
channel.say(ctx, &content).await?;
|
||||
ctx.say("I said what you said!").await?;
|
||||
|
||||
if let Some(channel_id) = ctx.data().config.discord.channels.say_log_channel_id {
|
||||
let log_channel = guild
|
||||
.channels
|
||||
.iter()
|
||||
.find(|c| c.0 == &channel_id)
|
||||
.ok_or_else(|| eyre!("Couldn't get log channel from guild!"))?;
|
||||
|
||||
log_channel
|
||||
.1
|
||||
.clone()
|
||||
.guild()
|
||||
.ok_or_else(|| eyre!("Couldn't cast channel we found from guild as GuildChannel?????"))?
|
||||
.send_message(ctx, |m| {
|
||||
m.embed(|e| {
|
||||
e.title("Say command used!")
|
||||
.description(content)
|
||||
.author(|a| {
|
||||
a.name(ctx.author().tag()).icon_url(
|
||||
ctx.author().avatar_url().unwrap_or("undefined".to_string()),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
30
src/commands/general/stars.rs
Normal file
30
src/commands/general/stars.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use crate::{consts::COLORS, Context};
|
||||
|
||||
use color_eyre::eyre::{Context as _, Result};
|
||||
|
||||
#[poise::command(slash_command, prefix_command)]
|
||||
pub async fn stars(ctx: Context<'_>) -> Result<()> {
|
||||
let prismlauncher = ctx
|
||||
.data()
|
||||
.octocrab
|
||||
.repos("PrismLauncher", "PrismLauncher")
|
||||
.get()
|
||||
.await
|
||||
.wrap_err_with(|| "Couldn't get PrismLauncher/PrismLauncher from GitHub!")?;
|
||||
|
||||
let count = if let Some(count) = prismlauncher.stargazers_count {
|
||||
count.to_string()
|
||||
} else {
|
||||
"undefined".to_string()
|
||||
};
|
||||
|
||||
ctx.send(|m| {
|
||||
m.embed(|e| {
|
||||
e.title(format!("⭐ {count} total stars!"))
|
||||
.color(COLORS["yellow"])
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
import type { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||
|
||||
export const jokeCommand = async (
|
||||
i: ChatInputCommandInteraction<CacheType>
|
||||
) => {
|
||||
await i.deferReply();
|
||||
const joke = await fetch('https://icanhazdadjoke.com', {
|
||||
headers: { Accept: 'text/plain' },
|
||||
}).then((r) => r.text());
|
||||
await i.editReply(joke);
|
||||
};
|
|
@ -1,24 +0,0 @@
|
|||
import type { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||
|
||||
import { COLORS } from '../constants';
|
||||
|
||||
export const membersCommand = async (
|
||||
i: ChatInputCommandInteraction<CacheType>
|
||||
) => {
|
||||
await i.deferReply();
|
||||
|
||||
const memes = await i.guild?.members.fetch().then((r) => r.toJSON());
|
||||
if (!memes) return;
|
||||
|
||||
await i.editReply({
|
||||
embeds: [
|
||||
{
|
||||
title: `${memes.length} total members!`,
|
||||
description: `${
|
||||
memes.filter((m) => m.presence?.status !== 'offline').length
|
||||
} online members`,
|
||||
color: COLORS.blue,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
20
src/commands/mod.rs
Normal file
20
src/commands/mod.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use crate::Data;
|
||||
|
||||
use color_eyre::eyre::Report;
|
||||
use poise::Command;
|
||||
|
||||
mod general;
|
||||
mod moderation;
|
||||
|
||||
pub fn to_global_commands() -> Vec<Command<Data, Report>> {
|
||||
vec![
|
||||
general::joke(),
|
||||
general::members(),
|
||||
general::modrinth(),
|
||||
general::rory(),
|
||||
general::say(),
|
||||
general::stars(),
|
||||
moderation::ban_user(),
|
||||
moderation::kick_user(),
|
||||
]
|
||||
}
|
80
src/commands/moderation/actions.rs
Normal file
80
src/commands/moderation/actions.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
use crate::{consts::COLORS, Context};
|
||||
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use poise::serenity_prelude::{CreateEmbed, User};
|
||||
|
||||
fn create_moderation_embed(
|
||||
title: String,
|
||||
user: &User,
|
||||
delete_messages_days: Option<u8>,
|
||||
reason: String,
|
||||
) -> impl FnOnce(&mut CreateEmbed) -> &mut CreateEmbed {
|
||||
let fields = [
|
||||
("User", format!("{} ({})", user.name, user.id), false),
|
||||
("Reason", reason, false),
|
||||
(
|
||||
"Deleted messages",
|
||||
format!("Last {} days", delete_messages_days.unwrap_or(0)),
|
||||
false,
|
||||
),
|
||||
];
|
||||
|
||||
|e: &mut CreateEmbed| e.title(title).fields(fields).color(COLORS["red"])
|
||||
}
|
||||
|
||||
// ban a user
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
prefix_command,
|
||||
default_member_permissions = "BAN_MEMBERS"
|
||||
)]
|
||||
pub async fn ban_user(
|
||||
ctx: Context<'_>,
|
||||
user: User,
|
||||
delete_messages_days: Option<u8>,
|
||||
reason: Option<String>,
|
||||
) -> Result<()> {
|
||||
let days = delete_messages_days.unwrap_or(1);
|
||||
let guild = ctx
|
||||
.guild()
|
||||
.ok_or_else(|| eyre!("Couldn't get guild from message; Unable to ban!"))?;
|
||||
|
||||
let reason = reason.unwrap_or("n/a".to_string());
|
||||
|
||||
if reason != "n/a" {
|
||||
guild.ban_with_reason(ctx, &user, days, &reason).await?;
|
||||
} else {
|
||||
guild.ban(ctx, &user, days).await?;
|
||||
}
|
||||
|
||||
let embed = create_moderation_embed("User banned!".to_string(), &user, Some(days), reason);
|
||||
|
||||
ctx.send(|m| m.embed(embed)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// kick a user
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
prefix_command,
|
||||
default_member_permissions = "KICK_MEMBERS"
|
||||
)]
|
||||
pub async fn kick_user(ctx: Context<'_>, user: User, reason: Option<String>) -> Result<()> {
|
||||
let guild = ctx
|
||||
.guild()
|
||||
.ok_or_else(|| eyre!("Couldn't get guild from message; Unable to ban!"))?;
|
||||
|
||||
let reason = reason.unwrap_or("n/a".to_string());
|
||||
|
||||
if reason != "n/a" {
|
||||
guild.kick_with_reason(ctx, &user, &reason).await?;
|
||||
} else {
|
||||
guild.kick(ctx, &user).await?;
|
||||
}
|
||||
|
||||
let embed = create_moderation_embed("User kicked!".to_string(), &user, None, reason);
|
||||
ctx.send(|m| m.embed(embed)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
3
src/commands/moderation/mod.rs
Normal file
3
src/commands/moderation/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
mod actions;
|
||||
|
||||
pub use actions::*;
|
|
@ -1,124 +0,0 @@
|
|||
type Side = 'required' | 'optional' | 'unsupported';
|
||||
|
||||
export interface ModrinthProject {
|
||||
slug: string;
|
||||
title: string;
|
||||
description: string;
|
||||
categories: string[];
|
||||
client_side: Side;
|
||||
server_side: Side;
|
||||
project_type: 'mod' | 'modpack';
|
||||
downloads: number;
|
||||
icon_url: string | null;
|
||||
id: string;
|
||||
team: string;
|
||||
}
|
||||
|
||||
import {
|
||||
EmbedBuilder,
|
||||
type CacheType,
|
||||
type ChatInputCommandInteraction,
|
||||
} from 'discord.js';
|
||||
|
||||
import { COLORS } from '../constants';
|
||||
|
||||
export const modrinthCommand = async (
|
||||
i: ChatInputCommandInteraction<CacheType>
|
||||
) => {
|
||||
await i.deferReply();
|
||||
|
||||
const { value: id } = i.options.get('id') ?? { value: null };
|
||||
|
||||
if (!id || typeof id !== 'string') {
|
||||
await i.editReply({
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setTitle('Error!')
|
||||
.setDescription('You need to provide a valid mod ID!')
|
||||
.setColor(COLORS.red),
|
||||
],
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await fetch('https://api.modrinth.com/v2/project/' + id);
|
||||
|
||||
if (!res.ok) {
|
||||
await i.editReply({
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setTitle('Error!')
|
||||
.setDescription('Not found!')
|
||||
.setColor(COLORS.red),
|
||||
],
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
i.deleteReply();
|
||||
}, 3000);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const data = (await res.json()) as
|
||||
| ModrinthProject
|
||||
| { error: string; description: string };
|
||||
|
||||
if ('error' in data) {
|
||||
console.error(data);
|
||||
|
||||
await i.editReply({
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setTitle('Error!')
|
||||
.setDescription(`\`${data.error}\` ${data.description}`)
|
||||
.setColor(COLORS.red),
|
||||
],
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
i.deleteReply();
|
||||
}, 3000);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await i.editReply({
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setTitle(data.title)
|
||||
.setDescription(data.description)
|
||||
.setThumbnail(data.icon_url)
|
||||
.setURL(`https://modrinth.com/project/${data.slug}`)
|
||||
.setFields([
|
||||
{
|
||||
name: 'Categories',
|
||||
value: data.categories.join(', '),
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: 'Project type',
|
||||
value: data.project_type,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: 'Downloads',
|
||||
value: data.downloads.toString(),
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: 'Client',
|
||||
value: data.client_side,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: 'Server',
|
||||
value: data.server_side,
|
||||
inline: true,
|
||||
},
|
||||
])
|
||||
.setColor(COLORS.green),
|
||||
],
|
||||
});
|
||||
};
|
|
@ -1,51 +0,0 @@
|
|||
import type { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { EmbedBuilder } from 'discord.js';
|
||||
|
||||
export interface RoryResponse {
|
||||
/**
|
||||
* The ID of this Rory
|
||||
*/
|
||||
id: number;
|
||||
/**
|
||||
* The URL to the image of this Rory
|
||||
*/
|
||||
url: string;
|
||||
/**
|
||||
* When error :(
|
||||
*/
|
||||
error: string | undefined;
|
||||
}
|
||||
|
||||
export const roryCommand = async (
|
||||
i: ChatInputCommandInteraction<CacheType>
|
||||
) => {
|
||||
await i.deferReply();
|
||||
|
||||
const { value: id } = i.options.get('id') ?? { value: '' };
|
||||
|
||||
const rory: RoryResponse = await fetch(`https://rory.cat/purr/${id}`, {
|
||||
headers: { Accept: 'application/json' },
|
||||
}).then((r) => r.json());
|
||||
|
||||
if (rory.error) {
|
||||
await i.editReply({
|
||||
embeds: [
|
||||
new EmbedBuilder().setTitle('Error!').setDescription(rory.error),
|
||||
],
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await i.editReply({
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setTitle('Rory :3')
|
||||
.setURL(`https://rory.cat/id/${rory.id}`)
|
||||
.setImage(rory.url)
|
||||
.setFooter({
|
||||
text: `ID ${rory.id}`,
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
|
@ -1,37 +0,0 @@
|
|||
import {
|
||||
CacheType,
|
||||
ChatInputCommandInteraction,
|
||||
EmbedBuilder,
|
||||
} from 'discord.js';
|
||||
|
||||
export const sayCommand = async (
|
||||
interaction: ChatInputCommandInteraction<CacheType>
|
||||
) => {
|
||||
if (!interaction.guild || !interaction.channel) return;
|
||||
|
||||
const content = interaction.options.getString('content', true);
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
const message = await interaction.channel.send(content);
|
||||
await interaction.editReply('I said what you said!');
|
||||
|
||||
if (process.env.SAY_LOGS_CHANNEL) {
|
||||
const logsChannel = await interaction.guild.channels.fetch(
|
||||
process.env.SAY_LOGS_CHANNEL
|
||||
);
|
||||
|
||||
if (!logsChannel?.isTextBased()) return;
|
||||
|
||||
await logsChannel.send({
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setTitle('Say command used')
|
||||
.setDescription(content)
|
||||
.setAuthor({
|
||||
name: interaction.user.tag,
|
||||
iconURL: interaction.user.avatarURL() ?? undefined,
|
||||
})
|
||||
.setURL(message.url),
|
||||
],
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,23 +0,0 @@
|
|||
import type { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { COLORS } from '../constants';
|
||||
|
||||
export const starsCommand = async (
|
||||
i: ChatInputCommandInteraction<CacheType>
|
||||
) => {
|
||||
await i.deferReply();
|
||||
|
||||
const count = await fetch(
|
||||
'https://api.github.com/repos/PrismLauncher/PrismLauncher'
|
||||
)
|
||||
.then((r) => r.json() as Promise<{ stargazers_count: number }>)
|
||||
.then((j) => j.stargazers_count);
|
||||
|
||||
await i.editReply({
|
||||
embeds: [
|
||||
{
|
||||
title: `⭐ ${count} total stars!`,
|
||||
color: COLORS.yellow,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
|
@ -1,38 +0,0 @@
|
|||
import {
|
||||
type ChatInputCommandInteraction,
|
||||
type CacheType,
|
||||
EmbedBuilder,
|
||||
} from 'discord.js';
|
||||
import { getTags } from '../tags';
|
||||
|
||||
export const tagsCommand = async (
|
||||
i: ChatInputCommandInteraction<CacheType>
|
||||
) => {
|
||||
const tags = await getTags();
|
||||
const tagName = i.options.getString('name', true);
|
||||
const mention = i.options.getUser('user', false);
|
||||
|
||||
const tag = tags.find(
|
||||
(tag) => tag.name === tagName || tag.aliases?.includes(tagName)
|
||||
);
|
||||
|
||||
if (!tag) {
|
||||
await i.reply({
|
||||
content: `Tag \`${tagName}\` does not exist.`,
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder();
|
||||
embed.setTitle(tag.title ?? tag.name);
|
||||
embed.setDescription(tag.content);
|
||||
if (tag.color) embed.setColor(tag.color);
|
||||
if (tag.image) embed.setImage(tag.image);
|
||||
if (tag.fields) embed.setFields(tag.fields);
|
||||
|
||||
await i.reply({
|
||||
content: mention ? `<@${mention.id}> ` : undefined,
|
||||
embeds: [embed],
|
||||
});
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue