11 Commits

25 changed files with 1656 additions and 556 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,3 @@
/target /target
**/target
.env .env

15
.gitmodules vendored
View File

@@ -1,3 +1,12 @@
[submodule "src/spotify-parser"] [submodule "libs/songbird"]
path = src/spotify-parser path = libs/songbird
url = https://github.com/eRgo35/spotify-parser url = git@github.com:eRgo35/songbird.git
[submodule "libs/serenity"]
path = libs/serenity
url = git@github.com:eRgo35/serenity.git
[submodule "libs/poise"]
path = libs/poise
url = git@github.com:eRgo35/poise.git
[submodule "libs/spotify-parser"]
path = libs/spotify-parser
url = git@github.com:eRgo35/spotify-parser.git

1840
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "lyra" name = "lyra"
version = "0.8.0" version = "0.9.1"
authors = ["Michał Czyż <mike@c2yz.com>"] authors = ["Michał Czyż <mike@c2yz.com>"]
edition = "2021" edition = "2021"
description = "A featureful Discord bot written in Rust." description = "A featureful Discord bot written in Rust."
@@ -10,24 +10,46 @@ homepage = "https://lyra.c2yz.com"
license-file = "LICENSE.md" license-file = "LICENSE.md"
keywords = ["discord", "bot", "rust", "music", "featureful"] keywords = ["discord", "bot", "rust", "music", "featureful"]
[dependencies] [dependencies]
lib-spotify-parser = { path = "./libs/spotify-parser" }
dotenv = "0.15.0" dotenv = "0.15.0"
fancy-regex = "0.13.0" fancy-regex = "0.13.0"
json = "0.12.4" json = "0.12.4"
openssl = { version = "0.10.63", features = ["vendored"] } openssl = { version = "0.10.66", features = ["vendored"] }
owoify = "0.1.5" owoify = "0.1.5"
poise = "0.6.1" poise = { default-features = true, version = "0.6.1" }
rand = "0.8.5" rand = "0.8.5"
regex = "1.10.3" regex = "1.10.6"
reqwest = { version = "0.11.23", features = ["json"]} reqwest = { version = "0.11.27", features = ["json"] }
serde = { version = "1.0.197", features = ["derive"] } serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.114" serde_json = "1.0.122"
serenity = { version = "0.12.0", features = ["cache", "framework", "standard_framework", "voice"] } songbird = { version = "0.4.3", default-features = true, features = [
songbird = { version = "0.4.0", features = ["builtin-queue", "serenity"] } "builtin-queue",
symphonia = { version = "0.5.3", features = ["aac", "adpcm", "alac", "flac", "mpa", "isomp4"] } ] }
tokio = { version = "1.35.1", features = ["macros", "full", "signal"] } serenity = { default-features = true, features = [
"cache",
"framework",
"standard_framework",
"voice",
"http",
"rustls_backend",
], version = "0.12" }
symphonia = { version = "0.5.4", features = [
"aac",
"adpcm",
"alac",
"flac",
"mpa",
"isomp4",
] }
tokio = { version = "1.39.2", features = ["macros", "full", "signal"] }
tracing = "0.1.40" tracing = "0.1.40"
tracing-futures = "0.2.5" tracing-futures = "0.2.5"
tracing-subscriber = "0.3.18" tracing-subscriber = "0.3.18"
url = "2.5.0" url = "2.5.2"
once_cell = "1.19.0"
[patch.crates-io.serenity-voice-model]
git = "https://github.com/serenity-rs/serenity"
branch = "current"

View File

@@ -1,19 +0,0 @@
FROM rust:1.75.0-alpine
RUN apk add --update \
alpine-sdk \
ffmpeg \
youtube-dl \
pkgconfig \
cmake \
openssl-dev \
musl-dev \
openssl
WORKDIR /app
COPY . .
RUN cargo build --release
CMD ["./target/release/lyra"]

View File

