mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +01:00
Add mentioned_users
to update types where it makes sense
This commit is contained in:
parent
16c20e371c
commit
35471a4a0b
10 changed files with 227 additions and 3 deletions
|
@ -117,6 +117,7 @@ mod bot;
|
|||
|
||||
// implementation details
|
||||
mod serde_multipart;
|
||||
mod util;
|
||||
|
||||
#[cfg(test)]
|
||||
mod codegen;
|
||||
|
|
|
@ -49,6 +49,20 @@ pub struct CallbackQuery {
|
|||
pub game_short_name: Option<String>,
|
||||
}
|
||||
|
||||
impl CallbackQuery {
|
||||
/// Returns all users that are "contained" in this `CallbackQuery`
|
||||
/// structure.
|
||||
///
|
||||
/// This might be useful to track information about users.
|
||||
/// Note that this function can return duplicate users.
|
||||
pub fn mentioned_users(&self) -> impl Iterator<Item = &User> {
|
||||
use crate::util::flatten;
|
||||
use std::iter::once;
|
||||
|
||||
once(&self.from).chain(flatten(self.message.as_ref().map(Message::mentioned_users)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::types::UserId;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::types::{ChatId, ChatLocation, ChatPermissions, ChatPhoto, Message, True};
|
||||
use crate::types::{ChatId, ChatLocation, ChatPermissions, ChatPhoto, Message, True, User};
|
||||
|
||||
/// This object represents a chat.
|
||||
///
|
||||
|
@ -493,6 +493,23 @@ impl Chat {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all users that are "contained" in this `Chat`
|
||||
/// structure.
|
||||
///
|
||||
/// This might be useful to track information about users.
|
||||
///
|
||||
/// Note that this function can return duplicate users.
|
||||
pub fn mentioned_users(&self) -> impl Iterator<Item = &User> {
|
||||
crate::util::flatten(self.pinned_message.as_ref().map(|m| m.mentioned_users()))
|
||||
}
|
||||
|
||||
/// `{Message, Chat}::mentioned_users` are mutually recursive, as such we
|
||||
/// can't use `->impl Iterator` everywhere, as it would make an infinite
|
||||
/// type. So we need to box somewhere.
|
||||
pub(crate) fn mentioned_users_rec(&self) -> impl Iterator<Item = &User> {
|
||||
crate::util::flatten(self.pinned_message.as_ref().map(|m| m.mentioned_users_rec()))
|
||||
}
|
||||
}
|
||||
|
||||
mod serde_helper {
|
||||
|
|
|
@ -18,3 +18,15 @@ pub struct ChatJoinRequest {
|
|||
/// Chat invite link that was used by the user to send the join request
|
||||
pub invite_link: Option<ChatInviteLink>,
|
||||
}
|
||||
|
||||
impl ChatJoinRequest {
|
||||
/// Returns all users that are "contained" in this `ChatJoinRequest`
|
||||
/// structure.
|
||||
///
|
||||
/// This might be useful to track information about users.
|
||||
///
|
||||
/// Note that this function can return duplicate users.
|
||||
pub fn mentioned_users(&self) -> impl Iterator<Item = &User> {
|
||||
std::iter::once(&self.from).chain(self.chat.mentioned_users())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,3 +20,21 @@ pub struct ChatMemberUpdated {
|
|||
/// joining by invite link events only.
|
||||
pub invite_link: Option<ChatInviteLink>,
|
||||
}
|
||||
|
||||
impl ChatMemberUpdated {
|
||||
/// Returns all users that are "contained" in this `ChatMemberUpdated`
|
||||
/// structure.
|
||||
///
|
||||
/// This might be useful to track information about users.
|
||||
///
|
||||
/// Note that this function can return duplicate users.
|
||||
pub fn mentioned_users(&self) -> impl Iterator<Item = &User> {
|
||||
[
|
||||
&self.from,
|
||||
/* ignore `old_chat_member.user`, it should always be the same as the new one */
|
||||
&self.new_chat_member.user,
|
||||
]
|
||||
.into_iter()
|
||||
.chain(self.chat.mentioned_users())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::types::{Animation, MessageEntity, PhotoSize};
|
||||
use crate::types::{Animation, MessageEntity, PhotoSize, User};
|
||||
|
||||
/// This object represents a game.
|
||||
///
|
||||
|
@ -39,3 +39,17 @@ pub struct Game {
|
|||
/// [@Botfather]: https://t.me/botfather
|
||||
pub animation: Option<Animation>,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
/// Returns all users that are "contained" in this `Game`
|
||||
/// structure.
|
||||
///
|
||||
/// This might be useful to track information about users.
|
||||
///
|
||||
/// Note that this function can return duplicate users.
|
||||
pub fn mentioned_users(&self) -> impl Iterator<Item = &User> {
|
||||
use crate::util::{flatten, mentioned_users_from_entities};
|
||||
|
||||
flatten(self.text_entities.as_deref().map(mentioned_users_from_entities))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1406,6 +1406,42 @@ impl Message {
|
|||
pub fn parse_caption_entities(&self) -> Option<Vec<MessageEntityRef<'_>>> {
|
||||
self.caption().zip(self.caption_entities()).map(|(t, e)| MessageEntityRef::parse(t, e))
|
||||
}
|
||||
|
||||
/// Returns all users that are "contained" in this `Message` structure.
|
||||
///
|
||||
/// This might be useful to track information about users.
|
||||
///
|
||||
/// Note that this function may return quite a few users as it scans
|
||||
/// replies, pinned messages, message entities and more. Also note that this
|
||||
/// function can return duplicate users.
|
||||
pub fn mentioned_users(&self) -> impl Iterator<Item = &User> {
|
||||
use crate::util::{flatten, mentioned_users_from_entities};
|
||||
|
||||
// Lets just hope we didn't forget something here...
|
||||
|
||||
self.from()
|
||||
.into_iter()
|
||||
.chain(self.via_bot.as_ref())
|
||||
.chain(self.chat.mentioned_users_rec())
|
||||
.chain(flatten(self.reply_to_message().map(Self::mentioned_users_rec)))
|
||||
.chain(flatten(self.new_chat_members()))
|
||||
.chain(self.left_chat_member())
|
||||
.chain(self.forward_from_user())
|
||||
.chain(flatten(self.forward_from_chat().map(Chat::mentioned_users_rec)))
|
||||
.chain(flatten(self.game().map(Game::mentioned_users)))
|
||||
.chain(flatten(self.entities().map(mentioned_users_from_entities)))
|
||||
.chain(flatten(self.caption_entities().map(mentioned_users_from_entities)))
|
||||
.chain(flatten(self.poll().map(Poll::mentioned_users)))
|
||||
.chain(flatten(self.proximity_alert_triggered().map(|a| [&a.traveler, &a.watcher])))
|
||||
.chain(flatten(self.video_chat_participants_invited().and_then(|i| i.users.as_deref())))
|
||||
}
|
||||
|
||||
/// `Message::mentioned_users` is recursive (due to replies), as such we
|
||||
/// can't use `->impl Iterator` everywhere, as it would make an infinite
|
||||
/// type. So we need to box somewhere.
|
||||
pub(crate) fn mentioned_users_rec(&self) -> Box<dyn Iterator<Item = &User> + Send + Sync + '_> {
|
||||
Box::new(self.mentioned_users())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::types::{MessageEntity, PollType};
|
||||
use crate::types::{MessageEntity, PollType, User};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -67,6 +67,20 @@ pub struct PollOption {
|
|||
pub voter_count: i32,
|
||||
}
|
||||
|
||||
impl Poll {
|
||||
/// Returns all users that are "contained" in this `Poll`
|
||||
/// structure.
|
||||
///
|
||||
/// This might be useful to track information about users.
|
||||
///
|
||||
/// Note that this function can return duplicate users.
|
||||
pub fn mentioned_users(&self) -> impl Iterator<Item = &User> {
|
||||
use crate::util::{flatten, mentioned_users_from_entities};
|
||||
|
||||
flatten(self.explanation_entities.as_deref().map(mentioned_users_from_entities))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -59,6 +59,48 @@ impl Update {
|
|||
Some(from)
|
||||
}
|
||||
|
||||
/// Returns all users that are "contained" in this `Update` structure.
|
||||
///
|
||||
/// This might be useful to track information about users.
|
||||
///
|
||||
/// Note that this function may return quite a few users as it scans
|
||||
/// replies, pinned messages, message entities, "via bot" fields and more.
|
||||
/// Also note that this function can return duplicate users.
|
||||
pub fn mentioned_users(&self) -> impl Iterator<Item = &User> {
|
||||
use either::Either::{Left, Right};
|
||||
use std::iter::{empty, once};
|
||||
|
||||
let i0 = Left;
|
||||
let i1 = |x| Right(Left(x));
|
||||
let i2 = |x| Right(Right(Left(x)));
|
||||
let i3 = |x| Right(Right(Right(Left(x))));
|
||||
let i4 = |x| Right(Right(Right(Right(Left(x)))));
|
||||
let i5 = |x| Right(Right(Right(Right(Right(Left(x))))));
|
||||
let i6 = |x| Right(Right(Right(Right(Right(Right(x))))));
|
||||
|
||||
match &self.kind {
|
||||
UpdateKind::Message(message)
|
||||
| UpdateKind::EditedMessage(message)
|
||||
| UpdateKind::ChannelPost(message)
|
||||
| UpdateKind::EditedChannelPost(message) => i0(message.mentioned_users()),
|
||||
|
||||
UpdateKind::InlineQuery(query) => i1(once(&query.from)),
|
||||
UpdateKind::ChosenInlineResult(query) => i1(once(&query.from)),
|
||||
UpdateKind::CallbackQuery(query) => i2(query.mentioned_users()),
|
||||
UpdateKind::ShippingQuery(query) => i1(once(&query.from)),
|
||||
UpdateKind::PreCheckoutQuery(query) => i1(once(&query.from)),
|
||||
UpdateKind::Poll(poll) => i3(poll.mentioned_users()),
|
||||
|
||||
UpdateKind::PollAnswer(answer) => i1(once(&answer.user)),
|
||||
|
||||
UpdateKind::MyChatMember(member) | UpdateKind::ChatMember(member) => {
|
||||
i4(member.mentioned_users())
|
||||
}
|
||||
UpdateKind::ChatJoinRequest(request) => i5(request.mentioned_users()),
|
||||
UpdateKind::Error(_) => i6(empty()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the chat in which is update has happened, if any.
|
||||
#[must_use]
|
||||
pub fn chat(&self) -> Option<&Chat> {
|
||||
|
|
56
crates/teloxide-core/src/util.rs
Normal file
56
crates/teloxide-core/src/util.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use crate::types::{MessageEntity, User};
|
||||
|
||||
/// Converts an optional iterator to a flattened iterator.
|
||||
pub(crate) fn flatten<I>(opt: Option<I>) -> impl Iterator<Item = I::Item>
|
||||
where
|
||||
I: IntoIterator,
|
||||
{
|
||||
struct Flat<I>(Option<I>);
|
||||
|
||||
impl<I> Iterator for Flat<I>
|
||||
where
|
||||
I: Iterator,
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.as_mut()?.next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match &self.0 {
|
||||
None => (0, Some(0)),
|
||||
Some(i) => i.size_hint(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Flat(opt.map(<_>::into_iter))
|
||||
}
|
||||
|
||||
pub(crate) fn mentioned_users_from_entities(
|
||||
entities: &[MessageEntity],
|
||||
) -> impl Iterator<Item = &User> {
|
||||
use crate::types::MessageEntityKind::*;
|
||||
|
||||
entities.iter().filter_map(|entity| match &entity.kind {
|
||||
TextMention { user } => Some(user),
|
||||
|
||||
Mention
|
||||
| Hashtag
|
||||
| Cashtag
|
||||
| BotCommand
|
||||
| Url
|
||||
| Email
|
||||
| PhoneNumber
|
||||
| Bold
|
||||
| Italic
|
||||
| Underline
|
||||
| Strikethrough
|
||||
| Spoiler
|
||||
| Code
|
||||
| Pre { language: _ }
|
||||
| TextLink { url: _ }
|
||||
| CustomEmoji { custom_emoji_id: _ } => None,
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue