mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 22:46:39 +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
|
### Added
|
||||||
|
|
||||||
- `ChatPermission::can_*` helper functions ([#851][pr851])
|
- `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
|
[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
|
## 0.9.1 - 2023-02-15
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -615,7 +615,7 @@ mod getters {
|
||||||
MessageGroupChatCreated, MessageInvoice, MessageLeftChatMember, MessageNewChatMembers,
|
MessageGroupChatCreated, MessageInvoice, MessageLeftChatMember, MessageNewChatMembers,
|
||||||
MessageNewChatPhoto, MessageNewChatTitle, MessagePassportData, MessagePinned,
|
MessageNewChatPhoto, MessageNewChatTitle, MessagePassportData, MessagePinned,
|
||||||
MessageProximityAlertTriggered, MessageSuccessfulPayment, MessageSupergroupChatCreated,
|
MessageProximityAlertTriggered, MessageSuccessfulPayment, MessageSupergroupChatCreated,
|
||||||
PhotoSize, True, User,
|
MessageVideoChatParticipantsInvited, PhotoSize, True, User,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Getters for [Message] fields from [telegram docs].
|
/// 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]
|
#[must_use]
|
||||||
pub fn reply_markup(&self) -> Option<&types::InlineKeyboardMarkup> {
|
pub fn reply_markup(&self) -> Option<&types::InlineKeyboardMarkup> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
|
@ -1394,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::*;
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub struct Update {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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
|
/// Returns the user that performed the action that caused this update, if
|
||||||
/// known.
|
/// known.
|
||||||
|
@ -37,7 +37,7 @@ impl Update {
|
||||||
/// This is generally the `from` field (except for `PollAnswer` where it's
|
/// 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).
|
/// `user` and `Poll` with `Error` which don't have such field at all).
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn user(&self) -> Option<&User> {
|
pub fn from(&self) -> Option<&User> {
|
||||||
use UpdateKind::*;
|
use UpdateKind::*;
|
||||||
|
|
||||||
let from = match &self.kind {
|
let from = match &self.kind {
|
||||||
|
@ -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> {
|
||||||
|
@ -82,6 +124,11 @@ impl Update {
|
||||||
|
|
||||||
Some(chat)
|
Some(chat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(note = "renamed to `from`", since = "0.10.0")]
|
||||||
|
pub fn user(&self) -> Option<&User> {
|
||||||
|
self.from()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[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