Rust Migration

This commit is contained in:
2023-07-04 14:10:56 -04:00
parent 1507e878dc
commit 1d9cd4adcf
23 changed files with 2602 additions and 278 deletions

View File

@@ -1,3 +1,4 @@
export POSTGRES_USER= DISCORD_TOKEN=
export POSTGRES_PASSWORD= POSTGRES_USER=
export POSTGRES_DB= POSTGRES_PASSWORD=
POSTGRES_DB=

14
.gitignore vendored
View File

@@ -1,8 +1,8 @@
.idea/
**/target/
**/data/
**/app/
**/settings.json
**/logs/
**/audio/
.env .env
target/
.idea/
.vscode/
audio/
logs/
settings.json

View File

@@ -1 +1 @@
export SIREN_VERSION=0.1.24 export SIREN_VERSION=0.2.0

2414
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

18
Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[package]
name = "siren"
version = "0.2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dotenv = "0.15.0"
serenity = "0.11"
tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
reqwest = { version = "0.11.18", features = ["json"] }
async-recursion = "1.0.4"
log = "0.4.19"
songbird = "0.3.2"
env_logger = "0.10.0"

View File

@@ -1,10 +1,10 @@
FROM amazoncorretto:17 FROM rust:1.67 as builder
RUN apt-get update && apt-get install -y cmake && rm -rf /var/lib/apt/lists/*
WORKDIR /usr/src/siren
COPY . .
RUN cargo install --path .
ARG VERSION FROM debian:bullseye-slim
RUN apt-get update && apt-get install -y libopus-dev ffmpeg youtube-dl && rm -rf /var/lib/apt/lists/*
WORKDIR /app COPY --from=builder /usr/local/cargo/bin/siren /usr/local/bin/siren
CMD ["siren"]
ADD https://repo.local.bensherriff.com/artifactory/libs-release/com/bensherriff/siren/${VERSION}/siren-${VERSION}.jar /usr/local/lib/
RUN mv /usr/local/lib/siren-${VERSION}.jar /usr/local/lib/siren.jar
ENTRYPOINT ["java", "-jar", "/usr/local/lib/siren.jar"]

View File

@@ -3,10 +3,12 @@ include .version
include .env include .env
build: build:
if docker inspect siren > /dev/null 2>&1; then docker rmi siren; fi; docker-compose build # if docker inspect siren > /dev/null 2>&1; then docker rmi siren; fi; docker-compose build
docker build -t siren .
test: test:
docker run --rm -it siren:latest bash # docker run --rm -it siren:latest bash
docker run --env-file .env -it --rm --name siren siren:latest
up: up:
if [[ ! $$(docker images -q siren 2> /dev/null) ]]; then docker-compose build; fi; \ if [[ ! $$(docker images -q siren 2> /dev/null) ]]; then docker-compose build; fi; \

View File

@@ -1,72 +1,24 @@
<img src="siren.png" alt="drawing" width="200"/>
# Siren # Siren
A Java/Docker Discord music bot A D&D Bot built for Discord. Includes music support and assistant DM tools.
## Running ## Running
Visit the [Discord Developer Portal](https://discord.com/developers/applications) and create a new application. Visit the [Discord Developer Portal](https://discord.com/developers/applications) and create a new application.
Guides and more information are available [here](https://discord.com/developers/docs/intro). Guides and more information are available [here](https://discord.com/developers/docs/intro).
### OAuth2 URL Generator ## Contributing
The bot requires the following permissions/scopes: Rust must be installed to run locally.
- bot
- applications.commands
Furthermore, the following packages must be installed for [serenity-rs/songbird](https://github.com/serenity-rs/songbird)
``` ```
https://discord.com/api/oauth2/authorize?client_id=<Client ID>&permissions=1088840792896&scope=applications.commands%20bot sudo apt install libopus-dev
https://discord.com/api/oauth2/authorize?client_id=<Client ID>&permissions=5469678065984&scope=applications.commands%20bot - bot sudo apt install ffmpeg
- text permissions sudo apt apt install youtube-dl
- send messages
- create public threads
- create private threads
- send messages in threads
- send tts messages
- embed links
- attach files
- read message history
- mention everyone
- use external emojis
- use external stickers
- add reactions
- use slash commands
- voice permissions
- connect
- speak
- use voice activity
- priority speaker
- request to speak
- use embedded activities
- use soundboard
- applications.commands
``` ```
The application can instead be tested from within a docker container.
``` ```
make build docker build -t siren .
make up docker run --env-file .env -it --rm --name siren siren
``` ```
## Development
Build container
`make build`
Run container locally
```
docker container run --name siren_test -d -t siren bash
docker exec -it siren_test bash
```
## Commands
### Play
### Skip
### Stop
### Volume
### Pause
### Resume
### Settings
View settings on the current guild.

View File

@@ -12,6 +12,7 @@ services:
volumes: volumes:
- ./app:/app - ./app:/app
environment: environment:
DISCORD_TOKEN: ${DISCORD_TOKEN}
DATABASE_URL: jdbc:postgresql://db:5432/${POSTGRES_DB} DATABASE_URL: jdbc:postgresql://db:5432/${POSTGRES_DB}
DATABASE_USERNAME: ${POSTGRES_USER} DATABASE_USERNAME: ${POSTGRES_USER}
DATABASE_PASSWORD: ${POSTGRES_PASSWORD} DATABASE_PASSWORD: ${POSTGRES_PASSWORD}

181
pom.xml
View File

@@ -1,181 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bensherriff</groupId>
<artifactId>siren</artifactId>
<version>${env.SIREN_VERSION}</version>
<packaging>jar</packaging>
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>libs-release</name>
<url>https://repo.local.bensherriff.com/artifactory/libs-release</url>
</repository>
<repository>
<snapshots />
<id>snapshots</id>
<name>libs-snapshot</name>
<url>https://repo.local.bensherriff.com/artifactory/libs-snapshot</url>
</repository>
<repository>
<id>dv8tion</id>
<name>m2-dv8tion</name>
<url>https://m2.dv8tion.net/releases</url>
</repository>
<repository>
<id>jitpack</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>maven_central</id>
<name>Maven Central</name>
<url>https://repo.maven.apache.org/maven2/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>libs-release</name>
<url>https://repo.local.bensherriff.com/artifactory/libs-release</url>
</pluginRepository>
<pluginRepository>
<snapshots />
<id>snapshots</id>
<name>libs-snapshot</name>
<url>https://repo.local.bensherriff.com/artifactory/libs-snapshot</url>
</pluginRepository>
</pluginRepositories>
<distributionManagement>
<repository>
<id>central</id>
<name>ffc58a58b429-releases</name>
<url>https://repo.local.bensherriff.com/artifactory/libs-release</url>
</repository>
</distributionManagement>
<properties>
<jda.version>5.0.0-beta.8</jda.version>
<lavaplayer.version>1.4.0</lavaplayer.version>
<lavaplayer-natives-extra.version>1.3.13</lavaplayer-natives-extra.version>
<jackson.version>2.14.2</jackson.version>
<theokanning-openai-gpt3.version>0.12.0</theokanning-openai-gpt3.version>
<postgresql.version>42.6.0</postgresql.version>
<corenlp.version>4.2.0</corenlp.version>
<slf4j.version>2.0.7</slf4j.version>
<log4j.version>2.20.0</log4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>${jda.version}</version>
</dependency>
<dependency>
<groupId>com.github.walkyst</groupId>
<artifactId>lavaplayer-fork</artifactId>
<version>${lavaplayer.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.theokanning.openai-gpt3-java</groupId>
<artifactId>service</artifactId>
<version>${theokanning-openai-gpt3.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<dependency>
<groupId>edu.stanford.nlp</groupId>
<artifactId>stanford-corenlp</artifactId>
<version>${corenlp.version}</version>
</dependency>
<dependency>
<groupId>edu.stanford.nlp</groupId>
<artifactId>stanford-corenlp</artifactId>
<version>${corenlp.version}</version>
<classifier>models</classifier>
<scope>runtime</scope>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<!-- <shadedArtifactAttached>true</shadedArtifactAttached>-->
<!-- <shadedClassifierName>All</shadedClassifierName>-->
<artifactSet>
<includes>
<include>*:*</include>
</includes>
</artifactSet>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>reference.conf</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.bensherriff.siren.Main</Main-Class>
<Specification-Title>${project.artifactId}</Specification-Title>
<Specification-Version>${project.version}</Specification-Version>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Implementation-Vendor-Id>${project.groupId}</Implementation-Vendor-Id>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

BIN
siren.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

View File

@@ -0,0 +1,6 @@
pub mod pause;
pub mod play;
pub mod resume;
pub mod skip;
pub mod stop;
pub mod volume;

View File

View File

@@ -0,0 +1,18 @@
use log::debug;
use serenity::{model::prelude::{interaction::application_command::CommandDataOption, application_command::CommandDataOptionValue}, builder::CreateApplicationCommand};
use serenity::model::application::interaction::application_command::ApplicationCommandInteraction;
pub async fn run(command: &ApplicationCommandInteraction) -> String {
let track_option: &CommandDataOptionValue = command.data.options.get(0).expect("Expected track option").resolved.as_ref().expect("Expected track option to be resolved");
debug!("Play command executed with track: {:?}", track_option);
"Playing xyz".to_string()
}
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
command.name("play").description("Plays the given track").create_option(|option| { option
.name("track")
.description("The track to be played")
.kind(serenity::model::prelude::command::CommandOptionType::String)
.required(true)
})
}

View File

View File

View File

View File

0
src/commands/help.rs Normal file
View File

2
src/commands/mod.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod ping;
pub mod audio;

11
src/commands/ping.rs Normal file
View File

@@ -0,0 +1,11 @@
use log::debug;
use serenity::{model::prelude::interaction::application_command::CommandDataOption, builder::CreateApplicationCommand};
pub fn run(_options: &[CommandDataOption]) -> String {
debug!("Ping command executed");
"pong".to_string()
}
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
command.name("ping").description("Replies with pong")
}

94
src/main.rs Normal file
View File

@@ -0,0 +1,94 @@
use std::collections::HashSet;
use std::env;
use dotenv::dotenv;
use log::{error, warn, info};
use serenity::async_trait;
use serenity::framework::StandardFramework;
use serenity::model::application::interaction::{Interaction, InteractionResponseType};
use serenity::model::gateway::Ready;
use serenity::http::Http;
use serenity::model::prelude::GuildId;
use serenity::prelude::*;
mod commands;
struct Handler;
#[async_trait]
impl EventHandler for Handler {
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
if let Interaction::ApplicationCommand(command) = interaction {
let content: String = match command.data.name.as_str() {
"ping" => commands::ping::run(&command.data.options),
"play" => commands::audio::play::run(&command).await,
_ => "Unknown command".to_string()
};
if let Err(why) = command.create_interaction_response(&ctx.http, |response: &mut serenity::builder::CreateInteractionResponse<'_>| {
response
.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message: &mut serenity::builder::CreateInteractionResponseData<'_>| message.content(content))
}).await {
warn!("Cannot respond to slash command: {}", why);
}
}
}
async fn ready(&self, ctx: Context, ready: Ready) {
for guild in ready.guilds {
if let Some(guild) = guild.id.to_guild_cached(&ctx.cache) {
info!("{} is connected to {}", ready.user.name, guild.name);
let commands: Result<Vec<serenity::model::prelude::command::Command>, SerenityError> = GuildId::set_application_commands(&guild.id, &ctx.http, |commands| {
commands.create_application_command(|command: &mut serenity::builder::CreateApplicationCommand| { commands::ping::register(command) })
.create_application_command(|command: &mut serenity::builder::CreateApplicationCommand| { commands::audio::play::register(command) })
}).await;
match commands {
Ok(commands) => info!("Registered {} commands", commands.len()),
Err(why) => error!("Could not register commands: {:?}", why)
}
}
}
}
}
#[tokio::main]
async fn main() {
env_logger::init();
dotenv().ok();
let token: String = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
let intents: GatewayIntents = GatewayIntents::all();
let http: Http = Http::new(&token);
let (owners, _bot_id) = match http.get_current_application_info().await {
Ok(info) => {
let mut owners: HashSet<serenity::model::id::UserId> = HashSet::new();
if let Some(team) = info.team {
owners.insert(team.owner_user_id);
} else {
owners.insert(info.owner.id);
}
match http.get_current_user().await {
Ok(bot) => (owners, bot.id),
Err(why) => panic!("Could not access the bot id: {:?}", why)
}
},
Err(why) => panic!("Could not access application info: {:?}", why)
};
let framework = StandardFramework::new()
.configure(|c| c
.owners(owners)
.prefix("!")
);
let mut client = Client::builder(token, intents)
.event_handler(Handler)
.framework(framework)
.await
.expect("Error creating client");
if let Err(why) = client.start_autosharded().await {
error!("An error occurred while running the client: {:?}", why);
}
}

View File

@@ -1,14 +0,0 @@
#/bin/bash
#if [ -f /config.txt ]; then
# echo "configuration file found"
# mv /config.txt .
#fi
#
#if [ ! -f JMusicBot-${VERSION}.jar ]; then
# wget https://github.com/jagrosh/MusicBot/releases/download/${VERSION}/JMusicBot-${VERSION}.jar
#fi
#
#java -Dnogui=true -jar JMusicBot-${VERSION}.jar
java -jar /usr/local/lib/siren.jar