mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 22:46:39 +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
|
// implementation details
|
||||||
mod serde_multipart;
|
mod serde_multipart;
|
||||||
|
mod util;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod codegen;
|
mod codegen;
|
||||||
|
|
|
@ -49,6 +49,20 @@ pub struct CallbackQuery {
|
||||||
pub game_short_name: Option<String>,
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::types::UserId;
|
use crate::types::UserId;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
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.
|
/// This object represents a chat.
|
||||||
///
|
///
|
||||||
|
@ -493,6 +493,23 @@ impl Chat {
|
||||||
_ => None,
|
_ => 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 {
|
mod serde_helper {
|
||||||
|
|
|
@ -18,3 +18,15 @@ pub struct ChatJoinRequest {
|
||||||
/// Chat invite link that was used by the user to send the join request
|
/// Chat invite link that was used by the user to send the join request
|
||||||
pub invite_link: Option<ChatInviteLink>,
|
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.
|
/// joining by invite link events only.
|
||||||
pub invite_link: Option<ChatInviteLink>,
|
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 serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{Animation, MessageEntity, PhotoSize};
|
use crate::types::{Animation, MessageEntity, PhotoSize, User};
|
||||||
|
|
||||||
/// This object represents a game.
|
/// This object represents a game.
|
||||||
///
|
///
|
||||||
|
@ -39,3 +39,17 @@ pub struct Game {
|
||||||
/// [@Botfather]: https://t.me/botfather
|
/// [@Botfather]: https://t.me/botfather
|
||||||
pub animation: Option<Animation>,
|
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<'_>>> {
|
pub fn parse_caption_entities(&self) -> Option<Vec<MessageEntityRef<'_>>> {
|
||||||
self.caption().zip(self.caption_entities()).map(|(t, e)| MessageEntityRef::parse(t, e))
|
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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::types::{MessageEntity, PollType};
|
use crate::types::{MessageEntity, PollType, User};
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -67,6 +67,20 @@ pub struct PollOption {
|
||||||
pub voter_count: i32,
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -59,6 +59,48 @@ impl Update {
|
||||||
Some(from)
|
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.
|
/// Returns the chat in which is update has happened, if any.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn chat(&self) -> Option<&Chat> {
|
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