@@ -1,7 +1,21 @@
# License MIT License
Copyright (C) 2024 Michał Czyż Copyright (c) 2024 Michał Czyż
All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
This build is private and shall not be distributed nor modified without permission. The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -3,10 +3,79 @@
![](assets/lyra-256.png) ![](assets/lyra-256.png)
Lyra is a music bot written in Rust. Lyra is a music bot written in Rust.
More features coming soon!
## Getting Started ## Getting Started
## Building Lyra is an open source, discord music bot written in Rust.
The idea behind this project is to allow a user to self-host one's own instance of the bot.
User no longer has to rely on 3rd parties to provide them an invite link.
The bot can be run even on a desktop or a phone because after compilation, it's just a simple binary.
As of now, the bot supports spotify url track recognition through a separate nodejs script. I plan to write the actual parser inside the bot iteself but as of now I postponed it into future release.
Slash commands are still work in progress! Currently bot is still heavily in development!
## Setting up
To compile the source code on your own, you need `rust` and `cargo`
To run a dev version use
```bash
$ cargo run
```
To build a production version use
```bash
$ cargo build --release
```
If you need an ARM version and just don't want to wait for ages for the program to compile, use
```bash
$ cross build -r --target aarch64-unknown-linux-gnu
```
To run a program, just type
```bash
$ ./lyra
```
if you want to disown it from the shell, I recommend using the script I provided in `scripts` folder
## Commands ## Commands
As of now, working commands are:
```
Music:
/deafen Deafens itself while in a voice channel; aliases: deafen, undeaden, shuush
/join Joins your voice channel
/leave Leaves the voice channel; aliases: leave, qa!
/mute Mutes itself while in a voice channel; aliases: mute, unmute, shhh
/pause Pauses the currently playing song
/play Plays a song; you can search by query or paste an url; aliases: play, p, enqueue
/queue Shows next tracks in queue; aliases: queue, q
/repeat Loops currently playing song provided amount of times; aliases: repeat, loop, while, for
/resume Resumes currently paused song
/seek Seeks a track by provided seconds
/skip Skips the currently playing song
/stop Stops playback and destroys the queue; aliases: stop, end
/volume Changes output volume
/effect Plays one of available audio effects
/stream Hijacks output and plays audio; search by query or paste an url; aliases: stream, override, hijack
Tools:
/ai Asks AI
/dice Rolls a dice
/owoify Owoifies whatever you want uwu
/ping Pings you backs with a response time
/posix Prints current time in POSIX format
/qr Creates a qr code from text
/verse Reference Bible by verse
Help:
/help Prints this help message; aliases: help, huh, welp
```

BIN
assets/lyra-nightly.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

View File

@@ -1,6 +0,0 @@
version: '2'
services:
lyra:
container_name: lyra
build: .

1
libs/poise Submodule

Submodule libs/poise added at 575025909b

1
libs/serenity Submodule

Submodule libs/serenity added at 658b6a7261

1
libs/songbird Submodule

Submodule libs/songbird added at 2d7dc29fd6

1
libs/spotify-parser Submodule

Submodule libs/spotify-parser added at 4398512b5c

View File

