moved message structs and ran cargo fmt
This commit is contained in:
+4
-4
@@ -45,16 +45,16 @@
|
|||||||
//! ```shell
|
//! ```shell
|
||||||
//! $ cargo run -- start [TOKEN]
|
//! $ cargo run -- start [TOKEN]
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # Running the Documentation Server
|
//! # Running the Documentation Server
|
||||||
//!
|
//!
|
||||||
//! Payphone has a built-in documentation server that serves the project's documentation and
|
//! Payphone has a built-in documentation server that serves the project's documentation and
|
||||||
//! refreshes it on changes. To start the documentation server, run the following command:
|
//! refreshes it on changes. To start the documentation server, run the following command:
|
||||||
//!
|
//!
|
||||||
//! ```shell
|
//! ```shell
|
||||||
//! $ cargo run -- docs --open
|
//! $ cargo run -- docs --open
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! The `--open` flag is optional and will open the documentation in the default browser after
|
//! The `--open` flag is optional and will open the documentation in the default browser after
|
||||||
//! starting the server. You can also use the `--bind` argument to specify the address to bind the
|
//! starting the server. You can also use the `--bind` argument to specify the address to bind the
|
||||||
//! server to, which defaults to `0.0.0.0:3000`.
|
//! server to, which defaults to `0.0.0.0:3000`.
|
||||||
|
|||||||
+90
-66
@@ -1,66 +1,4 @@
|
|||||||
use scylla::{SerializeValue, DeserializeValue};
|
use scylla::{DeserializeValue, SerializeValue};
|
||||||
#[derive(Debug, Clone, SerializeValue, DeserializeValue)]
|
|
||||||
pub struct MessageMetadata {
|
|
||||||
pub call_id: i64,
|
|
||||||
pub author_id: i64,
|
|
||||||
pub author_name: Option<String>,
|
|
||||||
pub author_avatar_url: Option<String>,
|
|
||||||
pub author_mask: Option<String>,
|
|
||||||
pub content: Option<String>,
|
|
||||||
pub attachments: Option<Vec<String>>,
|
|
||||||
pub sticker: Option<i64>,
|
|
||||||
pub propagation_message_id: Option<i64>,
|
|
||||||
pub propagation_channel_id: Option<i64>,
|
|
||||||
pub replying_to_id: Option<i64>,
|
|
||||||
pub message_replied_to_metadata: Option<Box<MessageMetadata>>,
|
|
||||||
pub is_deleted: Option<bool>,
|
|
||||||
pub is_edited: Option<bool>,
|
|
||||||
pub is_censored: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Object<'a> for MessageMetadata {
|
|
||||||
type Row = (
|
|
||||||
i64, i64, Option<String>, Option<String>, Option<String>, Option<String>, Option<Vec<String>>, Option<i64>, Option<i64>, Option<i64>, Option<i64>, Option<Box<MessageMetadata>>, Option<bool>, Option<bool>, Option<bool>
|
|
||||||
);
|
|
||||||
fn from_row(row: Self::Row) -> Self {
|
|
||||||
Self {
|
|
||||||
call_id: row.0,
|
|
||||||
author_id: row.1,
|
|
||||||
author_name: row.2,
|
|
||||||
author_avatar_url: row.3,
|
|
||||||
author_mask: row.4,
|
|
||||||
content: row.5,
|
|
||||||
attachments: row.6,
|
|
||||||
sticker: row.7,
|
|
||||||
propagation_message_id: row.8,
|
|
||||||
propagation_channel_id: row.9,
|
|
||||||
replying_to_id: row.10,
|
|
||||||
message_replied_to_metadata: row.11,
|
|
||||||
is_deleted: row.12,
|
|
||||||
is_edited: row.13,
|
|
||||||
is_censored: row.14,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn to_row(value: Self) -> Self::Row {
|
|
||||||
(
|
|
||||||
value.call_id,
|
|
||||||
value.author_id,
|
|
||||||
value.author_name,
|
|
||||||
value.author_avatar_url,
|
|
||||||
value.author_mask,
|
|
||||||
value.content,
|
|
||||||
value.attachments,
|
|
||||||
value.sticker,
|
|
||||||
value.propagation_message_id,
|
|
||||||
value.propagation_channel_id,
|
|
||||||
value.replying_to_id,
|
|
||||||
value.message_replied_to_metadata,
|
|
||||||
value.is_deleted,
|
|
||||||
value.is_edited,
|
|
||||||
value.is_censored,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
use scylla::{
|
use scylla::{
|
||||||
client::session::Session,
|
client::session::Session,
|
||||||
deserialize::row::DeserializeRow,
|
deserialize::row::DeserializeRow,
|
||||||
@@ -253,6 +191,83 @@ impl From<TwilightUser> for User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, SerializeValue, DeserializeValue)]
|
||||||
|
pub struct MessageMetadata {
|
||||||
|
pub call_id: i64,
|
||||||
|
pub author_id: i64,
|
||||||
|
pub author_name: Option<String>,
|
||||||
|
pub author_avatar_url: Option<String>,
|
||||||
|
pub author_mask: Option<String>,
|
||||||
|
pub content: Option<String>,
|
||||||
|
pub attachments: Option<Vec<String>>,
|
||||||
|
pub sticker: Option<i64>,
|
||||||
|
pub propagation_message_id: Option<i64>,
|
||||||
|
pub propagation_channel_id: Option<i64>,
|
||||||
|
pub replying_to_id: Option<i64>,
|
||||||
|
pub message_replied_to_metadata: Option<Box<MessageMetadata>>,
|
||||||
|
pub is_deleted: Option<bool>,
|
||||||
|
pub is_edited: Option<bool>,
|
||||||
|
pub is_censored: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Object<'a> for MessageMetadata {
|
||||||
|
type Row = (
|
||||||
|
i64,
|
||||||
|
i64,
|
||||||
|
Option<String>,
|
||||||
|
Option<String>,
|
||||||
|
Option<String>,
|
||||||
|
Option<String>,
|
||||||
|
Option<Vec<String>>,
|
||||||
|
Option<i64>,
|
||||||
|
Option<i64>,
|
||||||
|
Option<i64>,
|
||||||
|
Option<i64>,
|
||||||
|
Option<Box<MessageMetadata>>,
|
||||||
|
Option<bool>,
|
||||||
|
Option<bool>,
|
||||||
|
Option<bool>,
|
||||||
|
);
|
||||||
|
fn from_row(row: Self::Row) -> Self {
|
||||||
|
Self {
|
||||||
|
call_id: row.0,
|
||||||
|
author_id: row.1,
|
||||||
|
author_name: row.2,
|
||||||
|
author_avatar_url: row.3,
|
||||||
|
author_mask: row.4,
|
||||||
|
content: row.5,
|
||||||
|
attachments: row.6,
|
||||||
|
sticker: row.7,
|
||||||
|
propagation_message_id: row.8,
|
||||||
|
propagation_channel_id: row.9,
|
||||||
|
replying_to_id: row.10,
|
||||||
|
message_replied_to_metadata: row.11,
|
||||||
|
is_deleted: row.12,
|
||||||
|
is_edited: row.13,
|
||||||
|
is_censored: row.14,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn to_row(value: Self) -> Self::Row {
|
||||||
|
(
|
||||||
|
value.call_id,
|
||||||
|
value.author_id,
|
||||||
|
value.author_name,
|
||||||
|
value.author_avatar_url,
|
||||||
|
value.author_mask,
|
||||||
|
value.content,
|
||||||
|
value.attachments,
|
||||||
|
value.sticker,
|
||||||
|
value.propagation_message_id,
|
||||||
|
value.propagation_channel_id,
|
||||||
|
value.replying_to_id,
|
||||||
|
value.message_replied_to_metadata,
|
||||||
|
value.is_deleted,
|
||||||
|
value.is_edited,
|
||||||
|
value.is_censored,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- BEGIN: Payphone Message and Report Models ---
|
// --- BEGIN: Payphone Message and Report Models ---
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -263,7 +278,11 @@ pub struct Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
pub async fn get_by_id(call_id: u64, message_id: u64, session: &Session) -> Result<Option<Self>, DatabaseError> {
|
pub async fn get_by_id(
|
||||||
|
call_id: u64,
|
||||||
|
message_id: u64,
|
||||||
|
session: &Session,
|
||||||
|
) -> Result<Option<Self>, DatabaseError> {
|
||||||
let call_id = u64_to_i64(call_id);
|
let call_id = u64_to_i64(call_id);
|
||||||
let message_id = u64_to_i64(message_id);
|
let message_id = u64_to_i64(message_id);
|
||||||
let result = session
|
let result = session
|
||||||
@@ -287,7 +306,9 @@ impl Message {
|
|||||||
message_metadata: row.2,
|
message_metadata: row.2,
|
||||||
})),
|
})),
|
||||||
Err(error) => match error {
|
Err(error) => match error {
|
||||||
SingleRowError::DeserializationFailed(_) | SingleRowError::TypeCheckFailed(_) => Err(DatabaseError::DeserializationFailed),
|
SingleRowError::DeserializationFailed(_) | SingleRowError::TypeCheckFailed(_) => {
|
||||||
|
Err(DatabaseError::DeserializationFailed)
|
||||||
|
}
|
||||||
SingleRowError::UnexpectedRowCount(_) => Ok(None),
|
SingleRowError::UnexpectedRowCount(_) => Ok(None),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -325,7 +346,10 @@ impl Report {
|
|||||||
&self.message,
|
&self.message,
|
||||||
self.created_at,
|
self.created_at,
|
||||||
self.reviewed_at,
|
self.reviewed_at,
|
||||||
self.created_by_ids.iter().map(|&id| u64_to_i64(id)).collect::<Vec<i64>>(),
|
self.created_by_ids
|
||||||
|
.iter()
|
||||||
|
.map(|&id| u64_to_i64(id))
|
||||||
|
.collect::<Vec<i64>>(),
|
||||||
self.reviewed_by_id.map(u64_to_i64),
|
self.reviewed_by_id.map(u64_to_i64),
|
||||||
self.resolution.as_ref(),
|
self.resolution.as_ref(),
|
||||||
),
|
),
|
||||||
|
|||||||
+8
-25
@@ -37,24 +37,14 @@ pub trait IntoPrefixes<T> {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<T> IntoPrefixes<T> for String {
|
impl<T> IntoPrefixes<T> for String {
|
||||||
async fn get(
|
async fn get(&self, _: &Runtime<T>, _: Option<u64>, _: Option<u64>) -> Vec<String> {
|
||||||
&self,
|
|
||||||
_: &Runtime<T>,
|
|
||||||
_: Option<u64>,
|
|
||||||
_: Option<u64>,
|
|
||||||
) -> Vec<String> {
|
|
||||||
vec![self.to_string()]
|
vec![self.to_string()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<T> IntoPrefixes<T> for &str {
|
impl<T> IntoPrefixes<T> for &str {
|
||||||
async fn get(
|
async fn get(&self, _: &Runtime<T>, _: Option<u64>, _: Option<u64>) -> Vec<String> {
|
||||||
&self,
|
|
||||||
_: &Runtime<T>,
|
|
||||||
_: Option<u64>,
|
|
||||||
_: Option<u64>,
|
|
||||||
) -> Vec<String> {
|
|
||||||
vec![self.to_string()]
|
vec![self.to_string()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,12 +54,7 @@ impl<T: Send + Sync + 'static, const N: usize, S: Send + Sync + 'static> IntoPre
|
|||||||
where
|
where
|
||||||
T: ToString,
|
T: ToString,
|
||||||
{
|
{
|
||||||
async fn get(
|
async fn get(&self, _: &Runtime<S>, _: Option<u64>, _: Option<u64>) -> Vec<String> {
|
||||||
&self,
|
|
||||||
_: &Runtime<S>,
|
|
||||||
_: Option<u64>,
|
|
||||||
_: Option<u64>,
|
|
||||||
) -> Vec<String> {
|
|
||||||
self.iter().map(|s| s.to_string()).collect()
|
self.iter().map(|s| s.to_string()).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,12 +65,7 @@ where
|
|||||||
F: Fn() -> Fut + Send + Sync + 'static,
|
F: Fn() -> Fut + Send + Sync + 'static,
|
||||||
Fut: Future<Output = Vec<String>> + Send,
|
Fut: Future<Output = Vec<String>> + Send,
|
||||||
{
|
{
|
||||||
async fn get(
|
async fn get(&self, _: &Runtime<T>, _: Option<u64>, _: Option<u64>) -> Vec<String> {
|
||||||
&self,
|
|
||||||
_: &Runtime<T>,
|
|
||||||
_: Option<u64>,
|
|
||||||
_: Option<u64>,
|
|
||||||
) -> Vec<String> {
|
|
||||||
(self)().await
|
(self)().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,7 +247,10 @@ impl<T: 'static + Send + Sync> Bot<T> {
|
|||||||
}
|
}
|
||||||
Event::InteractionCreate(interaction) => {
|
Event::InteractionCreate(interaction) => {
|
||||||
// First, try message command registry
|
// First, try message command registry
|
||||||
if let Err(e) = message_command_registry.execute(&interaction, task_rt.clone()).await {
|
if let Err(e) = message_command_registry
|
||||||
|
.execute(&interaction, task_rt.clone())
|
||||||
|
.await
|
||||||
|
{
|
||||||
// If not a message command, fall back to interaction handlers
|
// If not a message command, fall back to interaction handlers
|
||||||
for handler in &task_bot.on_interaction_handlers {
|
for handler in &task_bot.on_interaction_handlers {
|
||||||
handler
|
handler
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use twilight_model::gateway::payload::incoming::InteractionCreate;
|
|
||||||
use crate::framework::interactions::{InteractionError, context::InteractionContext};
|
use crate::framework::interactions::{InteractionError, context::InteractionContext};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use twilight_model::gateway::payload::incoming::InteractionCreate;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait MessageCommand<T>: Send + Sync + Debug {
|
pub trait MessageCommand<T>: Send + Sync + Debug {
|
||||||
@@ -9,5 +9,9 @@ pub trait MessageCommand<T>: Send + Sync + Debug {
|
|||||||
fn description(&self) -> &'static str {
|
fn description(&self) -> &'static str {
|
||||||
"A message context menu command duh."
|
"A message context menu command duh."
|
||||||
}
|
}
|
||||||
async fn execute(&self, ctx: InteractionContext<T>, target_message_id: u64) -> Result<(), InteractionError>;
|
async fn execute(
|
||||||
|
&self,
|
||||||
|
ctx: InteractionContext<T>,
|
||||||
|
target_message_id: u64,
|
||||||
|
) -> Result<(), InteractionError>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
use scc::HashMap;
|
|
||||||
use twilight_model::{
|
|
||||||
application::interaction::InteractionData, application::command::CommandType, gateway::payload::incoming::InteractionCreate,
|
|
||||||
};
|
|
||||||
use crate::framework::{
|
use crate::framework::{
|
||||||
interactions::{InteractionError, context::InteractionContext, message_command::MessageCommand},
|
interactions::{
|
||||||
|
InteractionError, context::InteractionContext, message_command::MessageCommand,
|
||||||
|
},
|
||||||
runtime::Runtime,
|
runtime::Runtime,
|
||||||
};
|
};
|
||||||
|
use scc::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use twilight_model::{
|
||||||
|
application::command::CommandType, application::interaction::InteractionData,
|
||||||
|
gateway::payload::incoming::InteractionCreate,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MessageCommandRegistry<T> {
|
pub struct MessageCommandRegistry<T> {
|
||||||
@@ -53,7 +56,11 @@ where
|
|||||||
if let Some(target_id) = data.target_id {
|
if let Some(target_id) = data.target_id {
|
||||||
command
|
command
|
||||||
.execute(
|
.execute(
|
||||||
InteractionContext::new(event.clone(), rt.clone(), prefix.to_string()),
|
InteractionContext::new(
|
||||||
|
event.clone(),
|
||||||
|
rt.clone(),
|
||||||
|
prefix.to_string(),
|
||||||
|
),
|
||||||
target_id.get(),
|
target_id.get(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ use thiserror::Error;
|
|||||||
|
|
||||||
pub mod components;
|
pub mod components;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod registry;
|
|
||||||
pub mod runner;
|
|
||||||
pub mod message_command;
|
pub mod message_command;
|
||||||
pub mod message_command_registry;
|
pub mod message_command_registry;
|
||||||
|
pub mod registry;
|
||||||
|
pub mod runner;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum InteractionError {
|
pub enum InteractionError {
|
||||||
|
|||||||
+37
-37
@@ -1,35 +1,35 @@
|
|||||||
//! A framework for building Discord bots, built with Twilight.
|
//! A framework for building Discord bots, built with Twilight.
|
||||||
//!
|
//!
|
||||||
//! Since Twilight is a low-level library, it can be hard to build a bot with it. Payphone's
|
//! Since Twilight is a low-level library, it can be hard to build a bot with it. Payphone's
|
||||||
//! framework provides a high-level interface for building Discord bots, with a focus on modularity
|
//! framework provides a high-level interface for building Discord bots, with a focus on modularity
|
||||||
//! and ease of use. The framework is designed to be extensible, allowing you to add new features
|
//! and ease of use. The framework is designed to be extensible, allowing you to add new features
|
||||||
//! and commands without modifying the core codebase. It also provides a set of utilities for
|
//! and commands without modifying the core codebase. It also provides a set of utilities for
|
||||||
//! common tasks, such as sending messages, handling events, and managing state.
|
//! common tasks, such as sending messages, handling events, and managing state.
|
||||||
//!
|
//!
|
||||||
//! # Creating a Bot
|
//! # Creating a Bot
|
||||||
//!
|
//!
|
||||||
//! Creating a bot with Payphone is as simple as creating a new instance of the `Bot` struct and
|
//! Creating a bot with Payphone is as simple as creating a new instance of the `Bot` struct and
|
||||||
//! running it. The `Bot` struct is the main entry point for the framework and provides methods for
|
//! running it. The `Bot` struct is the main entry point for the framework and provides methods for
|
||||||
//! running the bot, handling events, and managing state.
|
//! running the bot, handling events, and managing state.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use crate::framework::bot::Bot;
|
//! use crate::framework::bot::Bot;
|
||||||
//! use crate::state::State;
|
//! use crate::state::State;
|
||||||
//!
|
//!
|
||||||
//! // Your app's state.
|
//! // Your app's state.
|
||||||
//! let state = State::new();
|
//! let state = State::new();
|
||||||
//!
|
//!
|
||||||
//! // Create a new bot instance with the state and a list of prefixes.
|
//! // Create a new bot instance with the state and a list of prefixes.
|
||||||
//! let bot: Bot<State> = Bot::new(state, ["p."]);
|
//! let bot: Bot<State> = Bot::new(state, ["p."]);
|
||||||
//!
|
//!
|
||||||
//! bot.run(&token).await.unwrap();
|
//! bot.run(&token).await.unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### Prefixes
|
//! ### Prefixes
|
||||||
//!
|
//!
|
||||||
//! The framework supports multiple prefixes per command and allows you to set them dynamically
|
//! The framework supports multiple prefixes per command and allows you to set them dynamically
|
||||||
//! based on server and user IDs.
|
//! based on server and user IDs.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! async fn get_prefixes(
|
//! async fn get_prefixes(
|
||||||
//! rt: &Runtime<State>,
|
//! rt: &Runtime<State>,
|
||||||
@@ -37,15 +37,15 @@
|
|||||||
//! user_id: Option<u64>
|
//! user_id: Option<u64>
|
||||||
//! ) -> Vec<String> {
|
//! ) -> Vec<String> {
|
||||||
//! // The runtime contains your state at [`Runtime::state`].
|
//! // The runtime contains your state at [`Runtime::state`].
|
||||||
//!
|
//!
|
||||||
//! vec!["p.".to_string()]
|
//! vec!["p.".to_string()]
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! let bot: Bot<State> = Bot::new(state, get_prefixes);
|
//! let bot: Bot<State> = Bot::new(state, get_prefixes);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Otherwise, you can just pass a static list of prefixes as shown in the previous example.
|
//! Otherwise, you can just pass a static list of prefixes as shown in the previous example.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! // All of these are valid ways to create a bot with a prefix.
|
//! // All of these are valid ways to create a bot with a prefix.
|
||||||
//! let bot: Bot<State> = Bot::new(state, ["p."]);
|
//! let bot: Bot<State> = Bot::new(state, ["p."]);
|
||||||
@@ -53,77 +53,77 @@
|
|||||||
//! let bot: Bot<State> = Bot::new(state, "p.".to_string());
|
//! let bot: Bot<State> = Bot::new(state, "p.".to_string());
|
||||||
//! let bot: Bot<State> = Bot::new(state, ["p.".to_string()]);
|
//! let bot: Bot<State> = Bot::new(state, ["p.".to_string()]);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Setting no prefix will make the bot raise errors when receiving messages.
|
//! Setting no prefix will make the bot raise errors when receiving messages.
|
||||||
//!
|
//!
|
||||||
//! ## Adding Commands
|
//! ## Adding Commands
|
||||||
//!
|
//!
|
||||||
//! Creating a command is quite simple. You just need to implement the `Command` trait and register
|
//! Creating a command is quite simple. You just need to implement the `Command` trait and register
|
||||||
//! it with the bot.
|
//! it with the bot.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use crate::framework::commands::{Command, CommandError, context::CommandContext};
|
//! use crate::framework::commands::{Command, CommandError, context::CommandContext};
|
||||||
//!
|
//!
|
||||||
//! pub struct MyCommand;
|
//! pub struct MyCommand;
|
||||||
//!
|
//!
|
||||||
//! #[async_trait::async_trait]
|
//! #[async_trait::async_trait]
|
||||||
//! impl Command<State> for MyCommand {
|
//! impl Command<State> for MyCommand {
|
||||||
//! fn name(&self) -> &'static str {
|
//! fn name(&self) -> &'static str {
|
||||||
//! "my_command"
|
//! "my_command"
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! async fn execute(&self, ctx: CommandContext<'_, T>, _args: &str) -> Result<(), CommandError> {
|
//! async fn execute(&self, ctx: CommandContext<'_, T>, _args: &str) -> Result<(), CommandError> {
|
||||||
//! // Your command logic here.
|
//! // Your command logic here.
|
||||||
//! ctx.send()
|
//! ctx.send()
|
||||||
//! .content("Hello, world!")
|
//! .content("Hello, world!")
|
||||||
//! .await
|
//! .await
|
||||||
//! .map_err(|_| CommandError::MessageSendingFailed)?;
|
//! .map_err(|_| CommandError::MessageSendingFailed)?;
|
||||||
//!
|
//!
|
||||||
//! Ok(())
|
//! Ok(())
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Then you just need to register the command with the bot:
|
//! Then you just need to register the command with the bot:
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! let bot = Bot::new(state, ["p."]).command(MyCommand);
|
//! let bot = Bot::new(state, ["p."]).command(MyCommand);
|
||||||
//!
|
//!
|
||||||
//! bot.run(&token).await.unwrap();
|
//! bot.run(&token).await.unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Log into your Discord account and invite the bot to your server. Then you can test the command
|
//! Log into your Discord account and invite the bot to your server. Then you can test the command
|
||||||
//! by sending `p.my_command` in a channel the bot has access to.
|
//! by sending `p.my_command` in a channel the bot has access to.
|
||||||
//!
|
//!
|
||||||
//! ### Configuring Commands Further
|
//! ### Configuring Commands Further
|
||||||
//!
|
//!
|
||||||
//! Commands can be configured with additional metadata, such as aliases, descriptions, and
|
//! Commands can be configured with additional metadata, such as aliases, descriptions, and
|
||||||
//! arguments. The following is a full example of a command with all these features:
|
//! arguments. The following is a full example of a command with all these features:
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use crate::framework::commands::{
|
//! use crate::framework::commands::{
|
||||||
//! Command, CommandError, context::CommandContext, arguments::NeedyGreedyString
|
//! Command, CommandError, context::CommandContext, arguments::NeedyGreedyString
|
||||||
//! };
|
//! };
|
||||||
//! use crate::state::State;
|
//! use crate::state::State;
|
||||||
//!
|
//!
|
||||||
//! pub struct HelloCommand;
|
//! pub struct HelloCommand;
|
||||||
//!
|
//!
|
||||||
//! #[async_trait::async_trait]
|
//! #[async_trait::async_trait]
|
||||||
//! impl Command<State> for HelloCommand {
|
//! impl Command<State> for HelloCommand {
|
||||||
//! fn name(&self) -> &'static str {
|
//! fn name(&self) -> &'static str {
|
||||||
//! "hello"
|
//! "hello"
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! /// A list of alternative names for the command.
|
//! /// A list of alternative names for the command.
|
||||||
//! fn aliases(&self) -> &'static [&'static str] {
|
//! fn aliases(&self) -> &'static [&'static str] {
|
||||||
//! &["hola", "hey", "hi", "konnichiwa"]
|
//! &["hola", "hey", "hi", "konnichiwa"]
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! /// A description of the command and its functionality.
|
//! /// A description of the command and its functionality.
|
||||||
//! fn description(&self) -> &'static str {
|
//! fn description(&self) -> &'static str {
|
||||||
//! "This command responds with a greeting."
|
//! "This command responds with a greeting."
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! /// A list of arguments the command accepts. These are solely for documentation purposes,
|
//! /// A list of arguments the command accepts. These are solely for documentation purposes,
|
||||||
//! /// they won't affect the parsing you do in the command's `execute` method.
|
//! /// they won't affect the parsing you do in the command's `execute` method.
|
||||||
//! fn arguments(&self) -> &'static [Argument] {
|
//! fn arguments(&self) -> &'static [Argument] {
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
//! },
|
//! },
|
||||||
//! ]
|
//! ]
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! async fn execute(
|
//! async fn execute(
|
||||||
//! &self,
|
//! &self,
|
||||||
//! ctx: CommandContext<'_, T>,
|
//! ctx: CommandContext<'_, T>,
|
||||||
@@ -146,12 +146,12 @@
|
|||||||
//! args
|
//! args
|
||||||
//! )
|
//! )
|
||||||
//! .unwrap_or(NeedyGreedyString("???".to_string()));
|
//! .unwrap_or(NeedyGreedyString("???".to_string()));
|
||||||
//!
|
//!
|
||||||
//! ctx.send()
|
//! ctx.send()
|
||||||
//! .content(format!("Hello, {}!", name.0))
|
//! .content(format!("Hello, {}!", name.0))
|
||||||
//! .await
|
//! .await
|
||||||
//! .map_err(|_| CommandError::MessageSendingFailed)?;
|
//! .map_err(|_| CommandError::MessageSendingFailed)?;
|
||||||
//!
|
//!
|
||||||
//! Ok(())
|
//! Ok(())
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ use twilight_model::id::Id;
|
|||||||
use crate::{
|
use crate::{
|
||||||
database::models::{Call, Channel, Message, Report},
|
database::models::{Call, Channel, Message, Report},
|
||||||
framework::commands::{Command, CommandError, context::CommandContext},
|
framework::commands::{Command, CommandError, context::CommandContext},
|
||||||
framework::interactions::{message_command::MessageCommand, InteractionError, context::InteractionContext},
|
framework::interactions::{
|
||||||
|
InteractionError, context::InteractionContext, message_command::MessageCommand,
|
||||||
|
},
|
||||||
state::State,
|
state::State,
|
||||||
utils::{Bye, get_random_u64},
|
utils::{Bye, get_random_u64},
|
||||||
};
|
};
|
||||||
@@ -22,15 +24,26 @@ impl MessageCommand<State> for ReportMessageCommand {
|
|||||||
"Report a message to the moderators."
|
"Report a message to the moderators."
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(&self, ctx: InteractionContext<State>, target_message_id: u64) -> Result<(), InteractionError> {
|
async fn execute(
|
||||||
|
&self,
|
||||||
|
ctx: InteractionContext<State>,
|
||||||
|
target_message_id: u64,
|
||||||
|
) -> Result<(), InteractionError> {
|
||||||
// Extract channel_id from resolved data or fallback to event.channel (Discord API v10+)
|
// Extract channel_id from resolved data or fallback to event.channel (Discord API v10+)
|
||||||
let channel_id = ctx.event.channel.as_ref().map(|c| c.id.get())
|
let channel_id = ctx
|
||||||
|
.event
|
||||||
|
.channel
|
||||||
|
.as_ref()
|
||||||
|
.map(|c| c.id.get())
|
||||||
.or_else(|| ctx.event.channel.as_ref().map(|c| c.id.get()))
|
.or_else(|| ctx.event.channel.as_ref().map(|c| c.id.get()))
|
||||||
.ok_or(InteractionError::InteractionNotFound)?;
|
.ok_or(InteractionError::InteractionNotFound)?;
|
||||||
let db = &ctx.rt.state.db;
|
let db = &ctx.rt.state.db;
|
||||||
let channel = Channel::get_by_id(channel_id, db).await.map_err(|_| InteractionError::InteractionNotFound)?;
|
let channel = Channel::get_by_id(channel_id, db)
|
||||||
let call_id = channel.and_then(|c| c.call_id).ok_or(InteractionError::InteractionNotFound)?;
|
.await
|
||||||
|
.map_err(|_| InteractionError::InteractionNotFound)?;
|
||||||
|
let call_id = channel
|
||||||
|
.and_then(|c| c.call_id)
|
||||||
|
.ok_or(InteractionError::InteractionNotFound)?;
|
||||||
|
|
||||||
// Fetch the message from the database
|
// Fetch the message from the database
|
||||||
let message = Message::get_by_id(call_id, target_message_id, db)
|
let message = Message::get_by_id(call_id, target_message_id, db)
|
||||||
@@ -41,8 +54,11 @@ impl MessageCommand<State> for ReportMessageCommand {
|
|||||||
// If the message is a reply, fetch the replied-to message and denormalize its metadata
|
// If the message is a reply, fetch the replied-to message and denormalize its metadata
|
||||||
let mut message_metadata = message.message_metadata.clone();
|
let mut message_metadata = message.message_metadata.clone();
|
||||||
if let Some(replying_to_id) = message_metadata.replying_to_id {
|
if let Some(replying_to_id) = message_metadata.replying_to_id {
|
||||||
if let Ok(Some(reply_msg)) = Message::get_by_id(call_id, replying_to_id as u64, db).await {
|
if let Ok(Some(reply_msg)) =
|
||||||
message_metadata.message_replied_to_metadata = Some(Box::new(reply_msg.message_metadata));
|
Message::get_by_id(call_id, replying_to_id as u64, db).await
|
||||||
|
{
|
||||||
|
message_metadata.message_replied_to_metadata =
|
||||||
|
Some(Box::new(reply_msg.message_metadata));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,10 +72,12 @@ impl MessageCommand<State> for ReportMessageCommand {
|
|||||||
reviewed_by_id: None,
|
reviewed_by_id: None,
|
||||||
resolution: None,
|
resolution: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Insert the report
|
// Insert the report
|
||||||
report.insert(db).await.map_err(|_| InteractionError::DeferringFailed)?;
|
report
|
||||||
|
.insert(db)
|
||||||
|
.await
|
||||||
|
.map_err(|_| InteractionError::DeferringFailed)?;
|
||||||
|
|
||||||
// Respond to the user
|
// Respond to the user
|
||||||
ctx.rt.http
|
ctx.rt.http
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ use twilight_model::{gateway::payload::incoming::MessageCreate, id::Id};
|
|||||||
use twilight_validate::request::webhook_username;
|
use twilight_validate::request::webhook_username;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::models::{Call, Channel}, discord::{images, sanitizing::SanitizeDiscordMarkdown}, framework::{
|
database::models::{Call, Channel},
|
||||||
|
discord::{images, sanitizing::SanitizeDiscordMarkdown},
|
||||||
|
framework::{
|
||||||
events::traits::{EventResult, OnMessageHandler},
|
events::traits::{EventResult, OnMessageHandler},
|
||||||
runtime::Runtime,
|
runtime::Runtime,
|
||||||
}, groups::calls::moderation::escape_message, state::State
|
},
|
||||||
|
groups::calls::moderation::escape_message,
|
||||||
|
state::State,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
+10
-10
@@ -1,16 +1,16 @@
|
|||||||
//! Log handling and formatting of the console's output.
|
//! Log handling and formatting of the console's output.
|
||||||
//!
|
//!
|
||||||
//! # Logging
|
//! # Logging
|
||||||
//!
|
//!
|
||||||
//! Payphone uses the [`log`] crate for logging, and the [`colored`] crate for formatting the
|
//! Payphone uses the [`log`] crate for logging, and the [`colored`] crate for formatting the
|
||||||
//! output.
|
//! output.
|
||||||
//!
|
//!
|
||||||
//! Any logs that are not coming from the `payphone` crate are ignored.
|
//! Any logs that are not coming from the `payphone` crate are ignored.
|
||||||
//!
|
//!
|
||||||
//! ## Logging Levels
|
//! ## Logging Levels
|
||||||
//!
|
//!
|
||||||
//! There are 5 different logging levels:
|
//! There are 5 different logging levels:
|
||||||
//!
|
//!
|
||||||
//! - `trace`: The most verbose level, used for debugging purposes.
|
//! - `trace`: The most verbose level, used for debugging purposes.
|
||||||
//! - `debug`: Events that are useful to be displayed during development, but not in production.
|
//! - `debug`: Events that are useful to be displayed during development, but not in production.
|
||||||
//! - `info`: Used for general information about the bot's state and milestones.
|
//! - `info`: Used for general information about the bot's state and milestones.
|
||||||
@@ -19,9 +19,9 @@
|
|||||||
//! - `error`: Used for errors, when something goes wrong and the bot cannot continue running.
|
//! - `error`: Used for errors, when something goes wrong and the bot cannot continue running.
|
||||||
//!
|
//!
|
||||||
//! ## Usage
|
//! ## Usage
|
||||||
//!
|
//!
|
||||||
//! Logging is quite simple as it works like any [`println!`] calls.
|
//! Logging is quite simple as it works like any [`println!`] calls.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! log::trace!("This is a trace message.");
|
//! log::trace!("This is a trace message.");
|
||||||
//! log::debug!("This is a debug message.");
|
//! log::debug!("This is a debug message.");
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
//! log::warn!("This is a warning message.");
|
//! log::warn!("This is a warning message.");
|
||||||
//! log::error!("This is an error message.");
|
//! log::error!("This is an error message.");
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Logging supports formatting, so you can use it like any other [`println!`] call:
|
//! Logging supports formatting, so you can use it like any other [`println!`] call:
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! let value = 42;
|
//! let value = 42;
|
||||||
//! log::info!("The value is: {value}");
|
//! log::info!("The value is: {value}");
|
||||||
|
|||||||
+4
-4
@@ -1,18 +1,18 @@
|
|||||||
//! Application state at runtime including shared resources.
|
//! Application state at runtime including shared resources.
|
||||||
//!
|
//!
|
||||||
//! Currently, it only contains the database connection, but it can be extended in the future to
|
//! Currently, it only contains the database connection, but it can be extended in the future to
|
||||||
//! include other resources that need to be shared across the application, such as caches or
|
//! include other resources that need to be shared across the application, such as caches or
|
||||||
//! configuration settings.
|
//! configuration settings.
|
||||||
//!
|
//!
|
||||||
//! It is shared around the application using an `Arc`, allowing it to be cloned and passed around
|
//! It is shared around the application using an `Arc`, allowing it to be cloned and passed around
|
||||||
//! without needing to worry about ownership or lifetimes. This is particularly useful in an
|
//! without needing to worry about ownership or lifetimes. This is particularly useful in an
|
||||||
//! asynchronous context.
|
//! asynchronous context.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! let state = State {
|
//! let state = State {
|
||||||
//! db: database::init("127.0.0.1:9042").await?,
|
//! db: database::init("127.0.0.1:9042").await?,
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! fn handler_which_needs_state(state: Arc<State>) {
|
//! fn handler_which_needs_state(state: Arc<State>) {
|
||||||
//! // Use the state here
|
//! // Use the state here
|
||||||
//! }
|
//! }
|
||||||
|
|||||||
Reference in New Issue
Block a user