Merge pull request #20 from Trivernis/develop

More Embed stuff
pull/22/head v0.6.3
Trivernis 3 years ago committed by GitHub
commit 765e19eb03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -41,7 +41,7 @@ jobs:
- name: Move binaries
run: mv target/x86_64-unknown-linux-gnu/release/tobi-rs target/tobi-rs-linux-x86_64
- name: Sign artifact
run: gpg --detach-sign --sign --armor --default-key steps.import_gpg.outputs.keyid --output target/tobi-rs-linux-x86_64.sig target/tobi-rs-linux-x86_64
run: gpg --batch --yes --pinentry-mode loopback --passphrase "${{ secrets.PASSPHRASE }}" --detach-sign --sign --armor --default-key steps.import_gpg.outputs.keyid --output target/tobi-rs-linux-x86_64.sig target/tobi-rs-linux-x86_64
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:

@ -55,7 +55,7 @@ jobs:
passphrase: ${{ secrets.PASSPHRASE }}
- name: Sign artifact
run: gpg --detach-sign --sign --armor --default-key steps.import_gpg.outputs.keyid --output target/tobi-rs-linux-x86_64_debug.sig target/tobi-rs-linux-x86_64_debug
run: gpg --batch --yes --pinentry-mode loopback --passphrase "${{ secrets.PASSPHRASE }}" --detach-sign --sign --armor --default-key steps.import_gpg.outputs.keyid --output target/tobi-rs-linux-x86_64_debug.sig target/tobi-rs-linux-x86_64_debug
- name: Upload artifacts
uses: actions/upload-artifact@v2

9
Cargo.lock generated

