first commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
target/
|
||||||
|
|
||||||
|
.gitignore
|
||||||
|
compose.dev.yml
|
||||||
|
Dockerfile
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
Generated
+2321
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
|||||||
|
[workspace]
|
||||||
|
resolver = "3"
|
||||||
|
members = ["master", "worker"]
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
FROM rust:1.93.1-alpine3.23 AS builder
|
||||||
|
|
||||||
|
WORKDIR build
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
mkdir release
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/build/target \
|
||||||
|
cargo build --workspace --release && \
|
||||||
|
cp target/release/master release && \
|
||||||
|
cp target/release/worker release
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FROM alpine:3.23 AS master
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /build/release/master .
|
||||||
|
|
||||||
|
CMD ["/app/master"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FROM alpine:3.23 AS worker
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apk add --no-cache python3 ffmpeg deno
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
wget https://github.com/yt-dlp/yt-dlp/releases/download/2026.02.21/yt-dlp -O /usr/local/bin/yt-dlp && \
|
||||||
|
chmod +x /usr/local/bin/yt-dlp
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /build/release/worker .
|
||||||
|
|
||||||
|
CMD ["/app/worker"]
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
services:
|
||||||
|
nats:
|
||||||
|
image: nats:2.12.4-alpine3.22
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:4222:4222
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "master"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
async-nats = { version = "0.46.0" }
|
||||||
|
serenity = { version = "0.12.5", default-features = false, features = [
|
||||||
|
"client",
|
||||||
|
"gateway",
|
||||||
|
"rustls_backend",
|
||||||
|
"model",
|
||||||
|
"collector",
|
||||||
|
] }
|
||||||
|
tokio = { version = "1.49.0", features = ["macros", "rt-multi-thread"] }
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod modal;
|
||||||
|
pub mod ping;
|
||||||
|
pub mod wonderful_command;
|
||||||
|
pub mod testnats;
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
use serenity::builder::*;
|
||||||
|
use serenity::model::prelude::*;
|
||||||
|
use serenity::prelude::*;
|
||||||
|
use serenity::utils::CreateQuickModal;
|
||||||
|
|
||||||
|
pub async fn run(ctx: &Context, interaction: &CommandInteraction) -> Result<(), serenity::Error> {
|
||||||
|
let modal = CreateQuickModal::new("About you")
|
||||||
|
.timeout(std::time::Duration::from_secs(600))
|
||||||
|
.short_field("First name")
|
||||||
|
.short_field("Last name")
|
||||||
|
.paragraph_field("Hobbies and interests");
|
||||||
|
let response = interaction.quick_modal(ctx, modal).await?.unwrap();
|
||||||
|
|
||||||
|
let inputs = response.inputs;
|
||||||
|
let (first_name, last_name, hobbies) = (&inputs[0], &inputs[1], &inputs[2]);
|
||||||
|
|
||||||
|
response
|
||||||
|
.interaction
|
||||||
|
.create_response(
|
||||||
|
ctx,
|
||||||
|
CreateInteractionResponse::Message(CreateInteractionResponseMessage::new().content(
|
||||||
|
format!("**Name**: {first_name} {last_name}\n\nHobbies and interests: {hobbies}"),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register() -> CreateCommand {
|
||||||
|
CreateCommand::new("modal").description("Asks some details about you")
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
use serenity::builder::CreateCommand;
|
||||||
|
use serenity::model::application::ResolvedOption;
|
||||||
|
|
||||||
|
pub fn run(_options: &[ResolvedOption]) -> String {
|
||||||
|
"Hey, I'm alive!".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register() -> CreateCommand {
|
||||||
|
CreateCommand::new("ping").description("A ping command")
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
use serenity::all::{
|
||||||
|
CommandInteraction, Context, CreateCommandOption, CreateInteractionResponse,
|
||||||
|
CreateInteractionResponseMessage,
|
||||||
|
};
|
||||||
|
use serenity::builder::CreateCommand;
|
||||||
|
use serenity::model::application::{CommandOptionType, ResolvedOption, ResolvedValue};
|
||||||
|
|
||||||
|
pub async fn run<'a>(
|
||||||
|
ctx: &Context,
|
||||||
|
interaction: &CommandInteraction,
|
||||||
|
nats_client: &async_nats::Client,
|
||||||
|
) -> Result<(), serenity::Error> {
|
||||||
|
let options = interaction.data.options();
|
||||||
|
|
||||||
|
if let Some(ResolvedOption {
|
||||||
|
value: ResolvedValue::String(value),
|
||||||
|
..
|
||||||
|
}) = options.first()
|
||||||
|
{
|
||||||
|
if let Err(why) = nats_client
|
||||||
|
.publish("jobs", format!("{}", value).into())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
return Err(serenity::Error::Other("send error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
interaction
|
||||||
|
.create_response(
|
||||||
|
ctx,
|
||||||
|
CreateInteractionResponse::Message(
|
||||||
|
CreateInteractionResponseMessage::new().content(format!("string: {}", value)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
interaction
|
||||||
|
.create_response(
|
||||||
|
ctx,
|
||||||
|
CreateInteractionResponse::Message(
|
||||||
|
CreateInteractionResponseMessage::new()
|
||||||
|
.content("Please provide a valid string"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register() -> CreateCommand {
|
||||||
|
CreateCommand::new("testnats")
|
||||||
|
.description("Get a user id")
|
||||||
|
.add_option(
|
||||||
|
CreateCommandOption::new(CommandOptionType::String, "str", "The user to lookup")
|
||||||
|
.required(false),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
use serenity::builder::CreateCommand;
|
||||||
|
|
||||||
|
pub fn register() -> CreateCommand {
|
||||||
|
CreateCommand::new("wonderful_command").description("An amazing command")
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
mod commands;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use serenity::async_trait;
|
||||||
|
use serenity::builder::{CreateInteractionResponse, CreateInteractionResponseMessage};
|
||||||
|
use serenity::model::application::{Command, Interaction};
|
||||||
|
use serenity::model::gateway::Ready;
|
||||||
|
use serenity::model::id::GuildId;
|
||||||
|
use serenity::prelude::*;
|
||||||
|
|
||||||
|
struct Handler {
|
||||||
|
nats_client: async_nats::Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl EventHandler for Handler {
|
||||||
|
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
||||||
|
if let Interaction::Command(command) = interaction {
|
||||||
|
println!("Received command interaction: {command:#?}");
|
||||||
|
|
||||||
|
let content = match command.data.name.as_str() {
|
||||||
|
"testnats" => {
|
||||||
|
commands::testnats::run(&ctx, &command, &self.nats_client)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
"ping" => Some(commands::ping::run(&command.data.options())),
|
||||||
|
"modal" => {
|
||||||
|
commands::modal::run(&ctx, &command).await.unwrap();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
_ => Some("not implemented :(".to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(content) = content {
|
||||||
|
let data = CreateInteractionResponseMessage::new().content(content);
|
||||||
|
let builder = CreateInteractionResponse::Message(data);
|
||||||
|
if let Err(why) = command.create_response(&ctx.http, builder).await {
|
||||||
|
println!("Cannot respond to slash command: {why}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||||
|
println!("{} is connected!", ready.user.name);
|
||||||
|
|
||||||
|
let guild_id = GuildId::new(302837320250294283);
|
||||||
|
|
||||||
|
let commands = guild_id
|
||||||
|
.set_commands(
|
||||||
|
&ctx.http,
|
||||||
|
vec![
|
||||||
|
commands::ping::register(),
|
||||||
|
commands::modal::register(),
|
||||||
|
commands::testnats::register(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
println!("I now have the following guild slash commands: {commands:#?}");
|
||||||
|
|
||||||
|
let global_command =
|
||||||
|
Command::create_global_command(&ctx.http, commands::wonderful_command::register())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
println!("I created the following global slash command: {global_command:#?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
// Configure the client with your Discord bot token in the environment.
|
||||||
|
let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
|
||||||
|
|
||||||
|
let nats_client = async_nats::connect("nats://localhost:4222")
|
||||||
|
.await
|
||||||
|
.expect("Error creating nats client");
|
||||||
|
|
||||||
|
let handler = Handler { nats_client };
|
||||||
|
|
||||||
|
// Build our client.
|
||||||
|
let mut discord_client = Client::builder(token, GatewayIntents::empty())
|
||||||
|
.event_handler(handler)
|
||||||
|
.await
|
||||||
|
.expect("Error creating discord client");
|
||||||
|
|
||||||
|
// Finally, start a single shard, and start listening to events.
|
||||||
|
//
|
||||||
|
// Shards will automatically attempt to reconnect, and will perform exponential backoff until
|
||||||
|
// it reconnects.
|
||||||
|
if let Err(why) = discord_client.start().await {
|
||||||
|
println!("Client error: {why:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "worker"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
async-nats = { version = "0.46.0" }
|
||||||
|
futures = { version = "0.3.32" }
|
||||||
|
futures-executor = { version = "0.3.32" }
|
||||||
|
tokio = { version = "1.49.0", features = ["macros", "rt-multi-thread"] }
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
use futures::StreamExt;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let nats_client = async_nats::connect("nats://localhost:4222")
|
||||||
|
.await
|
||||||
|
.expect("Error creating nats client");
|
||||||
|
let mut subscriber = nats_client
|
||||||
|
.queue_subscribe("jobs", "download".to_string())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Receive and process messages
|
||||||
|
while let Some(message) = subscriber.next().await {
|
||||||
|
println!("Received message {:?}", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user