serenity-rs / poise

Discord bot command framework for serenity, with advanced features like edit tracking and flexible argument parsing
MIT License
678 stars 117 forks source link

Message command context #51

Closed Gabriel-Paulucci closed 2 years ago

Gabriel-Paulucci commented 2 years ago

Hello, I was scheduled these days and something came to my mind that I believe would be a good addition

A way to have a struct that is initially defined in the before command and goes through all the states, the command check, the command itself and the after command

Something similar to how the bot's global data is set

https://github.com/kangalioo/poise/blob/0fc5d548e32fc7e3c8ea490afe95db00ec17ec24/examples/framework_usage/main.rs#L142-L148

So something like that would be in place of the current pre command

https://github.com/kangalioo/poise/blob/0fc5d548e32fc7e3c8ea490afe95db00ec17ec24/examples/framework_usage/main.rs#L117-L121

I believe that there would have to be a change in how the command context is currently defined, to have the definition of the type that would transit between the command states

https://github.com/kangalioo/poise/blob/0fc5d548e32fc7e3c8ea490afe95db00ec17ec24/examples/framework_usage/main.rs#L13

kangalio commented 2 years ago

Another user data field, but initialized and specific for each command invocation separately? Interesting. What would you need this for?

Gabriel-Paulucci commented 2 years ago

Yes that's right

This would be useful for example to pre-command to do some action in the database to get configs from the guild/user or also to get some cached data referring to the user in question, I believe it would be useful in this sense of specific configs for this case

Or something like there was an error in the internal actions of the command but which is not necessarily an error, and then in the pos command it could be treated and sent to the user something formatted in the embed or even some action in the database, but that would be for more complex things

And also avoid calls to the database, in case you need some data related to the user in the 3 states of the command

kangalio commented 2 years ago

Hmm. I'd like to avoid adding an additional global type parameter like error or user data, it would cause massive churn. An alternative would be a &dyn Any field, which can be accessed with .downcast_ref::<T>().unwrap(). What do you think of that solution?

Also, for the two use cases you mentioned, what do you think of these workarounds?:

Gabriel-Paulucci commented 2 years ago

I think the way you put it is interesting, that .downcast_ref::<T>().unwrap() would be in ctx ?

The part of the cache I didn't understand very well how it would be, but for example I would use the Data defined globally for this cache would that be?

I hadn't really thought of that in the error handling part, I'll use this

kangalio commented 2 years ago

I think the way you put it is interesting, that .downcast_ref::().unwrap() would be in ctx ?

I don't understand the question but maybe this example code helps

ctx.set_command_data(5);
let five = *ctx.command_data::<i32>().unwrap();

ctx.set_command_data("hi");
let hi = *ctx.command_data::<&str>().unwrap();

Playground link

The part of the cache I didn't understand very well how it would be, but for example I would use the Data defined globally for this cache would that be?

Yes. For example with a HashMap<UserId, YourCachedData> field in Data.

Gabriel-Paulucci commented 2 years ago
ctx.set_command_data(5);
let five = *ctx.command_data::<i32>().unwrap();

ctx.set_command_data("hi");
let hi = *ctx.command_data::<&str>().unwrap();

Yes, that was my question, I think this addition would be very interesting

And thank you very much for clearing my doubt from the cache that you had commented on.

kangalio commented 2 years ago

Would the API with set_command_data and command_data work for you? If yes, I can implement it

Gabriel-Paulucci commented 2 years ago

yes it would work

kangalio commented 2 years ago

Done

Implementation involved a bit of jank but it's ok

Feel free to test the changes by changing the dependency branch to "develop" temporarily in your Cargo.toml (cargo docs)

Gabriel-Paulucci commented 2 years ago

Hello I did some very shallow tests and I liked it a lot, I believe it needs a little polishing on how it works but maybe I can even help with that, this weekend I won't have much time with it but I believe that next week I can help and also do some tests further

kangalio commented 2 years ago

Thank you!

I believe it needs a little polishing on how it works but maybe I can even help with that

Yeah, specific ideas would be much appreciated

Gabriel-Paulucci commented 2 years ago

I believe I could use for example the tokio mutex using async await

Gabriel-Paulucci commented 2 years ago

https://github.com/kangalioo/poise/blob/c9d81eb567d9795c643ea076d9626d8ebb609bf3/src/structs/context.rs#L334-L335

I was reading the lib to help, and I came across your comment about parking lot, and internally tokio uses parking lot so I think it wouldn't have as much additional problem as dependency

https://github.com/tokio-rs/tokio/blob/702d6dccc948b6a460caa52da4e4c03b252c5c3b/tokio/Cargo.toml#L103

kangalio commented 2 years ago

Thanks for helping!

I just changed the std mutex to the tokio mutex as you suggested (56ce4fe5001c6387cf4696c02d79c2c7e73703f5), so we don't need to consider parking_lot anymore

kangalio commented 2 years ago

Can this issue be closed?

Gabriel-Paulucci commented 2 years ago

I believe so, I liked the implementation to my tests apparently it worked well, in case I can help in the future and just let me know

kangalio commented 2 years ago

Ok thanks for your feedback!

EDIT: invocation_data (just putting this here to make it easier to search for this issue)