## Current feature set
- minecraft information
- playing music from youtube
+- miscellaneous commands
+
+## System Dependencies
+
+The bot depends on a few programs to be installed on the system.
+
+### Data Storage
+
+- [postgresql](https://www.postgresql.org/)
+
+
+### Music
+
+- [FFmpeg](https://github.com/FFmpeg/FFmpeg)
+- [youtube-dl](https://github.com/ytdl-org/youtube-dl)
+
+
+### Misc Commands
+
+- [qalculate](https://github.com/Qalculate/libqalculate)
+
+
+## API Dependencies
+
+The bot depends on the following APIs
+
+- [Discord](https://discord.com/developers/applications): It's a discord bot...
+- [Spotify](https://developer.spotify.com/documentation/web-api/): To fetch song names to be searched on youtube for music playback
+- [lyrics.ohv](https://lyricsovh.docs.apiary.io): To fetch lyrics for playing songs
+
+
+## Dev Dependencies
+
+- Rust
+- Other stuff that you have to figure out yourself because it just works for me
+
+
+## Configuration
+
+The bot reads all required configuration values from the environment or optionally from a .env file.
+The required values are:
+- `BOT_TOKEN` (required): Discord bot token
+- `BOT_OWNER` (required): Discord UserID of the bot owner
+- `DATABASE_URL` (required): Connection uri to the postgres database in the schema `postgres://myuser:mypassword@localhost:5432/database`
+- `SPOTIFY_CLIENT_ID` (required): Spotify API Client ID
+- `SPOTIFY_CLIENT_SECRET` (required): Spotify API Client Secret
+- `BOT_PREFIX` (optional): The prefix of the bot. Defaults to `~` if not set.
+- `LOG_DIR` (optional): Directory to store log files in. Defaults to `logs` in the cwd.
## License
diff --git a/src/commands/misc/mod.rs b/src/commands/misc/mod.rs
index 2c1a64f..03b48eb 100644
--- a/src/commands/misc/mod.rs
+++ b/src/commands/misc/mod.rs
@@ -2,6 +2,7 @@ use serenity::framework::standard::macros::group;
use pekofy::PEKOFY_COMMAND;
use ping::PING_COMMAND;
+use qalc::QALC_COMMAND;
use shutdown::SHUTDOWN_COMMAND;
use stats::STATS_COMMAND;
use time::TIME_COMMAND;
@@ -10,11 +11,12 @@ use timezones::TIMEZONES_COMMAND;
pub(crate) mod help;
mod pekofy;
mod ping;
+mod qalc;
mod shutdown;
mod stats;
mod time;
mod timezones;
#[group]
-#[commands(ping, stats, shutdown, pekofy, time, timezones)]
+#[commands(ping, stats, shutdown, pekofy, time, timezones, qalc)]
pub struct Misc;
diff --git a/src/commands/misc/qalc.rs b/src/commands/misc/qalc.rs
new file mode 100644
index 0000000..e682764
--- /dev/null
+++ b/src/commands/misc/qalc.rs
@@ -0,0 +1,18 @@
+use crate::providers::qalc;
+use serenity::client::Context;
+use serenity::framework::standard::macros::command;
+use serenity::framework::standard::{Args, CommandResult};
+use serenity::model::channel::Message;
+
+#[command]
+#[description("Calculates an expression")]
+#[min_args(1)]
+#[usage("")]
+#[example("1 * 1 + 1 / sqrt(2)")]
+async fn qalc(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
+ let expression = args.message();
+ let result = qalc::qalc(expression).await?;
+ msg.reply(ctx, format!("`{}`", result)).await?;
+
+ Ok(())
+}
diff --git a/src/commands/music/mod.rs b/src/commands/music/mod.rs
index cd09ad4..bcf1462 100644
--- a/src/commands/music/mod.rs
+++ b/src/commands/music/mod.rs
@@ -1,9 +1,7 @@
use std::sync::Arc;
use crate::providers::music::queue::{MusicQueue, Song};
-use crate::providers::music::{
- get_video_information, get_videos_for_playlist, search_video_information,
-};
+use crate::providers::music::youtube_dl;
use crate::utils::context_data::{DatabaseContainer, Store};
use crate::utils::error::{BotError, BotResult};
use regex::Regex;
@@ -299,7 +297,7 @@ async fn get_songs_for_query(ctx: &Context, msg: &Message, query: &str) -> BotRe
if YOUTUBE_URL_REGEX.is_match(&query) {
log::debug!("Query is youtube video or playlist");
// try fetching the url as a playlist
- songs = get_videos_for_playlist(&query)
+ songs = youtube_dl::get_videos_for_playlist(&query)
.await?
.into_iter()
.map(Song::from)
@@ -308,7 +306,7 @@ async fn get_songs_for_query(ctx: &Context, msg: &Message, query: &str) -> BotRe
// if no songs were found fetch the song as a video
if songs.len() == 0 {
log::debug!("Query is youtube video");
- let mut song: Song = get_video_information(&query).await?.into();
+ let mut song: Song = youtube_dl::get_video_information(&query).await?.into();
added_one_msg(&ctx, msg, &mut song).await?;
songs.push(song);
} else {
@@ -333,7 +331,7 @@ async fn get_songs_for_query(ctx: &Context, msg: &Message, query: &str) -> BotRe
songs.push(song);
} else {
log::debug!("Query is a youtube search");
- let mut song: Song = search_video_information(query.clone())
+ let mut song: Song = youtube_dl::search_video_information(query.clone())
.await?
.ok_or(BotError::Msg(format!("Noting found for {}", query)))?
.into();
diff --git a/src/providers/mod.rs b/src/providers/mod.rs
index 26428cb..b26a4ed 100644
--- a/src/providers/mod.rs
+++ b/src/providers/mod.rs
@@ -1,2 +1,3 @@
pub(crate) mod music;
+pub(crate) mod qalc;
pub(crate) mod settings;
diff --git a/src/providers/music/lyrics.rs b/src/providers/music/lyrics.rs
index 4d9d306..5359d20 100644
--- a/src/providers/music/lyrics.rs
+++ b/src/providers/music/lyrics.rs
@@ -4,6 +4,7 @@ use serde_derive::Deserialize;
const API_ENDPOINT: &str = "https://api.lyrics.ovh/v1/";
+/// Returns the lyrics of a song
pub async fn get_lyrics(artist: &str, title: &str) -> BotResult