@@ -1,11 +1,9 @@
use crate::{Context, Error}; use crate::{Context, Error};
use poise::serenity_prelude::CreateEmbed; use poise::serenity_prelude::{
use poise::CreateReply; Color, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Timestamp,
use serenity::{
builder::{CreateEmbedAuthor, CreateEmbedFooter},
model::{Colour, Timestamp},
}; };
use poise::CreateReply;
pub async fn fail(ctx: Context<'_>, err: String) -> Result<(), Error> { pub async fn fail(ctx: Context<'_>, err: String) -> Result<(), Error> {
ctx.send( ctx.send(
@@ -25,7 +23,7 @@ pub async fn error_embed(ctx: Context<'_>, msg: &str) -> Result<CreateEmbed, Err
.author( .author(
CreateEmbedAuthor::new("Something went wrong!").icon_url(ctx.author().clone().face()), CreateEmbedAuthor::new("Something went wrong!").icon_url(ctx.author().clone().face()),
) )
.colour(Colour::from_rgb(255, 58, 97)) .colour(Color::from_rgb(255, 58, 97))
.title("Oopsie, Doopsie!") .title("Oopsie, Doopsie!")
.description(msg) .description(msg)
.timestamp(Timestamp::now()) .timestamp(Timestamp::now())
@@ -45,7 +43,7 @@ pub async fn embed(
) -> Result<CreateEmbed, Error> { ) -> Result<CreateEmbed, Error> {
let embed = CreateEmbed::default() let embed = CreateEmbed::default()
.author(CreateEmbedAuthor::new(author).icon_url(ctx.author().clone().face())) .author(CreateEmbedAuthor::new(author).icon_url(ctx.author().clone().face()))
.colour(Colour::from_rgb(255, 58, 97)) .colour(Color::from_rgb(255, 58, 97))
.title(title) .title(title)
.description(description) .description(description)
.timestamp(Timestamp::now()) .timestamp(Timestamp::now())

View File

@@ -2,13 +2,12 @@ use crate::commands::music::metadata::Metadata;
use crate::{commands::embeds::error_embed, Context, Error}; use crate::{commands::embeds::error_embed, Context, Error};
use fancy_regex::Regex; use fancy_regex::Regex;
use poise::serenity_prelude::model::Timestamp; use lib_spotify_parser;
use poise::serenity_prelude::Colour; use poise::serenity_prelude::{
use poise::serenity_prelude::CreateEmbed; Color, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Timestamp,
};
use poise::CreateReply; use poise::CreateReply;
use regex::Regex as Regex_Classic; use regex::Regex as Regex_Classic;
use serenity::builder::CreateEmbedAuthor;
use serenity::builder::CreateEmbedFooter;
use songbird::events::TrackEvent; use songbird::events::TrackEvent;
use songbird::input::AuxMetadata; use songbird::input::AuxMetadata;
use songbird::input::{Compose, YoutubeDl}; use songbird::input::{Compose, YoutubeDl};
@@ -87,14 +86,7 @@ pub async fn play(
handler.add_global_event(TrackEvent::Error.into(), TrackErrorNotifier); handler.add_global_event(TrackEvent::Error.into(), TrackErrorNotifier);
if is_playlist && is_spotify { if is_playlist && is_spotify {
let raw_list = Command::new("node") let tracks: Vec<String> = lib_spotify_parser::retrieve_async_url(&song).await.unwrap();
.args(["./src/spotify-parser", &song])
.output()
.expect("failed to execute process")
.stdout;
let list = String::from_utf8(raw_list.clone()).expect("Invalid UTF-8");
let tracks: Vec<String> = list.split("\n").map(str::to_string).collect();
for (index, url) in tracks.clone().iter().enumerate() { for (index, url) in tracks.clone().iter().enumerate() {
if url.is_empty() { if url.is_empty() {
@@ -161,13 +153,14 @@ pub async fn play(
} }
if is_spotify { if is_spotify {
let query = Command::new("node") song = format!(
.args(["./src/spotify-parser", &song]) "ytsearch:{}",
.output() lib_spotify_parser::retrieve_async_url(&song)
.expect("failed to execute process") .await
.stdout; .unwrap()
let query_str = String::from_utf8(query.clone()).expect("Invalid UTF-8"); .first()
song = format!("ytsearch:{}", query_str.to_string()); .unwrap()
);
} }
if is_query { if is_query {
@@ -217,7 +210,7 @@ async fn generate_embed(
let embed = CreateEmbed::default() let embed = CreateEmbed::default()
.author(CreateEmbedAuthor::new("Track enqueued").icon_url(ctx.author().clone().face())) .author(CreateEmbedAuthor::new("Track enqueued").icon_url(ctx.author().clone().face()))
.colour(Colour::from_rgb(255, 58, 97)) .colour(Color::from_rgb(255, 58, 97))
.title(title.unwrap()) .title(title.unwrap())
.url(source_url.unwrap()) .url(source_url.unwrap())
.thumbnail(thumbnail.unwrap_or(ctx.cache().current_user().face())) .thumbnail(thumbnail.unwrap_or(ctx.cache().current_user().face()))
@@ -265,7 +258,7 @@ async fn generate_playlist_embed(
let embed = CreateEmbed::default() let embed = CreateEmbed::default()
.author(CreateEmbedAuthor::new("Playlist enqueued").icon_url(ctx.author().clone().face())) .author(CreateEmbedAuthor::new("Playlist enqueued").icon_url(ctx.author().clone().face()))
.colour(Colour::from_rgb(255, 58, 97)) .colour(Color::from_rgb(255, 58, 97))
.title(title.as_ref().unwrap()) .title(title.as_ref().unwrap())
.url(source_url.as_ref().unwrap()) .url(source_url.as_ref().unwrap())
.thumbnail( .thumbnail(

View File

@@ -2,12 +2,10 @@ use std::time::Duration;
use crate::commands::music::metadata::Metadata; use crate::commands::music::metadata::Metadata;
use crate::{commands::embeds::error_embed, Context, Error}; use crate::{commands::embeds::error_embed, Context, Error};
use poise::serenity_prelude::CreateEmbed; use poise::serenity_prelude::{
use poise::CreateReply; Color, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Timestamp,
use serenity::{
builder::{CreateEmbedAuthor, CreateEmbedFooter},
model::{Colour, Timestamp},
}; };
use poise::CreateReply;
use songbird::input::AuxMetadata; use songbird::input::AuxMetadata;
const QUEUE_DISPLAY_LENGTH: usize = 10; const QUEUE_DISPLAY_LENGTH: usize = 10;
@@ -80,7 +78,7 @@ async fn embed(ctx: Context<'_>, queue: String) -> Result<CreateEmbed, Error> {
let embed = CreateEmbed::default() let embed = CreateEmbed::default()
.author(CreateEmbedAuthor::new("Queue").icon_url(ctx.author().clone().face())) .author(CreateEmbedAuthor::new("Queue").icon_url(ctx.author().clone().face()))
.colour(Colour::from_rgb(255, 58, 97)) .colour(Color::from_rgb(255, 58, 97))
.title(title) .title(title)
.description(queue) .description(queue)
.timestamp(timestamp) .timestamp(timestamp)

View File

@@ -5,15 +5,15 @@ use crate::{
commands::embeds::{embed, error_embed}, commands::embeds::{embed, error_embed},
Context, Error, Context, Error,
}; };
use poise::CreateReply; use poise::serenity_prelude::{
use serenity::{ Color, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Timestamp,
builder::{CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter},
model::{Colour, Timestamp},
}; };
use poise::CreateReply;
use songbird::{input::AuxMetadata, tracks::TrackHandle}; use songbird::{input::AuxMetadata, tracks::TrackHandle};
/// Skips the currently playing song /// Skips the currently playing song; \
#[poise::command(prefix_command, slash_command, category = "Music")] /// aliases: skip, :skipper:
#[poise::command(prefix_command, slash_command, aliases("skipper:"), category = "Music")]
pub async fn skip(ctx: Context<'_>) -> Result<(), Error> { pub async fn skip(ctx: Context<'_>) -> Result<(), Error> {
let guild_id = ctx.guild_id().unwrap(); let guild_id = ctx.guild_id().unwrap();
@@ -82,7 +82,7 @@ async fn generate_embed(
let embed = CreateEmbed::default() let embed = CreateEmbed::default()
.author(CreateEmbedAuthor::new("Skipped!").icon_url(ctx.author().clone().face())) .author(CreateEmbedAuthor::new("Skipped!").icon_url(ctx.author().clone().face()))
.colour(Colour::from_rgb(255, 58, 97)) .colour(Color::from_rgb(255, 58, 97))
.title(title.as_ref().unwrap()) .title(title.as_ref().unwrap())
.url(source_url.as_ref().unwrap()) .url(source_url.as_ref().unwrap())
.thumbnail( .thumbnail(

View File

@@ -1,11 +1,9 @@
use crate::{commands::embeds::error_embed, Context, Error}; use crate::{commands::embeds::error_embed, Context, Error};
use poise::serenity_prelude::model::Timestamp; use poise::serenity_prelude::{
use poise::serenity_prelude::Colour; Color, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Timestamp,
use poise::serenity_prelude::CreateEmbed; };
use poise::CreateReply; use poise::CreateReply;
use serenity::builder::CreateEmbedAuthor;
use serenity::builder::CreateEmbedFooter;
use songbird::events::TrackEvent; use songbird::events::TrackEvent;
use songbird::input::AuxMetadata; use songbird::input::AuxMetadata;
use songbird::input::{Compose, YoutubeDl}; use songbird::input::{Compose, YoutubeDl};
@@ -91,7 +89,7 @@ async fn generate_embed(ctx: Context<'_>, src: YoutubeDl) -> Result<CreateEmbed,
let embed = CreateEmbed::default() let embed = CreateEmbed::default()
.author(CreateEmbedAuthor::new("Playing an effect!").icon_url(ctx.author().clone().face())) .author(CreateEmbedAuthor::new("Playing an effect!").icon_url(ctx.author().clone().face()))
.colour(Colour::from_rgb(255, 58, 97)) .colour(Color::from_rgb(255, 58, 97))
.title(title.unwrap()) .title(title.unwrap())
.url(source_url.unwrap()) .url(source_url.unwrap())
.thumbnail(thumbnail.unwrap_or(ctx.cache().current_user().face())) .thumbnail(thumbnail.unwrap_or(ctx.cache().current_user().face()))

View File

@@ -1,11 +1,9 @@
use crate::{commands::embeds::error_embed, Context, Error}; use crate::{commands::embeds::error_embed, Context, Error};
use poise::serenity_prelude::model::Timestamp; use poise::serenity_prelude::{
use poise::serenity_prelude::Colour; Color, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Timestamp,
use poise::serenity_prelude::CreateEmbed; };
use poise::CreateReply; use poise::CreateReply;
use serenity::builder::CreateEmbedAuthor;
use serenity::builder::CreateEmbedFooter;
use songbird::events::TrackEvent; use songbird::events::TrackEvent;
use songbird::input::AuxMetadata; use songbird::input::AuxMetadata;
use songbird::input::{Compose, YoutubeDl}; use songbird::input::{Compose, YoutubeDl};
@@ -100,7 +98,7 @@ async fn generate_embed(ctx: Context<'_>, src: YoutubeDl) -> Result<CreateEmbed,
.author( .author(
CreateEmbedAuthor::new("Audio output hijacked!").icon_url(ctx.author().clone().face()), CreateEmbedAuthor::new("Audio output hijacked!").icon_url(ctx.author().clone().face()),
) )
.colour(Colour::from_rgb(255, 58, 97)) .colour(Color::from_rgb(255, 58, 97))
.title(title.unwrap()) .title(title.unwrap())
.url(source_url.unwrap()) .url(source_url.unwrap())
.thumbnail(thumbnail.unwrap_or(ctx.cache().current_user().face())) .thumbnail(thumbnail.unwrap_or(ctx.cache().current_user().face()))

View File

@@ -10,7 +10,7 @@ pub mod posix;
pub mod qr; pub mod qr;
pub mod register; pub mod register;
pub mod taf; pub mod taf;
// pub mod uptime; pub mod uptime;
pub mod verse; pub mod verse;
pub mod weather; pub mod weather;
@@ -26,6 +26,6 @@ pub use posix::posix;
pub use qr::qr; pub use qr::qr;
pub use register::register; pub use register::register;
pub use taf::taf; pub use taf::taf;
// pub use uptime::uptime; pub use uptime::uptime;
pub use verse::verse; pub use verse::verse;
pub use weather::weather; pub use weather::weather;

View File

@@ -35,7 +35,7 @@ pub async fn ai(
response = rng.gen_range(0..iamsorry.len()); response = rng.gen_range(0..iamsorry.len());
}; };
sleep(Duration::from_secs(3)); sleep(Duration::from_secs(1));
ctx.send( ctx.send(
CreateReply::default().embed( CreateReply::default().embed(

View File

@@ -1,8 +1,7 @@
use poise::CreateReply; use poise::serenity_prelude::{
use serenity::{ Color, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Timestamp,
builder::{CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter},
model::{Colour, Timestamp},
}; };
use poise::CreateReply;
use crate::{Context, Error}; use crate::{Context, Error};
use url::form_urlencoded; use url::form_urlencoded;
@@ -34,7 +33,7 @@ async fn generate_embed(ctx: Context<'_>, message: String) -> Result<CreateEmbed
CreateEmbedAuthor::new("Your message as a QR Code!") CreateEmbedAuthor::new("Your message as a QR Code!")
.icon_url(ctx.author().clone().face()), .icon_url(ctx.author().clone().face()),
) )
.colour(Colour::from_rgb(255, 58, 97)) .colour(Color::from_rgb(255, 58, 97))
.title("Your QR Code:") .title("Your QR Code:")
.url(url.clone()) .url(url.clone())
.image(url) .image(url)

View File

@@ -1,35 +1,48 @@
use once_cell::sync::Lazy;
use poise::CreateReply; use poise::CreateReply;
use std::sync::Mutex;
use crate::{commands::embeds::embed, Context, Error}; use crate::{commands::embeds::embed, Context, Error};
// Currently unable to get information on how long the thread was running. pub static PROCESS_UPTIME: Lazy<Mutex<std::time::SystemTime>> =
const PROCESS_UPTIME: i64 = 1000; Lazy::new(|| Mutex::new(std::time::SystemTime::now()));
/// Checks how long the bot has been running /// Checks how long the bot has been running
#[poise::command(prefix_command, slash_command, category = "Tools")] #[poise::command(prefix_command, slash_command, category = "Tools")]
pub async fn uptime(ctx: Context<'_>) -> Result<(), Error> { pub async fn uptime(ctx: Context<'_>) -> Result<(), Error> {
let uptime = PROCESS_UPTIME; let start = *PROCESS_UPTIME.lock().unwrap();
let days = uptime / (24 * 60 * 60); let uptime = std::time::SystemTime::now().duration_since(start).unwrap();
let hours = (uptime % (24 * 60 * 60)) / 3600;
let minutes = (uptime % 60 * 60) / 60;
let seconds = uptime % 60;
ctx.send( let (days, hours, minutes, seconds) = (
CreateReply::default().embed( uptime.as_secs() / 86400,
embed( (uptime.as_secs() / 3600) % 24,
ctx, (uptime.as_secs() / 60) % 60,
"I have been up and awake for", uptime.as_secs() % 60,
&format!("{} seconds", uptime), );
&format!(
"{} days, {} hours, {} minutes and {} seconds", let mut message = format!(
days, hours, minutes, seconds "I have been awake for {} days, {} hours, {} minutes and {} seconds!",
), days, hours, minutes, seconds
) );
.await
.unwrap(), if days != 0 {
), message = format!("I have been awake for {} days!", days);
) }
.await?;
if days == 0 && hours != 0 {
message = format!("I have been awake for {} hours!", hours);
}
if days == 0 && hours == 0 && minutes != 0 {
message = format!("I have been awake for {} minutes!", minutes);
}
if days == 0 && hours == 0 && minutes == 0 && seconds != 0 {
message = format!("I have been awake for {} seconds!", seconds);
}
ctx.send(CreateReply::default().embed(embed(ctx, "Uptime", "", &message).await.unwrap()))
.await?;
Ok(()) Ok(())
} }

View File

@@ -1,3 +1,4 @@
use commands::tools::uptime::PROCESS_UPTIME;
use poise::serenity_prelude::{self as serenity, ActivityData}; use poise::serenity_prelude::{self as serenity, ActivityData};
use reqwest::Client as HttpClient; use reqwest::Client as HttpClient;
use songbird::SerenityInit; use songbird::SerenityInit;
@@ -37,6 +38,8 @@ async fn main() {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
dotenv::dotenv().expect("Failed to load .env file."); dotenv::dotenv().expect("Failed to load .env file.");
let _ = *PROCESS_UPTIME.lock().unwrap();
let token = let token =
std::env::var("DISCORD_TOKEN").expect("Environment variable `DISCORD_TOKEN` not found!"); std::env::var("DISCORD_TOKEN").expect("Environment variable `DISCORD_TOKEN` not found!");
let prefix = std::env::var("PREFIX").expect("Environment variable `PREFIX` not found!"); let prefix = std::env::var("PREFIX").expect("Environment variable `PREFIX` not found!");
@@ -70,7 +73,7 @@ async fn main() {
tools::qr(), tools::qr(),
tools::register(), tools::register(),
tools::taf(), tools::taf(),
// tools::uptime(), tools::uptime(),
tools::verse(), tools::verse(),
tools::weather(), tools::weather(),
]; ];
@@ -78,7 +81,7 @@ async fn main() {
let options = poise::FrameworkOptions { let options = poise::FrameworkOptions {
commands, commands,
prefix_options: poise::PrefixFrameworkOptions { prefix_options: poise::PrefixFrameworkOptions {
prefix: Some(prefix.to_string().into()), prefix: Some(prefix.to_string()),
edit_tracker: Some(Arc::new(poise::EditTracker::for_timespan( edit_tracker: Some(Arc::new(poise::EditTracker::for_timespan(
Duration::from_secs(3600), Duration::from_secs(3600),
))), ))),
@@ -123,7 +126,7 @@ async fn main() {
}; };
let framework = poise::Framework::builder() let framework = poise::Framework::builder()
.setup(move |ctx, ready, _framework| { .setup(move |ctx, ready, framework| {
Box::pin(async move { Box::pin(async move {
info!( info!(
"{} [{}] connected successfully!", "{} [{}] connected successfully!",
@@ -131,6 +134,12 @@ async fn main() {
); );
ctx.set_activity(Some(ActivityData::listening(prefix + "help"))); ctx.set_activity(Some(ActivityData::listening(prefix + "help")));
// poise::builtins::register_globally(ctx, &framework.options().commands).await?; // poise::builtins::register_globally(ctx, &framework.options().commands).await?;
poise::builtins::register_in_guild(
ctx,
&framework.options().commands,
512680330495524873.into(),
)
.await?;
Ok(Data {}) Ok(Data {})
}) })