serenity/utils/
message_builder.rs

1use std::fmt::{self as fmt, Write};
2use std::ops::Add;
3
4use crate::model::guild::Emoji;
5use crate::model::id::{ChannelId, RoleId, UserId};
6use crate::model::mention::Mentionable;
7
8/// The Message Builder is an ergonomic utility to easily build a message, by adding text and
9/// mentioning mentionable structs.
10///
11/// The finalized value can be accessed via [`Self::build`] or the inner value.
12///
13/// # Examples
14///
15/// Build a message, mentioning a [`Self::user`] and an [`Self::emoji`], and retrieving the
16/// value:
17///
18/// ```rust,no_run
19/// # use serenity::model::prelude::*;
20/// #
21/// # fn run(user: UserId, emoji: Emoji) {
22/// #
23/// use serenity::utils::MessageBuilder;
24///
25/// // assuming an `emoji` and `user` have already been bound
26///
27/// let content = MessageBuilder::new()
28///     .push("You sent a message, ")
29///     .mention(&user)
30///     .push("! ")
31///     .emoji(&emoji)
32///     .build();
33/// # }
34/// ```
35#[derive(Clone, Debug, Default)]
36pub struct MessageBuilder(pub String);
37
38impl MessageBuilder {
39    /// Creates a new, empty builder.
40    ///
41    /// # Examples
42    ///
43    /// Create a new [`MessageBuilder`]:
44    ///
45    /// ```rust
46    /// use serenity::utils::MessageBuilder;
47    ///
48    /// let message = MessageBuilder::new();
49    ///
50    /// // alternatively:
51    /// let message = MessageBuilder::default();
52    /// ```
53    #[must_use]
54    pub fn new() -> MessageBuilder {
55        MessageBuilder::default()
56    }
57
58    /// Pulls the inner value out of the builder.
59    ///
60    /// # Examples
61    ///
62    /// Create a string mentioning a channel by Id, and then suffixing `"!"`, and finally building
63    /// it to retrieve the inner String:
64    ///
65    /// ```rust
66    /// use serenity::model::id::ChannelId;
67    /// use serenity::utils::MessageBuilder;
68    ///
69    /// let channel_id = ChannelId::new(81384788765712384);
70    ///
71    /// let content = MessageBuilder::new().channel(channel_id).push("!").build();
72    ///
73    /// assert_eq!(content, "<#81384788765712384>!");
74    /// ```
75    ///
76    /// This is equivalent to simply retrieving the tuple struct's first value:
77    ///
78    /// ```rust
79    /// use serenity::utils::MessageBuilder;
80    ///
81    /// let mut content = MessageBuilder::new();
82    /// content.push("test");
83    ///
84    /// assert_eq!(content.build(), "test");
85    /// ```
86    pub fn build(&mut self) -> String {
87        self.clone().0
88    }
89
90    /// Mentions the [`GuildChannel`] in the built message.
91    ///
92    /// This accepts anything that converts _into_ a [`ChannelId`]. Refer to [`ChannelId`]'s
93    /// documentation for more information.
94    ///
95    /// Refer to [`ChannelId`]'s [Display implementation] for more information on how this is
96    /// formatted.
97    ///
98    /// # Examples
99    ///
100    /// Mentioning a [`Channel`] by Id:
101    ///
102    /// ```rust
103    /// use serenity::model::id::ChannelId;
104    /// use serenity::utils::MessageBuilder;
105    ///
106    /// let channel_id = ChannelId::new(81384788765712384);
107    ///
108    /// let content = MessageBuilder::new().push("The channel is: ").channel(channel_id).build();
109    ///
110    /// assert_eq!(content, "The channel is: <#81384788765712384>");
111    /// ```
112    ///
113    /// [`Channel`]: crate::model::channel::Channel
114    /// [`GuildChannel`]: crate::model::channel::GuildChannel
115    /// [Display implementation]: ChannelId#impl-Display
116    #[inline]
117    pub fn channel<C: Into<ChannelId>>(&mut self, channel: C) -> &mut Self {
118        self.channel_(channel.into())
119    }
120
121    fn channel_(&mut self, channel: ChannelId) -> &mut Self {
122        self.push_(&channel.mention());
123        self
124    }
125
126    /// Displays the given emoji in the built message.
127    ///
128    /// Refer to [`Emoji`]s [Display implementation] for more information on how this is formatted.
129    ///
130    /// # Examples
131    ///
132    /// Mention an emoji in a message's content:
133    ///
134    /// ```rust
135    /// # use serenity::json::{json, from_value};
136    /// # use serenity::model::guild::Emoji;
137    /// # use serenity::model::id::EmojiId;
138    /// # use serenity::utils::MessageBuilder;
139    ///
140    /// # let emoji = from_value::<Emoji>(json!({
141    /// #     "id": EmojiId::new(302516740095606785),
142    /// #     "name": "smugAnimeFace",
143    /// # })).unwrap();
144    ///
145    /// let message = MessageBuilder::new().push("foo ").emoji(&emoji).push(".").build();
146    ///
147    /// assert_eq!(message, "foo <:smugAnimeFace:302516740095606785>.");
148    /// ```
149    ///
150    /// [Display implementation]: crate::model::guild::Emoji#impl-Display
151    pub fn emoji(&mut self, emoji: &Emoji) -> &mut Self {
152        self.push_(&emoji);
153        self
154    }
155
156    /// Mentions something that implements the [`Mentionable`] trait.
157    pub fn mention<M: Mentionable>(&mut self, item: &M) -> &mut Self {
158        self.push_(&item.mention());
159        self
160    }
161
162    /// Pushes a string to the internal message content.
163    ///
164    /// Note that this does not mutate either the given data or the internal message content in
165    /// anyway prior to appending the given content to the internal message.
166    ///
167    /// # Examples
168    ///
169    /// ```rust
170    /// use serenity::utils::MessageBuilder;
171    ///
172    /// let mut message = MessageBuilder::new();
173    /// message.push("test");
174    ///
175    /// assert_eq!(
176    ///     {
177    ///         message.push("ing");
178    ///         message.build()
179    ///     },
180    ///     "testing"
181    /// );
182    /// ```
183    #[inline]
184    pub fn push(&mut self, content: impl Into<Content>) -> &mut Self {
185        self.push_(&content.into())
186    }
187
188    #[inline]
189    fn push_<C: std::fmt::Display + ?Sized>(&mut self, content: &C) -> &mut Self {
190        write!(self.0, "{content}").unwrap();
191
192        self
193    }
194
195    /// Pushes a codeblock to the content, with optional syntax highlighting.
196    ///
197    /// # Examples
198    ///
199    /// Pushing a Rust codeblock:
200    ///
201    /// ```rust,ignore
202    /// use serenity::utils::MessageBuilder;
203    ///
204    /// let code = r#"
205    /// fn main() {
206    ///     println!("Hello, world!");
207    /// }
208    /// "#;
209    ///
210    /// let content = MessageBuilder::new()
211    ///     .push_codeblock(code, Some("rust"))
212    ///     .build();
213    ///
214    /// let expected = r#"```rust
215    /// fn main() {
216    ///     println!("Hello, world!");
217    /// }
218    /// ```"#;
219    ///
220    /// assert_eq!(content, expected);
221    /// ```
222    /// 
223    /// Pushing a codeblock without a language:
224    /// ```rust
225    /// use serenity::utils::MessageBuilder;
226    ///
227    /// let content = MessageBuilder::new()
228    ///     .push_codeblock("hello", None)
229    ///     .build();
230    ///
231    /// assert_eq!(content, "```\nhello\n```");
232    /// ```
233    pub fn push_codeblock(
234        &mut self,
235        content: impl Into<Content>,
236        language: Option<&str>,
237    ) -> &mut Self {
238        self.0.push_str("```");
239
240        if let Some(language) = language {
241            self.0.push_str(language);
242        }
243
244        self.0.push('\n');
245        self.push_(&content.into());
246        self.0.push_str("\n```");
247
248        self
249    }
250
251    /// Pushes inlined monospaced text to the content.
252    ///
253    /// # Examples
254    ///
255    /// Display a server configuration value to the user:
256    ///
257    /// ```rust
258    /// use serenity::utils::MessageBuilder;
259    ///
260    /// let key = "prefix";
261    /// let value = "&";
262    ///
263    /// let content = MessageBuilder::new()
264    ///     .push("The setting ")
265    ///     .push_mono(key)
266    ///     .push(" for this server is ")
267    ///     .push_mono(value)
268    ///     .push(".")
269    ///     .build();
270    ///
271    /// let expected = format!("The setting `{}` for this server is `{}`.", key, value);
272    ///
273    /// assert_eq!(content, expected);
274    /// ```
275    pub fn push_mono(&mut self, content: impl Into<Content>) -> &mut Self {
276        self.0.push('`');
277        self.push_(&content.into());
278        self.0.push('`');
279
280        self
281    }
282
283    /// Pushes inlined italicized text to the content.
284    ///
285    /// # Examples
286    ///
287    /// Emphasize information to the user:
288    ///
289    /// ```rust
290    /// use serenity::utils::MessageBuilder;
291    ///
292    /// let content = MessageBuilder::new()
293    ///     .push("You don't ")
294    ///     .push_italic("always need")
295    ///     .push(" to italicize ")
296    ///     .push_italic("everything")
297    ///     .push(".")
298    ///     .build();
299    ///
300    /// let expected = "You don't _always need_ to italicize _everything_.";
301    ///
302    /// assert_eq!(content, expected);
303    /// ```
304    pub fn push_italic(&mut self, content: impl Into<Content>) -> &mut Self {
305        self.0.push('_');
306        self.push_(&content.into());
307        self.0.push('_');
308
309        self
310    }
311
312    /// Pushes an inline bold text to the content.
313    pub fn push_bold(&mut self, content: impl Into<Content>) -> &mut Self {
314        self.0.push_str("**");
315        self.push_(&content.into());
316        self.0.push_str("**");
317
318        self
319    }
320
321    /// Pushes an underlined inline text to the content.
322    pub fn push_underline(&mut self, content: impl Into<Content>) -> &mut Self {
323        self.0.push_str("__");
324        self.push_(&content.into());
325        self.0.push_str("__");
326
327        self
328    }
329
330    /// Pushes a strikethrough inline text to the content.
331    pub fn push_strike(&mut self, content: impl Into<Content>) -> &mut Self {
332        self.0.push_str("~~");
333        self.push_(&content.into());
334        self.0.push_str("~~");
335
336        self
337    }
338
339    /// Pushes a spoiler'd inline text to the content.
340    pub fn push_spoiler(&mut self, content: impl Into<Content>) -> &mut Self {
341        self.0.push_str("||");
342        self.push_(&content.into());
343        self.0.push_str("||");
344
345        self
346    }
347
348    /// Pushes a quoted inline text to the content
349    pub fn push_quote(&mut self, content: impl Into<Content>) -> &mut Self {
350        self.0.push_str("> ");
351        self.push_(&content.into());
352
353        self
354    }
355
356    /// Pushes the given text with a newline appended to the content.
357    ///
358    /// # Examples
359    ///
360    /// Push content and then append a newline:
361    ///
362    /// ```rust
363    /// use serenity::utils::MessageBuilder;
364    ///
365    /// let content = MessageBuilder::new().push_line("hello").push("world").build();
366    ///
367    /// assert_eq!(content, "hello\nworld");
368    /// ```
369    pub fn push_line(&mut self, content: impl Into<Content>) -> &mut Self {
370        self.push(content);
371        self.0.push('\n');
372
373        self
374    }
375
376    /// Pushes inlined monospace text with an added newline to the content.
377    ///
378    /// # Examples
379    ///
380    /// Push content and then append a newline:
381    ///
382    /// ```rust
383    /// use serenity::utils::MessageBuilder;
384    ///
385    /// let content = MessageBuilder::new().push_mono_line("hello").push("world").build();
386    ///
387    /// assert_eq!(content, "`hello`\nworld");
388    /// ```
389    pub fn push_mono_line(&mut self, content: impl Into<Content>) -> &mut Self {
390        self.push_mono(content);
391        self.0.push('\n');
392
393        self
394    }
395
396    /// Pushes an inlined italicized text with an added newline to the content.
397    ///
398    /// # Examples
399    ///
400    /// Push content and then append a newline:
401    ///
402    /// ```rust
403    /// use serenity::utils::MessageBuilder;
404    ///
405    /// let content = MessageBuilder::new().push_italic_line("hello").push("world").build();
406    ///
407    /// assert_eq!(content, "_hello_\nworld");
408    /// ```
409    pub fn push_italic_line(&mut self, content: impl Into<Content>) -> &mut Self {
410        self.push_italic(content);
411        self.0.push('\n');
412
413        self
414    }
415
416    /// Pushes an inline bold text with an added newline to the content.
417    ///
418    /// # Examples
419    ///
420    /// Push content and then append a newline:
421    ///
422    /// ```rust
423    /// use serenity::utils::MessageBuilder;
424    ///
425    /// let content = MessageBuilder::new().push_bold_line("hello").push("world").build();
426    ///
427    /// assert_eq!(content, "**hello**\nworld");
428    /// ```
429    pub fn push_bold_line(&mut self, content: impl Into<Content>) -> &mut Self {
430        self.push_bold(content);
431        self.0.push('\n');
432
433        self
434    }
435
436    /// Pushes an underlined inline text with an added newline to the content.
437    ///
438    /// # Examples
439    ///
440    /// Push content and then append a newline:
441    ///
442    /// ```rust
443    /// use serenity::utils::MessageBuilder;
444    ///
445    /// let content = MessageBuilder::new().push_underline_line("hello").push("world").build();
446    ///
447    /// assert_eq!(content, "__hello__\nworld");
448    /// ```
449    pub fn push_underline_line(&mut self, content: impl Into<Content>) -> &mut Self {
450        self.push_underline(content);
451        self.0.push('\n');
452
453        self
454    }
455
456    /// Pushes a strikethrough inline text with a newline added to the content.
457    ///
458    /// # Examples
459    ///
460    /// Push content and then append a newline:
461    ///
462    /// ```rust
463    /// use serenity::utils::MessageBuilder;
464    ///
465    /// let content = MessageBuilder::new().push_strike_line("hello").push("world").build();
466    ///
467    /// assert_eq!(content, "~~hello~~\nworld");
468    /// ```
469    pub fn push_strike_line(&mut self, content: impl Into<Content>) -> &mut Self {
470        self.push_strike(content);
471        self.0.push('\n');
472
473        self
474    }
475
476    /// Pushes a spoiler'd inline text with a newline added to the content.
477    ///
478    /// # Examples
479    ///
480    /// Push content and then append a newline:
481    ///
482    /// ```rust
483    /// use serenity::utils::MessageBuilder;
484    ///
485    /// let content = MessageBuilder::new().push_spoiler_line("hello").push("world").build();
486    ///
487    /// assert_eq!(content, "||hello||\nworld");
488    /// ```
489    pub fn push_spoiler_line(&mut self, content: impl Into<Content>) -> &mut Self {
490        self.push_spoiler(content);
491        self.0.push('\n');
492
493        self
494    }
495
496    /// Pushes a quoted inline text to the content
497    ///
498    /// # Examples
499    ///
500    /// Push content and then append a newline:
501    ///
502    /// ```rust
503    /// use serenity::utils::MessageBuilder;
504    ///
505    /// let content = MessageBuilder::new().push_quote_line("hello").push("world").build();
506    ///
507    /// assert_eq!(content, "> hello\nworld");
508    /// ```
509    pub fn push_quote_line(&mut self, content: impl Into<Content>) -> &mut Self {
510        self.push_quote(content);
511        self.0.push('\n');
512
513        self
514    }
515
516    /// Pushes text to your message, but normalizing content - that means ensuring that there's no
517    /// unwanted formatting, mention spam etc.
518    pub fn push_safe(&mut self, content: impl Into<Content>) -> &mut Self {
519        {
520            let mut c = content.into();
521            c.inner =
522                normalize(&c.inner).replace('*', "\\*").replace('`', "\\`").replace('_', "\\_");
523
524            self.push_(&c);
525        }
526
527        self
528    }
529
530    /// Pushes a code-block to your message normalizing content.
531    pub fn push_codeblock_safe(
532        &mut self,
533        content: impl Into<Content>,
534        language: Option<&str>,
535    ) -> &mut Self {
536        self.0.push_str("```");
537
538        if let Some(language) = language {
539            self.0.push_str(language);
540        }
541
542        self.0.push('\n');
543        {
544            let mut c = content.into();
545            c.inner = normalize(&c.inner).replace("```", " ");
546            self.push_(&c);
547        }
548        self.0.push_str("\n```");
549
550        self
551    }
552
553    /// Pushes an inline monospaced text to the content normalizing content.
554    pub fn push_mono_safe(&mut self, content: impl Into<Content>) -> &mut Self {
555        self.0.push('`');
556        {
557            let mut c = content.into();
558            c.inner = normalize(&c.inner).replace('`', "'");
559            self.push_(&c);
560        }
561        self.0.push('`');
562
563        self
564    }
565
566    /// Pushes an inline italicized text to the content normalizing content.
567    pub fn push_italic_safe(&mut self, content: impl Into<Content>) -> &mut Self {
568        self.0.push('_');
569        {
570            let mut c = content.into();
571            c.inner = normalize(&c.inner).replace('_', " ");
572            self.push_(&c);
573        }
574        self.0.push('_');
575
576        self
577    }
578
579    /// Pushes an inline bold text to the content normalizing content.
580    pub fn push_bold_safe(&mut self, content: impl Into<Content>) -> &mut Self {
581        self.0.push_str("**");
582        {
583            let mut c = content.into();
584            c.inner = normalize(&c.inner).replace("**", " ");
585            self.push_(&c);
586        }
587        self.0.push_str("**");
588
589        self
590    }
591
592    /// Pushes an underlined inline text to the content normalizing content.
593    pub fn push_underline_safe(&mut self, content: impl Into<Content>) -> &mut Self {
594        self.0.push_str("__");
595        {
596            let mut c = content.into();
597            c.inner = normalize(&c.inner).replace("__", " ");
598            self.push_(&c);
599        }
600        self.0.push_str("__");
601
602        self
603    }
604
605    /// Pushes a strikethrough inline text to the content normalizing content.
606    pub fn push_strike_safe(&mut self, content: impl Into<Content>) -> &mut Self {
607        self.0.push_str("~~");
608        {
609            let mut c = content.into();
610            c.inner = normalize(&c.inner).replace("~~", " ");
611            self.push_(&c);
612        }
613        self.0.push_str("~~");
614
615        self
616    }
617
618    /// Pushes a spoiler'd inline text to the content normalizing content.
619    pub fn push_spoiler_safe(&mut self, content: impl Into<Content>) -> &mut Self {
620        self.0.push_str("||");
621        {
622            let mut c = content.into();
623            c.inner = normalize(&c.inner).replace("||", " ");
624            self.push_(&c);
625        }
626        self.0.push_str("||");
627
628        self
629    }
630
631    /// Pushes a quoted inline text to the content normalizing content.
632    pub fn push_quote_safe(&mut self, content: impl Into<Content>) -> &mut Self {
633        self.0.push_str("> ");
634        {
635            let mut c = content.into();
636            c.inner = normalize(&c.inner).replace("> ", " ");
637            self.push_(&c);
638        }
639
640        self
641    }
642
643    /// Pushes text with a newline appended to the content normalizing content.
644    ///
645    /// # Examples
646    ///
647    /// Push content and then append a newline:
648    ///
649    /// ```rust
650    /// use serenity::utils::MessageBuilder;
651    ///
652    /// let content =
653    ///     MessageBuilder::new().push_line_safe("Hello @everyone").push("How are you?").build();
654    ///
655    /// assert_eq!(content, "Hello @\u{200B}everyone\nHow are you?");
656    /// ```
657    pub fn push_line_safe(&mut self, content: impl Into<Content>) -> &mut Self {
658        self.push_safe(content);
659        self.0.push('\n');
660
661        self
662    }
663
664    /// Pushes an inline monospaced text with added newline to the content normalizing content.
665    ///
666    /// # Examples
667    ///
668    /// Push content and then append a newline:
669    ///
670    /// ```rust
671    /// use serenity::utils::MessageBuilder;
672    ///
673    /// let content =
674    ///     MessageBuilder::new().push_mono_line_safe("`hello @everyone`").push("world").build();
675    ///
676    /// assert_eq!(content, "`'hello @\u{200B}everyone'`\nworld");
677    /// ```
678    pub fn push_mono_line_safe(&mut self, content: impl Into<Content>) -> &mut Self {
679        self.push_mono_safe(content);
680        self.0.push('\n');
681
682        self
683    }
684
685    /// Pushes an inline italicized text with added newline to the content normalizing content.
686    ///
687    /// # Examples
688    ///
689    /// Push content and then append a newline:
690    ///
691    /// ```rust
692    /// use serenity::utils::MessageBuilder;
693    ///
694    /// let content =
695    ///     MessageBuilder::new().push_italic_line_safe("@everyone").push("Isn't a mention.").build();
696    ///
697    /// assert_eq!(content, "_@\u{200B}everyone_\nIsn't a mention.");
698    /// ```
699    pub fn push_italic_line_safe(&mut self, content: impl Into<Content>) -> &mut Self {
700        self.push_italic_safe(content);
701        self.0.push('\n');
702
703        self
704    }
705
706    /// Pushes an inline bold text with added newline to the content normalizing content.
707    ///
708    /// # Examples
709    ///
710    /// Push content and then append a newline:
711    ///
712    /// ```rust
713    /// use serenity::utils::MessageBuilder;
714    ///
715    /// let content =
716    ///     MessageBuilder::new().push_bold_line_safe("@everyone").push("Isn't a mention.").build();
717    ///
718    /// assert_eq!(content, "**@\u{200B}everyone**\nIsn't a mention.");
719    /// ```
720    pub fn push_bold_line_safe(&mut self, content: impl Into<Content>) -> &mut Self {
721        self.push_bold_safe(content);
722        self.0.push('\n');
723
724        self
725    }
726
727    /// Pushes an underlined inline text with added newline to the content normalizing content.
728    ///
729    /// # Examples
730    ///
731    /// Push content and then append a newline:
732    ///
733    /// ```rust
734    /// use serenity::utils::MessageBuilder;
735    ///
736    /// let content = MessageBuilder::new()
737    ///     .push_underline_line_safe("@everyone")
738    ///     .push("Isn't a mention.")
739    ///     .build();
740    ///
741    /// assert_eq!(content, "__@\u{200B}everyone__\nIsn't a mention.");
742    /// ```
743    pub fn push_underline_line_safe(&mut self, content: impl Into<Content>) -> &mut Self {
744        self.push_underline_safe(content);
745        self.0.push('\n');
746
747        self
748    }
749
750    /// Pushes a strikethrough inline text with added newline to the content normalizing content.
751    ///
752    /// # Examples
753    ///
754    /// Push content and then append a newline:
755    ///
756    /// ```rust
757    /// use serenity::utils::MessageBuilder;
758    ///
759    /// let content =
760    ///     MessageBuilder::new().push_strike_line_safe("@everyone").push("Isn't a mention.").build();
761    ///
762    /// assert_eq!(content, "~~@\u{200B}everyone~~\nIsn't a mention.");
763    /// ```
764    pub fn push_strike_line_safe(&mut self, content: impl Into<Content>) -> &mut Self {
765        self.push_strike_safe(content);
766        self.0.push('\n');
767
768        self
769    }
770
771    /// Pushes a spoiler'd inline text with added newline to the content normalizing content.
772    ///
773    /// # Examples
774    ///
775    /// Push content and then append a newline:
776    ///
777    /// ```rust
778    /// use serenity::utils::MessageBuilder;
779    ///
780    /// let content =
781    ///     MessageBuilder::new().push_spoiler_line_safe("@everyone").push("Isn't a mention.").build();
782    ///
783    /// assert_eq!(content, "||@\u{200B}everyone||\nIsn't a mention.");
784    /// ```
785    pub fn push_spoiler_line_safe(&mut self, content: impl Into<Content>) -> &mut Self {
786        self.push_spoiler_safe(content);
787        self.0.push('\n');
788
789        self
790    }
791
792    /// Pushes a quoted inline text with added newline to the content normalizing content.
793    ///
794    /// # Examples
795    ///
796    /// Push content and then append a newline:
797    ///
798    /// ```rust
799    /// use serenity::utils::MessageBuilder;
800    ///
801    /// let content =
802    ///     MessageBuilder::new().push_quote_line_safe("@everyone").push("Isn't a mention.").build();
803    ///
804    /// assert_eq!(content, "> @\u{200B}everyone\nIsn't a mention.");
805    /// ```
806    pub fn push_quote_line_safe(&mut self, content: impl Into<Content>) -> &mut Self {
807        self.push_quote_safe(content);
808        self.0.push('\n');
809
810        self
811    }
812
813    /// Starts a multi-line quote, every push after this one will be quoted
814    pub fn quote_rest(&mut self) -> &mut Self {
815        self.0.push_str("\n>>> ");
816
817        self
818    }
819
820    /// Mentions the [`Role`] in the built message.
821    ///
822    /// This accepts anything that converts _into_ a [`RoleId`]. Refer to [`RoleId`]'s
823    /// documentation for more information.
824    ///
825    /// Refer to [`RoleId`]'s [Display implementation] for more information on how this is
826    /// formatted.
827    ///
828    /// [`Role`]: crate::model::guild::Role
829    /// [Display implementation]: RoleId#impl-Display
830    pub fn role<R: Into<RoleId>>(&mut self, role: R) -> &mut Self {
831        self.push_(&role.into().mention());
832        self
833    }
834
835    /// Mentions the [`User`] in the built message.
836    ///
837    /// This accepts anything that converts _into_ a [`UserId`]. Refer to [`UserId`]'s
838    /// documentation for more information.
839    ///
840    /// Refer to [`UserId`]'s [Display implementation] for more information on how this is
841    /// formatted.
842    ///
843    /// [`User`]: crate::model::user::User
844    /// [Display implementation]: UserId#impl-Display
845    pub fn user<U: Into<UserId>>(&mut self, user: U) -> &mut Self {
846        self.push_(&user.into().mention());
847        self
848    }
849}
850
851impl fmt::Display for MessageBuilder {
852    /// Formats the message builder into a string.
853    ///
854    /// This is done by simply taking the internal value of the tuple-struct and writing it into
855    /// the formatter.
856    ///
857    /// # Examples
858    ///
859    /// Create a message builder, and format it into a string via the `format!`
860    /// macro:
861    ///
862    /// ```rust
863    /// use serenity::utils::MessageBuilder;
864    /// ```
865    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
866        fmt::Display::fmt(&self.0, f)
867    }
868}
869
870/// A trait with additional functionality over the [`MessageBuilder`] for creating content with
871/// additional functionality available only in embeds.
872///
873/// Namely, this allows you to create named links via the non-escaping [`Self::push_named_link`]
874/// method and the escaping [`Self::push_named_link_safe`] method.
875///
876/// # Examples
877///
878/// Make a named link to Rust's GitHub organization:
879///
880/// ```rust
881/// use serenity::utils::{EmbedMessageBuilding, MessageBuilder};
882///
883/// let msg = MessageBuilder::new()
884///     .push_named_link("Rust's GitHub", "https://github.com/rust-lang")
885///     .build();
886///
887/// assert_eq!(msg, "[Rust's GitHub](https://github.com/rust-lang)");
888/// ```
889pub trait EmbedMessageBuilding {
890    /// Pushes a named link to a message, intended for use in embeds.
891    ///
892    /// # Examples
893    ///
894    /// Make a simple link to Rust's homepage for use in an embed:
895    ///
896    /// ```rust
897    /// use serenity::utils::{EmbedMessageBuilding, MessageBuilder};
898    ///
899    /// let mut msg = MessageBuilder::new();
900    /// msg.push("Rust's website: ");
901    /// msg.push_named_link("Homepage", "https://rust-lang.org");
902    /// let content = msg.build();
903    ///
904    /// assert_eq!(content, "Rust's website: [Homepage](https://rust-lang.org)");
905    /// ```
906    fn push_named_link(&mut self, name: impl Into<Content>, url: impl Into<Content>) -> &mut Self;
907
908    /// Pushes a named link intended for use in an embed, but with a normalized name to avoid
909    /// escaping issues.
910    ///
911    /// Refer to [`Self::push_named_link`] for more information.
912    ///
913    /// # Examples
914    ///
915    /// ```rust
916    /// use serenity::utils::{EmbedMessageBuilding, MessageBuilder};
917    ///
918    /// let mut msg = MessageBuilder::new();
919    /// msg.push("A weird website name: ");
920    /// msg.push_named_link_safe("Try to ] break links (](", "https://rust-lang.org");
921    /// let content = msg.build();
922    ///
923    /// assert_eq!(content, "A weird website name: [Try to   break links ( (](https://rust-lang.org)");
924    /// ```
925    fn push_named_link_safe(
926        &mut self,
927        name: impl Into<Content>,
928        url: impl Into<Content>,
929    ) -> &mut Self;
930}
931
932impl EmbedMessageBuilding for MessageBuilder {
933    fn push_named_link(&mut self, name: impl Into<Content>, url: impl Into<Content>) -> &mut Self {
934        write!(self.0, "[{}]({})", name.into(), url.into()).unwrap();
935
936        self
937    }
938
939    fn push_named_link_safe(
940        &mut self,
941        name: impl Into<Content>,
942        url: impl Into<Content>,
943    ) -> &mut Self {
944        self.0.push('[');
945        {
946            let mut c = name.into();
947            c.inner = normalize(&c.inner).replace(']', " ");
948            self.push_(&c);
949        }
950        self.0.push_str("](");
951        {
952            let mut c = url.into();
953            c.inner = normalize(&c.inner).replace(')', " ");
954            self.push_(&c);
955        }
956        self.0.push(')');
957
958        self
959    }
960}
961
962/// Formatting modifiers for MessageBuilder content pushes
963///
964/// Provides an enum of formatting modifiers for a string, for combination with string types and
965/// Content types.
966///
967/// # Examples
968///
969/// Create a new Content type which describes a bold-italic "text":
970///
971/// ```rust,no_run
972/// use serenity::utils::Content;
973/// use serenity::utils::ContentModifier::{Bold, Italic};
974/// let content: Content = Bold + Italic + "text";
975/// ```
976#[non_exhaustive]
977pub enum ContentModifier {
978    Italic,
979    Bold,
980    Strikethrough,
981    Code,
982    Underline,
983    Spoiler,
984}
985
986/// Describes formatting on string content
987#[derive(Clone, Debug, Default)]
988pub struct Content {
989    pub italic: bool,
990    pub bold: bool,
991    pub strikethrough: bool,
992    pub inner: String,
993    pub code: bool,
994    pub underline: bool,
995    pub spoiler: bool,
996}
997
998impl<T: AsRef<str>> Add<T> for Content {
999    type Output = Content;
1000
1001    fn add(mut self, rhs: T) -> Content {
1002        self.inner += rhs.as_ref();
1003
1004        self
1005    }
1006}
1007
1008impl<T: AsRef<str>> Add<T> for ContentModifier {
1009    type Output = Content;
1010
1011    fn add(self, rhs: T) -> Content {
1012        let mut nc = self.to_content();
1013        nc.inner += rhs.as_ref();
1014        nc
1015    }
1016}
1017
1018impl Add<ContentModifier> for Content {
1019    type Output = Content;
1020
1021    fn add(mut self, rhs: ContentModifier) -> Content {
1022        self.apply(&rhs);
1023
1024        self
1025    }
1026}
1027
1028impl Add<ContentModifier> for ContentModifier {
1029    type Output = Content;
1030
1031    fn add(self, rhs: ContentModifier) -> Content {
1032        let mut nc = self.to_content();
1033        nc.apply(&rhs);
1034
1035        nc
1036    }
1037}
1038
1039impl ContentModifier {
1040    fn to_content(&self) -> Content {
1041        let mut nc = Content::default();
1042        nc.apply(self);
1043
1044        nc
1045    }
1046}
1047
1048impl Content {
1049    pub fn apply(&mut self, modifier: &ContentModifier) {
1050        match *modifier {
1051            ContentModifier::Italic => {
1052                self.italic = true;
1053            },
1054            ContentModifier::Bold => {
1055                self.bold = true;
1056            },
1057            ContentModifier::Strikethrough => {
1058                self.strikethrough = true;
1059            },
1060            ContentModifier::Code => {
1061                self.code = true;
1062            },
1063            ContentModifier::Underline => {
1064                self.underline = true;
1065            },
1066            ContentModifier::Spoiler => {
1067                self.spoiler = true;
1068            },
1069        }
1070    }
1071}
1072
1073impl std::fmt::Display for Content {
1074    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1075        if self.spoiler {
1076            fmt.write_str("||")?;
1077        }
1078
1079        if self.bold {
1080            fmt.write_str("**")?;
1081        }
1082
1083        if self.italic {
1084            fmt.write_char('*')?;
1085        }
1086
1087        if self.strikethrough {
1088            fmt.write_str("~~")?;
1089        }
1090
1091        if self.underline {
1092            fmt.write_str("__")?;
1093        }
1094
1095        if self.code {
1096            fmt.write_char('`')?;
1097        }
1098
1099        fmt.write_str(&self.inner)?;
1100
1101        if self.code {
1102            fmt.write_char('`')?;
1103        }
1104
1105        if self.underline {
1106            fmt.write_str("__")?;
1107        }
1108
1109        if self.strikethrough {
1110            fmt.write_str("~~")?;
1111        }
1112
1113        if self.italic {
1114            fmt.write_char('*')?;
1115        }
1116
1117        if self.bold {
1118            fmt.write_str("**")?;
1119        }
1120
1121        if self.spoiler {
1122            fmt.write_str("||")?;
1123        }
1124
1125        Ok(())
1126    }
1127}
1128
1129impl<T: Into<String>> From<T> for Content {
1130    fn from(t: T) -> Content {
1131        Content {
1132            inner: t.into(),
1133            ..Default::default()
1134        }
1135    }
1136}
1137
1138fn normalize(text: &str) -> String {
1139    // Remove invite links and popular scam websites, mostly to prevent the current user from
1140    // triggering various ad detectors and prevent embeds.
1141    text.replace("discord.gg", "discord\u{2024}gg")
1142        .replace("discord.me", "discord\u{2024}me")
1143        .replace("discordlist.net", "discordlist\u{2024}net")
1144        .replace("discordservers.com", "discordservers\u{2024}com")
1145        .replace("discord.com/invite", "discord\u{2024}com/invite")
1146        .replace("discordapp.com/invite", "discordapp\u{2024}com/invite")
1147        // Remove right-to-left override and other similar annoying symbols
1148        .replace([
1149            '\u{202E}', // RTL Override
1150            '\u{200F}', // RTL Mark
1151            '\u{202B}', // RTL Embedding
1152            '\u{200B}', // Zero-width space
1153            '\u{200D}', // Zero-width joiner
1154            '\u{200C}', // Zero-width non-joiner
1155        ], " ")
1156        // Remove everyone and here mentions. Has to be put after ZWS replacement because it
1157        // utilises it itself.
1158        .replace("@everyone", "@\u{200B}everyone")
1159        .replace("@here", "@\u{200B}here")
1160}
1161
1162#[cfg(test)]
1163mod test {
1164    use super::ContentModifier::{Bold, Code, Italic, Spoiler};
1165    use super::MessageBuilder;
1166    use crate::model::prelude::*;
1167
1168    macro_rules! gen {
1169        ($($fn:ident => [$($text:expr => $expected:expr),+]),+) => ({
1170            $(
1171                $(
1172                    assert_eq!(MessageBuilder::new().$fn($text).0, $expected);
1173                )+
1174            )+
1175        });
1176    }
1177
1178    #[test]
1179    fn code_blocks() {
1180        let content = MessageBuilder::new().push_codeblock("test", Some("rb")).build();
1181        assert_eq!(content, "```rb\ntest\n```");
1182    }
1183
1184    #[test]
1185    fn safe_content() {
1186        let content = MessageBuilder::new().push_safe("@everyone discord.gg/discord-api").build();
1187        assert_ne!(content, "@everyone discord.gg/discord-api");
1188    }
1189
1190    #[test]
1191    fn no_free_formatting() {
1192        let content = MessageBuilder::new().push_bold_safe("test**test").build();
1193        assert_ne!(content, "**test**test**");
1194    }
1195
1196    #[test]
1197    fn mentions() {
1198        let content_emoji = MessageBuilder::new()
1199            .emoji(&Emoji {
1200                animated: false,
1201                available: true,
1202                id: EmojiId::new(32),
1203                name: "Rohrkatze".to_string(),
1204                managed: false,
1205                require_colons: true,
1206                roles: vec![],
1207                user: None,
1208            })
1209            .build();
1210        let content_mentions = MessageBuilder::new()
1211            .channel(ChannelId::new(1))
1212            .mention(&UserId::new(2))
1213            .role(RoleId::new(3))
1214            .user(UserId::new(4))
1215            .build();
1216        assert_eq!(content_mentions, "<#1><@2><@&3><@4>");
1217        assert_eq!(content_emoji, "<:Rohrkatze:32>");
1218    }
1219
1220    #[test]
1221    fn content() {
1222        let content = Bold + Italic + Code + "Fun!";
1223
1224        assert_eq!(content.to_string(), "***`Fun!`***");
1225
1226        let content = Spoiler + Bold + "Divert your eyes elsewhere";
1227
1228        assert_eq!(content.to_string(), "||**Divert your eyes elsewhere**||");
1229    }
1230
1231    #[test]
1232    fn init() {
1233        assert_eq!(MessageBuilder::new().0, "");
1234        assert_eq!(MessageBuilder::default().0, "");
1235    }
1236
1237    #[test]
1238    fn message_content() {
1239        let message_content = MessageBuilder::new().push(Bold + Italic + Code + "Fun!").build();
1240
1241        assert_eq!(message_content, "***`Fun!`***");
1242    }
1243
1244    #[test]
1245    fn message_content_safe() {
1246        let message_content = MessageBuilder::new().push_safe(Bold + Italic + "test**test").build();
1247
1248        assert_eq!(message_content, "***test\\*\\*test***");
1249    }
1250
1251    #[test]
1252    fn push() {
1253        assert_eq!(MessageBuilder::new().push('a').0, "a");
1254        assert!(MessageBuilder::new().push("").0.is_empty());
1255    }
1256
1257    #[test]
1258    fn push_codeblock() {
1259        let content = &MessageBuilder::new().push_codeblock("foo", None).0.clone();
1260        assert_eq!(content, "```\nfoo\n```");
1261
1262        let content = &MessageBuilder::new().push_codeblock("fn main() { }", Some("rs")).0.clone();
1263        assert_eq!(content, "```rs\nfn main() { }\n```");
1264    }
1265
1266    #[test]
1267    fn push_codeblock_safe() {
1268        assert_eq!(
1269            MessageBuilder::new().push_codeblock_safe("foo", Some("rs")).0,
1270            "```rs\nfoo\n```",
1271        );
1272        assert_eq!(MessageBuilder::new().push_codeblock_safe("", None).0, "```\n\n```",);
1273        assert_eq!(MessageBuilder::new().push_codeblock_safe("1 * 2", None).0, "```\n1 * 2\n```",);
1274        assert_eq!(
1275            MessageBuilder::new().push_codeblock_safe("`1 * 3`", None).0,
1276            "```\n`1 * 3`\n```",
1277        );
1278        assert_eq!(MessageBuilder::new().push_codeblock_safe("```.```", None).0, "```\n . \n```",);
1279    }
1280
1281    #[test]
1282    fn push_safe() {
1283        gen! {
1284            push_safe => [
1285                "" => "",
1286                "foo" => "foo",
1287                "1 * 2" => "1 \\* 2"
1288            ],
1289            push_bold_safe => [
1290                "" => "****",
1291                "foo" => "**foo**",
1292                "*foo*" => "***foo***",
1293                "f*o**o" => "**f*o o**"
1294            ],
1295            push_italic_safe => [
1296                "" => "__",
1297                "foo" => "_foo_",
1298                "f_o_o" => "_f o o_"
1299            ],
1300            push_mono_safe => [
1301                "" => "``",
1302                "foo" => "`foo`",
1303                "asterisk *" => "`asterisk *`",
1304                "`ticks`" => "`'ticks'`"
1305            ],
1306            push_strike_safe => [
1307                "" => "~~~~",
1308                "foo" => "~~foo~~",
1309                "foo ~" => "~~foo ~~~",
1310                "~~foo" => "~~ foo~~",
1311                "~~fo~~o~~" => "~~ fo o ~~"
1312            ],
1313            push_underline_safe => [
1314                "" => "____",
1315                "foo" => "__foo__",
1316                "foo _" => "__foo ___",
1317                "__foo__ bar" => "__ foo  bar__"
1318            ],
1319            push_spoiler_safe => [
1320                "" => "||||",
1321                "foo" => "||foo||",
1322                "foo |" => "||foo |||",
1323                "||foo|| bar" =>"|| foo  bar||"
1324            ],
1325            push_line_safe => [
1326                "" => "\n",
1327                "foo" => "foo\n",
1328                "1 * 2" => "1 \\* 2\n"
1329            ],
1330            push_mono_line_safe => [
1331                "" => "``\n",
1332                "a ` b `" => "`a ' b '`\n"
1333            ],
1334            push_italic_line_safe => [
1335                "" => "__\n",
1336                "a * c" => "_a * c_\n"
1337            ],
1338            push_bold_line_safe => [
1339                "" => "****\n",
1340                "a ** d" => "**a   d**\n"
1341            ],
1342            push_underline_line_safe => [
1343                "" => "____\n",
1344                "a __ e" => "__a   e__\n"
1345            ],
1346            push_strike_line_safe => [
1347                "" => "~~~~\n",
1348                "a ~~ f" => "~~a   f~~\n"
1349            ],
1350            push_spoiler_line_safe => [
1351                "" => "||||\n",
1352                "a || f" => "||a   f||\n"
1353            ]
1354        };
1355    }
1356
1357    #[test]
1358    fn push_unsafe() {
1359        gen! {
1360            push_bold => [
1361                "a" => "**a**",
1362                "" => "****",
1363                '*' => "*****",
1364                "**" => "******"
1365            ],
1366            push_bold_line => [
1367                "" => "****\n",
1368                "foo" => "**foo**\n"
1369            ],
1370            push_italic => [
1371                "a" => "_a_",
1372                "" => "__",
1373                "_" => "___",
1374                "__" => "____"
1375            ],
1376            push_italic_line => [
1377                "" => "__\n",
1378                "foo" => "_foo_\n",
1379                "_?" => "__?_\n"
1380            ],
1381            push_line => [
1382                "" => "\n",
1383                "foo" => "foo\n",
1384                "\n\n" => "\n\n\n",
1385                "\nfoo\n" => "\nfoo\n\n"
1386            ],
1387            push_mono => [
1388                "a" => "`a`",
1389                "" => "``",
1390                "`" => "```",
1391                "``" => "````"
1392            ],
1393            push_mono_line => [
1394                "" => "``\n",
1395                "foo" => "`foo`\n",
1396                "\n" => "`\n`\n",
1397                "`\n`\n" => "``\n`\n`\n"
1398            ],
1399            push_strike => [
1400                "a" => "~~a~~",
1401                "" => "~~~~",
1402                "~" => "~~~~~",
1403                "~~" => "~~~~~~"
1404            ],
1405            push_strike_line => [
1406                "" => "~~~~\n",
1407                "foo" => "~~foo~~\n"
1408            ],
1409            push_underline => [
1410                "a" => "__a__",
1411                "" => "____",
1412                "_" => "_____",
1413                "__" => "______"
1414            ],
1415            push_underline_line => [
1416                "" => "____\n",
1417                "foo" => "__foo__\n"
1418            ],
1419            push_spoiler => [
1420                "a" => "||a||",
1421                "" => "||||",
1422                "|" => "|||||",
1423                "||" => "||||||"
1424            ],
1425            push_spoiler_line => [
1426                "" => "||||\n",
1427                "foo" => "||foo||\n"
1428            ]
1429        };
1430    }
1431
1432    #[test]
1433    fn normalize() {
1434        assert_eq!(super::normalize("@everyone"), "@\u{200B}everyone");
1435        assert_eq!(super::normalize("@here"), "@\u{200B}here");
1436        assert_eq!(super::normalize("discord.gg"), "discord\u{2024}gg");
1437        assert_eq!(super::normalize("discord.me"), "discord\u{2024}me");
1438        assert_eq!(super::normalize("discordlist.net"), "discordlist\u{2024}net");
1439        assert_eq!(super::normalize("discordservers.com"), "discordservers\u{2024}com");
1440        assert_eq!(super::normalize("discord.com/invite"), "discord\u{2024}com/invite");
1441        assert_eq!(super::normalize("\u{202E}"), " ");
1442        assert_eq!(super::normalize("\u{200F}"), " ");
1443        assert_eq!(super::normalize("\u{202B}"), " ");
1444        assert_eq!(super::normalize("\u{200B}"), " ");
1445        assert_eq!(super::normalize("\u{200D}"), " ");
1446        assert_eq!(super::normalize("\u{200C}"), " ");
1447    }
1448}