sparkle_impostor/
later_messages.rs

1//! Handling the message not being the last one in the channel
2
3use twilight_model::channel::Message;
4#[cfg(doc)]
5use twilight_model::guild::Permissions;
6use twilight_validate::message::MESSAGE_CONTENT_LENGTH_MAX;
7
8use crate::{error::Error, thread, MessageSource};
9
10/// Info about the later messages in the channel
11#[derive(Debug, Clone, PartialEq)]
12pub struct Info {
13    /// Messages sent later
14    ///
15    /// Has to be an owned value because some methods set it from owned values
16    pub messages: Vec<Message>,
17    /// Whether there are no more messages
18    pub is_complete: bool,
19    /// Whether [`MessageSource::create`] was called
20    pub is_source_created: bool,
21    /// Whether [`MessageSource::later_messages`] or
22    /// [`MessageSource::later_messages_batched`] was called
23    pub is_later_message_sources_created: bool,
24}
25
26impl<'a> MessageSource<'a> {
27    /// Check if this is in the last `n` messages in the channel, return
28    /// [`Error::SourceAboveLimit`] if not
29    ///
30    /// Make sure the bot has these additional permissions
31    /// - [`Permissions::READ_MESSAGE_HISTORY`]
32    /// - [`Permissions::VIEW_CHANNEL`]
33    ///
34    /// # Warnings
35    ///
36    /// If [`MessageSource::create`] was called before this method, don't
37    /// account for that message when setting the limit
38    ///
39    /// If the bot doesn't have [`Permissions::READ_MESSAGE_HISTORY`], it'll act
40    /// as if this is the last message, since that's what Discord responds with
41    ///
42    /// # Errors
43    ///
44    /// Returns [`Error::SourceAboveLimit`] if the message isn't in the last `n`
45    /// messages
46    ///
47    /// Returns [`Error::Http`] if getting channel messages fails
48    ///
49    /// Returns [`Error::DeserializeBody`] if deserializing channel messages
50    /// fails
51    pub async fn check_is_in_last(&mut self, n: u16) -> Result<(), Error> {
52        self.set_later_messages(Some(n)).await?;
53
54        Ok(())
55    }
56
57    /// Return [`MessageSource`] for messages sent after this
58    ///
59    /// Make sure the bot has these additional permissions
60    /// - [`Permissions::READ_MESSAGE_HISTORY`]
61    /// - [`Permissions::VIEW_CHANNEL`]
62    ///
63    /// Returned message sources don't implicitly call the same methods as
64    /// `self`, this is intentional so that you can choose what to handle
65    /// yourself, if you want to handle each one the same way, simply extract
66    /// it to a function
67    ///
68    /// # Warnings
69    ///
70    /// This method is potentially very expensive unless
71    /// [`MessageSource::check_is_in_last`] was called
72    ///
73    /// If the bot doesn't have [`Permissions::READ_MESSAGE_HISTORY`], it'll
74    /// always return an empty vector, since that's what Discord responds with
75    ///
76    /// Should not be combined with [`MessageSource::later_messages_batched`]
77    ///
78    /// # Errors
79    ///
80    /// The vector element will be an error if the message can't be resent (See
81    /// [`MessageSource::from_message`])
82    ///
83    /// Returns [`Error::Http`] if getting channel messages fails
84    ///
85    /// Returns [`Error::DeserializeBody`] if deserializing channel messages
86    /// fails
87    pub async fn later_messages(&mut self) -> Result<Vec<Result<MessageSource<'_>, Error>>, Error> {
88        self.set_later_messages(None).await?;
89
90        Ok(self.later_message_sources())
91    }
92
93    /// Return [`MessageSource`] for messages sent after this after combining
94    /// messages from the same author to the same message
95    ///
96    /// This combines the messages' content separated with a newline, it's
97    /// provided to reduce the number of webhook executions
98    ///
99    /// See [`MessageSource::later_messages`] for more
100    ///
101    /// # Warnings
102    ///
103    /// Should not be combined with [`MessageSource::later_messages`]
104    ///
105    /// # Errors
106    ///
107    /// Returns [`Error::Http`] if getting channel messages fails
108    ///
109    /// Returns [`Error::DeserializeBody`] if deserializing channel messages
110    /// fails
111    pub async fn later_messages_batched(
112        &mut self,
113    ) -> Result<Vec<Result<MessageSource<'_>, Error>>, Error> {
114        self.set_later_messages(None).await?;
115
116        // clone to another vec because removing elements from the vec is more expensive
117        let mut messages_batched = vec![];
118
119        for message in self.later_messages.messages.clone() {
120            let Some(last_message) = messages_batched.last_mut() else {
121                messages_batched.push(message);
122                continue;
123            };
124
125            if last_message.author.id == message.author.id
126                && last_message
127                    .content
128                    .chars()
129                    .count()
130                    .saturating_add(message.content.chars().count())
131                    < MESSAGE_CONTENT_LENGTH_MAX
132            // not <= because we push '\n' too
133            {
134                last_message.content.push('\n');
135                last_message.content.push_str(&message.content);
136            } else {
137                messages_batched.push(message);
138            }
139        }
140        self.later_messages.messages = messages_batched;
141
142        Ok(self.later_message_sources())
143    }
144
145    async fn set_later_messages(&mut self, limit: Option<u16>) -> Result<(), Error> {
146        loop {
147            if let Some(limit_inner) = limit {
148                if self.later_messages.messages.len() >= usize::from(limit_inner) {
149                    return Err(Error::SourceAboveLimit(limit_inner));
150                }
151            }
152            if self.later_messages.is_complete {
153                return Ok(());
154            }
155
156            let mut message_batch = self
157                .http
158                .channel_messages(self.source_thread_id.unwrap_or(self.source_channel_id))
159                .limit(limit.unwrap_or(100).min(100))?
160                .after(
161                    self.later_messages
162                        .messages
163                        .last()
164                        .map_or(self.source_id, |message: &Message| message.id),
165                )
166                .await?
167                .models()
168                .await?;
169
170            for message in &mut message_batch {
171                message.guild_id = Some(self.guild_id);
172            }
173
174            self.later_messages.is_complete =
175                message_batch.is_empty() || message_batch.len() % 100 != 0;
176
177            self.later_messages.messages.extend(
178                message_batch
179                    .into_iter()
180                    // skip message sent in self.create
181                    .skip(usize::from(
182                        self.later_messages.is_source_created
183                            && self.source_thread_id == self.thread_info.id()
184                            && self.channel_id == self.source_channel_id
185                            && self.later_messages.messages.is_empty(),
186                    ))
187                    .rev()
188                    // skip the system message when used in threads
189                    .skip(usize::from(self.thread_info.id().is_some())),
190            );
191        }
192    }
193
194    fn later_message_sources(&mut self) -> Vec<Result<MessageSource<'_>, Error>> {
195        self.later_messages.is_later_message_sources_created = true;
196
197        self.later_messages
198            .messages
199            .iter()
200            .map(|message| {
201                MessageSource::from_message(message, self.http).map(|mut source| {
202                    source.thread_info = self
203                        .thread_info
204                        .id()
205                        .map_or(thread::Info::NotIn, thread::Info::In);
206                    source.channel_id = self.channel_id;
207                    source
208                })
209            })
210            .collect()
211    }
212}