Support forwarding of messages to message threads.
This commit is contained in:
parent
0497c09047
commit
62bcac0198
@ -5161,13 +5161,14 @@ sendInlineQueryResultMessage chat_id:int53 message_thread_id:int53 reply_to_mess
|
||||
|
||||
//@description Forwards previously sent messages. Returns the forwarded messages in the same order as the message identifiers passed in message_ids. If a message can't be forwarded, null will be returned instead of the message
|
||||
//@chat_id Identifier of the chat to which to forward messages
|
||||
//@message_thread_id If not 0, a message thread identifier in which the message will be sent
|
||||
//@from_chat_id Identifier of the chat from which to forward messages
|
||||
//@message_ids Identifiers of the messages to forward. Message identifiers must be in a strictly increasing order. At most 100 messages can be forwarded simultaneously
|
||||
//@options Options to be used to send the messages; pass null to use default options
|
||||
//@send_copy Pass true to copy content of the messages without reference to the original sender. Always true if the messages are forwarded to a secret chat or are local
|
||||
//@remove_caption Pass true to remove media captions of message copies. Ignored if send_copy is false
|
||||
//@only_preview Pass true to get fake messages instead of actually forwarding them
|
||||
forwardMessages chat_id:int53 from_chat_id:int53 message_ids:vector<int53> options:messageSendOptions send_copy:Bool remove_caption:Bool only_preview:Bool = Messages;
|
||||
forwardMessages chat_id:int53 message_thread_id:int53 from_chat_id:int53 message_ids:vector<int53> options:messageSendOptions send_copy:Bool remove_caption:Bool only_preview:Bool = Messages;
|
||||
|
||||
//@description Resends messages which failed to send. Can be called only for messages for which messageSendingStateFailed.can_retry is true and after specified in messageSendingStateFailed.retry_after time passed.
|
||||
//-If a message is re-sent, the corresponding failed to send message is deleted. Returns the sent messages in the same order as the message identifiers passed in message_ids. If a message can't be re-sent, null will be returned instead of the message
|
||||
|
@ -19,7 +19,6 @@ struct MessageCopyOptions {
|
||||
bool send_copy = false;
|
||||
bool replace_caption = false;
|
||||
FormattedText new_caption;
|
||||
MessageId top_thread_message_id;
|
||||
MessageId reply_to_message_id;
|
||||
unique_ptr<ReplyMarkup> reply_markup;
|
||||
|
||||
@ -31,8 +30,7 @@ struct MessageCopyOptions {
|
||||
if (!send_copy) {
|
||||
return true;
|
||||
}
|
||||
if ((replace_caption && !new_caption.text.empty()) || top_thread_message_id.is_valid() ||
|
||||
reply_to_message_id.is_valid() || reply_markup != nullptr) {
|
||||
if ((replace_caption && !new_caption.text.empty()) || reply_to_message_id.is_valid() || reply_markup != nullptr) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -45,9 +43,6 @@ inline StringBuilder &operator<<(StringBuilder &string_builder, MessageCopyOptio
|
||||
if (copy_options.replace_caption) {
|
||||
string_builder << ", new_caption = " << copy_options.new_caption;
|
||||
}
|
||||
if (copy_options.top_thread_message_id.is_valid()) {
|
||||
string_builder << ", in thread of " << copy_options.top_thread_message_id;
|
||||
}
|
||||
if (copy_options.reply_to_message_id.is_valid()) {
|
||||
string_builder << ", in reply to " << copy_options.reply_to_message_id;
|
||||
}
|
||||
|
@ -4018,7 +4018,7 @@ class ForwardMessagesQuery final : public Td::ResultHandler {
|
||||
explicit ForwardMessagesQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
void send(int32 flags, DialogId to_dialog_id, DialogId from_dialog_id,
|
||||
void send(int32 flags, DialogId to_dialog_id, MessageId top_thread_message_id, DialogId from_dialog_id,
|
||||
tl_object_ptr<telegram_api::InputPeer> as_input_peer, const vector<MessageId> &message_ids,
|
||||
vector<int64> &&random_ids, int32 schedule_date) {
|
||||
random_ids_ = random_ids;
|
||||
@ -4038,12 +4038,16 @@ class ForwardMessagesQuery final : public Td::ResultHandler {
|
||||
if (as_input_peer != nullptr) {
|
||||
flags |= MessagesManager::SEND_MESSAGE_FLAG_HAS_SEND_AS;
|
||||
}
|
||||
if (top_thread_message_id.is_valid()) {
|
||||
flags |= MessagesManager::SEND_MESSAGE_FLAG_IS_FROM_THREAD;
|
||||
}
|
||||
|
||||
auto query = G()->net_query_creator().create(
|
||||
telegram_api::messages_forwardMessages(
|
||||
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
|
||||
false /*ignored*/, std::move(from_input_peer), MessagesManager::get_server_message_ids(message_ids),
|
||||
std::move(random_ids), std::move(to_input_peer), 0, schedule_date, std::move(as_input_peer)),
|
||||
std::move(random_ids), std::move(to_input_peer), top_thread_message_id.get_server_message_id().get(),
|
||||
schedule_date, std::move(as_input_peer)),
|
||||
{{to_dialog_id, MessageContentType::Text}, {to_dialog_id, MessageContentType::Photo}});
|
||||
if (td_->option_manager_->get_option_boolean("use_quick_ack")) {
|
||||
query->quick_ack_promise_ = PromiseCreator::lambda([random_ids = random_ids_](Result<Unit> result) {
|
||||
@ -26015,11 +26019,11 @@ Result<td_api::object_ptr<td_api::message>> MessagesManager::send_message(
|
||||
if (input_message_content->get_id() == td_api::inputMessageForwarded::ID) {
|
||||
auto input_message = td_api::move_object_as<td_api::inputMessageForwarded>(input_message_content);
|
||||
TRY_RESULT(copy_options, process_message_copy_options(dialog_id, std::move(input_message->copy_options_)));
|
||||
copy_options.top_thread_message_id = top_thread_message_id;
|
||||
copy_options.reply_to_message_id = reply_to_message_id;
|
||||
TRY_RESULT_ASSIGN(copy_options.reply_markup, get_dialog_reply_markup(dialog_id, std::move(reply_markup)));
|
||||
return forward_message(dialog_id, DialogId(input_message->from_chat_id_), MessageId(input_message->message_id_),
|
||||
std::move(options), input_message->in_game_share_, std::move(copy_options));
|
||||
return forward_message(dialog_id, top_thread_message_id, DialogId(input_message->from_chat_id_),
|
||||
MessageId(input_message->message_id_), std::move(options), input_message->in_game_share_,
|
||||
std::move(copy_options));
|
||||
}
|
||||
|
||||
TRY_STATUS(can_send_message(dialog_id));
|
||||
@ -28773,28 +28777,29 @@ void MessagesManager::do_forward_messages(DialogId to_dialog_id, DialogId from_d
|
||||
|
||||
vector<int64> random_ids =
|
||||
transform(messages, [this, to_dialog_id](const Message *m) { return begin_send_message(to_dialog_id, m); });
|
||||
send_closure_later(actor_id(this), &MessagesManager::send_forward_message_query, flags, to_dialog_id, from_dialog_id,
|
||||
std::move(as_input_peer), message_ids, std::move(random_ids), schedule_date,
|
||||
get_erase_log_event_promise(log_event_id));
|
||||
send_closure_later(actor_id(this), &MessagesManager::send_forward_message_query, flags, to_dialog_id,
|
||||
messages[0]->top_thread_message_id, from_dialog_id, std::move(as_input_peer), message_ids,
|
||||
std::move(random_ids), schedule_date, get_erase_log_event_promise(log_event_id));
|
||||
}
|
||||
|
||||
void MessagesManager::send_forward_message_query(int32 flags, DialogId to_dialog_id, DialogId from_dialog_id,
|
||||
void MessagesManager::send_forward_message_query(int32 flags, DialogId to_dialog_id, MessageId top_thread_message_id,
|
||||
DialogId from_dialog_id,
|
||||
tl_object_ptr<telegram_api::InputPeer> as_input_peer,
|
||||
vector<MessageId> message_ids, vector<int64> random_ids,
|
||||
int32 schedule_date, Promise<Unit> promise) {
|
||||
td_->create_handler<ForwardMessagesQuery>(std::move(promise))
|
||||
->send(flags, to_dialog_id, from_dialog_id, std::move(as_input_peer), message_ids, std::move(random_ids),
|
||||
schedule_date);
|
||||
->send(flags, to_dialog_id, top_thread_message_id, from_dialog_id, std::move(as_input_peer), message_ids,
|
||||
std::move(random_ids), schedule_date);
|
||||
}
|
||||
|
||||
Result<td_api::object_ptr<td_api::message>> MessagesManager::forward_message(
|
||||
DialogId to_dialog_id, DialogId from_dialog_id, MessageId message_id,
|
||||
DialogId to_dialog_id, MessageId top_thread_message_id, DialogId from_dialog_id, MessageId message_id,
|
||||
tl_object_ptr<td_api::messageSendOptions> &&options, bool in_game_share, MessageCopyOptions &©_options) {
|
||||
bool need_copy = copy_options.send_copy;
|
||||
vector<MessageCopyOptions> all_copy_options;
|
||||
all_copy_options.push_back(std::move(copy_options));
|
||||
TRY_RESULT(result, forward_messages(to_dialog_id, from_dialog_id, {message_id}, std::move(options), in_game_share,
|
||||
std::move(all_copy_options), false));
|
||||
TRY_RESULT(result, forward_messages(to_dialog_id, top_thread_message_id, from_dialog_id, {message_id},
|
||||
std::move(options), in_game_share, std::move(all_copy_options), false));
|
||||
CHECK(result->messages_.size() == 1);
|
||||
if (result->messages_[0] == nullptr) {
|
||||
return Status::Error(400,
|
||||
@ -28915,8 +28920,8 @@ void MessagesManager::fix_forwarded_message(Message *m, DialogId to_dialog_id, c
|
||||
}
|
||||
|
||||
Result<MessagesManager::ForwardedMessages> MessagesManager::get_forwarded_messages(
|
||||
DialogId to_dialog_id, DialogId from_dialog_id, const vector<MessageId> &message_ids,
|
||||
tl_object_ptr<td_api::messageSendOptions> &&options, bool in_game_share,
|
||||
DialogId to_dialog_id, MessageId top_thread_message_id, DialogId from_dialog_id,
|
||||
const vector<MessageId> &message_ids, tl_object_ptr<td_api::messageSendOptions> &&options, bool in_game_share,
|
||||
vector<MessageCopyOptions> &©_options) {
|
||||
CHECK(copy_options.size() == message_ids.size());
|
||||
if (message_ids.size() > 100) { // TODO replace with const from config or implement mass-forward
|
||||
@ -28951,6 +28956,7 @@ Result<MessagesManager::ForwardedMessages> MessagesManager::get_forwarded_messag
|
||||
|
||||
TRY_STATUS(can_send_message(to_dialog_id));
|
||||
TRY_RESULT(message_send_options, process_message_send_options(to_dialog_id, std::move(options), false));
|
||||
TRY_STATUS(can_use_top_thread_message_id(to_dialog, top_thread_message_id, MessageId()));
|
||||
|
||||
{
|
||||
MessageId last_message_id;
|
||||
@ -29029,7 +29035,6 @@ Result<MessagesManager::ForwardedMessages> MessagesManager::get_forwarded_messag
|
||||
|
||||
auto type = need_copy ? (is_local_copy ? MessageContentDupType::Copy : MessageContentDupType::ServerCopy)
|
||||
: MessageContentDupType::Forward;
|
||||
auto top_thread_message_id = copy_options[i].top_thread_message_id;
|
||||
auto reply_to_message_id = copy_options[i].reply_to_message_id;
|
||||
auto reply_markup = std::move(copy_options[i].reply_markup);
|
||||
unique_ptr<MessageContent> content =
|
||||
@ -29073,14 +29078,15 @@ Result<MessagesManager::ForwardedMessages> MessagesManager::get_forwarded_messag
|
||||
}
|
||||
|
||||
if (is_local_copy) {
|
||||
copied_messages.push_back({std::move(content), top_thread_message_id, reply_to_message_id,
|
||||
forwarded_message->message_id, forwarded_message->reply_to_message_id,
|
||||
std::move(reply_markup), forwarded_message->media_album_id,
|
||||
copied_messages.push_back({std::move(content), reply_to_message_id, forwarded_message->message_id,
|
||||
forwarded_message->reply_to_message_id, std::move(reply_markup),
|
||||
forwarded_message->media_album_id,
|
||||
get_message_disable_web_page_preview(forwarded_message), i});
|
||||
} else {
|
||||
forwarded_message_contents.push_back({std::move(content), forwarded_message->media_album_id, i});
|
||||
}
|
||||
}
|
||||
result.top_thread_message_id = top_thread_message_id;
|
||||
|
||||
if (2 <= forwarded_message_contents.size() && forwarded_message_contents.size() <= MAX_GROUPED_MESSAGES) {
|
||||
std::unordered_set<MessageContentType, MessageContentTypeHash> message_content_types;
|
||||
@ -29122,12 +29128,12 @@ Result<MessagesManager::ForwardedMessages> MessagesManager::get_forwarded_messag
|
||||
}
|
||||
|
||||
Result<td_api::object_ptr<td_api::messages>> MessagesManager::forward_messages(
|
||||
DialogId to_dialog_id, DialogId from_dialog_id, vector<MessageId> message_ids,
|
||||
DialogId to_dialog_id, MessageId top_thread_message_id, DialogId from_dialog_id, vector<MessageId> message_ids,
|
||||
tl_object_ptr<td_api::messageSendOptions> &&options, bool in_game_share, vector<MessageCopyOptions> &©_options,
|
||||
bool only_preview) {
|
||||
TRY_RESULT(forwarded_messages_info,
|
||||
get_forwarded_messages(to_dialog_id, from_dialog_id, message_ids, std::move(options), in_game_share,
|
||||
std::move(copy_options)));
|
||||
get_forwarded_messages(to_dialog_id, top_thread_message_id, from_dialog_id, message_ids,
|
||||
std::move(options), in_game_share, std::move(copy_options)));
|
||||
auto from_dialog = forwarded_messages_info.from_dialog;
|
||||
auto to_dialog = forwarded_messages_info.to_dialog;
|
||||
auto message_send_options = forwarded_messages_info.message_send_options;
|
||||
@ -29135,6 +29141,7 @@ Result<td_api::object_ptr<td_api::messages>> MessagesManager::forward_messages(
|
||||
auto &forwarded_message_contents = forwarded_messages_info.forwarded_message_contents;
|
||||
auto drop_author = forwarded_messages_info.drop_author;
|
||||
auto drop_media_captions = forwarded_messages_info.drop_media_captions;
|
||||
top_thread_message_id = forwarded_messages_info.top_thread_message_id;
|
||||
|
||||
FlatHashMap<MessageId, MessageId, MessageIdHash> forwarded_message_id_to_new_message_id;
|
||||
vector<td_api::object_ptr<td_api::message>> result(message_ids.size());
|
||||
@ -29169,7 +29176,7 @@ Result<td_api::object_ptr<td_api::messages>> MessagesManager::forward_messages(
|
||||
unique_ptr<Message> message;
|
||||
Message *m;
|
||||
if (only_preview) {
|
||||
message = create_message_to_send(to_dialog, MessageId(), reply_to_message_id, message_send_options,
|
||||
message = create_message_to_send(to_dialog, top_thread_message_id, reply_to_message_id, message_send_options,
|
||||
std::move(content), j + 1 != forwarded_message_contents.size(),
|
||||
std::move(forward_info), false, DialogId());
|
||||
MessageId new_message_id =
|
||||
@ -29179,8 +29186,8 @@ Result<td_api::object_ptr<td_api::messages>> MessagesManager::forward_messages(
|
||||
set_message_id(message, new_message_id);
|
||||
m = message.get();
|
||||
} else {
|
||||
m = get_message_to_send(to_dialog, MessageId(), reply_to_message_id, message_send_options, std::move(content),
|
||||
&need_update_dialog_pos, j + 1 != forwarded_message_contents.size(),
|
||||
m = get_message_to_send(to_dialog, top_thread_message_id, reply_to_message_id, message_send_options,
|
||||
std::move(content), &need_update_dialog_pos, j + 1 != forwarded_message_contents.size(),
|
||||
std::move(forward_info));
|
||||
}
|
||||
fix_forwarded_message(m, to_dialog_id, forwarded_message, forwarded_message_contents[j].media_album_id,
|
||||
@ -29226,9 +29233,8 @@ Result<td_api::object_ptr<td_api::messages>> MessagesManager::forward_messages(
|
||||
unique_ptr<Message> message;
|
||||
Message *m;
|
||||
if (only_preview) {
|
||||
message = create_message_to_send(to_dialog, copied_message.top_thread_message_id, reply_to_message_id,
|
||||
message_send_options, std::move(copied_message.content), false, nullptr, is_copy,
|
||||
DialogId());
|
||||
message = create_message_to_send(to_dialog, top_thread_message_id, reply_to_message_id, message_send_options,
|
||||
std::move(copied_message.content), false, nullptr, is_copy, DialogId());
|
||||
MessageId new_message_id =
|
||||
message_send_options.schedule_date != 0
|
||||
? get_next_yet_unsent_scheduled_message_id(to_dialog, message_send_options.schedule_date)
|
||||
@ -29236,9 +29242,8 @@ Result<td_api::object_ptr<td_api::messages>> MessagesManager::forward_messages(
|
||||
set_message_id(message, new_message_id);
|
||||
m = message.get();
|
||||
} else {
|
||||
m = get_message_to_send(to_dialog, copied_message.top_thread_message_id, reply_to_message_id,
|
||||
message_send_options, std::move(copied_message.content), &need_update_dialog_pos, false,
|
||||
nullptr, is_copy);
|
||||
m = get_message_to_send(to_dialog, top_thread_message_id, reply_to_message_id, message_send_options,
|
||||
std::move(copied_message.content), &need_update_dialog_pos, false, nullptr, is_copy);
|
||||
}
|
||||
m->disable_web_page_preview = copied_message.disable_web_page_preview;
|
||||
m->media_album_id = copied_message.media_album_id;
|
||||
|
@ -461,8 +461,8 @@ class MessagesManager final : public Actor {
|
||||
int64 query_id, const string &result_id,
|
||||
bool hide_via_bot) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
Result<td_api::object_ptr<td_api::messages>> forward_messages(DialogId to_dialog_id, DialogId from_dialog_id,
|
||||
vector<MessageId> message_ids,
|
||||
Result<td_api::object_ptr<td_api::messages>> forward_messages(DialogId to_dialog_id, MessageId top_thread_message_id,
|
||||
DialogId from_dialog_id, vector<MessageId> message_ids,
|
||||
tl_object_ptr<td_api::messageSendOptions> &&options,
|
||||
bool in_game_share,
|
||||
vector<MessageCopyOptions> &©_options,
|
||||
@ -1989,12 +1989,13 @@ class MessagesManager final : public Actor {
|
||||
const vector<MessageId> &message_ids, bool drop_author, bool drop_media_captions,
|
||||
uint64 log_event_id);
|
||||
|
||||
void send_forward_message_query(int32 flags, DialogId to_dialog_id, DialogId from_dialog_id,
|
||||
tl_object_ptr<telegram_api::InputPeer> as_input_peer, vector<MessageId> message_ids,
|
||||
vector<int64> random_ids, int32 schedule_date, Promise<Unit> promise);
|
||||
void send_forward_message_query(int32 flags, DialogId to_dialog_id, MessageId top_thread_message_id,
|
||||
DialogId from_dialog_id, tl_object_ptr<telegram_api::InputPeer> as_input_peer,
|
||||
vector<MessageId> message_ids, vector<int64> random_ids, int32 schedule_date,
|
||||
Promise<Unit> promise);
|
||||
|
||||
Result<td_api::object_ptr<td_api::message>> forward_message(DialogId to_dialog_id, DialogId from_dialog_id,
|
||||
MessageId message_id,
|
||||
Result<td_api::object_ptr<td_api::message>> forward_message(DialogId to_dialog_id, MessageId top_thread_message_id,
|
||||
DialogId from_dialog_id, MessageId message_id,
|
||||
tl_object_ptr<td_api::messageSendOptions> &&options,
|
||||
bool in_game_share,
|
||||
MessageCopyOptions &©_options) TD_WARN_UNUSED_RESULT;
|
||||
@ -2008,7 +2009,6 @@ class MessagesManager final : public Actor {
|
||||
struct ForwardedMessages {
|
||||
struct CopiedMessage {
|
||||
unique_ptr<MessageContent> content;
|
||||
MessageId top_thread_message_id;
|
||||
MessageId reply_to_message_id;
|
||||
MessageId original_message_id;
|
||||
MessageId original_reply_to_message_id;
|
||||
@ -2029,12 +2029,13 @@ class MessagesManager final : public Actor {
|
||||
bool drop_media_captions = false;
|
||||
|
||||
Dialog *from_dialog;
|
||||
MessageId top_thread_message_id;
|
||||
Dialog *to_dialog;
|
||||
MessageSendOptions message_send_options;
|
||||
};
|
||||
|
||||
Result<ForwardedMessages> get_forwarded_messages(DialogId to_dialog_id, DialogId from_dialog_id,
|
||||
const vector<MessageId> &message_ids,
|
||||
Result<ForwardedMessages> get_forwarded_messages(DialogId to_dialog_id, MessageId top_thread_message_id,
|
||||
DialogId from_dialog_id, const vector<MessageId> &message_ids,
|
||||
tl_object_ptr<td_api::messageSendOptions> &&options,
|
||||
bool in_game_share, vector<MessageCopyOptions> &©_options);
|
||||
|
||||
|
@ -5550,9 +5550,10 @@ void Td::on_request(uint64 id, td_api::forwardMessages &request) {
|
||||
auto message_copy_options =
|
||||
transform(input_message_ids, [send_copy = request.send_copy_, remove_caption = request.remove_caption_](
|
||||
MessageId) { return MessageCopyOptions(send_copy, remove_caption); });
|
||||
auto r_messages = messages_manager_->forward_messages(DialogId(request.chat_id_), DialogId(request.from_chat_id_),
|
||||
std::move(input_message_ids), std::move(request.options_),
|
||||
false, std::move(message_copy_options), request.only_preview_);
|
||||
auto r_messages = messages_manager_->forward_messages(
|
||||
DialogId(request.chat_id_), MessageId(request.message_thread_id_), DialogId(request.from_chat_id_),
|
||||
std::move(input_message_ids), std::move(request.options_), false, std::move(message_copy_options),
|
||||
request.only_preview_);
|
||||
if (r_messages.is_error()) {
|
||||
send_closure(actor_id(this), &Td::send_error, id, r_messages.move_as_error());
|
||||
} else {
|
||||
|
@ -3131,11 +3131,11 @@ class CliClient final : public Actor {
|
||||
ChatId chat_id;
|
||||
ChatId from_chat_id;
|
||||
string message_ids;
|
||||
get_args(args, chat_id, from_chat_id, message_ids);
|
||||
auto chat = chat_id;
|
||||
send_request(td_api::make_object<td_api::forwardMessages>(chat, from_chat_id, as_message_ids(message_ids),
|
||||
default_message_send_options(), op[0] == 'c',
|
||||
rand_bool(), op.back() == 'p'));
|
||||
string message_thread_id;
|
||||
get_args(args, chat_id, from_chat_id, message_ids, message_thread_id);
|
||||
send_request(td_api::make_object<td_api::forwardMessages>(
|
||||
chat_id, as_message_thread_id(message_thread_id), from_chat_id, as_message_ids(message_ids),
|
||||
default_message_send_options(), op[0] == 'c', rand_bool(), op.back() == 'p'));
|
||||
} else if (op == "resend") {
|
||||
ChatId chat_id;
|
||||
string message_ids;
|
||||
@ -3871,9 +3871,8 @@ class CliClient final : public Actor {
|
||||
int64 query_id;
|
||||
string result_id;
|
||||
get_args(args, chat_id, query_id, result_id);
|
||||
auto chat = chat_id;
|
||||
send_request(td_api::make_object<td_api::sendInlineQueryResultMessage>(
|
||||
chat, as_message_thread_id(message_thread_id_), 0, default_message_send_options(), query_id, result_id,
|
||||
chat_id, as_message_thread_id(message_thread_id_), 0, default_message_send_options(), query_id, result_id,
|
||||
op == "siqrh"));
|
||||
} else if (op == "gcqa") {
|
||||
ChatId chat_id;
|
||||
|
Loading…
Reference in New Issue
Block a user