mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +01:00
Merge pull request #850 from teloxide/update_users
Rename `Update::{user -> from}`, add `Update::mentioned_users` & co
This commit is contained in:
commit
6bbf0ed386
11 changed files with 256 additions and 6 deletions
|
@ -10,9 +10,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Added
|
||||
|
||||
- `ChatPermission::can_*` helper functions ([#851][pr851])
|
||||
- `mentioned_users` functions for `CallbackQuery`, `Chat`, `ChatJoinRequest`, `ChatMemberUpdated`, `Game`, `Message`, `Poll`, `Update` which return all contained `User` instances ([#850][pr850])
|
||||
- `Message::video_chat_participants_invited` ([#850][pr850])
|
||||
- `Update::from`, a replacement for `Update::user` ([#850][pr850])
|
||||
|
||||
[pr851]: https://github.com/teloxide/teloxide/pull/851
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `Update::user`, use `Update::from` instead ([#850][pr850])
|
||||
|
||||
[pr850]: https://github.com/teloxide/teloxide/pull/850
|
||||
|
||||
## 0.9.1 - 2023-02-15
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -615,7 +615,7 @@ mod getters {
|
|||
MessageGroupChatCreated, MessageInvoice, MessageLeftChatMember, MessageNewChatMembers,
|
||||
MessageNewChatPhoto, MessageNewChatTitle, MessagePassportData, MessagePinned,
|
||||
MessageProximityAlertTriggered, MessageSuccessfulPayment, MessageSupergroupChatCreated,
|
||||
PhotoSize, True, User,
|
||||
MessageVideoChatParticipantsInvited, PhotoSize, True, User,
|
||||
};
|
||||
|
||||
/// Getters for [Message] fields from [telegram docs].
|
||||
|
@ -1178,6 +1178,18 @@ mod getters {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn video_chat_participants_invited(
|
||||
&self,
|
||||
) -> Option<&types::VideoChatParticipantsInvited> {
|
||||
match &self.kind {
|
||||
VideoChatParticipantsInvited(MessageVideoChatParticipantsInvited {
|
||||
video_chat_participants_invited,
|
||||
}) => Some(video_chat_participants_invited),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn reply_markup(&self) -> Option<&types::InlineKeyboardMarkup> {
|
||||
match &self.kind {
|
||||
|
@ -1394,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::*;
|
||||
|
|
|
@ -29,7 +29,7 @@ pub struct Update {
|
|||
}
|
||||
|
||||
impl Update {
|
||||
// FIXME: rename user => from, add mentioned_users -> impl Iterator<&User>
|
||||
// FIXME: add mentioned_users -> impl Iterator<&User>
|
||||
|
||||
/// Returns the user that performed the action that caused this update, if
|
||||
/// known.
|
||||
|
@ -37,7 +37,7 @@ impl Update {
|
|||
/// This is generally the `from` field (except for `PollAnswer` where it's
|
||||
/// `user` and `Poll` with `Error` which don't have such field at all).
|
||||
#[must_use]
|
||||
pub fn user(&self) -> Option<&User> {
|
||||
pub fn from(&self) -> Option<&User> {
|
||||
use UpdateKind::*;
|
||||
|
||||
let from = match &self.kind {
|
||||
|
@ -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> {
|
||||
|
@ -82,6 +124,11 @@ impl Update {
|
|||
|
||||
Some(chat)
|
||||
}
|
||||
|
||||
#[deprecated(note = "renamed to `from`", since = "0.10.0")]
|
||||
pub fn user(&self) -> Option<&User> {
|
||||
self.from()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
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