Skip to content

Commit

Permalink
feature: added replies, slash command, and better invalid message errors
Browse files Browse the repository at this point in the history
  • Loading branch information
MineBartekSA committed Aug 2, 2023
1 parent 8cb9d7b commit 6dd2925
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 17 deletions.
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,18 @@ Test site: https://nilpointer-software.github.io/mdbook-discord-components/
content: | # You can put html tags inside yaml strings
Welcome, <i style="color: a155ab;">Snazzah</i>. We hope you brought pizza.
- username: Snazzah
content: No.
reply:
author: Spen
mentions: true
content: "!echo"
content: No. <e:https://avatars.githubusercontent.com/u/63750675>
- username: Wiki Bot
avatar: https://avatars.githubusercontent.com/u/63750675
command:
author: Big_O
command: /test
content: |
Hello <!@Big_O>
\```
```

Expand Down Expand Up @@ -161,7 +172,9 @@ SystemMessageType is a String with the following valid values:
| attachments? | Array of [Attachment](#5-attachment) | Array of image attachments
| components? | Array of [ActionRow](#6-actionrow) | Array of action rows
| invites? | Array of [Invite](#8-invite) | Array of invitations
| content | String | The message content
| reply? | [Reply](#9-reply) | Reply information
| command? | [Command](#10-command) | Slash command information
| content? | String | The message content

### 3. Embed

Expand Down Expand Up @@ -270,3 +283,30 @@ ButtonType is a String with the following valid values:
| icon? | String | Server icon url
| partnered? | Boolean | Is the server partnered
| verified? | Boolean | Is the server verified

### 9. Reply

| Field | Type | Description
|-------------|-----------|-------------
| content | String | Referenced message content
| author? | String | Referenced message author username
| user_id? | Snowflake | Referrenced message author ID. Will override author, avatar, and bot
| avatar? | String | Referenced message author avatar url
| bot? | Boolean | Whether the referenced message author is a bot
| verified? | Boolean | Whether the referenced message author is a verified bot
| mentions? | Boolean | Whether the reply should mention the referenced message author
| op? | Boolean | Whether the referenced message author was the original poster in a thread
| color? | String | Referenced message author role color
| attachment? | Boolean | Whether the referenced message contains attachments
| edited? | Boolean | Whether the referenced message was edited
| command? | Bollean | Whether the referenced message was a response to a slash command

### 10. Command

| Field | Type | Description
|----------|-----------|-------------
| command | String | The invoked slash command name
| author? | String | The user who executed the slash command
| user_id? | Snowflake | Executing user ID. Will override author, and avatar
| avatar? | String | Executing user avatar url
| color? | String | Executing user role color
24 changes: 24 additions & 0 deletions mdbook-discord-components/src/components/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,27 @@ pub struct Attachment {
pub width: Option<u64>,
pub alt: Option<String>,
}

#[derive(Generatable)]
#[gen(slot = "reply")]
pub struct Reply {
pub author: String,
pub avatar: Option<String>,
pub role_color: Option<String>,
pub attachment: Option<bool>,
pub edited: Option<bool>,
pub bot: Option<bool>,
pub verified: Option<bool>,
pub mentions: Option<bool>,
pub op: Option<bool>,
pub command: Option<bool>,
}

#[derive(Generatable)]
#[gen(slot = "reply")]
pub struct Command {
pub command: String,
pub author: String,
pub avatar: Option<String>,
pub role_color: Option<String>,
}
5 changes: 4 additions & 1 deletion mdbook-discord-components/src/generators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::components::Components;
pub mod html;

lazy_static::lazy_static! {
static ref MENTION_REGEX: Regex = Regex::new("<(!?)(t:|@|#)(.*?)>").unwrap();
static ref MENTION_REGEX: Regex = Regex::new("<(!?)(t:|e:|@|#)(.*?)>").unwrap();
}

pub trait Generator {
Expand All @@ -26,6 +26,9 @@ fn format_mentions(roles: &HashMap<String, String>, text: String) -> String {
if &captures[2] == "t:" {
return format!("<discord-time>{}</discord-time>", &captures[3]);
}
if &captures[2] == "e:" {
return format!("<discord-custom-emoji url=\"{}\"></discord-custom-emoji>", &captures[3]);
}
let attr = if &captures[2] == "#" {
" type=\"channel\"".to_owned()
} else if roles.contains_key(&captures[3]) {
Expand Down
136 changes: 122 additions & 14 deletions mdbook-discord-components/src/parsers/yaml_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ impl Parser for YamlParser {
Ok(mut m) => {
for (i, mut mess) in m.drain(..).enumerate() {
mess.prepare();
if !mess.is_valid() {
return Err(YamlParserError::new(format!("Invalid message #{}", i+1)).anyhow());
if let Some(err) = mess.is_valid() {
return Err(YamlParserError::new(format!("Invalid message #{}: {}", i+1, err)).anyhow());
}
mess.push_to_tree(&mut components);
}
},
Err(_) => {
let mut message = serde_yaml::from_str::<YamlMessage>(&code_block.code)?;
message.prepare();
if !message.is_valid() {
return Err(YamlParserError::new("Invalid message").anyhow());
if let Some(err) = message.is_valid() {
return Err(YamlParserError::new(format!("Invalid message: {}", err)).anyhow());
}
message.push_to_tree(&mut components);
}
Expand Down Expand Up @@ -69,6 +69,9 @@ struct YamlBasicMessage {
highlight: Option<bool>,
verified: Option<bool>,

reply: Option<YamlReply>,
command: Option<YamlCommand>,

roles: Option<HashMap<String, String>>,

embed: Option<YamlEmbed>,
Expand Down Expand Up @@ -129,43 +132,59 @@ impl YamlMessage {
}

#[cfg(feature = "http")]
fn is_valid(&self) -> bool { // TODO: Rewrite to a Result and return a proper Error with information of what is wrong
fn is_valid(&self) -> Option<&'static str> {
match self {
YamlMessage::Basic(ref basic) => {
if basic.components.is_some() && basic.components.as_ref().unwrap().len() > 5 {
return false
return Some("too many components");
}
if basic.content.is_empty() {
if basic.embeds.is_none() || basic.embeds.as_ref().unwrap().is_empty() {
return false
return Some("no message content or embeds");
}
}
!(basic.user_id.is_none() && basic.username.is_none())
if basic.reply.is_some() && basic.command.is_some() {
return Some("message can't be a reply and a slash command at the same time");
}
if basic.user_id.is_none() && basic.username.is_none() {
return Some("no user_id or username");
}
},
YamlMessage::System(ref system) => {
!system.content.is_empty()
if system.content.is_empty() {
return Some("no system message content");
}
},
}
None
}

#[cfg(not(feature = "http"))]
fn is_valid(&self) -> bool {
fn is_valid(&self) -> Option<&'static str> {
match self {
YamlMessage::Basic(ref basic) => {
if basic.components.is_some() && basic.components.as_ref().unwrap().len() > 5 {
return false
return Some("too many components");
}
if basic.content.is_empty() {
if basic.embeds.is_none() || basic.embeds.as_ref().unwrap().is_empty() {
return false
return Some("no message content or embeds");
}
}
!basic.username.is_none()
if basic.reply.is_some() && basic.command.is_some() {
return Some("message can't be a reply and a slash command at the same time");
}
if basic.username.is_none() {
return Some("no username");
}
},
YamlMessage::System(ref system) => {
!system.content.is_empty()
if system.content.is_empty() {
return Some("no system message content");
}
},
}
None
}

fn into_component(self) -> (Option<HashMap<String, String>>, ComponentTree) {
Expand Down Expand Up @@ -208,6 +227,12 @@ impl YamlMessage {
} else {
vec![ComponentTree::Text(basic.content)]
};
if let Some(reply) = basic.reply {
tree.splice(0..0, [reply.into_component()]);
}
if let Some(command) = basic.command {
tree.splice(0..0, [command.into_component()]);
}
if let Some(embeds) = basic.embeds {
for mut embed in embeds {
embed.prepare();
Expand Down Expand Up @@ -500,6 +525,89 @@ impl YamlInvite {
}
}

#[derive(Debug, Deserialize)]
struct YamlReply {
#[serde(default)]
author: String,
content: String,

avatar: Option<String>,
color: Option<String>,

attachment: Option<bool>,
edited: Option<bool>,
bot: Option<bool>,
verified: Option<bool>,
mentions: Option<bool>,
op: Option<bool>,
command: Option<bool>,

#[cfg(feature = "http")]
user_id: Option<u64>,
}

impl YamlReply {
fn into_component(mut self) -> ComponentTree {
#[cfg(feature = "http")]
if let Some(user_id) = self.user_id {
if let Some(user) = DISCORD_CLIENT.user(user_id) {
self.author = user.display_name();
self.avatar = Some(user.avatar_url());
self.bot = Some(user.is_bot());
}
}
let data = Reply{
author: self.author,
avatar: self.avatar,
role_color: self.color,
attachment: self.attachment,
edited: self.edited,
bot: self.bot,
verified: self.verified,
mentions: self.mentions,
op: self.op,
command: self.command,
};
ComponentTree::Node {
data: data.into(),
nodes: vec![ComponentTree::Text(self.content)],
}
}
}

#[derive(Debug, Deserialize)]
struct YamlCommand {
command: String,
#[serde(default)]
author: String,
avatar: Option<String>,
color: Option<String>,
#[cfg(feature = "http")]
user_id: Option<u64>,
}

impl YamlCommand {
fn into_component(mut self) -> ComponentTree {
#[cfg(feature = "http")]
if let Some(user_id) = self.user_id {
if let Some(user) = DISCORD_CLIENT.user(user_id) {
self.author = user.display_name();
self.avatar = Some(user.avatar_url());
}
}
let data = Command{
author: self.author,
avatar: self.avatar,
role_color: self.color,
command: self.command,
};
ComponentTree::Node {
data: data.into(),
nodes: vec![],
}
}
}

#[derive(Debug)]
struct YamlParserError {
message: String,
Expand Down

0 comments on commit 6dd2925

Please sign in to comment.