Move set_channel_participant_status and similar methods to DialogParticipantManager.

This commit is contained in:
levlam 2024-01-08 18:44:13 +03:00
parent 9660a7f2b1
commit b1d72276a7
5 changed files with 633 additions and 622 deletions

View File

@ -2280,219 +2280,6 @@ class DeleteChatUserQuery final : public Td::ResultHandler {
}
};
class JoinChannelQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
public:
explicit JoinChannelQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id) {
channel_id_ = channel_id;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(
G()->net_query_creator().create(telegram_api::channels_joinChannel(std::move(input_channel)), {{channel_id}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_joinChannel>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for JoinChannelQuery: " << to_string(ptr);
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(Status status) final {
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "JoinChannelQuery");
promise_.set_error(std::move(status));
}
};
class InviteToChannelQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
vector<UserId> user_ids_;
public:
explicit InviteToChannelQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, vector<UserId> user_ids,
vector<tl_object_ptr<telegram_api::InputUser>> &&input_users) {
channel_id_ = channel_id;
user_ids_ = std::move(user_ids);
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(G()->net_query_creator().create(
telegram_api::channels_inviteToChannel(std::move(input_channel), std::move(input_users))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_inviteToChannel>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for InviteToChannelQuery: " << to_string(ptr);
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "InviteToChannelQuery");
auto user_ids = td_->updates_manager_->extract_group_invite_privacy_forbidden_updates(ptr);
auto promise = PromiseCreator::lambda([dialog_id = DialogId(channel_id_), user_ids = std::move(user_ids),
promise = std::move(promise_)](Result<Unit> &&result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
promise.set_value(Unit());
if (!user_ids.empty()) {
send_closure(G()->contacts_manager(), &ContactsManager::send_update_add_chat_members_privacy_forbidden,
dialog_id, std::move(user_ids), "InviteToChannelQuery");
}
});
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise));
}
void on_error(Status status) final {
if (!td_->auth_manager_->is_bot() && status.message() == "USER_PRIVACY_RESTRICTED") {
td_->contacts_manager_->send_update_add_chat_members_privacy_forbidden(
DialogId(channel_id_), std::move(user_ids_), "InviteToChannelQuery");
return promise_.set_error(Status::Error(406, "USER_PRIVACY_RESTRICTED"));
}
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "InviteToChannelQuery");
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "InviteToChannelQuery");
promise_.set_error(std::move(status));
}
};
class EditChannelAdminQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
UserId user_id_;
DialogParticipantStatus status_ = DialogParticipantStatus::Left();
public:
explicit EditChannelAdminQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, UserId user_id, tl_object_ptr<telegram_api::InputUser> &&input_user,
const DialogParticipantStatus &status) {
channel_id_ = channel_id;
user_id_ = user_id;
status_ = status;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(G()->net_query_creator().create(telegram_api::channels_editAdmin(
std::move(input_channel), std::move(input_user), status.get_chat_admin_rights(), status.get_rank())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_editAdmin>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for EditChannelAdminQuery: " << to_string(ptr);
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "EditChannelAdminQuery");
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
td_->dialog_participant_manager_->on_set_channel_participant_status(channel_id_, DialogId(user_id_), status_);
}
void on_error(Status status) final {
if (!td_->auth_manager_->is_bot() && status.message() == "USER_PRIVACY_RESTRICTED") {
td_->contacts_manager_->send_update_add_chat_members_privacy_forbidden(DialogId(channel_id_), {user_id_},
"EditChannelAdminQuery");
return promise_.set_error(Status::Error(406, "USER_PRIVACY_RESTRICTED"));
}
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "EditChannelAdminQuery");
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "EditChannelAdminQuery");
promise_.set_error(std::move(status));
}
};
class EditChannelBannedQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
DialogId participant_dialog_id_;
DialogParticipantStatus status_ = DialogParticipantStatus::Left();
public:
explicit EditChannelBannedQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, DialogId participant_dialog_id, tl_object_ptr<telegram_api::InputPeer> &&input_peer,
const DialogParticipantStatus &status) {
channel_id_ = channel_id;
participant_dialog_id_ = participant_dialog_id;
status_ = status;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(G()->net_query_creator().create(telegram_api::channels_editBanned(
std::move(input_channel), std::move(input_peer), status.get_chat_banned_rights())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_editBanned>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for EditChannelBannedQuery: " << to_string(ptr);
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "EditChannelBannedQuery");
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
td_->dialog_participant_manager_->on_set_channel_participant_status(channel_id_, participant_dialog_id_, status_);
}
void on_error(Status status) final {
if (participant_dialog_id_.get_type() != DialogType::Channel) {
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "EditChannelBannedQuery");
}
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "EditChannelBannedQuery");
promise_.set_error(std::move(status));
}
};
class LeaveChannelQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
public:
explicit LeaveChannelQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id) {
channel_id_ = channel_id;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(
G()->net_query_creator().create(telegram_api::channels_leaveChannel(std::move(input_channel)), {{channel_id}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_leaveChannel>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for LeaveChannelQuery: " << to_string(ptr);
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(Status status) final {
if (status.message() == "USER_NOT_PARTICIPANT") {
return td_->contacts_manager_->reload_channel(channel_id_, std::move(promise_), "LeaveChannelQuery");
}
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "LeaveChannelQuery");
promise_.set_error(std::move(status));
}
};
class CanEditChannelCreatorQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
@ -8036,232 +7823,6 @@ void ContactsManager::add_chat_participant(ChatId chat_id, UserId user_id, int32
->send(chat_id, user_id, std::move(input_user), forward_limit);
}
void ContactsManager::add_channel_participant(ChannelId channel_id, UserId user_id,
const DialogParticipantStatus &old_status, Promise<Unit> &&promise) {
if (td_->auth_manager_->is_bot()) {
return promise.set_error(Status::Error(400, "Bots can't add new chat members"));
}
const Channel *c = get_channel(channel_id);
if (c == nullptr) {
return promise.set_error(Status::Error(400, "Chat info not found"));
}
TRY_RESULT_PROMISE(promise, input_user, get_input_user(user_id));
if (user_id == get_my_id()) {
// join the channel
if (get_channel_status(c).is_banned()) {
return promise.set_error(Status::Error(400, "Can't return to kicked from chat"));
}
if (!get_channel_join_request(c)) {
speculative_add_channel_user(channel_id, user_id, DialogParticipantStatus::Member(), c->status);
}
td_->create_handler<JoinChannelQuery>(std::move(promise))->send(channel_id);
return;
}
if (!get_channel_permissions(c).can_invite_users()) {
return promise.set_error(Status::Error(400, "Not enough rights to invite members to the supergroup chat"));
}
speculative_add_channel_user(channel_id, user_id, DialogParticipantStatus::Member(), old_status);
vector<tl_object_ptr<telegram_api::InputUser>> input_users;
input_users.push_back(std::move(input_user));
td_->create_handler<InviteToChannelQuery>(std::move(promise))->send(channel_id, {user_id}, std::move(input_users));
}
void ContactsManager::add_channel_participants(ChannelId channel_id, const vector<UserId> &user_ids,
Promise<Unit> &&promise) {
if (td_->auth_manager_->is_bot()) {
return promise.set_error(Status::Error(400, "Bots can't add new chat members"));
}
const Channel *c = get_channel(channel_id);
if (c == nullptr) {
return promise.set_error(Status::Error(400, "Chat info not found"));
}
if (!get_channel_permissions(c).can_invite_users()) {
return promise.set_error(Status::Error(400, "Not enough rights to invite members to the supergroup chat"));
}
vector<tl_object_ptr<telegram_api::InputUser>> input_users;
for (auto user_id : user_ids) {
TRY_RESULT_PROMISE(promise, input_user, get_input_user(user_id));
if (user_id == get_my_id()) {
// can't invite self
continue;
}
input_users.push_back(std::move(input_user));
speculative_add_channel_user(channel_id, user_id, DialogParticipantStatus::Member(),
DialogParticipantStatus::Left());
}
if (input_users.empty()) {
return promise.set_value(Unit());
}
td_->create_handler<InviteToChannelQuery>(std::move(promise))->send(channel_id, user_ids, std::move(input_users));
}
void ContactsManager::set_channel_participant_status(ChannelId channel_id, DialogId participant_dialog_id,
td_api::object_ptr<td_api::ChatMemberStatus> &&chat_member_status,
Promise<Unit> &&promise) {
auto c = get_channel(channel_id);
if (c == nullptr) {
return promise.set_error(Status::Error(400, "Chat info not found"));
}
auto status = get_dialog_participant_status(chat_member_status, get_channel_type(c));
if (participant_dialog_id == DialogId(get_my_id())) {
// fast path is needed, because get_channel_status may return Creator, while GetChannelParticipantQuery returning Left
return set_channel_participant_status_impl(channel_id, participant_dialog_id, std::move(status),
get_channel_status(c), std::move(promise));
}
if (participant_dialog_id.get_type() != DialogType::User) {
if (status.is_administrator() || status.is_member() || status.is_restricted()) {
return promise.set_error(Status::Error(400, "Other chats can be only banned or unbanned"));
}
// always pretend that old_status is different
return restrict_channel_participant(
channel_id, participant_dialog_id, std::move(status),
status.is_banned() ? DialogParticipantStatus::Left() : DialogParticipantStatus::Banned(0), std::move(promise));
}
auto on_result_promise =
PromiseCreator::lambda([actor_id = actor_id(this), channel_id, participant_dialog_id, status,
promise = std::move(promise)](Result<DialogParticipant> r_dialog_participant) mutable {
// ResultHandlers are cleared before managers, so it is safe to capture this
if (r_dialog_participant.is_error()) {
return promise.set_error(r_dialog_participant.move_as_error());
}
send_closure(actor_id, &ContactsManager::set_channel_participant_status_impl, channel_id, participant_dialog_id,
std::move(status), r_dialog_participant.ok().status_, std::move(promise));
});
td_->dialog_participant_manager_->get_channel_participant(channel_id, participant_dialog_id,
std::move(on_result_promise));
}
void ContactsManager::set_channel_participant_status_impl(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus new_status,
DialogParticipantStatus old_status, Promise<Unit> &&promise) {
if (old_status == new_status && !old_status.is_creator()) {
return promise.set_value(Unit());
}
CHECK(participant_dialog_id.get_type() == DialogType::User);
LOG(INFO) << "Change status of " << participant_dialog_id << " in " << channel_id << " from " << old_status << " to "
<< new_status;
bool need_add = false;
bool need_promote = false;
bool need_restrict = false;
if (new_status.is_creator() || old_status.is_creator()) {
if (!old_status.is_creator()) {
return promise.set_error(Status::Error(400, "Can't add another owner to the chat"));
}
if (!new_status.is_creator()) {
return promise.set_error(Status::Error(400, "Can't remove chat owner"));
}
auto user_id = get_my_id();
if (participant_dialog_id != DialogId(user_id)) {
return promise.set_error(Status::Error(400, "Not enough rights to edit chat owner rights"));
}
if (new_status.is_member() == old_status.is_member()) {
// change rank and is_anonymous
auto r_input_user = get_input_user(user_id);
CHECK(r_input_user.is_ok());
td_->create_handler<EditChannelAdminQuery>(std::move(promise))
->send(channel_id, user_id, r_input_user.move_as_ok(), new_status);
return;
}
if (new_status.is_member()) {
// creator not member -> creator member
need_add = true;
} else {
// creator member -> creator not member
need_restrict = true;
}
} else if (new_status.is_administrator()) {
need_promote = true;
} else if (!new_status.is_member() || new_status.is_restricted()) {
if (new_status.is_member() && !old_status.is_member()) {
// TODO there is no way in server API to invite someone and change restrictions
// we need to first add user and change restrictions again after that
// but if restrictions aren't changed, then adding is enough
auto copy_old_status = old_status;
copy_old_status.set_is_member(true);
if (copy_old_status == new_status) {
need_add = true;
} else {
need_restrict = true;
}
} else {
need_restrict = true;
}
} else {
// regular member
if (old_status.is_administrator()) {
need_promote = true;
} else if (old_status.is_restricted() || old_status.is_banned()) {
need_restrict = true;
} else {
CHECK(!old_status.is_member());
need_add = true;
}
}
if (need_promote) {
if (participant_dialog_id.get_type() != DialogType::User) {
return promise.set_error(Status::Error(400, "Can't promote chats to chat administrators"));
}
return promote_channel_participant(channel_id, participant_dialog_id.get_user_id(), new_status, old_status,
std::move(promise));
} else if (need_restrict) {
return restrict_channel_participant(channel_id, participant_dialog_id, std::move(new_status), std::move(old_status),
std::move(promise));
} else {
CHECK(need_add);
if (participant_dialog_id.get_type() != DialogType::User) {
return promise.set_error(Status::Error(400, "Can't add chats as chat members"));
}
return add_channel_participant(channel_id, participant_dialog_id.get_user_id(), old_status, std::move(promise));
}
}
void ContactsManager::promote_channel_participant(ChannelId channel_id, UserId user_id,
const DialogParticipantStatus &new_status,
const DialogParticipantStatus &old_status, Promise<Unit> &&promise) {
LOG(INFO) << "Promote " << user_id << " in " << channel_id << " from " << old_status << " to " << new_status;
const Channel *c = get_channel(channel_id);
CHECK(c != nullptr);
if (user_id == get_my_id()) {
if (new_status.is_administrator()) {
return promise.set_error(Status::Error(400, "Can't promote self"));
}
CHECK(new_status.is_member());
// allow to demote self. TODO is it allowed server-side?
} else {
if (!get_channel_permissions(c).can_promote_members()) {
return promise.set_error(Status::Error(400, "Not enough rights"));
}
CHECK(!old_status.is_creator());
CHECK(!new_status.is_creator());
}
TRY_RESULT_PROMISE(promise, input_user, get_input_user(user_id));
speculative_add_channel_user(channel_id, user_id, new_status, old_status);
td_->create_handler<EditChannelAdminQuery>(std::move(promise))
->send(channel_id, user_id, std::move(input_user), new_status);
}
void ContactsManager::set_chat_participant_status(ChatId chat_id, UserId user_id, DialogParticipantStatus status,
Promise<Unit> &&promise) {
if (!status.is_member()) {
@ -8493,129 +8054,6 @@ void ContactsManager::delete_chat_participant(ChatId chat_id, UserId user_id, bo
td_->create_handler<DeleteChatUserQuery>(std::move(promise))->send(chat_id, std::move(input_user), revoke_messages);
}
void ContactsManager::restrict_channel_participant(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus &&new_status,
DialogParticipantStatus &&old_status, Promise<Unit> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
LOG(INFO) << "Restrict " << participant_dialog_id << " in " << channel_id << " from " << old_status << " to "
<< new_status;
const Channel *c = get_channel(channel_id);
if (c == nullptr) {
return promise.set_error(Status::Error(400, "Chat info not found"));
}
if (!c->status.is_member() && !c->status.is_creator()) {
if (participant_dialog_id == DialogId(get_my_id())) {
if (new_status.is_member()) {
return promise.set_error(Status::Error(400, "Can't unrestrict self"));
}
return promise.set_value(Unit());
} else {
return promise.set_error(Status::Error(400, "Not in the chat"));
}
}
auto input_peer = td_->dialog_manager_->get_input_peer(participant_dialog_id, AccessRights::Know);
if (input_peer == nullptr) {
return promise.set_error(Status::Error(400, "Member not found"));
}
if (participant_dialog_id == DialogId(get_my_id())) {
if (new_status.is_restricted() || new_status.is_banned()) {
return promise.set_error(Status::Error(400, "Can't restrict self"));
}
if (new_status.is_member()) {
return promise.set_error(Status::Error(400, "Can't unrestrict self"));
}
// leave the channel
speculative_add_channel_user(channel_id, participant_dialog_id.get_user_id(), new_status, c->status);
td_->create_handler<LeaveChannelQuery>(std::move(promise))->send(channel_id);
return;
}
switch (participant_dialog_id.get_type()) {
case DialogType::User:
// ok;
break;
case DialogType::Channel:
if (new_status.is_administrator() || new_status.is_member() || new_status.is_restricted()) {
return promise.set_error(Status::Error(400, "Other chats can be only banned or unbanned"));
}
break;
default:
return promise.set_error(Status::Error(400, "Can't restrict the chat"));
}
CHECK(!old_status.is_creator());
CHECK(!new_status.is_creator());
if (!get_channel_permissions(c).can_restrict_members()) {
return promise.set_error(Status::Error(400, "Not enough rights to restrict/unrestrict chat member"));
}
if (old_status.is_member() && !new_status.is_member() && !new_status.is_banned()) {
// we can't make participant Left without kicking it first
auto on_result_promise = PromiseCreator::lambda([actor_id = actor_id(this), channel_id, participant_dialog_id,
new_status = std::move(new_status),
promise = std::move(promise)](Result<> result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
create_actor<SleepActor>(
"RestrictChannelParticipantSleepActor", 1.0,
PromiseCreator::lambda([actor_id, channel_id, participant_dialog_id, new_status = std::move(new_status),
promise = std::move(promise)](Result<> result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
send_closure(actor_id, &ContactsManager::restrict_channel_participant, channel_id, participant_dialog_id,
std::move(new_status), DialogParticipantStatus::Banned(0), std::move(promise));
}))
.release();
});
promise = std::move(on_result_promise);
new_status = DialogParticipantStatus::Banned(G()->unix_time() + 60);
}
if (new_status.is_member() && !old_status.is_member()) {
// there is no way in server API to invite someone and change restrictions
// we need to first change restrictions and then try to add the user
CHECK(participant_dialog_id.get_type() == DialogType::User);
new_status.set_is_member(false);
auto on_result_promise =
PromiseCreator::lambda([actor_id = actor_id(this), channel_id, participant_dialog_id, old_status = new_status,
promise = std::move(promise)](Result<> result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
create_actor<SleepActor>(
"AddChannelParticipantSleepActor", 1.0,
PromiseCreator::lambda([actor_id, channel_id, participant_dialog_id, old_status = std::move(old_status),
promise = std::move(promise)](Result<> result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
send_closure(actor_id, &ContactsManager::add_channel_participant, channel_id,
participant_dialog_id.get_user_id(), old_status, std::move(promise));
}))
.release();
});
promise = std::move(on_result_promise);
}
if (participant_dialog_id.get_type() == DialogType::User) {
speculative_add_channel_user(channel_id, participant_dialog_id.get_user_id(), new_status, old_status);
}
td_->create_handler<EditChannelBannedQuery>(std::move(promise))
->send(channel_id, participant_dialog_id, std::move(input_peer), new_status);
}
ChannelId ContactsManager::migrate_chat_to_megagroup(ChatId chat_id, Promise<Unit> &promise) {
auto c = get_chat(chat_id);
if (c == nullptr) {
@ -17388,32 +16826,6 @@ std::pair<int32, vector<DialogId>> ContactsManager::search_among_dialogs(const v
return {narrow_cast<int32>(result.first), transform(result.second, [](int64 key) { return DialogId(key); })};
}
void ContactsManager::leave_dialog(DialogId dialog_id, Promise<Unit> &&promise) {
if (!td_->dialog_manager_->have_dialog_force(dialog_id, "leave_dialog")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
switch (dialog_id.get_type()) {
case DialogType::User:
return promise.set_error(Status::Error(400, "Can't leave private chats"));
case DialogType::Chat:
return delete_chat_participant(dialog_id.get_chat_id(), get_my_id(), false, std::move(promise));
case DialogType::Channel: {
auto channel_id = dialog_id.get_channel_id();
auto old_status = get_channel_status(channel_id);
auto new_status = old_status;
new_status.set_is_member(false);
return restrict_channel_participant(channel_id, DialogId(get_my_id()), std::move(new_status),
std::move(old_status), std::move(promise));
}
case DialogType::SecretChat:
return promise.set_error(Status::Error(400, "Can't leave secret chats"));
case DialogType::None:
default:
UNREACHABLE();
}
}
DialogParticipants ContactsManager::search_private_chat_participants(UserId my_user_id, UserId peer_user_id,
const string &query, int32 limit,
DialogParticipantFilter filter) const {

View File

@ -649,24 +649,16 @@ class ContactsManager final : public Actor {
void add_chat_participant(ChatId chat_id, UserId user_id, int32 forward_limit, Promise<Unit> &&promise);
void add_channel_participant(ChannelId channel_id, UserId user_id, const DialogParticipantStatus &old_status,
Promise<Unit> &&promise);
void add_channel_participants(ChannelId channel_id, const vector<UserId> &user_ids, Promise<Unit> &&promise);
void set_chat_participant_status(ChatId chat_id, UserId user_id, DialogParticipantStatus status,
Promise<Unit> &&promise);
void set_channel_participant_status(ChannelId channel_id, DialogId participant_dialog_id,
td_api::object_ptr<td_api::ChatMemberStatus> &&chat_member_status,
Promise<Unit> &&promise);
void delete_chat_participant(ChatId chat_id, UserId user_id, bool revoke_messages, Promise<Unit> &&promise);
void leave_dialog(DialogId dialog_id, Promise<Unit> &&promise);
void get_chat_participant(ChatId chat_id, UserId user_id, Promise<DialogParticipant> &&promise);
void speculative_add_channel_user(ChannelId channel_id, UserId user_id, const DialogParticipantStatus &new_status,
const DialogParticipantStatus &old_status);
void search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, DialogParticipantFilter filter,
Promise<DialogParticipants> &&promise);
@ -1532,9 +1524,6 @@ class ContactsManager final : public Actor {
void speculative_add_channel_participant_count(ChannelId channel_id, int32 delta_participant_count, bool by_me);
void speculative_add_channel_user(ChannelId channel_id, UserId user_id, const DialogParticipantStatus &new_status,
const DialogParticipantStatus &old_status);
void drop_chat_full(ChatId chat_id);
void do_invalidate_channel_full(ChannelFull *channel_full, ChannelId channel_id, bool need_drop_slow_mode_delay);
@ -1816,17 +1805,6 @@ class ContactsManager final : public Actor {
tl_object_ptr<telegram_api::channels_channelParticipants> &&channel_participants,
Promise<DialogParticipants> &&promise);
void set_channel_participant_status_impl(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus new_status, DialogParticipantStatus old_status,
Promise<Unit> &&promise);
void promote_channel_participant(ChannelId channel_id, UserId user_id, const DialogParticipantStatus &new_status,
const DialogParticipantStatus &old_status, Promise<Unit> &&promise);
void restrict_channel_participant(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus &&new_status, DialogParticipantStatus &&old_status,
Promise<Unit> &&promise);
void transfer_channel_ownership(ChannelId channel_id, UserId user_id,
tl_object_ptr<telegram_api::InputCheckPasswordSRP> input_check_password,
Promise<Unit> &&promise);

View File

@ -13,6 +13,7 @@
#include "td/telegram/DialogFilter.hpp"
#include "td/telegram/DialogFilterInviteLink.h"
#include "td/telegram/DialogManager.h"
#include "td/telegram/DialogParticipantManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/LinkManager.h"
#include "td/telegram/logevent/LogEvent.h"
@ -1625,7 +1626,7 @@ void DialogFilterManager::delete_dialog_filter(DialogFilterId dialog_filter_id,
auto lock = mpas.get_promise();
for (auto &leave_dialog_id : leave_dialog_ids) {
td_->contacts_manager_->leave_dialog(leave_dialog_id, mpas.get_promise());
td_->dialog_participant_manager_->leave_dialog(leave_dialog_id, mpas.get_promise());
}
lock.set_value(Unit());

View File

@ -23,6 +23,7 @@
#include "td/db/SqliteKeyValueAsync.h"
#include "td/actor/MultiPromise.h"
#include "td/actor/SleepActor.h"
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
@ -365,6 +366,219 @@ class GetChannelParticipantQuery final : public Td::ResultHandler {
}
};
class JoinChannelQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
public:
explicit JoinChannelQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id) {
channel_id_ = channel_id;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(
G()->net_query_creator().create(telegram_api::channels_joinChannel(std::move(input_channel)), {{channel_id}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_joinChannel>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for JoinChannelQuery: " << to_string(ptr);
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(Status status) final {
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "JoinChannelQuery");
promise_.set_error(std::move(status));
}
};
class InviteToChannelQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
vector<UserId> user_ids_;
public:
explicit InviteToChannelQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, vector<UserId> user_ids,
vector<tl_object_ptr<telegram_api::InputUser>> &&input_users) {
channel_id_ = channel_id;
user_ids_ = std::move(user_ids);
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(G()->net_query_creator().create(
telegram_api::channels_inviteToChannel(std::move(input_channel), std::move(input_users))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_inviteToChannel>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for InviteToChannelQuery: " << to_string(ptr);
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "InviteToChannelQuery");
auto user_ids = td_->updates_manager_->extract_group_invite_privacy_forbidden_updates(ptr);
auto promise = PromiseCreator::lambda([dialog_id = DialogId(channel_id_), user_ids = std::move(user_ids),
promise = std::move(promise_)](Result<Unit> &&result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
promise.set_value(Unit());
if (!user_ids.empty()) {
send_closure(G()->contacts_manager(), &ContactsManager::send_update_add_chat_members_privacy_forbidden,
dialog_id, std::move(user_ids), "InviteToChannelQuery");
}
});
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise));
}
void on_error(Status status) final {
if (!td_->auth_manager_->is_bot() && status.message() == "USER_PRIVACY_RESTRICTED") {
td_->contacts_manager_->send_update_add_chat_members_privacy_forbidden(
DialogId(channel_id_), std::move(user_ids_), "InviteToChannelQuery");
return promise_.set_error(Status::Error(406, "USER_PRIVACY_RESTRICTED"));
}
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "InviteToChannelQuery");
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "InviteToChannelQuery");
promise_.set_error(std::move(status));
}
};
class EditChannelAdminQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
UserId user_id_;
DialogParticipantStatus status_ = DialogParticipantStatus::Left();
public:
explicit EditChannelAdminQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, UserId user_id, tl_object_ptr<telegram_api::InputUser> &&input_user,
const DialogParticipantStatus &status) {
channel_id_ = channel_id;
user_id_ = user_id;
status_ = status;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(G()->net_query_creator().create(telegram_api::channels_editAdmin(
std::move(input_channel), std::move(input_user), status.get_chat_admin_rights(), status.get_rank())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_editAdmin>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for EditChannelAdminQuery: " << to_string(ptr);
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "EditChannelAdminQuery");
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
td_->dialog_participant_manager_->on_set_channel_participant_status(channel_id_, DialogId(user_id_), status_);
}
void on_error(Status status) final {
if (!td_->auth_manager_->is_bot() && status.message() == "USER_PRIVACY_RESTRICTED") {
td_->contacts_manager_->send_update_add_chat_members_privacy_forbidden(DialogId(channel_id_), {user_id_},
"EditChannelAdminQuery");
return promise_.set_error(Status::Error(406, "USER_PRIVACY_RESTRICTED"));
}
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "EditChannelAdminQuery");
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "EditChannelAdminQuery");
promise_.set_error(std::move(status));
}
};
class EditChannelBannedQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
DialogId participant_dialog_id_;
DialogParticipantStatus status_ = DialogParticipantStatus::Left();
public:
explicit EditChannelBannedQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, DialogId participant_dialog_id, tl_object_ptr<telegram_api::InputPeer> &&input_peer,
const DialogParticipantStatus &status) {
channel_id_ = channel_id;
participant_dialog_id_ = participant_dialog_id;
status_ = status;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(G()->net_query_creator().create(telegram_api::channels_editBanned(
std::move(input_channel), std::move(input_peer), status.get_chat_banned_rights())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_editBanned>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for EditChannelBannedQuery: " << to_string(ptr);
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "EditChannelBannedQuery");
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
td_->dialog_participant_manager_->on_set_channel_participant_status(channel_id_, participant_dialog_id_, status_);
}
void on_error(Status status) final {
if (participant_dialog_id_.get_type() != DialogType::Channel) {
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "EditChannelBannedQuery");
}
td_->contacts_manager_->invalidate_channel_full(channel_id_, false, "EditChannelBannedQuery");
promise_.set_error(std::move(status));
}
};
class LeaveChannelQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
public:
explicit LeaveChannelQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id) {
channel_id_ = channel_id;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(
G()->net_query_creator().create(telegram_api::channels_leaveChannel(std::move(input_channel)), {{channel_id}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_leaveChannel>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for LeaveChannelQuery: " << to_string(ptr);
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(Status status) final {
if (status.message() == "USER_NOT_PARTICIPANT") {
return td_->contacts_manager_->reload_channel(channel_id_, std::move(promise_), "LeaveChannelQuery");
}
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "LeaveChannelQuery");
promise_.set_error(std::move(status));
}
};
DialogParticipantManager::DialogParticipantManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
update_dialog_online_member_count_timeout_.set_callback(on_update_dialog_online_member_count_timeout_callback);
update_dialog_online_member_count_timeout_.set_callback_data(static_cast<void *>(this));
@ -1144,8 +1358,8 @@ void DialogParticipantManager::add_dialog_participant(DialogId dialog_id, UserId
return td_->contacts_manager_->add_chat_participant(dialog_id.get_chat_id(), user_id, forward_limit,
std::move(promise));
case DialogType::Channel:
return td_->contacts_manager_->add_channel_participant(dialog_id.get_channel_id(), user_id,
DialogParticipantStatus::Left(), std::move(promise));
return add_channel_participant(dialog_id.get_channel_id(), user_id, DialogParticipantStatus::Left(),
std::move(promise));
case DialogType::SecretChat:
return promise.set_error(Status::Error(400, "Can't add members to a secret chat"));
case DialogType::None:
@ -1170,7 +1384,7 @@ void DialogParticipantManager::add_dialog_participants(DialogId dialog_id, const
}
return promise.set_error(Status::Error(400, "Can't add many members at once to a basic group chat"));
case DialogType::Channel:
return td_->contacts_manager_->add_channel_participants(dialog_id.get_channel_id(), user_ids, std::move(promise));
return add_channel_participants(dialog_id.get_channel_id(), user_ids, std::move(promise));
case DialogType::SecretChat:
return promise.set_error(Status::Error(400, "Can't add members to a secret chat"));
case DialogType::None:
@ -1202,8 +1416,8 @@ void DialogParticipantManager::set_dialog_participant_status(
dialog_id.get_chat_id(), participant_dialog_id.get_user_id(), status, std::move(promise));
}
case DialogType::Channel:
return td_->contacts_manager_->set_channel_participant_status(dialog_id.get_channel_id(), participant_dialog_id,
std::move(chat_member_status), std::move(promise));
return set_channel_participant_status(dialog_id.get_channel_id(), participant_dialog_id,
std::move(chat_member_status), std::move(promise));
case DialogType::SecretChat:
return promise.set_error(Status::Error(400, "Chat member status can't be changed in secret chats"));
case DialogType::None:
@ -1230,9 +1444,9 @@ void DialogParticipantManager::ban_dialog_participant(DialogId dialog_id, Dialog
dialog_id.get_chat_id(), participant_dialog_id.get_user_id(), revoke_messages, std::move(promise));
case DialogType::Channel:
// must use td_api::chatMemberStatusBanned to properly fix banned_until_date
return td_->contacts_manager_->set_channel_participant_status(
dialog_id.get_channel_id(), participant_dialog_id,
td_api::make_object<td_api::chatMemberStatusBanned>(banned_until_date), std::move(promise));
return set_channel_participant_status(dialog_id.get_channel_id(), participant_dialog_id,
td_api::make_object<td_api::chatMemberStatusBanned>(banned_until_date),
std::move(promise));
case DialogType::SecretChat:
return promise.set_error(Status::Error(400, "Can't ban members in secret chats"));
case DialogType::None:
@ -1241,6 +1455,390 @@ void DialogParticipantManager::ban_dialog_participant(DialogId dialog_id, Dialog
}
}
void DialogParticipantManager::leave_dialog(DialogId dialog_id, Promise<Unit> &&promise) {
if (!td_->dialog_manager_->have_dialog_force(dialog_id, "leave_dialog")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
switch (dialog_id.get_type()) {
case DialogType::User:
return promise.set_error(Status::Error(400, "Can't leave private chats"));
case DialogType::Chat:
return td_->contacts_manager_->delete_chat_participant(
dialog_id.get_chat_id(), td_->contacts_manager_->get_my_id(), false, std::move(promise));
case DialogType::Channel: {
auto channel_id = dialog_id.get_channel_id();
auto old_status = td_->contacts_manager_->get_channel_status(channel_id);
auto new_status = old_status;
new_status.set_is_member(false);
return restrict_channel_participant(channel_id, td_->dialog_manager_->get_my_dialog_id(), std::move(new_status),
std::move(old_status), std::move(promise));
}
case DialogType::SecretChat:
return promise.set_error(Status::Error(400, "Can't leave secret chats"));
case DialogType::None:
default:
UNREACHABLE();
}
}
void DialogParticipantManager::add_channel_participant(ChannelId channel_id, UserId user_id,
const DialogParticipantStatus &old_status,
Promise<Unit> &&promise) {
if (td_->auth_manager_->is_bot()) {
return promise.set_error(Status::Error(400, "Bots can't add new chat members"));
}
if (!td_->contacts_manager_->have_channel(channel_id)) {
return promise.set_error(Status::Error(400, "Chat info not found"));
}
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(user_id));
if (user_id == td_->contacts_manager_->get_my_id()) {
// join the channel
auto my_status = td_->contacts_manager_->get_channel_status(channel_id);
if (my_status.is_banned()) {
return promise.set_error(Status::Error(400, "Can't return to kicked from chat"));
}
if (!td_->contacts_manager_->get_channel_join_request(channel_id)) {
td_->contacts_manager_->speculative_add_channel_user(channel_id, user_id, DialogParticipantStatus::Member(),
my_status);
}
td_->create_handler<JoinChannelQuery>(std::move(promise))->send(channel_id);
return;
}
if (!td_->contacts_manager_->get_channel_permissions(channel_id).can_invite_users()) {
return promise.set_error(Status::Error(400, "Not enough rights to invite members to the supergroup chat"));
}
td_->contacts_manager_->speculative_add_channel_user(channel_id, user_id, DialogParticipantStatus::Member(),
old_status);
vector<tl_object_ptr<telegram_api::InputUser>> input_users;
input_users.push_back(std::move(input_user));
td_->create_handler<InviteToChannelQuery>(std::move(promise))->send(channel_id, {user_id}, std::move(input_users));
}
void DialogParticipantManager::add_channel_participants(ChannelId channel_id, const vector<UserId> &user_ids,
Promise<Unit> &&promise) {
if (td_->auth_manager_->is_bot()) {
return promise.set_error(Status::Error(400, "Bots can't add new chat members"));
}
if (!td_->contacts_manager_->have_channel(channel_id)) {
return promise.set_error(Status::Error(400, "Chat info not found"));
}
if (!td_->contacts_manager_->get_channel_permissions(channel_id).can_invite_users()) {
return promise.set_error(Status::Error(400, "Not enough rights to invite members to the supergroup chat"));
}
vector<tl_object_ptr<telegram_api::InputUser>> input_users;
for (auto user_id : user_ids) {
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(user_id));
if (user_id == td_->contacts_manager_->get_my_id()) {
// can't invite self
continue;
}
input_users.push_back(std::move(input_user));
td_->contacts_manager_->speculative_add_channel_user(channel_id, user_id, DialogParticipantStatus::Member(),
DialogParticipantStatus::Left());
}
if (input_users.empty()) {
return promise.set_value(Unit());
}
td_->create_handler<InviteToChannelQuery>(std::move(promise))->send(channel_id, user_ids, std::move(input_users));
}
void DialogParticipantManager::set_channel_participant_status(
ChannelId channel_id, DialogId participant_dialog_id,
td_api::object_ptr<td_api::ChatMemberStatus> &&chat_member_status, Promise<Unit> &&promise) {
if (!td_->contacts_manager_->have_channel(channel_id)) {
return promise.set_error(Status::Error(400, "Chat info not found"));
}
auto new_status =
get_dialog_participant_status(chat_member_status, td_->contacts_manager_->get_channel_type(channel_id));
if (participant_dialog_id == td_->dialog_manager_->get_my_dialog_id()) {
// fast path is needed, because get_channel_status may return Creator, while GetChannelParticipantQuery returning Left
return set_channel_participant_status_impl(channel_id, participant_dialog_id, std::move(new_status),
td_->contacts_manager_->get_channel_status(channel_id),
std::move(promise));
}
if (participant_dialog_id.get_type() != DialogType::User) {
if (new_status.is_administrator() || new_status.is_member() || new_status.is_restricted()) {
return promise.set_error(Status::Error(400, "Other chats can be only banned or unbanned"));
}
// always pretend that old_status is different
return restrict_channel_participant(
channel_id, participant_dialog_id, std::move(new_status),
new_status.is_banned() ? DialogParticipantStatus::Left() : DialogParticipantStatus::Banned(0),
std::move(promise));
}
auto on_result_promise =
PromiseCreator::lambda([actor_id = actor_id(this), channel_id, participant_dialog_id, new_status,
promise = std::move(promise)](Result<DialogParticipant> r_dialog_participant) mutable {
// ResultHandlers are cleared before managers, so it is safe to capture this
if (r_dialog_participant.is_error()) {
return promise.set_error(r_dialog_participant.move_as_error());
}
send_closure(actor_id, &DialogParticipantManager::set_channel_participant_status_impl, channel_id,
participant_dialog_id, std::move(new_status), r_dialog_participant.ok().status_,
std::move(promise));
});
td_->dialog_participant_manager_->get_channel_participant(channel_id, participant_dialog_id,
std::move(on_result_promise));
}
void DialogParticipantManager::set_channel_participant_status_impl(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus new_status,
DialogParticipantStatus old_status,
Promise<Unit> &&promise) {
if (old_status == new_status && !old_status.is_creator()) {
return promise.set_value(Unit());
}
CHECK(participant_dialog_id.get_type() == DialogType::User);
LOG(INFO) << "Change status of " << participant_dialog_id << " in " << channel_id << " from " << old_status << " to "
<< new_status;
bool need_add = false;
bool need_promote = false;
bool need_restrict = false;
if (new_status.is_creator() || old_status.is_creator()) {
if (!old_status.is_creator()) {
return promise.set_error(Status::Error(400, "Can't add another owner to the chat"));
}
if (!new_status.is_creator()) {
return promise.set_error(Status::Error(400, "Can't remove chat owner"));
}
auto user_id = td_->contacts_manager_->get_my_id();
if (participant_dialog_id != DialogId(user_id)) {
return promise.set_error(Status::Error(400, "Not enough rights to edit chat owner rights"));
}
if (new_status.is_member() == old_status.is_member()) {
// change rank and is_anonymous
auto r_input_user = td_->contacts_manager_->get_input_user(user_id);
CHECK(r_input_user.is_ok());
td_->create_handler<EditChannelAdminQuery>(std::move(promise))
->send(channel_id, user_id, r_input_user.move_as_ok(), new_status);
return;
}
if (new_status.is_member()) {
// creator not member -> creator member
need_add = true;
} else {
// creator member -> creator not member
need_restrict = true;
}
} else if (new_status.is_administrator()) {
need_promote = true;
} else if (!new_status.is_member() || new_status.is_restricted()) {
if (new_status.is_member() && !old_status.is_member()) {
// TODO there is no way in server API to invite someone and change restrictions
// we need to first add user and change restrictions again after that
// but if restrictions aren't changed, then adding is enough
auto copy_old_status = old_status;
copy_old_status.set_is_member(true);
if (copy_old_status == new_status) {
need_add = true;
} else {
need_restrict = true;
}
} else {
need_restrict = true;
}
} else {
// regular member
if (old_status.is_administrator()) {
need_promote = true;
} else if (old_status.is_restricted() || old_status.is_banned()) {
need_restrict = true;
} else {
CHECK(!old_status.is_member());
need_add = true;
}
}
if (need_promote) {
if (participant_dialog_id.get_type() != DialogType::User) {
return promise.set_error(Status::Error(400, "Can't promote chats to chat administrators"));
}
return promote_channel_participant(channel_id, participant_dialog_id.get_user_id(), new_status, old_status,
std::move(promise));
} else if (need_restrict) {
return restrict_channel_participant(channel_id, participant_dialog_id, std::move(new_status), std::move(old_status),
std::move(promise));
} else {
CHECK(need_add);
if (participant_dialog_id.get_type() != DialogType::User) {
return promise.set_error(Status::Error(400, "Can't add chats as chat members"));
}
return add_channel_participant(channel_id, participant_dialog_id.get_user_id(), old_status, std::move(promise));
}
}
void DialogParticipantManager::promote_channel_participant(ChannelId channel_id, UserId user_id,
const DialogParticipantStatus &new_status,
const DialogParticipantStatus &old_status,
Promise<Unit> &&promise) {
LOG(INFO) << "Promote " << user_id << " in " << channel_id << " from " << old_status << " to " << new_status;
if (user_id == td_->contacts_manager_->get_my_id()) {
if (new_status.is_administrator()) {
return promise.set_error(Status::Error(400, "Can't promote self"));
}
CHECK(new_status.is_member());
// allow to demote self. TODO is it allowed server-side?
} else {
if (!td_->contacts_manager_->get_channel_permissions(channel_id).can_promote_members()) {
return promise.set_error(Status::Error(400, "Not enough rights"));
}
CHECK(!old_status.is_creator());
CHECK(!new_status.is_creator());
}
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(user_id));
td_->contacts_manager_->speculative_add_channel_user(channel_id, user_id, new_status, old_status);
td_->create_handler<EditChannelAdminQuery>(std::move(promise))
->send(channel_id, user_id, std::move(input_user), new_status);
}
void DialogParticipantManager::restrict_channel_participant(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus &&new_status,
DialogParticipantStatus &&old_status,
Promise<Unit> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
LOG(INFO) << "Restrict " << participant_dialog_id << " in " << channel_id << " from " << old_status << " to "
<< new_status;
if (!td_->contacts_manager_->have_channel(channel_id)) {
return promise.set_error(Status::Error(400, "Chat info not found"));
}
auto my_status = td_->contacts_manager_->get_channel_status(channel_id);
if (!my_status.is_member() && !my_status.is_creator()) {
if (participant_dialog_id == td_->dialog_manager_->get_my_dialog_id()) {
if (new_status.is_member()) {
return promise.set_error(Status::Error(400, "Can't unrestrict self"));
}
return promise.set_value(Unit());
} else {
return promise.set_error(Status::Error(400, "Not in the chat"));
}
}
auto input_peer = td_->dialog_manager_->get_input_peer(participant_dialog_id, AccessRights::Know);
if (input_peer == nullptr) {
return promise.set_error(Status::Error(400, "Member not found"));
}
if (participant_dialog_id == td_->dialog_manager_->get_my_dialog_id()) {
if (new_status.is_restricted() || new_status.is_banned()) {
return promise.set_error(Status::Error(400, "Can't restrict self"));
}
if (new_status.is_member()) {
return promise.set_error(Status::Error(400, "Can't unrestrict self"));
}
// leave the channel
td_->contacts_manager_->speculative_add_channel_user(channel_id, participant_dialog_id.get_user_id(), new_status,
my_status);
td_->create_handler<LeaveChannelQuery>(std::move(promise))->send(channel_id);
return;
}
switch (participant_dialog_id.get_type()) {
case DialogType::User:
// ok;
break;
case DialogType::Channel:
if (new_status.is_administrator() || new_status.is_member() || new_status.is_restricted()) {
return promise.set_error(Status::Error(400, "Other chats can be only banned or unbanned"));
}
break;
default:
return promise.set_error(Status::Error(400, "Can't restrict the chat"));
}
CHECK(!old_status.is_creator());
CHECK(!new_status.is_creator());
if (!td_->contacts_manager_->get_channel_permissions(channel_id).can_restrict_members()) {
return promise.set_error(Status::Error(400, "Not enough rights to restrict/unrestrict chat member"));
}
if (old_status.is_member() && !new_status.is_member() && !new_status.is_banned()) {
// we can't make participant Left without kicking it first
auto on_result_promise = PromiseCreator::lambda([actor_id = actor_id(this), channel_id, participant_dialog_id,
new_status = std::move(new_status),
promise = std::move(promise)](Result<> result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
create_actor<SleepActor>(
"RestrictChannelParticipantSleepActor", 1.0,
PromiseCreator::lambda([actor_id, channel_id, participant_dialog_id, new_status = std::move(new_status),
promise = std::move(promise)](Result<> result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
send_closure(actor_id, &DialogParticipantManager::restrict_channel_participant, channel_id,
participant_dialog_id, std::move(new_status), DialogParticipantStatus::Banned(0),
std::move(promise));
}))
.release();
});
promise = std::move(on_result_promise);
new_status = DialogParticipantStatus::Banned(G()->unix_time() + 60);
}
if (new_status.is_member() && !old_status.is_member()) {
// there is no way in server API to invite someone and change restrictions
// we need to first change restrictions and then try to add the user
CHECK(participant_dialog_id.get_type() == DialogType::User);
new_status.set_is_member(false);
auto on_result_promise =
PromiseCreator::lambda([actor_id = actor_id(this), channel_id, participant_dialog_id, old_status = new_status,
promise = std::move(promise)](Result<> result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
create_actor<SleepActor>(
"AddChannelParticipantSleepActor", 1.0,
PromiseCreator::lambda([actor_id, channel_id, participant_dialog_id, old_status = std::move(old_status),
promise = std::move(promise)](Result<> result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
send_closure(actor_id, &DialogParticipantManager::add_channel_participant, channel_id,
participant_dialog_id.get_user_id(), old_status, std::move(promise));
}))
.release();
});
promise = std::move(on_result_promise);
}
if (participant_dialog_id.get_type() == DialogType::User) {
td_->contacts_manager_->speculative_add_channel_user(channel_id, participant_dialog_id.get_user_id(), new_status,
old_status);
}
td_->create_handler<EditChannelBannedQuery>(std::move(promise))
->send(channel_id, participant_dialog_id, std::move(input_peer), new_status);
}
void DialogParticipantManager::on_set_channel_participant_status(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus status) {
if (G()->close_flag() || participant_dialog_id == td_->dialog_manager_->get_my_dialog_id()) {

View File

@ -96,6 +96,8 @@ class DialogParticipantManager final : public Actor {
void ban_dialog_participant(DialogId dialog_id, DialogId participant_dialog_id, int32 banned_until_date,
bool revoke_messages, Promise<Unit> &&promise);
void leave_dialog(DialogId dialog_id, Promise<Unit> &&promise);
void on_set_channel_participant_status(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus status);
@ -156,6 +158,26 @@ class DialogParticipantManager final : public Actor {
void finish_get_channel_participant(ChannelId channel_id, DialogParticipant &&dialog_participant,
Promise<DialogParticipant> &&promise);
void add_channel_participant(ChannelId channel_id, UserId user_id, const DialogParticipantStatus &old_status,
Promise<Unit> &&promise);
void add_channel_participants(ChannelId channel_id, const vector<UserId> &user_ids, Promise<Unit> &&promise);
void set_channel_participant_status(ChannelId channel_id, DialogId participant_dialog_id,
td_api::object_ptr<td_api::ChatMemberStatus> &&chat_member_status,
Promise<Unit> &&promise);
void set_channel_participant_status_impl(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus new_status, DialogParticipantStatus old_status,
Promise<Unit> &&promise);
void promote_channel_participant(ChannelId channel_id, UserId user_id, const DialogParticipantStatus &new_status,
const DialogParticipantStatus &old_status, Promise<Unit> &&promise);
void restrict_channel_participant(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus &&new_status, DialogParticipantStatus &&old_status,
Promise<Unit> &&promise);
void update_channel_participant_status_cache(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus &&dialog_participant_status);