@ -207,7 +207,7 @@ dependencies = [
[[package]]
name = "bot-serenityutils"
version = "0.2.1"
version = "0.2.2"
dependencies = [
"futures",
"log 0.4.14",
@ -2318,7 +2318,7 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tobi-rs"
version = "0.6.2"
version = "0.6.3"
dependencies = [
"aspotify",
"bot-coreutils",
@ -2347,6 +2347,7 @@ dependencies = [
"thiserror",
"tokio",
"trigram",
"typemap_rev",
]
[[package]]
@ -2535,9 +2536,9 @@ dependencies = [
[[package]]
name = "typemap_rev"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335fb14412163adc9ed4a3e53335afaa7a4b72bdd122e5f72f51b8f1db1a131e"
checksum = "ed5b74f0a24b5454580a79abb6994393b09adf0ab8070f15827cb666255de155"
[[package]]
name = "typenum"

@ -1,6 +1,6 @@
[package]
name = "tobi-rs"
version = "0.6.2"
version = "0.6.3"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"
@ -36,4 +36,5 @@ reqwest = "0.11.2"
chrono-tz = "0.5.3"
sauce-api = "0.7.1"
rustc_version_runtime = "0.2.0"
trigram = "0.4.4"
trigram = "0.4.4"
typemap_rev = "0.1.5"

@ -1,6 +1,6 @@
[package]
name = "bot-serenityutils"
version = "0.2.1"
version = "0.2.2"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"

@ -1,9 +1,14 @@
use crate::error::{SerenityUtilsError, SerenityUtilsResult};
use crate::menu::container::get_listeners_from_context;
use crate::menu::menu::Menu;
use crate::menu::typedata::HelpActiveContainer;
use crate::menu::ActionContainer;
use serde_json::json;
use serde_json::Value;
use serenity::client::Context;
use serenity::http::CacheHttp;
use serenity::model::channel::Reaction;
use std::sync::atomic::Ordering;
/// Shows the next page in the menu
pub async fn next_page(ctx: &Context, menu: &mut Menu<'_>, _: Reaction) -> SerenityUtilsResult<()> {
@ -47,6 +52,77 @@ pub async fn close_menu(
Ok(())
}
pub async fn toggle_help(
ctx: &Context,
menu: &mut Menu<'_>,
_: Reaction,
) -> SerenityUtilsResult<()> {
log::debug!("Displaying help");
let show_help = menu
.data
.get::<HelpActiveContainer>()
.expect("Missing HelpActiveContainer in menu data")
.clone();
if show_help.load(Ordering::Relaxed) {
display_page(ctx, menu).await?;
show_help.store(false, Ordering::Relaxed);
return Ok(());
}
let page = menu
.pages
.get(menu.current_page)
.ok_or(SerenityUtilsError::PageNotFound(menu.current_page))?
.get()
.await?;
let mut message = menu.get_message(ctx.http()).await?;
log::debug!("Building help entries");
let mut help_entries = menu
.help_entries
.iter()
.filter_map(|(e, h)| Some((menu.controls.get(e)?, e, h)))
.collect::<Vec<(&ActionContainer, &String, &String)>>();
help_entries.sort_by_key(|(c, _, _)| c.position());
let help_message = help_entries
.into_iter()
.map(|(_, e, h)| format!(" - {} {}", e, h))
.collect::<Vec<String>>()
.join("\n");
log::trace!("Help message is {}", help_message);
message
.edit(ctx, |m| {
m.0.clone_from(&mut page.0.clone());
if let Some(embed) = m.0.get_mut("embed") {
let embed = embed.as_object_mut().unwrap();
let fields = embed
.entry("fields")
.or_insert_with(|| Value::Array(vec![]));
if let Value::Array(ref mut inner) = *fields {
inner.push(json!({
"inline": false,
"name": "Help".to_string(),
"value": help_message,
}));
}
} else {
m.embed(|e| {
e.field("Help", help_message, false);
e
});
}
m
})
.await?;
log::debug!("Help message displayed");
show_help.store(true, Ordering::Relaxed);
Ok(())
}
/// Displays the menu page
async fn display_page(ctx: &Context, menu: &mut Menu<'_>) -> SerenityUtilsResult<()> {
log::debug!("Displaying page {}", menu.current_page);

@ -1,8 +1,9 @@
use crate::core::MessageHandle;
use crate::error::{SerenityUtilsError, SerenityUtilsResult};
use crate::menu::container::get_listeners_from_context;
use crate::menu::controls::{close_menu, next_page, previous_page};
use crate::menu::controls::{close_menu, next_page, previous_page, toggle_help};
use crate::menu::traits::EventDrivenMessage;
use crate::menu::typedata::HelpActiveContainer;
use crate::menu::{EventDrivenMessagesRef, Page};
use futures::FutureExt;
use serenity::async_trait;
@ -10,9 +11,11 @@ use serenity::client::Context;
use serenity::http::Http;
use serenity::model::channel::{Message, Reaction, ReactionType};
use serenity::model::id::ChannelId;
use serenity::prelude::{TypeMap, TypeMapKey};
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::{Mutex, RwLock};
@ -20,6 +23,7 @@ use tokio::sync::{Mutex, RwLock};
pub static NEXT_PAGE_EMOJI: &str = "➡️";
pub static PREVIOUS_PAGE_EMOJI: &str = "⬅️";
pub static CLOSE_MENU_EMOJI: &str = "❌";
pub static HELP_EMOJI: &str = "❔";
pub type ControlActionResult<'b> =
Pin<Box<dyn Future<Output = SerenityUtilsResult<()>> + Send + 'b>>;
@ -33,12 +37,12 @@ pub type ControlActionArc = Arc<
#[derive(Clone)]
pub struct ActionContainer {
inner: ControlActionArc,
position: usize,
position: isize,
}
impl ActionContainer {
/// Creates a new control action
pub fn new<F: 'static>(position: usize, callback: F) -> Self
pub fn new<F: 'static>(position: isize, callback: F) -> Self
where
F: for<'b> Fn(&'b Context, &'b mut Menu<'_>, Reaction) -> ControlActionResult<'b>
+ Send
@ -60,10 +64,14 @@ impl ActionContainer {
self.inner.clone()(ctx, menu, reaction).await?;
Ok(())
}
/// Returns the position of the action
pub fn position(&self) -> isize {
self.position
}
}
/// A menu message
#[derive(Clone)]
pub struct Menu<'a> {
pub message: Arc<RwLock<MessageHandle>>,
pub pages: Vec<Page<'a>>,
@ -71,6 +79,8 @@ pub struct Menu<'a> {
pub controls: HashMap<String, ActionContainer>,
pub timeout: Instant,
pub sticky: bool,
pub data: TypeMap,
pub help_entries: HashMap<String, String>,
closed: bool,
listeners: EventDrivenMessagesRef,
}
@ -221,6 +231,8 @@ pub struct MenuBuilder {
controls: HashMap<String, ActionContainer>,
timeout: Duration,
sticky: bool,
data: TypeMap,
help_entries: HashMap<String, String>,
}
impl Default for MenuBuilder {
@ -231,6 +243,8 @@ impl Default for MenuBuilder {
controls: HashMap::new(),
timeout: Duration::from_secs(60),
sticky: false,
data: TypeMap::new(),
help_entries: HashMap::new(),
}
}
}
@ -240,21 +254,35 @@ impl MenuBuilder {
pub fn new_paginator() -> Self {
log::debug!("Creating new paginator");
let mut controls = HashMap::new();
let mut help_entries = HashMap::new();
controls.insert(
PREVIOUS_PAGE_EMOJI.to_string(),
ActionContainer::new(0, |c, m, r| previous_page(c, m, r).boxed()),
);
help_entries.insert(
PREVIOUS_PAGE_EMOJI.to_string(),
"Displays the previous page".to_string(),
);
controls.insert(
CLOSE_MENU_EMOJI.to_string(),
ActionContainer::new(1, |c, m, r| close_menu(c, m, r).boxed()),
);
help_entries.insert(
CLOSE_MENU_EMOJI.to_string(),
"Closes the menu buttons".to_string(),
);
controls.insert(
NEXT_PAGE_EMOJI.to_string(),
ActionContainer::new(2, |c, m, r| next_page(c, m, r).boxed()),
);
help_entries.insert(
NEXT_PAGE_EMOJI.to_string(),
"Displays the next page".to_string(),
);
Self {
controls,
help_entries,
..Default::default()
}
}
@ -278,7 +306,7 @@ impl MenuBuilder {
}
/// Adds a single control to the message
pub fn add_control<S, F: 'static>(mut self, position: usize, emoji: S, action: F) -> Self
pub fn add_control<S, F: 'static>(mut self, position: isize, emoji: S, action: F) -> Self
where
S: ToString,
F: for<'b> Fn(&'b Context, &'b mut Menu<'_>, Reaction) -> ControlActionResult<'b>
@ -295,7 +323,7 @@ impl MenuBuilder {
pub fn add_controls<S, I>(mut self, controls: I) -> Self
where
S: ToString,
I: IntoIterator<Item = (usize, S, ControlActionArc)>,
I: IntoIterator<Item = (isize, S, ControlActionArc)>,
{
for (position, emoji, action) in controls {
self.controls.insert(
@ -332,6 +360,30 @@ impl MenuBuilder {
self
}
/// Adds data to the menu typemap
pub fn add_data<T>(mut self, value: T::Value) -> Self
where
T: TypeMapKey,
{
self.data.insert::<T>(value);
self
}
/// Adds a help entry
pub fn add_help<S: ToString>(mut self, button: S, help: S) -> Self {
self.help_entries
.insert(button.to_string(), help.to_string());
self
}
/// Turns showing help for buttons on
pub fn show_help(self) -> Self {
self.add_control(100, HELP_EMOJI, |c, m, r| Box::pin(toggle_help(c, m, r)))
.add_data::<HelpActiveContainer>(Arc::new(AtomicBool::new(false)))
}
/// builds the menu
pub async fn build(
self,
@ -371,6 +423,8 @@ impl MenuBuilder {
closed: false,
listeners: Arc::clone(&listeners),
sticky: self.sticky,
data: self.data,
help_entries: self.help_entries,
};
log::debug!("Storing menu to listeners...");

@ -3,6 +3,7 @@ pub(crate) mod controls;
pub(crate) mod menu;
pub(crate) mod page;
pub(crate) mod traits;
pub(crate) mod typedata;
pub use container::*;
pub use controls::*;

@ -0,0 +1,9 @@
use serenity::prelude::TypeMapKey;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
pub struct HelpActiveContainer;
impl TypeMapKey for HelpActiveContainer {
type Value = Arc<AtomicBool>;
}

@ -8,6 +8,7 @@ use crate::commands::music::{
get_channel_for_author, get_queue_for_guild, get_songs_for_query, get_voice_manager,
join_channel, play_next_in_queue,
};
use crate::messages::music::now_playing::create_now_playing_msg;
use crate::providers::settings::{get_setting, Setting};
#[command]
@ -43,7 +44,7 @@ async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let queue = get_queue_for_guild(ctx, &guild.id).await?;
let play_first = {
let (play_first, create_now_playing) = {
log::debug!("Adding song to queue");
let mut queue_lock = queue.lock().await;
for song in songs {
@ -57,13 +58,19 @@ async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
log::debug!("Autoshuffeling");
queue_lock.shuffle();
}
queue_lock.current().is_none()
(
queue_lock.current().is_none(),
queue_lock.now_playing_msg.is_none(),
)
};
if play_first {
log::debug!("Playing first song in queue");
while !play_next_in_queue(&ctx.http, &msg.channel_id, &queue, &handler_lock).await {}
}
if create_now_playing {
create_now_playing_msg(ctx, queue, msg.channel_id).await?;
}
handle_autodelete(ctx, msg).await?;
Ok(())

@ -8,6 +8,7 @@ use crate::commands::music::{
get_channel_for_author, get_queue_for_guild, get_songs_for_query, get_voice_manager,
join_channel, play_next_in_queue, DJ_CHECK,
};
use crate::messages::music::now_playing::create_now_playing_msg;
#[command]
#[only_in(guilds)]
@ -41,7 +42,7 @@ async fn play_next(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let mut songs = get_songs_for_query(&ctx, msg, query).await?;
let queue = get_queue_for_guild(ctx, &guild.id).await?;
let play_first = {
let (play_first, create_now_playing) = {
let mut queue_lock = queue.lock().await;
songs.reverse();
log::debug!("Enqueueing songs as next songs in the queue");
@ -49,12 +50,18 @@ async fn play_next(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
for song in songs {
queue_lock.add_next(song);
}
queue_lock.current().is_none()
(
queue_lock.current().is_none(),
queue_lock.now_playing_msg.is_none(),
)
};
if play_first {
while !play_next_in_queue(&ctx.http, &msg.channel_id, &queue, &handler).await {}
}
if create_now_playing {
create_now_playing_msg(ctx, queue, msg.channel_id).await?;
}
handle_autodelete(ctx, msg).await?;
Ok(())

@ -120,10 +120,11 @@ impl EventHandler for Handler {
if let Some(count) = member_count {
log::debug!("{} Members in channel", count);
let queue = get_queue_for_guild(&ctx, &guild_id).await.unwrap();
let mut queue_lock = queue.lock().await;
log::debug!("Setting leave flag to {}", count == 0);
queue_lock.leave_flag = count == 0;
if let Ok(queue) = get_queue_for_guild(&ctx, &guild_id).await {
let mut queue_lock = queue.lock().await;
log::debug!("Setting leave flag to {}", count == 0);
queue_lock.leave_flag = count == 0;
}
}
}
}

@ -22,6 +22,7 @@ pub async fn create_gifs_menu(
MenuBuilder::new_paginator()
.timeout(Duration::from_secs(120))
.add_pages(pages)
.show_help()
.build(ctx, channel_id)
.await?;

@ -21,6 +21,7 @@ use std::env;
use std::time::Duration;
use tokio::sync::{Mutex, RwLock};
static DELETE_BUTTON: &str = "🗑️";
static PAUSE_BUTTON: &str = "⏯️";
static SKIP_BUTTON: &str = "⏭️";
static STOP_BUTTON: &str = "⏹️";
@ -34,18 +35,30 @@ pub async fn create_now_playing_msg(
) -> BotResult<Arc<RwLock<MessageHandle>>> {
log::debug!("Creating now playing menu");
let handle = MenuBuilder::default()
.add_control(-1, DELETE_BUTTON, |c, m, r| {
Box::pin(delete_action(c, m, r))
})
.add_help(DELETE_BUTTON, "Deletes this message")
.add_control(0, STOP_BUTTON, |c, m, r| {
Box::pin(stop_button_action(c, m, r))
})
.add_help(STOP_BUTTON, "Stops the music and leaves the channel")
.add_control(1, PAUSE_BUTTON, |c, m, r| {
Box::pin(play_pause_button_action(c, m, r))
})
.add_help(PAUSE_BUTTON, "Pauses the music")
.add_control(2, SKIP_BUTTON, |c, m, r| {
Box::pin(skip_button_action(c, m, r))
})
.add_help(SKIP_BUTTON, "Skips to the next song")
.add_control(3, GOOD_PICK_BUTTON, |c, m, r| {
Box::pin(good_pick_action(c, m, r))
})
.add_help(
GOOD_PICK_BUTTON,
"Remembers this video for spotify-youtube mappings",
)
.show_help()
.add_page(Page::new_builder(move || {
let queue = Arc::clone(&queue);
Box::pin(async move {
@ -249,3 +262,25 @@ async fn good_pick_action(
Ok(())
}
async fn delete_action(
ctx: &Context,
menu: &mut Menu<'_>,
reaction: Reaction,
) -> SerenityUtilsResult<()> {
let guild_id = reaction.guild_id.unwrap();
let handle = {
let handle = menu.message.read().await;
handle.clone()
};
{
let queue = get_queue_for_guild(ctx, &guild_id).await?;
let mut queue = queue.lock().await;
queue.now_playing_msg = None;
}
ctx.http
.delete_message(handle.channel_id, handle.message_id)
.await?;
Ok(())
}

@ -38,6 +38,7 @@ pub async fn show_sauce_menu(
MenuBuilder::new_paginator()
.timeout(Duration::from_secs(600))
.add_pages(pages)
.show_help()
.build(ctx, msg.channel_id)
.await?;
}

Loading…
Cancel
Save