2020-02-16 17:46:52 +01:00
|
|
|
#include "td-client.h"
|
2020-05-25 23:21:04 +02:00
|
|
|
#include "purple-info.h"
|
2020-02-16 17:59:19 +01:00
|
|
|
#include "config.h"
|
2020-05-12 15:21:16 +02:00
|
|
|
#include "format.h"
|
2020-10-11 11:48:51 +02:00
|
|
|
#include "receiving.h"
|
2020-07-26 14:51:51 +02:00
|
|
|
#include "file-transfer.h"
|
2020-07-26 23:44:51 +02:00
|
|
|
#include "call.h"
|
2020-10-04 16:06:53 +02:00
|
|
|
#include "secret-chat.h"
|
2020-10-12 19:48:44 +02:00
|
|
|
#include "sticker.h"
|
|
|
|
#include "receiving.h"
|
2020-05-13 22:22:55 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
2020-07-05 15:01:24 +02:00
|
|
|
#include <algorithm>
|
2020-05-12 15:21:16 +02:00
|
|
|
|
2020-03-21 12:47:47 +01:00
|
|
|
enum {
|
|
|
|
// Typing notifications seems to be resent every 5-6 seconds, so 10s timeout hould be appropriate
|
|
|
|
REMOTE_TYPING_NOTICE_TIMEOUT = 10,
|
2020-07-05 15:01:24 +02:00
|
|
|
SUPERGROUP_MEMBER_LIMIT = 200,
|
2020-03-21 12:47:47 +01:00
|
|
|
};
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
PurpleTdClient::PurpleTdClient(PurpleAccount *acct, ITransceiverBackend *testBackend)
|
2020-05-22 13:05:11 +02:00
|
|
|
: m_transceiver(this, acct, &PurpleTdClient::processUpdate, testBackend),
|
2021-01-01 14:47:21 +01:00
|
|
|
m_data(acct, m_transceiver)
|
2020-05-10 16:32:13 +02:00
|
|
|
{
|
2020-10-18 18:33:41 +02:00
|
|
|
StickerConversionThread::setCallback(&PurpleTdClient::onAnimatedStickerConverted);
|
2020-05-10 16:32:13 +02:00
|
|
|
m_account = acct;
|
2020-08-27 17:23:59 +02:00
|
|
|
setPurpleConnectionInProgress();
|
2020-05-10 16:32:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
PurpleTdClient::~PurpleTdClient()
|
|
|
|
{
|
2020-10-10 15:51:16 +02:00
|
|
|
std::vector<IncomingMessage> messages;
|
|
|
|
m_data.pendingMessages.flush(messages);
|
2020-10-12 22:41:13 +02:00
|
|
|
|
|
|
|
// This avoids re-sending download request when displaying message. Doing this for messages
|
|
|
|
// that don't involve inline downloads is fine.
|
|
|
|
for (IncomingMessage &fullMessage: messages)
|
|
|
|
fullMessage.inlineDownloadTimeout = true;
|
|
|
|
|
2020-10-12 20:23:29 +02:00
|
|
|
showMessages(messages, m_transceiver, m_data);
|
2020-05-10 16:32:13 +02:00
|
|
|
}
|
2020-02-16 17:46:52 +01:00
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
void PurpleTdClient::setLogLevel(int level)
|
|
|
|
{
|
|
|
|
// Why not just call setLogVerbosityLevel? No idea!
|
|
|
|
td::Client::execute({0, td::td_api::make_object<td::td_api::setLogVerbosityLevel>(level)});
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:55:48 +02:00
|
|
|
void PurpleTdClient::setTdlibFatalErrorCallback(td::Log::FatalErrorCallbackPtr callback)
|
|
|
|
{
|
|
|
|
td::Log::set_fatal_error_callback(callback);
|
|
|
|
}
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
void PurpleTdClient::processUpdate(td::td_api::Object &update)
|
|
|
|
{
|
|
|
|
purple_debug_misc(config::pluginId, "Incoming update\n");
|
|
|
|
|
|
|
|
switch (update.get_id()) {
|
|
|
|
case td::td_api::updateAuthorizationState::ID: {
|
|
|
|
auto &update_authorization_state = static_cast<td::td_api::updateAuthorizationState &>(update);
|
2020-02-16 17:59:19 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: authorization state\n");
|
2020-05-07 21:15:32 +02:00
|
|
|
if (update_authorization_state.authorization_state_) {
|
2020-05-10 16:32:13 +02:00
|
|
|
m_lastAuthState = update_authorization_state.authorization_state_->get_id();
|
|
|
|
processAuthorizationState(*update_authorization_state.authorization_state_);
|
2020-05-07 21:15:32 +02:00
|
|
|
}
|
2020-05-10 16:32:13 +02:00
|
|
|
break;
|
2020-02-16 17:46:52 +01:00
|
|
|
}
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
case td::td_api::updateUser::ID: {
|
|
|
|
auto &userUpdate = static_cast<td::td_api::updateUser &>(update);
|
|
|
|
updateUser(std::move(userUpdate.user_));
|
|
|
|
break;
|
2020-02-22 19:03:14 +01:00
|
|
|
}
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
case td::td_api::updateNewChat::ID: {
|
|
|
|
auto &newChat = static_cast<td::td_api::updateNewChat &>(update);
|
2020-02-22 19:03:14 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: new chat\n");
|
2020-05-10 16:32:13 +02:00
|
|
|
addChat(std::move(newChat.chat_));
|
|
|
|
break;
|
2020-02-22 19:03:14 +01:00
|
|
|
}
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
case td::td_api::updateNewMessage::ID: {
|
|
|
|
auto &newMessageUpdate = static_cast<td::td_api::updateNewMessage &>(update);
|
2020-02-27 21:16:12 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: new message\n");
|
|
|
|
if (newMessageUpdate.message_)
|
2020-05-10 16:32:13 +02:00
|
|
|
onIncomingMessage(std::move(newMessageUpdate.message_));
|
2020-02-27 21:16:12 +01:00
|
|
|
else
|
|
|
|
purple_debug_warning(config::pluginId, "Received null new message\n");
|
2020-05-10 16:32:13 +02:00
|
|
|
break;
|
2020-02-27 21:16:12 +01:00
|
|
|
}
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
case td::td_api::updateUserStatus::ID: {
|
|
|
|
auto &updateStatus = static_cast<td::td_api::updateUserStatus &>(update);
|
2020-02-27 22:27:56 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: user status\n");
|
|
|
|
if (updateStatus.status_)
|
2020-10-04 15:17:04 +02:00
|
|
|
updateUserStatus(getUserId(updateStatus), std::move(updateStatus.status_));
|
2020-05-10 16:32:13 +02:00
|
|
|
break;
|
2020-02-27 22:27:56 +01:00
|
|
|
}
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
case td::td_api::updateUserChatAction::ID: {
|
|
|
|
auto &updateChatAction = static_cast<td::td_api::updateUserChatAction &>(update);
|
2020-03-15 21:47:47 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: chat action %d\n",
|
2020-05-07 21:15:32 +02:00
|
|
|
updateChatAction.action_ ? updateChatAction.action_->get_id() : 0);
|
2020-05-10 16:32:13 +02:00
|
|
|
handleUserChatAction(updateChatAction);
|
|
|
|
break;
|
2020-03-15 21:47:47 +01:00
|
|
|
}
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
case td::td_api::updateBasicGroup::ID: {
|
|
|
|
auto &groupUpdate = static_cast<td::td_api::updateBasicGroup &>(update);
|
|
|
|
updateGroup(std::move(groupUpdate.basic_group_));
|
|
|
|
break;
|
2020-05-09 14:07:21 +02:00
|
|
|
}
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
case td::td_api::updateSupergroup::ID: {
|
|
|
|
auto &groupUpdate = static_cast<td::td_api::updateSupergroup &>(update);
|
|
|
|
updateSupergroup(std::move(groupUpdate.supergroup_));
|
|
|
|
break;
|
2020-05-09 14:07:21 +02:00
|
|
|
}
|
|
|
|
|
2020-06-02 17:25:06 +02:00
|
|
|
case td::td_api::updateBasicGroupFullInfo::ID: {
|
|
|
|
auto &groupUpdate = static_cast<td::td_api::updateBasicGroupFullInfo &>(update);
|
2020-10-04 15:17:04 +02:00
|
|
|
updateGroupFull(getBasicGroupId(groupUpdate), std::move(groupUpdate.basic_group_full_info_));
|
2020-06-02 17:25:06 +02:00
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
|
|
|
case td::td_api::updateSupergroupFullInfo::ID: {
|
|
|
|
auto &groupUpdate = static_cast<td::td_api::updateSupergroupFullInfo &>(update);
|
2020-10-04 15:17:04 +02:00
|
|
|
updateSupergroupFull(getSupergroupId(groupUpdate), std::move(groupUpdate.supergroup_full_info_));
|
2020-06-02 17:25:06 +02:00
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
2020-05-16 00:16:06 +02:00
|
|
|
case td::td_api::updateMessageSendSucceeded::ID: {
|
|
|
|
auto &sendSucceeded = static_cast<const td::td_api::updateMessageSendSucceeded &>(update);
|
2020-05-20 13:33:47 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: message %" G_GINT64_FORMAT " send succeeded\n",
|
|
|
|
sendSucceeded.old_message_id_);
|
2020-05-16 00:16:06 +02:00
|
|
|
removeTempFile(sendSucceeded.old_message_id_);
|
|
|
|
break;
|
2020-05-20 13:33:47 +02:00
|
|
|
}
|
2020-05-16 00:16:06 +02:00
|
|
|
|
|
|
|
case td::td_api::updateMessageSendFailed::ID: {
|
|
|
|
auto &sendFailed = static_cast<const td::td_api::updateMessageSendFailed &>(update);
|
2020-05-20 13:33:47 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: message %" G_GINT64_FORMAT " send failed\n",
|
|
|
|
sendFailed.old_message_id_);
|
2020-05-16 00:16:06 +02:00
|
|
|
removeTempFile(sendFailed.old_message_id_);
|
2020-06-05 20:14:17 +02:00
|
|
|
notifySendFailed(sendFailed, m_data);
|
2020-06-02 17:25:06 +02:00
|
|
|
// TODO notify in chat
|
2020-05-16 00:16:06 +02:00
|
|
|
break;
|
2020-05-20 13:33:47 +02:00
|
|
|
}
|
2020-05-16 00:16:06 +02:00
|
|
|
|
2020-05-19 16:36:58 +02:00
|
|
|
case td::td_api::updateChatChatList::ID: {
|
|
|
|
auto &chatListUpdate = static_cast<td::td_api::updateChatChatList &>(update);
|
2020-05-20 13:33:47 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: update chat list for chat %" G_GINT64_FORMAT "\n",
|
|
|
|
chatListUpdate.chat_id_);
|
2020-10-04 15:17:04 +02:00
|
|
|
m_data.updateChatChatList(getChatId(chatListUpdate), std::move(chatListUpdate.chat_list_));
|
|
|
|
updateChat(m_data.getChat(getChatId(chatListUpdate)));
|
2020-05-19 16:36:58 +02:00
|
|
|
break;
|
2020-05-20 13:33:47 +02:00
|
|
|
}
|
|
|
|
|
2020-06-05 23:20:44 +02:00
|
|
|
case td::td_api::updateChatOrder::ID: {
|
|
|
|
auto &chatOrderUpdate = static_cast<td::td_api::updateChatOrder&>(update);
|
2020-10-04 15:17:04 +02:00
|
|
|
m_data.updateChatOrder(getChatId(chatOrderUpdate), chatOrderUpdate.order_);
|
2020-06-05 23:20:44 +02:00
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
2020-05-20 13:33:47 +02:00
|
|
|
case td::td_api::updateChatTitle::ID: {
|
|
|
|
auto &chatTitleUpdate = static_cast<td::td_api::updateChatTitle &>(update);
|
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: update chat title for chat %" G_GINT64_FORMAT "\n",
|
|
|
|
chatTitleUpdate.chat_id_);
|
2020-10-04 15:17:04 +02:00
|
|
|
m_data.updateChatTitle(getChatId(chatTitleUpdate), chatTitleUpdate.title_);
|
|
|
|
updateChat(m_data.getChat(getChatId(chatTitleUpdate)));
|
2020-05-20 13:33:47 +02:00
|
|
|
break;
|
|
|
|
}
|
2020-05-19 16:36:58 +02:00
|
|
|
|
2021-01-02 19:27:47 +01:00
|
|
|
case td::td_api::updateChatLastMessage::ID: {
|
|
|
|
auto &lastMessage = static_cast<td::td_api::updateChatLastMessage &>(update);
|
|
|
|
m_data.updateChatOrder(getChatId(lastMessage), lastMessage.order_);
|
|
|
|
if (lastMessage.last_message_)
|
|
|
|
saveChatLastMessage(m_data, getChatId(lastMessage), getId(*lastMessage.last_message_));
|
|
|
|
else {
|
|
|
|
// TODO: mind the gap
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-05-26 00:15:03 +02:00
|
|
|
case td::td_api::updateOption::ID: {
|
|
|
|
const td::td_api::updateOption &option = static_cast<const td::td_api::updateOption &>(update);
|
2020-06-06 13:57:36 +02:00
|
|
|
updateOption(option, m_data);
|
2020-05-26 00:15:03 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-05-28 21:29:58 +02:00
|
|
|
case td::td_api::updateFile::ID: {
|
|
|
|
auto &fileUpdate = static_cast<const td::td_api::updateFile &>(update);
|
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: file update, id %d\n",
|
|
|
|
fileUpdate.file_ ? fileUpdate.file_->id_ : 0);
|
2020-06-02 21:08:11 +02:00
|
|
|
if (fileUpdate.file_)
|
2020-06-05 20:14:17 +02:00
|
|
|
updateFileTransferProgress(*fileUpdate.file_, m_transceiver, m_data,
|
|
|
|
&PurpleTdClient::sendMessageResponse);
|
2020-05-28 21:29:58 +02:00
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
2020-05-31 15:55:28 +02:00
|
|
|
case td::td_api::updateSecretChat::ID: {
|
|
|
|
auto &chatUpdate = static_cast<td::td_api::updateSecretChat &>(update);
|
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: secret chat, id %d\n",
|
|
|
|
chatUpdate.secret_chat_ ? chatUpdate.secret_chat_->id_ : 0);
|
|
|
|
updateSecretChat(std::move(chatUpdate.secret_chat_), m_transceiver, m_data);
|
2020-05-31 17:22:04 +02:00
|
|
|
break;
|
2020-05-31 15:55:28 +02:00
|
|
|
};
|
|
|
|
|
2020-07-26 23:44:51 +02:00
|
|
|
case td::td_api::updateCall::ID: {
|
|
|
|
auto &callUpdate = static_cast<const td::td_api::updateCall &>(update);
|
|
|
|
if (callUpdate.call_) {
|
|
|
|
purple_debug_misc(config::pluginId, "Call update: id %d, outgoing=%d, user id %d, state %d\n",
|
|
|
|
callUpdate.call_->id_, callUpdate.call_->user_id_,
|
|
|
|
(int)callUpdate.call_->is_outgoing_,
|
|
|
|
callUpdate.call_->state_ ? callUpdate.call_->state_->get_id() : 0);
|
|
|
|
updateCall(*callUpdate.call_, m_data, m_transceiver);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
default:
|
2020-02-16 17:59:19 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: ignorig ID=%d\n", update.get_id());
|
2020-05-10 16:32:13 +02:00
|
|
|
break;
|
2020-02-16 17:59:19 +01:00
|
|
|
}
|
2020-05-10 16:32:13 +02:00
|
|
|
}
|
2020-02-16 17:46:52 +01:00
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
void PurpleTdClient::processAuthorizationState(td::td_api::AuthorizationState &authState)
|
|
|
|
{
|
|
|
|
switch (authState.get_id()) {
|
|
|
|
case td::td_api::authorizationStateWaitEncryptionKey::ID:
|
2020-02-16 17:59:19 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: encriytion key requested\n");
|
2020-05-10 16:32:13 +02:00
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::checkDatabaseEncryptionKey>(""),
|
|
|
|
&PurpleTdClient::authResponse);
|
|
|
|
break;
|
2020-02-16 17:46:52 +01:00
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
case td::td_api::authorizationStateWaitTdlibParameters::ID:
|
2020-02-22 11:10:30 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: TDLib parameters requested\n");
|
2020-05-20 18:31:37 +02:00
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::disableProxy>(), nullptr);
|
|
|
|
if (addProxy()) {
|
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::getProxies>(),
|
|
|
|
&PurpleTdClient::getProxiesResponse);
|
|
|
|
sendTdlibParameters();
|
|
|
|
}
|
2020-05-10 16:32:13 +02:00
|
|
|
break;
|
2020-02-22 11:10:30 +01:00
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
case td::td_api::authorizationStateWaitPhoneNumber::ID:
|
2020-02-22 12:08:02 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: phone number requested\n");
|
2020-05-10 16:32:13 +02:00
|
|
|
sendPhoneNumber();
|
|
|
|
break;
|
2020-02-22 12:08:02 +01:00
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
case td::td_api::authorizationStateWaitCode::ID: {
|
|
|
|
auto &codeState = static_cast<td::td_api::authorizationStateWaitCode &>(authState);
|
2020-02-22 14:58:32 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: authentication code requested\n");
|
2020-05-14 14:42:05 +02:00
|
|
|
requestAuthCode(codeState.code_info_.get());
|
2020-05-10 16:32:13 +02:00
|
|
|
break;
|
2020-02-22 14:58:32 +01:00
|
|
|
}
|
|
|
|
|
2020-05-14 14:42:05 +02:00
|
|
|
case td::td_api::authorizationStateWaitRegistration::ID: {
|
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: new user registration\n");
|
|
|
|
registerUser();
|
2020-05-18 11:07:19 +02:00
|
|
|
break;
|
2020-05-14 14:42:05 +02:00
|
|
|
}
|
|
|
|
|
2020-05-25 23:53:21 +02:00
|
|
|
case td::td_api::authorizationStateWaitPassword::ID: {
|
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: password requested\n");
|
|
|
|
auto &pwInfo = static_cast<const td::td_api::authorizationStateWaitPassword &>(authState);
|
|
|
|
requestPassword(pwInfo);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
case td::td_api::authorizationStateReady::ID:
|
2020-02-22 14:58:32 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: ready\n");
|
2020-08-27 17:23:59 +02:00
|
|
|
onLoggedIn();
|
2020-05-10 16:32:13 +02:00
|
|
|
break;
|
2020-02-22 14:58:32 +01:00
|
|
|
}
|
2020-02-16 17:46:52 +01:00
|
|
|
}
|
2020-02-22 11:10:30 +01:00
|
|
|
|
2020-05-20 18:31:37 +02:00
|
|
|
bool PurpleTdClient::addProxy()
|
|
|
|
{
|
|
|
|
PurpleProxyInfo *purpleProxy = purple_proxy_get_setup(m_account);
|
|
|
|
PurpleProxyType proxyType = purpleProxy ? purple_proxy_info_get_type(purpleProxy) : PURPLE_PROXY_NONE;
|
|
|
|
const char * username = purpleProxy ? purple_proxy_info_get_username(purpleProxy) : "";
|
|
|
|
const char * password = purpleProxy ? purple_proxy_info_get_password(purpleProxy) : "";
|
|
|
|
const char * host = purpleProxy ? purple_proxy_info_get_host(purpleProxy) : "";
|
|
|
|
int port = purpleProxy ? purple_proxy_info_get_port(purpleProxy) : 0;
|
|
|
|
if (username == NULL) username = "";
|
|
|
|
if (password == NULL) password = "";
|
|
|
|
if (host == NULL) host = "";
|
|
|
|
std::string errorMessage;
|
|
|
|
|
|
|
|
td::td_api::object_ptr<td::td_api::ProxyType> tdProxyType;
|
|
|
|
switch (proxyType) {
|
|
|
|
case PURPLE_PROXY_NONE:
|
|
|
|
tdProxyType = nullptr;
|
|
|
|
break;
|
|
|
|
case PURPLE_PROXY_SOCKS5:
|
|
|
|
tdProxyType = td::td_api::make_object<td::td_api::proxyTypeSocks5>(username, password);
|
|
|
|
break;
|
|
|
|
case PURPLE_PROXY_HTTP:
|
|
|
|
tdProxyType = td::td_api::make_object<td::td_api::proxyTypeHttp>(username, password, true);
|
|
|
|
break;
|
|
|
|
default:
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Buddy-window error message, argument will be some kind of proxy-identifier.
|
2020-05-20 18:31:37 +02:00
|
|
|
errorMessage = formatMessage(_("Proxy type {} is not supported"), proxyTypeToString(proxyType));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!errorMessage.empty()) {
|
|
|
|
purple_connection_error(purple_account_get_connection(m_account), errorMessage.c_str());
|
|
|
|
return false;
|
|
|
|
} else if (tdProxyType) {
|
|
|
|
auto addProxy = td::td_api::make_object<td::td_api::addProxy>();
|
|
|
|
addProxy->server_ = host;
|
|
|
|
addProxy->port_ = port;
|
|
|
|
addProxy->enable_ = true;
|
|
|
|
addProxy->type_ = std::move(tdProxyType);
|
|
|
|
m_transceiver.sendQuery(std::move(addProxy), &PurpleTdClient::addProxyResponse);
|
|
|
|
m_isProxyAdded = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::addProxyResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
if (object && (object->get_id() == td::td_api::proxy::ID)) {
|
|
|
|
m_addedProxy = td::move_tl_object_as<td::td_api::proxy>(object);
|
|
|
|
if (m_proxies)
|
|
|
|
removeOldProxies();
|
|
|
|
} else {
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Buddy-window error message
|
2020-05-20 18:31:37 +02:00
|
|
|
std::string message = formatMessage(_("Could not set proxy: {}"), getDisplayedError(object));
|
|
|
|
purple_connection_error(purple_account_get_connection(m_account), message.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::getProxiesResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
if (object && (object->get_id() == td::td_api::proxies::ID)) {
|
|
|
|
m_proxies = td::move_tl_object_as<td::td_api::proxies>(object);
|
|
|
|
if (!m_isProxyAdded || m_addedProxy)
|
|
|
|
removeOldProxies();
|
|
|
|
} else {
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Buddy-window error message
|
2020-05-20 18:31:37 +02:00
|
|
|
std::string message = formatMessage(_("Could not get proxies: {}"), getDisplayedError(object));
|
|
|
|
purple_connection_error(purple_account_get_connection(m_account), message.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::removeOldProxies()
|
|
|
|
{
|
|
|
|
for (const td::td_api::object_ptr<td::td_api::proxy> &proxy: m_proxies->proxies_)
|
|
|
|
if (proxy && (!m_addedProxy || (proxy->id_ != m_addedProxy->id_)))
|
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::removeProxy>(proxy->id_), nullptr);
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:55:48 +02:00
|
|
|
std::string PurpleTdClient::getBaseDatabasePath()
|
|
|
|
{
|
|
|
|
return std::string(purple_user_dir()) + G_DIR_SEPARATOR_S + config::configSubdir;
|
|
|
|
}
|
|
|
|
|
2020-06-07 20:54:19 +02:00
|
|
|
static void stuff(td::td_api::tdlibParameters ¶meters)
|
|
|
|
{
|
|
|
|
std::string s(config::stuff);
|
|
|
|
for (size_t i = 0; i < s.length(); i++)
|
|
|
|
s[i] -= 16;
|
|
|
|
size_t i = s.find('i');
|
|
|
|
if (i == std::string::npos)
|
|
|
|
return;
|
|
|
|
s[i] = ' ';
|
|
|
|
sscanf(s.c_str(), "%" G_GINT32_FORMAT, ¶meters.api_id_);
|
|
|
|
parameters.api_hash_ = s.c_str()+i+1;
|
|
|
|
}
|
|
|
|
|
2020-02-22 11:10:30 +01:00
|
|
|
void PurpleTdClient::sendTdlibParameters()
|
|
|
|
{
|
2020-02-22 11:42:04 +01:00
|
|
|
auto parameters = td::td_api::make_object<td::td_api::tdlibParameters>();
|
|
|
|
const char *username = purple_account_get_username(m_account);
|
2020-06-02 16:55:48 +02:00
|
|
|
parameters->database_directory_ = getBaseDatabasePath() + G_DIR_SEPARATOR_S + username;
|
2020-02-22 11:42:04 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Account %s using database directory %s\n",
|
|
|
|
username, parameters->database_directory_.c_str());
|
2020-10-08 00:54:27 +02:00
|
|
|
parameters->use_chat_info_database_ = true;
|
2020-10-18 17:23:00 +02:00
|
|
|
parameters->use_secret_chats_ = (purple_account_get_bool(m_account, AccountOptions::EnableSecretChats,
|
|
|
|
AccountOptions::EnableSecretChatsDefault) != FALSE);
|
2020-06-07 00:05:26 +02:00
|
|
|
parameters->api_id_ = config::api_id;
|
|
|
|
parameters->api_hash_ = config::api_hash;
|
2020-06-07 20:54:19 +02:00
|
|
|
if (*config::stuff)
|
|
|
|
stuff(*parameters);
|
2020-02-22 11:42:04 +01:00
|
|
|
parameters->system_language_code_ = "en";
|
|
|
|
parameters->device_model_ = "Desktop";
|
|
|
|
parameters->system_version_ = "Unknown";
|
|
|
|
parameters->application_version_ = "1.0";
|
2020-10-18 17:23:00 +02:00
|
|
|
parameters->enable_storage_optimizer_ = (purple_account_get_bool(m_account, AccountOptions::KeepInlineDownloads,
|
|
|
|
AccountOptions::KeepInlineDownloadsDefault) == FALSE);
|
2020-05-02 12:44:07 +02:00
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::setTdlibParameters>(std::move(parameters)),
|
|
|
|
&PurpleTdClient::authResponse);
|
2020-02-22 11:42:04 +01:00
|
|
|
}
|
|
|
|
|
2020-02-22 12:08:02 +01:00
|
|
|
void PurpleTdClient::sendPhoneNumber()
|
|
|
|
{
|
|
|
|
const char *number = purple_account_get_username(m_account);
|
2020-05-02 12:44:07 +02:00
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::setAuthenticationPhoneNumber>(number, nullptr),
|
|
|
|
&PurpleTdClient::authResponse);
|
2020-02-22 12:08:02 +01:00
|
|
|
}
|
|
|
|
|
2020-02-22 14:58:32 +01:00
|
|
|
static std::string getAuthCodeDesc(const td::td_api::AuthenticationCodeType &codeType)
|
|
|
|
{
|
|
|
|
switch (codeType.get_id()) {
|
|
|
|
case td::td_api::authenticationCodeTypeTelegramMessage::ID:
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Authentication dialog, secondary content. Appears after a colon (':'). Argument is a number.
|
2020-05-12 15:21:16 +02:00
|
|
|
return formatMessage(_("Telegram message (length: {})"),
|
|
|
|
static_cast<const td::td_api::authenticationCodeTypeTelegramMessage &>(codeType).length_);
|
2020-02-22 14:58:32 +01:00
|
|
|
case td::td_api::authenticationCodeTypeSms::ID:
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Authentication dialog, secondary content. Appears after a colon (':'). Argument is a number.
|
2020-05-12 15:21:16 +02:00
|
|
|
return formatMessage(_("SMS (length: {})"),
|
|
|
|
static_cast<const td::td_api::authenticationCodeTypeSms &>(codeType).length_);
|
2020-02-22 14:58:32 +01:00
|
|
|
case td::td_api::authenticationCodeTypeCall::ID:
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Authentication dialog, secondary content. Appears after a colon (':'). Argument is a number.
|
2020-05-12 15:21:16 +02:00
|
|
|
return formatMessage(_("Phone call (length: {})"),
|
|
|
|
static_cast<const td::td_api::authenticationCodeTypeCall &>(codeType).length_);
|
2020-02-22 14:58:32 +01:00
|
|
|
case td::td_api::authenticationCodeTypeFlashCall::ID:
|
2020-09-02 21:20:53 +02:00
|
|
|
// TRANSLATOR: Authentication dialog, secondary content. Official name "flash call". Appears after a colon (':'). Argument is some text-string-ish.
|
2020-05-12 15:21:16 +02:00
|
|
|
return formatMessage(_("Poor man's phone call (pattern: {})"),
|
|
|
|
static_cast<const td::td_api::authenticationCodeTypeFlashCall &>(codeType).pattern_);
|
2020-02-22 14:58:32 +01:00
|
|
|
default:
|
2020-08-19 22:51:21 +02:00
|
|
|
// Shouldn't happen, so don't translate.
|
2020-02-22 14:58:32 +01:00
|
|
|
return "Pigeon post";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-14 14:42:05 +02:00
|
|
|
void PurpleTdClient::requestAuthCode(const td::td_api::authenticationCodeInfo *codeInfo)
|
2020-02-22 14:58:32 +01:00
|
|
|
{
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Authentication dialog, primary content. Will be followed by instructions and an input box.
|
2020-05-12 15:21:16 +02:00
|
|
|
std::string message = _("Enter authentication code") + std::string("\n");
|
2020-02-22 14:58:32 +01:00
|
|
|
|
2020-05-14 14:42:05 +02:00
|
|
|
if (codeInfo) {
|
2020-08-19 22:51:21 +02:00
|
|
|
if (codeInfo->type_) {
|
|
|
|
// TRANSLATOR: Authentication dialog, secondary content. Argument will be a term.
|
2020-05-14 14:42:05 +02:00
|
|
|
message += formatMessage(_("Code sent via: {}"), getAuthCodeDesc(*codeInfo->type_)) + "\n";
|
2020-08-19 22:51:21 +02:00
|
|
|
}
|
|
|
|
if (codeInfo->next_type_) {
|
|
|
|
// TRANSLATOR: Authentication dialog, secondary content. Argument will be a term.
|
2020-05-14 14:42:05 +02:00
|
|
|
message += formatMessage(_("Next code will be: {}"), getAuthCodeDesc(*codeInfo->next_type_)) + "\n";
|
2020-08-19 22:51:21 +02:00
|
|
|
}
|
2020-02-22 14:58:32 +01:00
|
|
|
}
|
|
|
|
|
2020-07-08 20:21:42 +03:00
|
|
|
purple_request_input (purple_account_get_connection(m_account),
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Authentication dialog, title.
|
2020-05-12 15:21:16 +02:00
|
|
|
_("Login code"),
|
2020-02-22 14:58:32 +01:00
|
|
|
message.c_str(),
|
|
|
|
NULL, // secondary message
|
|
|
|
NULL, // default value
|
|
|
|
FALSE, // multiline input
|
|
|
|
FALSE, // masked input
|
2020-09-02 21:20:53 +02:00
|
|
|
NULL,
|
2020-08-20 19:33:45 +02:00
|
|
|
// TRANSLATOR: Authentication dialog, alternative is "_Cancel". The underscore marks accelerator keys, they must be different!
|
|
|
|
_("_OK"), G_CALLBACK(requestCodeEntered),
|
|
|
|
// TRANSLATOR: Authentication dialog, alternative is "_OK". The underscore marks accelerator keys, they must be different!
|
|
|
|
_("_Cancel"), G_CALLBACK(requestCodeCancelled),
|
2020-05-02 13:40:48 +02:00
|
|
|
m_account,
|
2020-02-22 14:58:32 +01:00
|
|
|
NULL, // buddy
|
|
|
|
NULL, // conversation
|
2020-07-08 20:21:42 +03:00
|
|
|
this);
|
2020-02-22 14:58:32 +01:00
|
|
|
}
|
|
|
|
|
2020-05-22 22:06:02 +02:00
|
|
|
void PurpleTdClient::requestCodeEntered(PurpleTdClient *self, const gchar *code)
|
|
|
|
{
|
|
|
|
purple_debug_misc(config::pluginId, "Authentication code entered: '%s'\n", code);
|
2020-05-25 23:53:21 +02:00
|
|
|
auto checkCode = td::td_api::make_object<td::td_api::checkAuthenticationCode>();
|
|
|
|
if (code)
|
|
|
|
checkCode->code_ = code;
|
|
|
|
self->m_transceiver.sendQuery(std::move(checkCode), &PurpleTdClient::authResponse);
|
2020-05-22 22:06:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::requestCodeCancelled(PurpleTdClient *self)
|
|
|
|
{
|
|
|
|
purple_connection_error(purple_account_get_connection(self->m_account),
|
2020-09-02 21:20:53 +02:00
|
|
|
// TRANSLATOR: Connection failure, error message (title; empty content)
|
2020-05-22 22:06:02 +02:00
|
|
|
_("Authentication code required"));
|
|
|
|
}
|
|
|
|
|
2020-05-25 23:53:21 +02:00
|
|
|
void PurpleTdClient::passwordEntered(PurpleTdClient *self, const gchar *password)
|
|
|
|
{
|
|
|
|
purple_debug_misc(config::pluginId, "Password code entered\n");
|
|
|
|
auto checkPassword = td::td_api::make_object<td::td_api::checkAuthenticationPassword>();
|
|
|
|
if (password)
|
|
|
|
checkPassword->password_ = password;
|
|
|
|
self->m_transceiver.sendQuery(std::move(checkPassword), &PurpleTdClient::authResponse);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::passwordCancelled(PurpleTdClient *self)
|
|
|
|
{
|
2020-09-02 21:20:53 +02:00
|
|
|
// TRANSLATOR: Connection failure, error message title (title; empty content)
|
2020-05-25 23:53:21 +02:00
|
|
|
purple_connection_error(purple_account_get_connection(self->m_account), _("Password required"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::requestPassword(const td::td_api::authorizationStateWaitPassword &pwInfo)
|
|
|
|
{
|
|
|
|
std::string hints;
|
2020-08-19 22:51:21 +02:00
|
|
|
if (!pwInfo.password_hint_.empty()) {
|
|
|
|
// TRANSLATOR: 2FA dialog, secondary content, appears in new line. Argument is an arbitrary string from Telegram.
|
2020-05-25 23:53:21 +02:00
|
|
|
hints = formatMessage(_("Hint: {}"), pwInfo.password_hint_);
|
2020-08-19 22:51:21 +02:00
|
|
|
}
|
2020-05-25 23:53:21 +02:00
|
|
|
if (!pwInfo.recovery_email_address_pattern_.empty()) {
|
2020-08-19 22:51:21 +02:00
|
|
|
if (!hints.empty())
|
|
|
|
hints += '\n';
|
|
|
|
// TRANSLATOR: 2FA dialog, secondary content, appears in new line. Argument is an e-mail address.
|
2020-05-25 23:53:21 +02:00
|
|
|
hints += formatMessage(_("Recovery e-mail may have been sent to {}"), pwInfo.recovery_email_address_pattern_);
|
|
|
|
}
|
|
|
|
if (!purple_request_input (purple_account_get_connection(m_account),
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: 2FA dialog, title
|
2020-05-25 23:53:21 +02:00
|
|
|
_("Password"),
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: 2FA dialog, primary content
|
2020-05-31 14:19:09 +02:00
|
|
|
_("Enter password for two-factor authentication"),
|
2020-05-25 23:53:21 +02:00
|
|
|
hints.empty() ? NULL : hints.c_str(),
|
|
|
|
NULL, // default value
|
|
|
|
FALSE, // multiline input
|
|
|
|
FALSE, // masked input
|
2020-09-02 21:20:53 +02:00
|
|
|
NULL,
|
2020-08-20 19:33:45 +02:00
|
|
|
// TRANSLATOR: 2FA dialog, alternative is "_Cancel". The underscore marks accelerator keys, they must be different!
|
|
|
|
_("_OK"), G_CALLBACK(passwordEntered),
|
|
|
|
// TRANSLATOR: 2FA dialog, alternative is "_OK". The underscore marks accelerator keys, they must be different!
|
|
|
|
_("_Cancel"), G_CALLBACK(passwordCancelled),
|
2020-05-25 23:53:21 +02:00
|
|
|
m_account,
|
|
|
|
NULL, // buddy
|
|
|
|
NULL, // conversation
|
|
|
|
this))
|
|
|
|
{
|
2020-09-02 21:20:53 +02:00
|
|
|
// Only happens with like empathy, not worth translating
|
2020-05-25 23:53:21 +02:00
|
|
|
purple_connection_error(purple_account_get_connection(m_account),
|
2020-09-02 21:20:53 +02:00
|
|
|
"Authentication code is required but this libpurple doesn't support input requests");
|
2020-05-25 23:53:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-14 14:42:05 +02:00
|
|
|
void PurpleTdClient::registerUser()
|
|
|
|
{
|
|
|
|
std::string firstName, lastName;
|
|
|
|
getNamesFromAlias(purple_account_get_alias(m_account), firstName, lastName);
|
2020-05-14 15:00:17 +02:00
|
|
|
|
2020-05-22 22:06:02 +02:00
|
|
|
if (firstName.empty() && lastName.empty()) {
|
|
|
|
if (!purple_request_input (purple_account_get_connection(m_account),
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Registration dialog, title
|
2020-05-22 22:06:02 +02:00
|
|
|
_("Registration"),
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Registration dialog, content
|
2020-05-22 22:06:02 +02:00
|
|
|
_("New account is being created. Please enter your display name."),
|
|
|
|
NULL,
|
|
|
|
NULL, // default value
|
|
|
|
FALSE, // multiline input
|
|
|
|
FALSE, // masked input
|
|
|
|
NULL,
|
2020-08-20 19:33:45 +02:00
|
|
|
// TRANSLATOR: Registration dialog, alternative is "_Cancel". The underscore marks accelerator keys, they must be different!
|
|
|
|
_("_OK"), G_CALLBACK(displayNameEntered),
|
|
|
|
// TRANSLATOR: Registration dialog, alternative is "_OK". The underscore marks accelerator keys, they must be different!
|
|
|
|
_("_Cancel"), G_CALLBACK(displayNameCancelled),
|
2020-05-22 22:06:02 +02:00
|
|
|
m_account,
|
|
|
|
NULL, // buddy
|
|
|
|
NULL, // conversation
|
|
|
|
this))
|
|
|
|
{
|
2020-09-02 21:20:53 +02:00
|
|
|
// Same as when requesting authentication code - not worth translating
|
2020-05-22 22:06:02 +02:00
|
|
|
purple_connection_error(purple_account_get_connection(m_account),
|
2020-09-02 21:20:53 +02:00
|
|
|
"Registration is required but this libpurple doesn't support input requests");
|
2020-05-22 22:06:02 +02:00
|
|
|
}
|
|
|
|
} else
|
2020-05-14 15:00:17 +02:00
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::registerUser>(firstName, lastName),
|
|
|
|
&PurpleTdClient::authResponse);
|
2020-05-14 14:42:05 +02:00
|
|
|
}
|
|
|
|
|
2020-05-22 22:06:02 +02:00
|
|
|
void PurpleTdClient::displayNameEntered(PurpleTdClient *self, const gchar *name)
|
2020-02-22 14:58:32 +01:00
|
|
|
{
|
2020-05-22 22:06:02 +02:00
|
|
|
std::string firstName, lastName;
|
|
|
|
getNamesFromAlias(name, firstName, lastName);
|
|
|
|
if (firstName.empty() && lastName.empty())
|
|
|
|
purple_connection_error(purple_account_get_connection(self->m_account),
|
2020-09-02 21:20:53 +02:00
|
|
|
// TRANSLATOR: Connection error message after failed registration.
|
2020-05-22 22:06:02 +02:00
|
|
|
_("Display name is required for registration"));
|
|
|
|
else
|
|
|
|
self->m_transceiver.sendQuery(td::td_api::make_object<td::td_api::registerUser>(firstName, lastName),
|
|
|
|
&PurpleTdClient::authResponse);
|
2020-02-22 14:58:32 +01:00
|
|
|
}
|
|
|
|
|
2020-05-22 22:06:02 +02:00
|
|
|
void PurpleTdClient::displayNameCancelled(PurpleTdClient *self)
|
2020-02-22 14:58:32 +01:00
|
|
|
{
|
|
|
|
purple_connection_error(purple_account_get_connection(self->m_account),
|
2020-09-02 21:20:53 +02:00
|
|
|
// TRANSLATOR: Connection error message after failed registration.
|
2020-05-22 22:06:02 +02:00
|
|
|
_("Display name is required for registration"));
|
2020-02-22 14:58:32 +01:00
|
|
|
}
|
|
|
|
|
2020-02-22 13:23:01 +01:00
|
|
|
void PurpleTdClient::authResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
2020-05-12 16:00:07 +02:00
|
|
|
if (object && (object->get_id() == td::td_api::ok::ID))
|
2020-02-22 13:23:01 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Authentication success on query %lu\n", (unsigned long)requestId);
|
2020-05-12 16:00:07 +02:00
|
|
|
else
|
|
|
|
notifyAuthError(object);
|
2020-02-22 13:23:01 +01:00
|
|
|
}
|
|
|
|
|
2020-05-12 16:00:07 +02:00
|
|
|
void PurpleTdClient::notifyAuthError(const td::td_api::object_ptr<td::td_api::Object> &response)
|
2020-02-22 13:23:01 +01:00
|
|
|
{
|
|
|
|
std::string message;
|
2020-05-02 13:40:48 +02:00
|
|
|
switch (m_lastAuthState) {
|
2020-02-22 13:23:01 +01:00
|
|
|
case td::td_api::authorizationStateWaitEncryptionKey::ID:
|
2020-09-02 21:20:53 +02:00
|
|
|
// TRANSLATOR: Connection error message, argument is text (a proper reason)
|
2020-05-12 15:21:16 +02:00
|
|
|
message = _("Error applying database encryption key: {}");
|
2020-02-22 13:23:01 +01:00
|
|
|
break;
|
|
|
|
default:
|
2020-09-02 21:20:53 +02:00
|
|
|
// TRANSLATOR: Connection error message, argument is text (a proper reason)
|
2020-05-12 15:21:16 +02:00
|
|
|
message = _("Authentication error: {}");
|
2020-09-02 21:20:53 +02:00
|
|
|
break;
|
2020-02-22 13:23:01 +01:00
|
|
|
}
|
|
|
|
|
2020-05-12 16:00:07 +02:00
|
|
|
message = formatMessage(message.c_str(), getDisplayedError(response));
|
2020-02-22 14:58:32 +01:00
|
|
|
|
2020-05-02 13:40:48 +02:00
|
|
|
purple_connection_error(purple_account_get_connection(m_account), message.c_str());
|
2020-02-22 13:23:01 +01:00
|
|
|
}
|
2020-02-22 14:58:32 +01:00
|
|
|
|
2020-05-02 13:40:48 +02:00
|
|
|
void PurpleTdClient::setPurpleConnectionInProgress()
|
2020-02-22 14:58:32 +01:00
|
|
|
{
|
2020-02-29 15:30:24 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Connection in progress\n");
|
2020-05-02 13:40:48 +02:00
|
|
|
PurpleConnection *gc = purple_account_get_connection(m_account);
|
2020-02-22 14:58:32 +01:00
|
|
|
|
2020-05-06 20:19:26 +02:00
|
|
|
if (PURPLE_CONNECTION_IS_CONNECTED(gc))
|
|
|
|
purple_blist_remove_account(m_account);
|
2020-02-29 15:30:24 +01:00
|
|
|
purple_connection_set_state (gc, PURPLE_CONNECTING);
|
2020-08-27 17:23:59 +02:00
|
|
|
purple_connection_update_progress(gc, "Connecting", 1, 2);
|
2020-02-22 14:58:32 +01:00
|
|
|
}
|
2020-02-22 19:03:14 +01:00
|
|
|
|
2020-05-13 13:14:38 +02:00
|
|
|
void PurpleTdClient::onLoggedIn()
|
|
|
|
{
|
2020-08-27 17:23:59 +02:00
|
|
|
purple_connection_set_state (purple_account_get_connection(m_account), PURPLE_CONNECTED);
|
|
|
|
|
2020-05-13 13:14:38 +02:00
|
|
|
// This query ensures an updateUser for every contact
|
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::getContacts>(),
|
|
|
|
&PurpleTdClient::getContactsResponse);
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:49:26 +01:00
|
|
|
void PurpleTdClient::getContactsResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
2020-05-13 15:59:45 +12:00
|
|
|
purple_debug_misc(config::pluginId, "getContacts response to request %" G_GUINT64_FORMAT "\n", requestId);
|
2020-05-12 16:00:07 +02:00
|
|
|
if (object && (object->get_id() == td::td_api::users::ID)) {
|
2020-10-04 15:17:04 +02:00
|
|
|
m_data.setContacts(*td::move_tl_object_as<td::td_api::users>(object));
|
2020-06-05 23:20:44 +02:00
|
|
|
auto getChatsRequest = td::td_api::make_object<td::td_api::getChats>();
|
|
|
|
getChatsRequest->chat_list_ = td::td_api::make_object<td::td_api::chatListMain>();
|
|
|
|
getChatsRequest->offset_order_ = INT64_MAX;
|
|
|
|
getChatsRequest->offset_chat_id_ = 0;
|
|
|
|
getChatsRequest->limit_ = 200;
|
|
|
|
m_lastChatOrderOffset = INT64_MAX;
|
|
|
|
m_transceiver.sendQuery(std::move(getChatsRequest), &PurpleTdClient::getChatsResponse);
|
2020-05-02 13:40:48 +02:00
|
|
|
} else
|
2020-05-12 16:00:07 +02:00
|
|
|
notifyAuthError(object);
|
2020-03-25 16:49:26 +01:00
|
|
|
}
|
|
|
|
|
2020-02-22 19:03:14 +01:00
|
|
|
void PurpleTdClient::getChatsResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
2020-05-13 15:59:45 +12:00
|
|
|
purple_debug_misc(config::pluginId, "getChats response to request %" G_GUINT64_FORMAT "\n", requestId);
|
2020-05-12 16:00:07 +02:00
|
|
|
if (object && (object->get_id() == td::td_api::chats::ID)) {
|
2020-02-22 19:03:14 +01:00
|
|
|
td::td_api::object_ptr<td::td_api::chats> chats = td::move_tl_object_as<td::td_api::chats>(object);
|
2020-06-05 23:20:44 +02:00
|
|
|
if (chats->chat_ids_.empty()) {
|
|
|
|
m_data.getContactsWithNoChat(m_usersForNewPrivateChats);
|
|
|
|
requestMissingPrivateChats();
|
|
|
|
} else {
|
|
|
|
auto getChatsRequest = td::td_api::make_object<td::td_api::getChats>();
|
|
|
|
getChatsRequest->chat_list_ = td::td_api::make_object<td::td_api::chatListMain>();
|
2020-10-04 15:17:04 +02:00
|
|
|
ChatId chatId;
|
2020-06-05 23:20:44 +02:00
|
|
|
int64_t chatOrder;
|
|
|
|
m_data.getSmallestOrderChat(*getChatsRequest->chat_list_, chatId, chatOrder);
|
|
|
|
if (chatOrder < m_lastChatOrderOffset) {
|
|
|
|
getChatsRequest->offset_order_ = chatOrder;
|
2020-10-04 15:17:04 +02:00
|
|
|
getChatsRequest->offset_chat_id_ = chatId.value();
|
2020-06-05 23:20:44 +02:00
|
|
|
getChatsRequest->limit_ = 200;
|
|
|
|
m_lastChatOrderOffset = chatOrder;
|
|
|
|
m_transceiver.sendQuery(std::move(getChatsRequest), &PurpleTdClient::getChatsResponse);
|
|
|
|
} else {
|
|
|
|
m_data.getContactsWithNoChat(m_usersForNewPrivateChats);
|
|
|
|
requestMissingPrivateChats();
|
|
|
|
}
|
|
|
|
}
|
2020-05-02 13:40:48 +02:00
|
|
|
} else
|
2020-05-12 16:00:07 +02:00
|
|
|
notifyAuthError(object);
|
2020-02-22 19:03:14 +01:00
|
|
|
}
|
|
|
|
|
2020-05-02 12:35:31 +02:00
|
|
|
void PurpleTdClient::requestMissingPrivateChats()
|
|
|
|
{
|
|
|
|
if (m_usersForNewPrivateChats.empty()) {
|
|
|
|
purple_debug_misc(config::pluginId, "Login sequence complete\n");
|
2020-08-27 17:23:59 +02:00
|
|
|
onChatListReady();
|
2020-05-02 12:35:31 +02:00
|
|
|
} else {
|
2020-10-04 15:17:04 +02:00
|
|
|
UserId userId = m_usersForNewPrivateChats.back();
|
2020-05-02 12:35:31 +02:00
|
|
|
m_usersForNewPrivateChats.pop_back();
|
2020-10-04 15:17:04 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Requesting private chat for user id %d\n", userId.value());
|
2020-05-02 12:35:31 +02:00
|
|
|
td::td_api::object_ptr<td::td_api::createPrivateChat> createChat =
|
2020-10-04 15:17:04 +02:00
|
|
|
td::td_api::make_object<td::td_api::createPrivateChat>(userId.value(), false);
|
2020-05-02 12:44:07 +02:00
|
|
|
m_transceiver.sendQuery(std::move(createChat), &PurpleTdClient::loginCreatePrivateChatResponse);
|
2020-05-02 12:35:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::loginCreatePrivateChatResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
2020-05-25 23:21:04 +02:00
|
|
|
if (object && (object->get_id() == td::td_api::chat::ID)) {
|
2020-05-02 12:35:31 +02:00
|
|
|
td::td_api::object_ptr<td::td_api::chat> chat = td::move_tl_object_as<td::td_api::chat>(object);
|
2020-05-20 23:36:28 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Requested private chat received: id %" G_GINT64_FORMAT "\n",
|
2020-05-13 15:59:45 +12:00
|
|
|
chat->id_);
|
2020-05-04 13:35:16 +02:00
|
|
|
// Here the "new" chat already exists in AccountData because there has just been
|
|
|
|
// updateNewChat about this same chat. But do addChat anyway, just in case.
|
2020-05-02 12:35:31 +02:00
|
|
|
m_data.addChat(std::move(chat));
|
|
|
|
} else
|
|
|
|
purple_debug_misc(config::pluginId, "Failed to get requested private chat\n");
|
|
|
|
requestMissingPrivateChats();
|
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
void PurpleTdClient::requestBasicGroupFullInfo(BasicGroupId groupId)
|
2020-05-11 19:35:27 +02:00
|
|
|
{
|
2020-05-20 21:31:58 +02:00
|
|
|
if (!m_data.isBasicGroupInfoRequested(groupId)) {
|
|
|
|
m_data.setBasicGroupInfoRequested(groupId);
|
2020-10-04 15:17:04 +02:00
|
|
|
uint64_t requestId = m_transceiver.sendQuery(td::td_api::make_object<td::td_api::getBasicGroupFullInfo>(groupId.value()),
|
2020-05-20 21:31:58 +02:00
|
|
|
&PurpleTdClient::groupInfoResponse);
|
|
|
|
m_data.addPendingRequest<GroupInfoRequest>(requestId, groupId);
|
|
|
|
}
|
2020-05-11 19:35:27 +02:00
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
void PurpleTdClient::requestSupergroupFullInfo(SupergroupId groupId)
|
2020-06-01 11:12:26 +02:00
|
|
|
{
|
|
|
|
if (!m_data.isSupergroupInfoRequested(groupId)) {
|
|
|
|
m_data.setSupergroupInfoRequested(groupId);
|
2020-10-04 15:17:04 +02:00
|
|
|
uint64_t requestId = m_transceiver.sendQuery(td::td_api::make_object<td::td_api::getSupergroupFullInfo>(groupId.value()),
|
2020-06-01 11:12:26 +02:00
|
|
|
&PurpleTdClient::supergroupInfoResponse);
|
2020-10-04 15:17:04 +02:00
|
|
|
m_data.addPendingRequest<SupergroupInfoRequest>(requestId, groupId);
|
2020-07-05 15:01:24 +02:00
|
|
|
|
|
|
|
auto getMembersReq = td::td_api::make_object<td::td_api::getSupergroupMembers>();
|
2020-10-04 15:17:04 +02:00
|
|
|
getMembersReq->supergroup_id_ = groupId.value();
|
2020-07-05 15:01:24 +02:00
|
|
|
getMembersReq->filter_ = td::td_api::make_object<td::td_api::supergroupMembersFilterRecent>();
|
|
|
|
getMembersReq->limit_ = SUPERGROUP_MEMBER_LIMIT;
|
|
|
|
requestId = m_transceiver.sendQuery(std::move(getMembersReq), &PurpleTdClient::supergroupMembersResponse);
|
2020-10-04 15:17:04 +02:00
|
|
|
m_data.addPendingRequest<SupergroupInfoRequest>(requestId, groupId);
|
2020-06-01 11:12:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-11 19:35:27 +02:00
|
|
|
// TODO process messageChatAddMembers and messageChatDeleteMember
|
|
|
|
// TODO process messageChatUpgradeTo and messageChatUpgradeFrom
|
|
|
|
void PurpleTdClient::groupInfoResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
std::unique_ptr<GroupInfoRequest> request = m_data.getPendingRequest<GroupInfoRequest>(requestId);
|
|
|
|
|
|
|
|
if (request && object && (object->get_id() == td::td_api::basicGroupFullInfo::ID)) {
|
|
|
|
td::td_api::object_ptr<td::td_api::basicGroupFullInfo> groupInfo =
|
|
|
|
td::move_tl_object_as<td::td_api::basicGroupFullInfo>(object);
|
2020-06-02 17:25:06 +02:00
|
|
|
updateGroupFull(request->groupId, std::move(groupInfo));
|
2020-05-11 19:35:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-01 11:12:26 +02:00
|
|
|
void PurpleTdClient::supergroupInfoResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
2020-10-04 15:17:04 +02:00
|
|
|
std::unique_ptr<SupergroupInfoRequest> request = m_data.getPendingRequest<SupergroupInfoRequest>(requestId);
|
2020-06-01 11:12:26 +02:00
|
|
|
|
|
|
|
if (request && object && (object->get_id() == td::td_api::supergroupFullInfo::ID)) {
|
|
|
|
td::td_api::object_ptr<td::td_api::supergroupFullInfo> groupInfo =
|
|
|
|
td::move_tl_object_as<td::td_api::supergroupFullInfo>(object);
|
2020-06-02 17:25:06 +02:00
|
|
|
updateSupergroupFull(request->groupId, std::move(groupInfo));
|
|
|
|
}
|
|
|
|
}
|
2020-06-01 11:12:26 +02:00
|
|
|
|
2020-07-05 15:01:24 +02:00
|
|
|
void PurpleTdClient::supergroupMembersResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
2020-10-04 15:17:04 +02:00
|
|
|
std::unique_ptr<SupergroupInfoRequest> request = m_data.getPendingRequest<SupergroupInfoRequest>(requestId);
|
2020-07-05 15:01:24 +02:00
|
|
|
|
|
|
|
if (request && object && (object->get_id() == td::td_api::chatMembers::ID)) {
|
|
|
|
td::td_api::object_ptr<td::td_api::chatMembers> members =
|
|
|
|
td::move_tl_object_as<td::td_api::chatMembers>(object);
|
|
|
|
|
|
|
|
auto getMembersReq = td::td_api::make_object<td::td_api::getSupergroupMembers>();
|
2020-10-04 15:17:04 +02:00
|
|
|
getMembersReq->supergroup_id_ = request->groupId.value();
|
2020-07-05 15:01:24 +02:00
|
|
|
getMembersReq->filter_ = td::td_api::make_object<td::td_api::supergroupMembersFilterAdministrators>();
|
|
|
|
getMembersReq->limit_ = SUPERGROUP_MEMBER_LIMIT;
|
|
|
|
uint64_t newRequestId = m_transceiver.sendQuery(std::move(getMembersReq), &PurpleTdClient::supergroupAdministratorsResponse);
|
|
|
|
m_data.addPendingRequest<GroupMembersRequestCont>(newRequestId, request->groupId, members.release());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::supergroupAdministratorsResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
std::unique_ptr<GroupMembersRequestCont> request = m_data.getPendingRequest<GroupMembersRequestCont>(requestId);
|
|
|
|
if (request) {
|
|
|
|
auto members = std::move(request->members);
|
|
|
|
|
|
|
|
if (object && (object->get_id() == td::td_api::chatMembers::ID)) {
|
|
|
|
td::td_api::object_ptr<td::td_api::chatMembers> newMembers =
|
|
|
|
td::move_tl_object_as<td::td_api::chatMembers>(object);
|
|
|
|
for (auto &pMember: newMembers->members_) {
|
|
|
|
if (! pMember) continue;
|
2020-07-06 23:46:36 +02:00
|
|
|
int32_t userId = pMember->user_id_;
|
2020-07-05 15:01:24 +02:00
|
|
|
if (std::find_if(members->members_.begin(), members->members_.end(),
|
|
|
|
[userId](const td::td_api::object_ptr<td::td_api::chatMember> &pMember) {
|
|
|
|
return (pMember && (pMember->user_id_ == userId));
|
|
|
|
}) == members->members_.end())
|
|
|
|
{
|
|
|
|
members->members_.push_back(std::move(pMember));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const td::td_api::chat *chat = m_data.getSupergroupChatByGroup(request->groupId);
|
|
|
|
if (chat) {
|
|
|
|
PurpleConvChat *purpleChat = findChatConversation(m_account, *chat);
|
|
|
|
if (purpleChat)
|
|
|
|
updateSupergroupChatMembers(purpleChat, *members, m_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_data.updateSupergroupMembers(request->groupId, std::move(members));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
void PurpleTdClient::updateGroupFull(BasicGroupId groupId, td::td_api::object_ptr<td::td_api::basicGroupFullInfo> groupInfo)
|
2020-06-02 17:25:06 +02:00
|
|
|
{
|
|
|
|
const td::td_api::chat *chat = m_data.getBasicGroupChatByGroup(groupId);
|
2020-06-01 11:12:26 +02:00
|
|
|
|
2020-06-02 17:25:06 +02:00
|
|
|
if (chat) {
|
|
|
|
PurpleConvChat *purpleChat = findChatConversation(m_account, *chat);
|
|
|
|
if (purpleChat)
|
|
|
|
updateChatConversation(purpleChat, *groupInfo, m_data);
|
2020-06-01 11:12:26 +02:00
|
|
|
}
|
2020-06-02 17:25:06 +02:00
|
|
|
|
|
|
|
m_data.updateBasicGroupInfo(groupId, std::move(groupInfo));
|
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
void PurpleTdClient::updateSupergroupFull(SupergroupId groupId, td::td_api::object_ptr<td::td_api::supergroupFullInfo> groupInfo)
|
2020-06-02 17:25:06 +02:00
|
|
|
{
|
|
|
|
const td::td_api::chat *chat = m_data.getSupergroupChatByGroup(groupId);
|
|
|
|
|
|
|
|
if (chat) {
|
|
|
|
PurpleConvChat *purpleChat = findChatConversation(m_account, *chat);
|
|
|
|
if (purpleChat)
|
|
|
|
updateChatConversation(purpleChat, *groupInfo, m_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_data.updateSupergroupInfo(groupId, std::move(groupInfo));
|
2020-06-01 11:12:26 +02:00
|
|
|
}
|
|
|
|
|
2020-08-27 17:23:59 +02:00
|
|
|
void PurpleTdClient::onChatListReady()
|
2020-02-22 19:03:14 +01:00
|
|
|
{
|
2020-08-27 17:23:59 +02:00
|
|
|
m_chatListReady = true;
|
2020-05-09 15:12:32 +02:00
|
|
|
std::vector<const td::td_api::chat *> chats;
|
2020-05-20 21:31:58 +02:00
|
|
|
m_data.getChats(chats);
|
2020-02-23 14:02:47 +01:00
|
|
|
|
2020-05-09 15:12:32 +02:00
|
|
|
for (const td::td_api::chat *chat: chats) {
|
2020-05-04 14:08:57 +02:00
|
|
|
const td::td_api::user *user = m_data.getUserByPrivateChat(*chat);
|
2020-05-20 21:31:58 +02:00
|
|
|
if (user && isChatInContactList(*chat, user)) {
|
2020-05-16 20:38:58 +02:00
|
|
|
std::string userName = getPurpleBuddyName(*user);
|
2020-05-13 11:21:19 +02:00
|
|
|
purple_prpl_got_user_status(m_account, userName.c_str(),
|
2020-05-08 19:50:55 +02:00
|
|
|
getPurpleStatusId(*user->status_), NULL);
|
2020-05-09 13:37:38 +02:00
|
|
|
}
|
2020-02-22 23:15:43 +01:00
|
|
|
}
|
|
|
|
|
2020-06-11 00:04:29 +02:00
|
|
|
for (PurpleRoomlist *roomlist: m_pendingRoomLists) {
|
|
|
|
populateGroupChatList(roomlist, chats, m_data);
|
|
|
|
purple_roomlist_unref(roomlist);
|
|
|
|
}
|
|
|
|
m_pendingRoomLists.clear();
|
|
|
|
|
2020-05-04 16:21:50 +02:00
|
|
|
// Here we could remove buddies for which no private chat exists, meaning they have been remove
|
|
|
|
// from the contact list perhaps in another client
|
|
|
|
|
2020-05-02 13:40:48 +02:00
|
|
|
const td::td_api::user *selfInfo = m_data.getUserByPhone(purple_account_get_username(m_account));
|
2020-03-08 23:27:58 +01:00
|
|
|
if (selfInfo != nullptr) {
|
2020-07-25 12:49:38 +02:00
|
|
|
std::string alias = makeBasicDisplayName(*selfInfo);
|
2020-03-08 23:27:58 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Setting own alias to '%s'\n", alias.c_str());
|
2020-05-02 13:40:48 +02:00
|
|
|
purple_account_set_alias(m_account, alias.c_str());
|
2020-03-08 23:27:58 +01:00
|
|
|
} else
|
|
|
|
purple_debug_warning(config::pluginId, "Did not receive user information for self (%s) at login\n",
|
2020-05-02 13:40:48 +02:00
|
|
|
purple_account_get_username(m_account));
|
2020-05-07 13:55:48 +02:00
|
|
|
|
|
|
|
purple_blist_add_account(m_account);
|
2020-02-22 19:03:14 +01:00
|
|
|
}
|
2020-02-22 23:15:43 +01:00
|
|
|
|
2020-10-18 18:33:41 +02:00
|
|
|
void PurpleTdClient::onAnimatedStickerConverted(AccountThread *arg)
|
2020-06-13 16:01:05 +02:00
|
|
|
{
|
|
|
|
std::unique_ptr<AccountThread> baseThread(arg);
|
|
|
|
StickerConversionThread *thread = dynamic_cast<StickerConversionThread *>(arg);
|
|
|
|
const td::td_api::chat *chat = thread ? m_data.getChat(thread->chatId) : nullptr;
|
|
|
|
if (!chat || !thread)
|
|
|
|
return;
|
2020-10-18 18:33:41 +02:00
|
|
|
IncomingMessage *pendingMessage = m_data.pendingMessages.findPendingMessage(getId(*chat), thread->message().id);
|
2020-06-13 16:01:05 +02:00
|
|
|
|
|
|
|
std::string errorMessage = thread->getErrorMessage();
|
|
|
|
gchar *imageData = NULL;
|
|
|
|
gsize imageSize = 0;
|
|
|
|
bool success = false;
|
|
|
|
if (errorMessage.empty()) {
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
g_file_get_contents(thread->getOutputFileName().c_str(), &imageData, &imageSize, &error);
|
|
|
|
if (error) {
|
|
|
|
// unlikely error message not worth translating
|
|
|
|
errorMessage = formatMessage("Could not read converted file {}: {}", {
|
|
|
|
thread->getOutputFileName(), error->message});
|
|
|
|
g_error_free(error);
|
|
|
|
} else
|
|
|
|
success = true;
|
|
|
|
remove(thread->getOutputFileName().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
int id = purple_imgstore_add_with_id (imageData, imageSize, NULL);
|
2020-10-18 18:33:41 +02:00
|
|
|
if (pendingMessage) {
|
|
|
|
pendingMessage->animatedStickerConverted = true;
|
|
|
|
pendingMessage->animatedStickerConvertSuccess = true;
|
|
|
|
pendingMessage->animatedStickerImageId = id;
|
|
|
|
checkMessageReady(pendingMessage, m_transceiver, m_data);
|
|
|
|
pendingMessage = nullptr;
|
|
|
|
} else {
|
|
|
|
std::string text = makeInlineImageText(id);
|
|
|
|
showMessageText(m_data, *chat, thread->message(), text.c_str(), NULL, PURPLE_MESSAGE_IMAGES);
|
|
|
|
}
|
2020-06-13 16:01:05 +02:00
|
|
|
} else {
|
2020-10-18 18:33:41 +02:00
|
|
|
if (pendingMessage) {
|
|
|
|
pendingMessage->animatedStickerConverted = true;
|
|
|
|
pendingMessage->animatedStickerConvertSuccess = false;
|
|
|
|
checkMessageReady(pendingMessage, m_transceiver, m_data);
|
|
|
|
pendingMessage = nullptr;
|
|
|
|
}
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: In-chat error message, arguments will be a file name and a proper reason
|
2020-08-20 19:13:30 +02:00
|
|
|
errorMessage = formatMessage(_("Could not read sticker file {0}: {1}"),
|
2020-06-13 16:01:05 +02:00
|
|
|
{thread->inputFileName, errorMessage});
|
2020-10-18 18:33:41 +02:00
|
|
|
errorMessage = makeNoticeWithSender(*chat, thread->message(), errorMessage.c_str(), m_account);
|
|
|
|
showMessageText(m_data, *chat, thread->message(), NULL, errorMessage.c_str());
|
2020-06-13 16:01:05 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-12 13:01:18 +02:00
|
|
|
|
2021-01-01 14:14:16 +01:00
|
|
|
void PurpleTdClient::sendReadReceipts(PurpleConversation *conversation)
|
2020-12-31 17:10:52 +01:00
|
|
|
{
|
2021-01-01 14:14:16 +01:00
|
|
|
if (conversation != NULL) {
|
|
|
|
sendConversationReadReceipts(m_data, conversation);
|
|
|
|
return;
|
|
|
|
}
|
2020-12-31 17:10:52 +01:00
|
|
|
}
|
|
|
|
|
2020-05-05 18:35:06 +02:00
|
|
|
void PurpleTdClient::onIncomingMessage(td::td_api::object_ptr<td::td_api::message> message)
|
|
|
|
{
|
2020-05-07 21:15:32 +02:00
|
|
|
if (!message)
|
|
|
|
return;
|
|
|
|
|
2020-10-11 19:41:18 +02:00
|
|
|
ChatId chatId = getChatId(*message);
|
|
|
|
const td::td_api::chat *chat = m_data.getChat(chatId);
|
2020-02-23 14:02:47 +01:00
|
|
|
if (!chat) {
|
2020-05-20 23:36:28 +02:00
|
|
|
purple_debug_warning(config::pluginId, "Received message with unknown chat id %" G_GINT64_FORMAT "\n",
|
2020-05-13 15:59:45 +12:00
|
|
|
message->chat_id_);
|
2020-02-22 23:15:43 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-01 14:47:21 +01:00
|
|
|
if (isReadReceiptsEnabled(m_account))
|
2020-12-31 17:10:52 +01:00
|
|
|
m_data.addPendingReadReceipt(chatId, getId(*message));
|
2020-05-17 14:19:59 +02:00
|
|
|
|
2020-10-11 10:51:16 +02:00
|
|
|
IncomingMessage fullMessage;
|
2020-10-11 19:41:18 +02:00
|
|
|
makeFullMessage(*chat, std::move(message), fullMessage, m_data);
|
2020-10-11 10:51:16 +02:00
|
|
|
|
2020-10-11 11:39:07 +02:00
|
|
|
if (isMessageReady(fullMessage, m_data)) {
|
2020-10-11 10:51:16 +02:00
|
|
|
IncomingMessage readyMessage = m_data.pendingMessages.addReadyMessage(std::move(fullMessage));
|
|
|
|
if (readyMessage.message)
|
2020-10-12 20:23:29 +02:00
|
|
|
showMessage(*chat, readyMessage, m_transceiver, m_data);
|
2020-10-10 15:51:16 +02:00
|
|
|
} else {
|
2020-10-11 11:39:07 +02:00
|
|
|
MessageId messageId = getId(*fullMessage.message);
|
2020-10-18 18:33:41 +02:00
|
|
|
IncomingMessage &addedMessage = m_data.pendingMessages.addPendingMessage(std::move(fullMessage));
|
|
|
|
fetchExtras(addedMessage, m_transceiver, m_data,
|
2020-10-11 19:41:18 +02:00
|
|
|
[this, chatId, messageId](uint64_t, td::td_api::object_ptr<td::td_api::Object> object) {
|
2020-10-11 11:39:07 +02:00
|
|
|
findMessageResponse(chatId, messageId, std::move(object));
|
|
|
|
}
|
|
|
|
);
|
2020-10-10 15:51:16 +02:00
|
|
|
}
|
2020-10-11 13:55:44 +02:00
|
|
|
}
|
2020-10-11 10:51:16 +02:00
|
|
|
|
2020-10-10 15:51:16 +02:00
|
|
|
void PurpleTdClient::findMessageResponse(ChatId chatId, MessageId pendingMessageId,
|
|
|
|
td::td_api::object_ptr<td::td_api::Object> object)
|
2020-05-17 14:19:59 +02:00
|
|
|
{
|
2020-10-11 13:55:44 +02:00
|
|
|
IncomingMessage *pendingMessage = m_data.pendingMessages.findPendingMessage(chatId, pendingMessageId);
|
|
|
|
if (!pendingMessage) return;
|
|
|
|
|
2020-10-25 14:57:45 +01:00
|
|
|
pendingMessage->repliedMessageFetchDoneOrFailed = true;
|
2020-10-11 13:55:44 +02:00
|
|
|
if (object && (object->get_id() == td::td_api::message::ID))
|
2020-10-10 15:51:16 +02:00
|
|
|
pendingMessage->repliedMessage = td::move_tl_object_as<td::td_api::message>(object);
|
2020-10-25 14:57:45 +01:00
|
|
|
else
|
2020-05-17 14:19:59 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Failed to fetch reply source for message %" G_GINT64_FORMAT "\n",
|
2020-10-10 15:51:16 +02:00
|
|
|
pendingMessageId.value());
|
2020-05-17 14:19:59 +02:00
|
|
|
|
2020-10-12 20:23:29 +02:00
|
|
|
checkMessageReady(pendingMessage, m_transceiver, m_data);
|
2020-02-22 23:15:43 +01:00
|
|
|
}
|
2020-02-27 21:16:12 +01:00
|
|
|
|
2020-05-19 21:53:43 +02:00
|
|
|
int PurpleTdClient::sendMessage(const char *buddyName, const char *message)
|
|
|
|
{
|
2020-10-04 19:59:27 +02:00
|
|
|
SecretChatId secretChatId = purpleBuddyNameToSecretChatId(buddyName);
|
|
|
|
const td::td_api::user *privateUser = nullptr;
|
|
|
|
const td::td_api::chat *chat = nullptr;
|
|
|
|
|
|
|
|
if (secretChatId.valid()) {
|
|
|
|
chat = m_data.getChatBySecretChat(secretChatId);
|
|
|
|
if (!chat) {
|
|
|
|
showMessageTextIm(m_data, buddyName, NULL, "Secret chat not found", time(NULL), PURPLE_MESSAGE_ERROR);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
std::vector<const td::td_api::user *> users = getUsersByPurpleName(buddyName, m_data, "send message");
|
|
|
|
if (users.size() != 1) {
|
|
|
|
// Unlikely error messages not worth translating
|
|
|
|
std::string errorMessage;
|
|
|
|
if (users.empty())
|
|
|
|
errorMessage = "User not found";
|
|
|
|
else
|
|
|
|
errorMessage = formatMessage("More than one user known with name '{}'", std::string(buddyName));
|
|
|
|
showMessageTextIm(m_data, buddyName, NULL, errorMessage.c_str(), time(NULL), PURPLE_MESSAGE_ERROR);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
privateUser = users[0];
|
|
|
|
chat = m_data.getPrivateChatByUserId(getId(*privateUser));
|
2020-06-06 20:12:12 +02:00
|
|
|
}
|
2020-06-06 14:20:56 +02:00
|
|
|
|
|
|
|
if (chat) {
|
2020-10-04 15:17:04 +02:00
|
|
|
int ret = transmitMessage(getId(*chat), message, m_transceiver, m_data, &PurpleTdClient::sendMessageResponse);
|
2020-06-06 13:57:36 +02:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2020-05-28 21:29:58 +02:00
|
|
|
// Message shall not be echoed: tdlib will shortly present it as a new message and it will be displayed then
|
|
|
|
return 0;
|
2020-10-04 19:59:27 +02:00
|
|
|
} else if (privateUser) {
|
|
|
|
purple_debug_misc(config::pluginId, "Requesting private chat for user id %d\n", privateUser->id_);
|
2020-06-06 20:12:12 +02:00
|
|
|
td::td_api::object_ptr<td::td_api::createPrivateChat> createChat =
|
2020-10-04 19:59:27 +02:00
|
|
|
td::td_api::make_object<td::td_api::createPrivateChat>(privateUser->id_, false);
|
2020-06-06 20:12:12 +02:00
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(createChat), &PurpleTdClient::sendMessageCreatePrivateChatResponse);
|
|
|
|
m_data.addPendingRequest<NewPrivateChatForMessage>(requestId, buddyName, message);
|
|
|
|
return 0;
|
2020-05-28 21:29:58 +02:00
|
|
|
}
|
2020-10-04 19:59:27 +02:00
|
|
|
|
|
|
|
return -1;
|
2020-02-27 21:49:02 +01:00
|
|
|
}
|
2020-02-27 22:27:56 +01:00
|
|
|
|
2020-05-22 18:20:29 +02:00
|
|
|
void PurpleTdClient::sendMessageResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
std::unique_ptr<SendMessageRequest> request = m_data.getPendingRequest<SendMessageRequest>(requestId);
|
|
|
|
if (!request)
|
|
|
|
return;
|
|
|
|
if (object && (object->get_id() == td::td_api::message::ID)) {
|
2020-06-05 20:14:17 +02:00
|
|
|
if (!request->tempFile.empty()) {
|
|
|
|
const td::td_api::message &message = static_cast<td::td_api::message &>(*object);
|
|
|
|
m_data.addTempFileUpload(message.id_, request->tempFile);
|
|
|
|
}
|
|
|
|
} else {
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: In-chat error message, argument will be a user-sent message
|
2020-06-05 20:14:17 +02:00
|
|
|
std::string errorMessage = formatMessage(_("Failed to send message: {}"), getDisplayedError(object));
|
|
|
|
const td::td_api::chat *chat = m_data.getChat(request->chatId);
|
2020-06-07 14:07:58 +02:00
|
|
|
if (chat)
|
|
|
|
showChatNotification(m_data, *chat, errorMessage.c_str());
|
2020-05-22 18:20:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::sendTyping(const char *buddyName, bool isTyping)
|
|
|
|
{
|
2020-10-04 19:59:27 +02:00
|
|
|
const td::td_api::chat *chat = nullptr;
|
|
|
|
SecretChatId secretChatId = purpleBuddyNameToSecretChatId(buddyName);
|
|
|
|
if (secretChatId.valid())
|
|
|
|
chat = m_data.getChatBySecretChat(secretChatId);
|
|
|
|
else {
|
|
|
|
std::vector<const td::td_api::user *> users = getUsersByPurpleName(buddyName, m_data, "send typing notification");
|
|
|
|
if (users.size() == 1)
|
|
|
|
chat = m_data.getPrivateChatByUserId(getId(*users[0]));
|
|
|
|
}
|
2020-05-22 18:20:29 +02:00
|
|
|
|
2020-06-06 14:20:56 +02:00
|
|
|
if (chat) {
|
2020-05-22 18:20:29 +02:00
|
|
|
auto sendAction = td::td_api::make_object<td::td_api::sendChatAction>();
|
2020-06-06 14:20:56 +02:00
|
|
|
sendAction->chat_id_ = chat->id_;
|
2020-05-22 18:20:29 +02:00
|
|
|
if (isTyping)
|
|
|
|
sendAction->action_ = td::td_api::make_object<td::td_api::chatActionTyping>();
|
|
|
|
else
|
|
|
|
sendAction->action_ = td::td_api::make_object<td::td_api::chatActionCancel>();
|
|
|
|
m_transceiver.sendQuery(std::move(sendAction), nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
void PurpleTdClient::updateUserStatus(UserId userId, td::td_api::object_ptr<td::td_api::UserStatus> status)
|
2020-02-27 22:27:56 +01:00
|
|
|
{
|
2020-05-13 11:21:19 +02:00
|
|
|
const td::td_api::user *user = m_data.getUser(userId);
|
|
|
|
if (user) {
|
2020-05-16 20:38:58 +02:00
|
|
|
std::string userName = getPurpleBuddyName(*user);
|
2020-05-13 11:21:19 +02:00
|
|
|
purple_prpl_got_user_status(m_account, userName.c_str(), getPurpleStatusId(*status), NULL);
|
2020-05-16 21:08:05 +02:00
|
|
|
m_data.setUserStatus(userId, std::move(status));
|
2020-05-13 11:21:19 +02:00
|
|
|
}
|
2020-03-08 23:27:58 +01:00
|
|
|
}
|
|
|
|
|
2020-05-13 11:21:19 +02:00
|
|
|
void PurpleTdClient::updateUser(td::td_api::object_ptr<td::td_api::user> userInfo)
|
2020-03-08 23:27:58 +01:00
|
|
|
{
|
2020-05-13 11:21:19 +02:00
|
|
|
if (!userInfo) {
|
2020-05-07 21:15:32 +02:00
|
|
|
purple_debug_warning(config::pluginId, "updateUser with null user info\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
UserId userId = getId(*userInfo);
|
|
|
|
purple_debug_misc(config::pluginId, "Update user: %d '%s' '%s'\n", userId.value(),
|
2020-05-13 11:21:19 +02:00
|
|
|
userInfo->first_name_.c_str(), userInfo->last_name_.c_str());
|
2020-05-09 14:07:21 +02:00
|
|
|
|
2020-05-13 11:21:19 +02:00
|
|
|
m_data.updateUser(std::move(userInfo));
|
2020-02-27 22:27:56 +01:00
|
|
|
|
2020-05-13 11:21:19 +02:00
|
|
|
// For chats, find_chat doesn't work if account is not yet connected, so just in case, don't
|
2020-08-27 17:23:59 +02:00
|
|
|
// user find_buddy either.
|
|
|
|
// Updates are only supposed to come after authorizationStateReady which sets account to connected.
|
|
|
|
// But check purple_account_is_connected just in case.
|
2020-05-20 21:31:58 +02:00
|
|
|
if (purple_account_is_connected(m_account)) {
|
|
|
|
const td::td_api::user *user = m_data.getUser(userId);
|
|
|
|
const td::td_api::chat *chat = m_data.getPrivateChatByUserId(userId);
|
|
|
|
|
2020-06-06 21:36:42 +02:00
|
|
|
if (user)
|
|
|
|
updateUserInfo(*user, chat);
|
2020-06-04 22:57:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool shouldDownloadAvatar(const td::td_api::file &file)
|
|
|
|
{
|
|
|
|
return (file.local_ && !file.local_->is_downloading_completed_ &&
|
|
|
|
!file.local_->is_downloading_active_ && file.remote_ && file.remote_->is_uploading_completed_ &&
|
|
|
|
file.local_->can_be_downloaded_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::downloadProfilePhoto(const td::td_api::user &user)
|
|
|
|
{
|
|
|
|
if (user.profile_photo_ && user.profile_photo_->small_ &&
|
|
|
|
shouldDownloadAvatar(*user.profile_photo_->small_))
|
|
|
|
{
|
|
|
|
auto downloadReq = td::td_api::make_object<td::td_api::downloadFile>();
|
|
|
|
downloadReq->file_id_ = user.profile_photo_->small_->id_;
|
|
|
|
downloadReq->priority_ = FILE_DOWNLOAD_PRIORITY;
|
|
|
|
downloadReq->synchronous_ = true;
|
|
|
|
uint64_t queryId = m_transceiver.sendQuery(std::move(downloadReq), &PurpleTdClient::avatarDownloadResponse);
|
|
|
|
m_data.addPendingRequest<AvatarDownloadRequest>(queryId, &user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::avatarDownloadResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
std::unique_ptr<AvatarDownloadRequest> request = m_data.getPendingRequest<AvatarDownloadRequest>(requestId);
|
|
|
|
if (request && object && (object->get_id() == td::td_api::file::ID)) {
|
|
|
|
auto file = td::move_tl_object_as<td::td_api::file>(object);
|
|
|
|
if (file->local_ && file->local_->is_downloading_completed_) {
|
2020-10-04 15:17:04 +02:00
|
|
|
if (request->userId.valid()) {
|
2020-06-04 22:57:01 +02:00
|
|
|
m_data.updateSmallProfilePhoto(request->userId, std::move(file));
|
|
|
|
const td::td_api::user *user = m_data.getUser(request->userId);
|
|
|
|
const td::td_api::chat *chat = m_data.getPrivateChatByUserId(request->userId);
|
|
|
|
if (user && chat && isChatInContactList(*chat, user))
|
2020-08-27 17:23:59 +02:00
|
|
|
updatePrivateChat(m_data, chat, *user);
|
2020-10-04 15:17:04 +02:00
|
|
|
} else if (request->chatId.valid()) {
|
2020-06-04 22:57:01 +02:00
|
|
|
m_data.updateSmallChatPhoto(request->chatId, std::move(file));
|
|
|
|
const td::td_api::chat *chat = m_data.getPrivateChatByUserId(request->userId);
|
|
|
|
if (chat && isChatInContactList(*chat, nullptr)) {
|
2020-10-04 15:17:04 +02:00
|
|
|
BasicGroupId basicGroupId = getBasicGroupId(*chat);
|
|
|
|
SupergroupId supergroupId = getSupergroupId(*chat);
|
|
|
|
if (basicGroupId.valid())
|
2020-06-04 22:57:01 +02:00
|
|
|
updateBasicGroupChat(m_data, basicGroupId);
|
2020-10-04 15:17:04 +02:00
|
|
|
if (supergroupId.valid())
|
2020-06-04 22:57:01 +02:00
|
|
|
updateSupergroupChat(m_data, supergroupId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-20 21:31:58 +02:00
|
|
|
}
|
2020-02-27 22:27:56 +01:00
|
|
|
}
|
2020-03-15 21:47:47 +01:00
|
|
|
|
2020-05-09 14:07:21 +02:00
|
|
|
void PurpleTdClient::updateGroup(td::td_api::object_ptr<td::td_api::basicGroup> group)
|
|
|
|
{
|
|
|
|
if (!group) {
|
|
|
|
purple_debug_warning(config::pluginId, "updateBasicGroup with null group\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
purple_debug_misc(config::pluginId, "updateBasicGroup id=%d\n", group->id_);
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
BasicGroupId id = getId(*group);
|
2020-05-09 14:07:21 +02:00
|
|
|
m_data.updateBasicGroup(std::move(group));
|
|
|
|
|
2020-08-27 17:23:59 +02:00
|
|
|
// purple_blist_find_chat doesn't work if account is not connected.
|
|
|
|
// Updates are only supposed to come after authorizationStateReady which sets account to connected.
|
|
|
|
// But check purple_account_is_connected just in case.
|
2020-05-09 14:07:21 +02:00
|
|
|
if (purple_account_is_connected(m_account))
|
2020-05-22 13:05:11 +02:00
|
|
|
updateBasicGroupChat(m_data, id);
|
2020-05-09 14:07:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::updateSupergroup(td::td_api::object_ptr<td::td_api::supergroup> group)
|
|
|
|
{
|
|
|
|
if (!group) {
|
|
|
|
purple_debug_warning(config::pluginId, "updateSupergroup with null group\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
purple_debug_misc(config::pluginId, "updateSupergroup id=%d\n", group->id_);
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
SupergroupId id = getId(*group);
|
2020-05-09 14:07:21 +02:00
|
|
|
m_data.updateSupergroup(std::move(group));
|
|
|
|
|
2020-08-27 17:23:59 +02:00
|
|
|
// purple_blist_find_chat doesn't work if account is not connected.
|
|
|
|
// Updates are only supposed to come after authorizationStateReady which sets account to connected.
|
|
|
|
// But check purple_account_is_connected just in case.
|
2020-05-09 14:07:21 +02:00
|
|
|
if (purple_account_is_connected(m_account))
|
2020-05-22 13:05:11 +02:00
|
|
|
updateSupergroupChat(m_data, id);
|
2020-05-09 14:07:21 +02:00
|
|
|
}
|
|
|
|
|
2020-05-20 21:31:58 +02:00
|
|
|
void PurpleTdClient::updateChat(const td::td_api::chat *chat)
|
2020-05-08 19:50:55 +02:00
|
|
|
{
|
2020-05-20 14:25:26 +02:00
|
|
|
if (!chat) return;
|
2020-05-08 19:50:55 +02:00
|
|
|
|
|
|
|
const td::td_api::user *privateChatUser = m_data.getUserByPrivateChat(*chat);
|
2020-10-04 15:17:04 +02:00
|
|
|
BasicGroupId basicGroupId = getBasicGroupId(*chat);
|
|
|
|
SupergroupId supergroupId = getSupergroupId(*chat);
|
2020-10-04 17:54:32 +02:00
|
|
|
SecretChatId secretChatId = getSecretChatId(*chat);
|
2020-05-20 21:31:58 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Update chat: %" G_GINT64_FORMAT " private user=%d basic group=%d supergroup=%d\n",
|
2020-10-04 15:17:04 +02:00
|
|
|
chat->id_, privateChatUser ? privateChatUser->id_ : 0, basicGroupId.value(), supergroupId.value());
|
2020-05-20 14:25:26 +02:00
|
|
|
|
2020-10-04 17:54:32 +02:00
|
|
|
// For secret chats, chat photo is same as user profile photo, so hopefully already downloaded.
|
|
|
|
// But if not (such as when creating secret chat while downloading new photo for the user),
|
|
|
|
// then don't bother.
|
|
|
|
if (!privateChatUser && !secretChatId.valid())
|
2020-06-04 22:57:01 +02:00
|
|
|
downloadChatPhoto(*chat);
|
|
|
|
|
2020-05-09 13:37:38 +02:00
|
|
|
// For chats, find_chat doesn't work if account is not yet connected, so just in case, don't
|
2020-08-27 17:23:59 +02:00
|
|
|
// user find_buddy either.
|
|
|
|
// Updates are only supposed to come after authorizationStateReady which sets account to connected.
|
|
|
|
// But check purple_account_is_connected just in case.
|
2020-05-22 13:46:37 +02:00
|
|
|
if (!purple_account_is_connected(m_account))
|
|
|
|
return;
|
|
|
|
|
2020-06-06 21:36:42 +02:00
|
|
|
if (privateChatUser)
|
|
|
|
updateUserInfo(*privateChatUser, chat);
|
2020-05-19 16:02:42 +02:00
|
|
|
|
2020-06-06 21:36:42 +02:00
|
|
|
if (isChatInContactList(*chat, privateChatUser)) {
|
2020-05-19 16:02:42 +02:00
|
|
|
// purple_blist_find_chat doesn't work if account is not connected
|
2020-10-04 15:17:04 +02:00
|
|
|
if (basicGroupId.valid()) {
|
2020-06-02 17:25:06 +02:00
|
|
|
requestBasicGroupFullInfo(basicGroupId);
|
2020-05-22 13:05:11 +02:00
|
|
|
updateBasicGroupChat(m_data, basicGroupId);
|
2020-05-20 21:31:58 +02:00
|
|
|
}
|
2020-10-04 15:17:04 +02:00
|
|
|
if (supergroupId.valid()) {
|
2020-06-01 11:12:26 +02:00
|
|
|
requestSupergroupFullInfo(supergroupId);
|
2020-05-22 13:05:11 +02:00
|
|
|
updateSupergroupChat(m_data, supergroupId);
|
2020-06-01 11:12:26 +02:00
|
|
|
}
|
2020-05-22 13:46:37 +02:00
|
|
|
} else {
|
2020-10-09 23:40:34 +02:00
|
|
|
if (basicGroupId.valid() || supergroupId.valid())
|
|
|
|
removeGroupChat(m_account, *chat);
|
2020-05-09 13:37:38 +02:00
|
|
|
}
|
2020-10-04 17:54:32 +02:00
|
|
|
|
|
|
|
if (secretChatId.valid())
|
2020-10-05 23:29:20 +02:00
|
|
|
updateKnownSecretChat(secretChatId, m_transceiver, m_data);
|
2020-05-19 16:02:42 +02:00
|
|
|
}
|
2020-05-08 19:50:55 +02:00
|
|
|
|
2020-06-06 21:36:42 +02:00
|
|
|
void PurpleTdClient::updateUserInfo(const td::td_api::user &user, const td::td_api::chat *privateChat)
|
|
|
|
{
|
2021-01-02 19:27:47 +01:00
|
|
|
if (privateChat) {
|
|
|
|
if (isChatInContactList(*privateChat, &user)) {
|
|
|
|
downloadProfilePhoto(user);
|
|
|
|
updatePrivateChat(m_data, privateChat, user);
|
|
|
|
} else
|
|
|
|
removePrivateChat(m_data, *privateChat);
|
2020-06-06 21:36:42 +02:00
|
|
|
}
|
2020-06-06 22:33:56 +02:00
|
|
|
|
|
|
|
// User could have renamed, or they may have become, or ceased being, libpurple buddy.
|
|
|
|
// Update member list in all chat conversation where this user is a member.
|
2020-10-04 15:17:04 +02:00
|
|
|
std::vector<std::pair<BasicGroupId, const td::td_api::basicGroupFullInfo *>> groups;
|
|
|
|
groups = m_data.getBasicGroupsWithMember(getId(user));
|
2020-06-06 22:33:56 +02:00
|
|
|
for (const auto &groupInfo: groups) {
|
|
|
|
const td::td_api::chat *groupChat = m_data.getBasicGroupChatByGroup(groupInfo.first);
|
|
|
|
PurpleConvChat *purpleChat = groupChat ? findChatConversation(m_account, *groupChat) : nullptr;
|
|
|
|
if (purpleChat)
|
|
|
|
updateChatConversation(purpleChat, *groupInfo.second, m_data);
|
|
|
|
}
|
2020-06-06 21:36:42 +02:00
|
|
|
}
|
|
|
|
|
2020-06-04 22:57:01 +02:00
|
|
|
void PurpleTdClient::downloadChatPhoto(const td::td_api::chat &chat)
|
|
|
|
{
|
|
|
|
if (chat.photo_ && chat.photo_->small_ && shouldDownloadAvatar(*chat.photo_->small_)) {
|
|
|
|
auto downloadReq = td::td_api::make_object<td::td_api::downloadFile>();
|
|
|
|
downloadReq->file_id_ = chat.photo_->small_->id_;
|
|
|
|
downloadReq->priority_ = FILE_DOWNLOAD_PRIORITY;
|
|
|
|
downloadReq->synchronous_ = true;
|
|
|
|
uint64_t queryId = m_transceiver.sendQuery(std::move(downloadReq), &PurpleTdClient::avatarDownloadResponse);
|
|
|
|
m_data.addPendingRequest<AvatarDownloadRequest>(queryId, &chat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-19 16:02:42 +02:00
|
|
|
void PurpleTdClient::addChat(td::td_api::object_ptr<td::td_api::chat> chat)
|
|
|
|
{
|
|
|
|
if (!chat) {
|
|
|
|
purple_debug_warning(config::pluginId, "updateNewChat with null chat info\n");
|
|
|
|
return;
|
2020-05-11 19:35:27 +02:00
|
|
|
}
|
2020-05-19 16:02:42 +02:00
|
|
|
|
|
|
|
purple_debug_misc(config::pluginId, "Add chat: '%s'\n", chat->title_.c_str());
|
2020-10-04 15:17:04 +02:00
|
|
|
ChatId chatId = getId(*chat);
|
2020-05-19 16:02:42 +02:00
|
|
|
m_data.addChat(std::move(chat));
|
2020-05-20 21:31:58 +02:00
|
|
|
updateChat(m_data.getChat(chatId));
|
2020-05-08 19:50:55 +02:00
|
|
|
}
|
|
|
|
|
2020-03-15 21:47:47 +01:00
|
|
|
void PurpleTdClient::handleUserChatAction(const td::td_api::updateUserChatAction &updateChatAction)
|
|
|
|
{
|
2020-10-04 15:17:04 +02:00
|
|
|
const td::td_api::chat *chat = m_data.getChat(getChatId(updateChatAction));
|
2020-05-20 15:13:06 +02:00
|
|
|
if (!chat) {
|
2020-05-20 23:36:28 +02:00
|
|
|
purple_debug_warning(config::pluginId, "Got user chat action for unknown chat %" G_GINT64_FORMAT "\n",
|
2020-05-13 15:59:45 +12:00
|
|
|
updateChatAction.chat_id_);
|
2020-05-20 15:13:06 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
UserId chatUserId = getUserIdByPrivateChat(*chat);
|
|
|
|
if (!chatUserId.valid()) {
|
2020-05-20 23:36:28 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Ignoring user chat action for non-private chat %" G_GINT64_FORMAT "\n",
|
2020-05-13 15:59:45 +12:00
|
|
|
updateChatAction.chat_id_);
|
2020-05-20 15:13:06 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
if (chatUserId != getUserId(updateChatAction))
|
2020-05-20 23:36:28 +02:00
|
|
|
purple_debug_warning(config::pluginId, "Got user action for private chat %" G_GINT64_FORMAT " (with user %d) for another user %d\n",
|
2020-10-04 15:17:04 +02:00
|
|
|
updateChatAction.chat_id_, chatUserId.value(), updateChatAction.user_id_);
|
2020-05-20 15:13:06 +02:00
|
|
|
else if (updateChatAction.action_) {
|
|
|
|
if (updateChatAction.action_->get_id() == td::td_api::chatActionCancel::ID) {
|
|
|
|
purple_debug_misc(config::pluginId, "User (id %d) stopped chat action\n",
|
|
|
|
updateChatAction.user_id_);
|
2020-10-04 15:17:04 +02:00
|
|
|
showUserChatAction(getUserId(updateChatAction), false);
|
2020-05-20 15:13:06 +02:00
|
|
|
} else if (updateChatAction.action_->get_id() == td::td_api::chatActionStartPlayingGame::ID) {
|
|
|
|
purple_debug_misc(config::pluginId, "User (id %d): treating chatActionStartPlayingGame as cancel\n",
|
|
|
|
updateChatAction.user_id_);
|
2020-10-04 15:17:04 +02:00
|
|
|
showUserChatAction(getUserId(updateChatAction), false);
|
2020-05-20 15:13:06 +02:00
|
|
|
} else {
|
|
|
|
purple_debug_misc(config::pluginId, "User (id %d) started chat action (id %d)\n",
|
|
|
|
updateChatAction.user_id_, updateChatAction.action_->get_id());
|
2020-10-04 15:17:04 +02:00
|
|
|
showUserChatAction(getUserId(updateChatAction), true);
|
2020-05-20 15:13:06 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-15 21:47:47 +01:00
|
|
|
}
|
2020-03-21 12:47:47 +01:00
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
void PurpleTdClient::showUserChatAction(UserId userId, bool isTyping)
|
2020-03-21 12:47:47 +01:00
|
|
|
{
|
2020-05-02 13:40:48 +02:00
|
|
|
const td::td_api::user *user = m_data.getUser(userId);
|
|
|
|
if (user) {
|
2020-05-16 20:38:58 +02:00
|
|
|
std::string userName = getPurpleBuddyName(*user);
|
2020-05-02 13:40:48 +02:00
|
|
|
if (isTyping)
|
|
|
|
serv_got_typing(purple_account_get_connection(m_account),
|
2020-05-13 11:21:19 +02:00
|
|
|
userName.c_str(), REMOTE_TYPING_NOTICE_TIMEOUT,
|
2020-05-02 13:40:48 +02:00
|
|
|
PURPLE_TYPING);
|
|
|
|
else
|
|
|
|
serv_got_typing_stopped(purple_account_get_connection(m_account),
|
2020-05-13 11:21:19 +02:00
|
|
|
userName.c_str());
|
2020-03-21 12:47:47 +01:00
|
|
|
}
|
2020-03-21 14:32:33 +01:00
|
|
|
}
|
|
|
|
|
2020-05-20 21:31:08 +02:00
|
|
|
static void showFailedContactMessage(void *handle, const std::string &errorMessage)
|
|
|
|
{
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Error dialog, content
|
2020-05-20 21:31:08 +02:00
|
|
|
std::string message = formatMessage(_("Failed to add contact: {}"), errorMessage);
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Error dialog, title
|
2020-05-20 21:31:08 +02:00
|
|
|
purple_notify_error(handle, _("Failed to add contact"), message.c_str(), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int failedContactIdle(gpointer userdata)
|
|
|
|
{
|
|
|
|
char *message = static_cast<char *>(userdata);
|
|
|
|
showFailedContactMessage(NULL, message);
|
|
|
|
free(message);
|
|
|
|
return FALSE; // This idle callback will not be called again
|
|
|
|
}
|
|
|
|
|
|
|
|
static void notifyFailedContactDeferred(const std::string &message)
|
|
|
|
{
|
|
|
|
g_idle_add(failedContactIdle, strdup(message.c_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::addContact(const std::string &purpleName, const std::string &alias,
|
2020-05-13 12:31:03 +02:00
|
|
|
const std::string &groupName)
|
2020-03-21 14:32:33 +01:00
|
|
|
{
|
2020-05-20 21:31:08 +02:00
|
|
|
if (m_data.getUserByPhone(purpleName.c_str())) {
|
|
|
|
purple_debug_info(config::pluginId, "User with phone number %s already exists\n", purpleName.c_str());
|
2020-05-02 12:53:25 +02:00
|
|
|
return;
|
2020-03-21 14:32:33 +01:00
|
|
|
}
|
|
|
|
|
2020-05-20 21:31:08 +02:00
|
|
|
std::vector<const td::td_api::user *> users;
|
|
|
|
m_data.getUsersByDisplayName(purpleName.c_str(), users);
|
|
|
|
if (users.size() > 1) {
|
2020-06-06 20:12:12 +02:00
|
|
|
notifyFailedContactDeferred(formatMessage("More than one user known with name '{}'", purpleName));
|
2020-05-20 21:31:08 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (users.size() == 1)
|
2020-10-04 15:17:04 +02:00
|
|
|
addContactById(getId(*users[0]), "", purpleName, groupName);
|
2020-08-27 10:26:42 +02:00
|
|
|
else if (isPhoneNumber(purpleName.c_str())) {
|
2020-05-20 21:31:08 +02:00
|
|
|
td::td_api::object_ptr<td::td_api::contact> contact =
|
|
|
|
td::td_api::make_object<td::td_api::contact>(purpleName, "", "", "", 0);
|
|
|
|
td::td_api::object_ptr<td::td_api::importContacts> importReq =
|
|
|
|
td::td_api::make_object<td::td_api::importContacts>();
|
|
|
|
importReq->contacts_.push_back(std::move(contact));
|
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(importReq),
|
|
|
|
&PurpleTdClient::importContactResponse);
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
m_data.addPendingRequest<ContactRequest>(requestId, purpleName, alias, groupName, UserId::invalid);
|
2020-08-27 10:26:42 +02:00
|
|
|
} else {
|
|
|
|
auto request = td::td_api::make_object<td::td_api::searchPublicChat>(purpleName);
|
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(request), &PurpleTdClient::addBuddySearchChatResponse);
|
2020-10-04 15:17:04 +02:00
|
|
|
m_data.addPendingRequest<ContactRequest>(requestId, "", alias, groupName, UserId::invalid);
|
2020-05-20 21:31:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-27 10:26:42 +02:00
|
|
|
void PurpleTdClient::addBuddySearchChatResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
std::unique_ptr<ContactRequest> request = m_data.getPendingRequest<ContactRequest>(requestId);
|
|
|
|
|
|
|
|
if (object && (object->get_id() == td::td_api::chat::ID)) {
|
|
|
|
const td::td_api::chat *chat = static_cast<const td::td_api::chat *>(object.get());
|
|
|
|
int32_t chatType = chat->type_ ? chat->type_->get_id() : 0;
|
|
|
|
if (chatType == td::td_api::chatTypePrivate::ID) {
|
|
|
|
if (request)
|
|
|
|
addContactById(getUserIdByPrivateChat(*chat), "", request->alias, request->groupName);
|
|
|
|
} else if ((chatType == td::td_api::chatTypeBasicGroup::ID) ||
|
|
|
|
(chatType == td::td_api::chatTypeSupergroup::ID))
|
|
|
|
{
|
|
|
|
// When trying to join a group but finding a user instead, we display an error message.
|
|
|
|
// When it's vice versa here, don't make it an error: there are enough error messages to
|
|
|
|
// translate as it is.
|
|
|
|
joinGroupSearchChatResponse(requestId, std::move(object));
|
|
|
|
chat = NULL;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
notifyFailedContact(getDisplayedError(object));
|
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
void PurpleTdClient::addContactById(UserId userId, const std::string &phoneNumber, const std::string &alias,
|
2020-05-20 21:31:08 +02:00
|
|
|
const std::string &groupName)
|
|
|
|
{
|
2020-10-04 15:17:04 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Adding contact: id=%d alias=%s\n", userId.value(), alias.c_str());
|
2020-05-20 21:31:08 +02:00
|
|
|
std::string firstName, lastName;
|
|
|
|
getNamesFromAlias(alias.c_str(), firstName, lastName);
|
2020-03-21 14:32:33 +01:00
|
|
|
|
2020-05-20 21:31:08 +02:00
|
|
|
td::td_api::object_ptr<td::td_api::contact> contact =
|
2020-10-04 15:17:04 +02:00
|
|
|
td::td_api::make_object<td::td_api::contact>(phoneNumber, firstName, lastName, "", userId.value());
|
2020-05-20 21:31:08 +02:00
|
|
|
td::td_api::object_ptr<td::td_api::addContact> addContact =
|
|
|
|
td::td_api::make_object<td::td_api::addContact>(std::move(contact), true);
|
|
|
|
uint64_t newRequestId = m_transceiver.sendQuery(std::move(addContact),
|
|
|
|
&PurpleTdClient::addContactResponse);
|
|
|
|
m_data.addPendingRequest<ContactRequest>(newRequestId, phoneNumber, alias, groupName, userId);
|
2020-03-21 14:32:33 +01:00
|
|
|
}
|
|
|
|
|
2020-03-23 22:56:16 +01:00
|
|
|
void PurpleTdClient::importContactResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
2020-05-13 12:31:03 +02:00
|
|
|
std::unique_ptr<ContactRequest> request = m_data.getPendingRequest<ContactRequest>(requestId);
|
|
|
|
if (!request)
|
2020-03-23 22:56:16 +01:00
|
|
|
return;
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
UserId userId = UserId::invalid;
|
2020-05-25 23:21:04 +02:00
|
|
|
if (object && (object->get_id() == td::td_api::importedContacts::ID)) {
|
2020-03-23 22:56:16 +01:00
|
|
|
td::td_api::object_ptr<td::td_api::importedContacts> reply =
|
|
|
|
td::move_tl_object_as<td::td_api::importedContacts>(object);
|
|
|
|
if (!reply->user_ids_.empty())
|
2020-10-04 15:17:04 +02:00
|
|
|
userId = getUserId(*reply, 0);
|
2020-03-23 22:56:16 +01:00
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
if (userId.valid())
|
2020-05-20 21:31:08 +02:00
|
|
|
addContactById(userId, request->phoneNumber, request->alias, request->groupName);
|
2020-08-19 22:51:21 +02:00
|
|
|
else {
|
|
|
|
// TRANSLATOR: Buddy-window error message, title (no content), argument will be a phone number.
|
2020-05-20 21:31:08 +02:00
|
|
|
notifyFailedContact(formatMessage(_("No user found with phone number '{}'"), request->phoneNumber));
|
2020-08-19 22:51:21 +02:00
|
|
|
}
|
2020-03-23 22:56:16 +01:00
|
|
|
}
|
|
|
|
|
2020-03-21 14:32:33 +01:00
|
|
|
void PurpleTdClient::addContactResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
2020-05-13 12:31:03 +02:00
|
|
|
std::unique_ptr<ContactRequest> request = m_data.getPendingRequest<ContactRequest>(requestId);
|
|
|
|
if (!request)
|
2020-03-21 14:32:33 +01:00
|
|
|
return;
|
|
|
|
|
2020-05-25 23:21:04 +02:00
|
|
|
if (object && (object->get_id() == td::td_api::ok::ID)) {
|
2020-03-24 00:14:19 +01:00
|
|
|
td::td_api::object_ptr<td::td_api::createPrivateChat> createChat =
|
2020-10-04 15:17:04 +02:00
|
|
|
td::td_api::make_object<td::td_api::createPrivateChat>(request->userId.value(), false);
|
2020-05-02 12:44:07 +02:00
|
|
|
uint64_t newRequestId = m_transceiver.sendQuery(std::move(createChat),
|
|
|
|
&PurpleTdClient::addContactCreatePrivateChatResponse);
|
2020-05-13 12:31:03 +02:00
|
|
|
m_data.addPendingRequest(newRequestId, std::move(request));
|
2020-05-04 17:07:53 +02:00
|
|
|
} else
|
2020-05-20 21:31:08 +02:00
|
|
|
notifyFailedContact(getDisplayedError(object));
|
2020-03-24 00:14:19 +01:00
|
|
|
}
|
|
|
|
|
2020-05-02 12:35:31 +02:00
|
|
|
void PurpleTdClient::addContactCreatePrivateChatResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
2020-03-24 00:14:19 +01:00
|
|
|
{
|
2020-05-13 12:31:03 +02:00
|
|
|
std::unique_ptr<ContactRequest> request = m_data.getPendingRequest<ContactRequest>(requestId);
|
|
|
|
if (!request)
|
2020-03-24 00:14:19 +01:00
|
|
|
return;
|
|
|
|
|
2020-08-27 11:16:46 +02:00
|
|
|
if (object && (object->get_id() == td::td_api::chat::ID)) {
|
|
|
|
const td::td_api::chat &chat = static_cast<const td::td_api::chat &>(*object);
|
|
|
|
const td::td_api::user *user = m_data.getUserByPrivateChat(chat);
|
|
|
|
if (user && !isChatInContactList(chat, user)) {
|
|
|
|
// Normally, the user will become a contact and this won't happen. But it does happen
|
|
|
|
// when adding BotFather, for example. Nothing will be added to buddy list, so open chat
|
|
|
|
// window just to make something happen.
|
|
|
|
// Since the user is not in buddy list, we have to use display name as libpurple name,
|
|
|
|
// otherwise conversation title will be idXXXXXXX
|
|
|
|
std::string displayName = m_data.getDisplayName(*user);
|
|
|
|
getImConversation(m_account, displayName.c_str());
|
|
|
|
}
|
|
|
|
} else {
|
2020-05-12 16:00:07 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Failed to create private chat to %s\n",
|
2020-05-13 12:31:03 +02:00
|
|
|
request->phoneNumber.c_str());
|
2020-05-20 21:31:08 +02:00
|
|
|
notifyFailedContact(getDisplayedError(object));
|
2020-03-21 14:32:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-20 21:31:08 +02:00
|
|
|
void PurpleTdClient::notifyFailedContact(const std::string &errorMessage)
|
2020-03-21 14:32:33 +01:00
|
|
|
{
|
2020-05-20 21:31:08 +02:00
|
|
|
showFailedContactMessage(purple_account_get_connection(m_account), errorMessage);
|
2020-03-21 12:47:47 +01:00
|
|
|
}
|
2020-05-10 12:05:55 +02:00
|
|
|
|
2020-05-20 13:33:47 +02:00
|
|
|
void PurpleTdClient::renameContact(const char *buddyName, const char *newAlias)
|
|
|
|
{
|
2020-10-04 15:17:04 +02:00
|
|
|
UserId userId = purpleBuddyNameToUserId(buddyName);
|
|
|
|
if (!userId.valid()) {
|
2020-05-20 23:36:28 +02:00
|
|
|
purple_debug_warning(config::pluginId, "Cannot rename %s: not a valid id\n", buddyName);
|
2020-05-20 13:33:47 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string firstName, lastName;
|
|
|
|
getNamesFromAlias(newAlias, firstName, lastName);
|
2020-10-04 15:17:04 +02:00
|
|
|
auto contact = td::td_api::make_object<td::td_api::contact>("", firstName, lastName, "", userId.value());
|
2020-05-20 13:33:47 +02:00
|
|
|
auto addContact = td::td_api::make_object<td::td_api::addContact>(std::move(contact), true);
|
|
|
|
m_transceiver.sendQuery(std::move(addContact), nullptr);
|
|
|
|
}
|
|
|
|
|
2020-05-22 18:20:29 +02:00
|
|
|
void PurpleTdClient::removeContactAndPrivateChat(const std::string &buddyName)
|
|
|
|
{
|
2020-10-04 22:09:19 +02:00
|
|
|
const td::td_api::chat *chat = nullptr;
|
|
|
|
UserId userId = purpleBuddyNameToUserId(buddyName.c_str());
|
|
|
|
SecretChatId secretChatId = purpleBuddyNameToSecretChatId(buddyName.c_str());
|
|
|
|
|
|
|
|
if (userId.valid())
|
|
|
|
chat = m_data.getPrivateChatByUserId(userId);
|
|
|
|
else if (secretChatId.valid())
|
|
|
|
chat = m_data.getChatBySecretChat(secretChatId);
|
|
|
|
|
|
|
|
if (chat) {
|
|
|
|
ChatId chatId = getId(*chat);
|
|
|
|
chat = nullptr;
|
2020-10-05 23:29:20 +02:00
|
|
|
// Prevent accidentally re-creating buddy if any updateChat* or updateUser arrives
|
|
|
|
m_data.deleteChat(chatId);
|
2020-10-04 22:09:19 +02:00
|
|
|
|
|
|
|
auto deleteChat = td::td_api::make_object<td::td_api::deleteChatHistory>();
|
|
|
|
deleteChat->chat_id_ = chatId.value();
|
|
|
|
deleteChat->remove_from_chat_list_ = true;
|
|
|
|
deleteChat->revoke_ = false;
|
|
|
|
m_transceiver.sendQuery(std::move(deleteChat), nullptr);
|
|
|
|
}
|
2020-05-22 18:20:29 +02:00
|
|
|
|
2020-10-04 22:09:19 +02:00
|
|
|
if (userId.valid()) {
|
2020-05-22 18:20:29 +02:00
|
|
|
auto removeContact = td::td_api::make_object<td::td_api::removeContacts>();
|
2020-10-04 15:17:04 +02:00
|
|
|
removeContact->user_ids_.push_back(userId.value());
|
2020-05-22 18:20:29 +02:00
|
|
|
m_transceiver.sendQuery(std::move(removeContact), nullptr);
|
|
|
|
}
|
2020-10-04 22:09:19 +02:00
|
|
|
|
|
|
|
if (secretChatId.valid()) {
|
|
|
|
auto closeChat = td::td_api::make_object<td::td_api::closeSecretChat>(secretChatId.value());
|
|
|
|
m_transceiver.sendQuery(std::move(closeChat), nullptr);
|
|
|
|
}
|
2020-05-22 18:20:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::getUsers(const char *username, std::vector<const td::td_api::user *> &users)
|
|
|
|
{
|
2020-06-06 20:12:12 +02:00
|
|
|
users = getUsersByPurpleName(username, m_data, NULL);
|
2020-05-22 18:20:29 +02:00
|
|
|
}
|
|
|
|
|
2020-05-10 12:05:55 +02:00
|
|
|
bool PurpleTdClient::joinChat(const char *chatName)
|
|
|
|
{
|
2020-10-04 15:17:04 +02:00
|
|
|
ChatId id = getTdlibChatId(chatName);
|
2020-05-10 12:05:55 +02:00
|
|
|
const td::td_api::chat *chat = m_data.getChat(id);
|
|
|
|
int32_t purpleId = m_data.getPurpleChatId(id);
|
|
|
|
PurpleConvChat *conv = NULL;
|
|
|
|
|
2020-10-09 23:40:34 +02:00
|
|
|
if (!chat) {
|
|
|
|
// Either the user is actively trying to join non-existent chat (for example by entering
|
|
|
|
// a chat ID when joining with pidgin), or this is the pidgin auto-rejoin that usually
|
|
|
|
// happens before we get info about telegram chats.
|
|
|
|
// Check if the latter is the case and if it is, schedule to rejoin when telegram chat appears
|
|
|
|
PurpleConversation *baseConv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
|
|
|
|
chatName, m_account);
|
|
|
|
if (baseConv && purple_conv_chat_has_left(purple_conversation_get_chat_data(baseConv))) {
|
|
|
|
purple_debug_misc(config::pluginId, "Scheduling to rejoin group chat %s - "
|
|
|
|
"no telegram chat found at the moment\n", chatName);
|
|
|
|
m_data.addExpectedChat(id);
|
|
|
|
} else
|
|
|
|
purple_debug_warning(config::pluginId, "No telegram chat found for purple name %s\n", chatName);
|
|
|
|
} else if (!m_data.isGroupChatWithMembership(*chat))
|
2020-05-10 14:24:15 +02:00
|
|
|
purple_debug_warning(config::pluginId, "Chat %s (%s) is not a group we a member of\n",
|
|
|
|
chatName, chat->title_.c_str());
|
|
|
|
else if (purpleId) {
|
2020-05-22 13:05:11 +02:00
|
|
|
conv = getChatConversation(m_data, *chat, purpleId);
|
2020-05-10 12:05:55 +02:00
|
|
|
if (conv)
|
|
|
|
purple_conversation_present(purple_conv_chat_get_conversation(conv));
|
|
|
|
}
|
|
|
|
|
|
|
|
return conv ? true : false;
|
|
|
|
}
|
2020-05-10 14:24:15 +02:00
|
|
|
|
|
|
|
int PurpleTdClient::sendGroupMessage(int purpleChatId, const char *message)
|
|
|
|
{
|
|
|
|
const td::td_api::chat *chat = m_data.getChatByPurpleId(purpleChatId);
|
|
|
|
|
|
|
|
if (!chat)
|
|
|
|
purple_debug_warning(config::pluginId, "No chat found for purple id %d\n", purpleChatId);
|
|
|
|
else if (!m_data.isGroupChatWithMembership(*chat))
|
2020-05-22 14:02:48 +02:00
|
|
|
purple_debug_misc(config::pluginId, "purple id %d (chat %s) is not a group we a member of\n",
|
2020-05-10 14:24:15 +02:00
|
|
|
purpleChatId, chat->title_.c_str());
|
|
|
|
else {
|
2020-10-04 15:17:04 +02:00
|
|
|
int ret = transmitMessage(getId(*chat), message, m_transceiver, m_data, &PurpleTdClient::sendMessageResponse);
|
2020-06-06 13:57:36 +02:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2020-05-10 14:24:15 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2020-05-12 19:37:59 +02:00
|
|
|
|
2020-08-13 13:34:23 +02:00
|
|
|
void PurpleTdClient::joinChatByInviteLink(const char *inviteLink)
|
2020-05-12 19:37:59 +02:00
|
|
|
{
|
|
|
|
auto request = td::td_api::make_object<td::td_api::joinChatByInviteLink>(inviteLink);
|
2020-08-13 13:34:23 +02:00
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(request), &PurpleTdClient::joinChatResponse);
|
|
|
|
m_data.addPendingRequest<GroupJoinRequest>(requestId, inviteLink, GroupJoinRequest::Type::InviteLink);
|
2020-05-12 19:37:59 +02:00
|
|
|
}
|
|
|
|
|
2020-08-13 13:34:23 +02:00
|
|
|
void PurpleTdClient::joinChatResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
2020-05-12 19:37:59 +02:00
|
|
|
{
|
|
|
|
std::unique_ptr<GroupJoinRequest> request = m_data.getPendingRequest<GroupJoinRequest>(requestId);
|
2020-08-13 13:34:23 +02:00
|
|
|
if (object && ((object->get_id() == td::td_api::chat::ID) || (object->get_id() == td::td_api::ok::ID))) {
|
2020-05-12 19:37:59 +02:00
|
|
|
// If the chat was added with something like "Add chat" function from Pidgin, the chat in
|
|
|
|
// contact list was created without id component (for if there was the id component,
|
|
|
|
// tgprpl_chat_join would not have called PurpleTdClient::joinChatByLink).
|
|
|
|
|
|
|
|
// So when updateNewChat came prior to this response (as it must have), a new chat with
|
|
|
|
// correct id component (but without invite link component) was added to the contact list
|
|
|
|
// by PurpleTdClient::addChat calling updateBasicGroupChat, or whatever happens for
|
|
|
|
// supergroups.
|
|
|
|
|
|
|
|
// Therefore, remove the original manually added chat, and keep the auto-added one.
|
|
|
|
// Furthermore, user could have added same chat like that multiple times, in which case
|
|
|
|
// remove all of them.
|
|
|
|
if (request) {
|
2020-08-27 10:26:42 +02:00
|
|
|
if (!request->joinString.empty()) {
|
|
|
|
std::vector<PurpleChat *> obsoleteChats = findChatsByJoinString(request->joinString);
|
|
|
|
for (PurpleChat *chat: obsoleteChats)
|
|
|
|
purple_blist_remove_chat(chat);
|
|
|
|
}
|
2020-08-13 13:34:23 +02:00
|
|
|
// Conversation window for the chat should be presented. If joining by invite link, it
|
|
|
|
// will happen automatically due to messageChatJoinByLink message. If joining a public
|
|
|
|
// group, conversation window needs to be created explicitly instead
|
|
|
|
if (request->type != GroupJoinRequest::Type::InviteLink) {
|
|
|
|
const td::td_api::chat *chat = m_data.getChat(request->chatId);
|
|
|
|
int32_t purpleId = m_data.getPurpleChatId(request->chatId);
|
|
|
|
if (chat)
|
|
|
|
getChatConversation(m_data, *chat, purpleId);
|
|
|
|
}
|
2020-05-12 19:37:59 +02:00
|
|
|
}
|
|
|
|
} else {
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Error dialog, content
|
2020-05-12 19:37:59 +02:00
|
|
|
std::string message = formatMessage(_("Failed to join chat: {}"), getDisplayedError(object));
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Error dialog, title
|
2020-05-12 19:37:59 +02:00
|
|
|
purple_notify_error(purple_account_get_connection(m_account), _("Failed to join chat"),
|
|
|
|
message.c_str(), NULL);
|
|
|
|
}
|
|
|
|
}
|
2020-05-16 00:16:06 +02:00
|
|
|
|
2020-08-13 13:34:23 +02:00
|
|
|
void PurpleTdClient::joinChatByGroupName(const char *joinString, const char *groupName)
|
|
|
|
{
|
|
|
|
auto request = td::td_api::make_object<td::td_api::searchPublicChat>(groupName);
|
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(request), &PurpleTdClient::joinGroupSearchChatResponse);
|
|
|
|
m_data.addPendingRequest<GroupJoinRequest>(requestId, joinString, GroupJoinRequest::Type::Username);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::joinGroupSearchChatResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
std::unique_ptr<GroupJoinRequest> request = m_data.getPendingRequest<GroupJoinRequest>(requestId);
|
|
|
|
if (object && (object->get_id() == td::td_api::chat::ID)) {
|
|
|
|
const td::td_api::chat &chat = static_cast<const td::td_api::chat &>(*object);
|
|
|
|
if (chat.type_ && ((chat.type_->get_id() == td::td_api::chatTypeBasicGroup::ID) ||
|
|
|
|
(chat.type_->get_id() == td::td_api::chatTypeSupergroup::ID))) {
|
|
|
|
auto joinRequest = td::td_api::make_object<td::td_api::joinChat>(chat.id_);
|
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(joinRequest), &PurpleTdClient::joinChatResponse);
|
|
|
|
m_data.addPendingRequest<GroupJoinRequest>(requestId, request ? request->joinString : std::string(),
|
2020-10-04 15:17:04 +02:00
|
|
|
GroupJoinRequest::Type::Username, getId(chat));
|
2020-08-13 13:34:23 +02:00
|
|
|
} else
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Error dialog, title
|
2020-08-13 13:34:23 +02:00
|
|
|
purple_notify_error(purple_account_get_connection(m_account), _("Failed to join chat"),
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Error dialog, content
|
2020-08-13 13:34:23 +02:00
|
|
|
_("The name belongs to a user, not a group"), NULL);
|
|
|
|
} else {
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Error dialog, content, argument is a reason (text)
|
2020-08-13 13:34:23 +02:00
|
|
|
std::string message = formatMessage(_("Could not find group: {}"), getDisplayedError(object));
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Error dialog, title
|
2020-08-13 13:34:23 +02:00
|
|
|
purple_notify_error(purple_account_get_connection(m_account), _("Failed to join chat"),
|
|
|
|
message.c_str(), NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-22 14:48:17 +02:00
|
|
|
void PurpleTdClient::createGroup(const char *name, int type,
|
|
|
|
const std::vector<std::string> &basicGroupMembers)
|
|
|
|
{
|
|
|
|
td::td_api::object_ptr<td::td_api::Function> request;
|
|
|
|
if (type == GROUP_TYPE_BASIC) {
|
|
|
|
auto createRequest = td::td_api::make_object<td::td_api::createNewBasicGroupChat>();
|
|
|
|
createRequest->title_ = name;
|
|
|
|
|
|
|
|
std::string errorMessage;
|
2020-08-19 22:51:21 +02:00
|
|
|
if (basicGroupMembers.empty()) {
|
|
|
|
// TRANSLATOR: Error dialog, secondary content
|
2020-05-22 14:48:17 +02:00
|
|
|
errorMessage = _("Cannot create basic group without additional members");
|
2020-08-19 22:51:21 +02:00
|
|
|
}
|
2020-05-22 14:48:17 +02:00
|
|
|
for (const std::string &memberName: basicGroupMembers) {
|
2020-10-04 15:17:04 +02:00
|
|
|
UserId userId = purpleBuddyNameToUserId(memberName.c_str());
|
|
|
|
if (userId.valid()) {
|
2020-08-19 22:51:21 +02:00
|
|
|
if (!m_data.getUser(userId)) {
|
2020-05-27 20:21:00 +02:00
|
|
|
errorMessage = formatMessage(_("No known user with id {}"), userId);
|
2020-08-19 22:51:21 +02:00
|
|
|
}
|
2020-05-22 14:48:17 +02:00
|
|
|
} else {
|
|
|
|
std::vector<const td::td_api::user*> users;
|
|
|
|
m_data.getUsersByDisplayName(memberName.c_str(), users);
|
|
|
|
if (users.size() == 1)
|
2020-10-04 15:17:04 +02:00
|
|
|
userId = getId(*users[0]);
|
2020-08-19 22:51:21 +02:00
|
|
|
else if (users.empty()) {
|
|
|
|
// TRANSLATOR: Error dialog, secondary content, argument is a username
|
2020-05-22 14:48:17 +02:00
|
|
|
errorMessage = formatMessage(_("No known user by the name '{}'"), memberName);
|
2020-08-19 22:51:21 +02:00
|
|
|
} else {
|
2020-09-02 21:20:53 +02:00
|
|
|
// Unlikely error message not worth translating
|
|
|
|
errorMessage = formatMessage("More than one user known with name '{}'", memberName);
|
2020-08-19 22:51:21 +02:00
|
|
|
}
|
2020-05-22 14:48:17 +02:00
|
|
|
}
|
|
|
|
if (!errorMessage.empty())
|
|
|
|
break;
|
2020-10-04 15:17:04 +02:00
|
|
|
createRequest->user_ids_.push_back(userId.value());
|
2020-05-22 14:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!errorMessage.empty())
|
|
|
|
purple_notify_error(purple_account_get_connection(m_account),
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Error dialog, title
|
|
|
|
_("Failed to create group"),
|
|
|
|
// TRANSLATOR: Error dialog, primary content
|
|
|
|
_("Invalid group members"),
|
2020-05-22 14:48:17 +02:00
|
|
|
errorMessage.c_str());
|
|
|
|
else
|
|
|
|
request = std::move(createRequest);
|
|
|
|
} else if ((type == GROUP_TYPE_SUPER) || (type == GROUP_TYPE_CHANNEL)) {
|
|
|
|
auto createRequest = td::td_api::make_object<td::td_api::createNewSupergroupChat>();
|
|
|
|
createRequest->title_ = name;
|
|
|
|
createRequest->is_channel_ = (type == GROUP_TYPE_CHANNEL);
|
|
|
|
request = std::move(createRequest);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (request) {
|
|
|
|
// Same as for joining by invite link
|
|
|
|
std::vector<PurpleChat *> obsoleteChats = findChatsByNewGroup(name, type);
|
|
|
|
for (PurpleChat *chat: obsoleteChats)
|
|
|
|
purple_blist_remove_chat(chat);
|
|
|
|
|
|
|
|
m_transceiver.sendQuery(std::move(request), nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-22 21:21:50 +02:00
|
|
|
BasicGroupMembership PurpleTdClient::getBasicGroupMembership(const char *purpleChatName)
|
|
|
|
{
|
2020-10-04 15:17:04 +02:00
|
|
|
ChatId chatId = getTdlibChatId(purpleChatName);
|
|
|
|
const td::td_api::chat *chat = chatId.valid() ? m_data.getChat(chatId) : nullptr;
|
|
|
|
BasicGroupId groupId = chat ? getBasicGroupId(*chat) : BasicGroupId::invalid;
|
|
|
|
const td::td_api::basicGroup *basicGroup = groupId.valid() ? m_data.getBasicGroup(groupId) : nullptr;
|
2020-05-22 21:21:50 +02:00
|
|
|
|
|
|
|
if (basicGroup) {
|
|
|
|
if (basicGroup->status_ && (basicGroup->status_->get_id() == td::td_api::chatMemberStatusCreator::ID))
|
|
|
|
return BasicGroupMembership::Creator;
|
|
|
|
else
|
|
|
|
return BasicGroupMembership::NonCreator;
|
|
|
|
}
|
|
|
|
return BasicGroupMembership::Invalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::leaveGroup(const std::string &purpleChatName, bool deleteSupergroup)
|
|
|
|
{
|
2020-10-04 15:17:04 +02:00
|
|
|
ChatId chatId = getTdlibChatId(purpleChatName.c_str());
|
|
|
|
const td::td_api::chat *chat = chatId.valid() ? m_data.getChat(chatId) : nullptr;
|
2020-05-22 21:21:50 +02:00
|
|
|
if (!chat) return;
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
SupergroupId supergroupId = getSupergroupId(*chat);
|
|
|
|
if (deleteSupergroup && supergroupId.valid()) {
|
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::deleteSupergroup>(supergroupId.value()),
|
2020-05-22 21:21:50 +02:00
|
|
|
&PurpleTdClient::deleteSupergroupResponse);
|
|
|
|
} else {
|
2020-10-04 15:17:04 +02:00
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::leaveChat>(chatId.value()), nullptr);
|
2020-05-22 21:21:50 +02:00
|
|
|
auto deleteChatRequest = td::td_api::make_object<td::td_api::deleteChatHistory>();
|
2020-10-04 15:17:04 +02:00
|
|
|
deleteChatRequest->chat_id_ = chatId.value();
|
2020-05-22 21:21:50 +02:00
|
|
|
deleteChatRequest->remove_from_chat_list_ = true;
|
|
|
|
deleteChatRequest->revoke_ = false;
|
|
|
|
m_transceiver.sendQuery(std::move(deleteChatRequest), nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::deleteSupergroupResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
if (!object || (object->get_id() != td::td_api::ok::ID)) {
|
|
|
|
std::string errorMessage = getDisplayedError(object).c_str();
|
2020-08-19 22:51:21 +02:00
|
|
|
purple_notify_error(m_account,
|
|
|
|
// TRANSLATOR: Error dialog, title
|
|
|
|
_("Failed to delete group or channel"),
|
|
|
|
errorMessage.c_str(), NULL);
|
2020-05-22 21:21:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-01 11:13:23 +02:00
|
|
|
void PurpleTdClient::setGroupDescription(int purpleChatId, const char *description)
|
|
|
|
{
|
|
|
|
const td::td_api::chat *chat = m_data.getChatByPurpleId(purpleChatId);
|
|
|
|
if (!chat) {
|
|
|
|
purple_debug_warning(config::pluginId, "Unknown libpurple chat id %d\n", purpleChatId);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
if (getBasicGroupId(*chat).valid() || getSupergroupId(*chat).valid()) {
|
2020-06-01 11:13:23 +02:00
|
|
|
auto request = td::td_api::make_object<td::td_api::setChatDescription>();
|
|
|
|
request->chat_id_ = chat->id_;
|
|
|
|
request->description_ = description ? description : "";
|
|
|
|
m_transceiver.sendQuery(std::move(request), &PurpleTdClient::setGroupDescriptionResponse);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::setGroupDescriptionResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
if (!object || (object->get_id() != td::td_api::ok::ID)) {
|
|
|
|
std::string message = getDisplayedError(object);
|
2020-08-19 22:51:21 +02:00
|
|
|
purple_notify_error(m_account,
|
|
|
|
// TRANSLATOR: Error dialog, title
|
|
|
|
_("Failed to set group description"),
|
|
|
|
message.c_str(), NULL);
|
2020-06-01 11:13:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 13:09:35 +02:00
|
|
|
void PurpleTdClient::kickUserFromChat(PurpleConversation *conv, const char *name)
|
|
|
|
{
|
|
|
|
int purpleChatId = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
|
|
|
|
const td::td_api::chat *chat = m_data.getChatByPurpleId(purpleChatId);
|
|
|
|
|
|
|
|
if (!chat) {
|
|
|
|
// Unlikely error message not worth translating
|
2020-08-27 20:42:57 +02:00
|
|
|
purple_conversation_write(conv, "", "Chat not found", PURPLE_MESSAGE_NO_LOG, time(NULL));
|
2020-06-07 13:09:35 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<const td::td_api::user *> users = getUsersByPurpleName(name, m_data, "kick user");
|
|
|
|
if (users.size() != 1) {
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: In-chat error message, appears after a colon (':')
|
2020-06-07 14:07:58 +02:00
|
|
|
const char *reason = users.empty() ? _("User not found") :
|
|
|
|
// Unlikely error message not worth translating
|
|
|
|
"More than one user found with this name";
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: In-chat error message, argument is a reason (text)
|
2020-06-07 14:07:58 +02:00
|
|
|
std::string message = formatMessage(_("Cannot kick user: {}"), std::string(reason));
|
2020-08-27 20:42:57 +02:00
|
|
|
purple_conversation_write(conv, "", message.c_str(), PURPLE_MESSAGE_NO_LOG, 0);
|
2020-06-07 13:09:35 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto setStatusRequest = td::td_api::make_object<td::td_api::setChatMemberStatus>();
|
|
|
|
setStatusRequest->chat_id_ = chat->id_;
|
|
|
|
setStatusRequest->user_id_ = users[0]->id_;
|
|
|
|
setStatusRequest->status_ = td::td_api::make_object<td::td_api::chatMemberStatusLeft>();
|
|
|
|
|
2020-06-07 14:07:58 +02:00
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(setStatusRequest), &PurpleTdClient::chatActionResponse);
|
2020-10-04 15:17:04 +02:00
|
|
|
m_data.addPendingRequest<ChatActionRequest>(requestId, ChatActionRequest::Type::Kick, getId(*chat));
|
2020-06-07 13:09:35 +02:00
|
|
|
}
|
|
|
|
|
2020-06-07 14:07:58 +02:00
|
|
|
void PurpleTdClient::chatActionResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
2020-06-07 13:09:35 +02:00
|
|
|
{
|
2020-06-07 15:18:22 +02:00
|
|
|
std::unique_ptr<ChatActionRequest> request = m_data.getPendingRequest<ChatActionRequest>(requestId);
|
|
|
|
if (!request) return;
|
|
|
|
|
|
|
|
int32_t expectedId = 0;
|
|
|
|
switch (request->type) {
|
|
|
|
case ChatActionRequest::Type::Kick:
|
|
|
|
case ChatActionRequest::Type::Invite:
|
|
|
|
expectedId = td::td_api::ok::ID;
|
|
|
|
break;
|
|
|
|
case ChatActionRequest::Type::GenerateInviteLink:
|
|
|
|
expectedId = td::td_api::chatInviteLink::ID;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!object || (object->get_id() != expectedId)) {
|
2020-06-07 14:07:58 +02:00
|
|
|
const td::td_api::chat *chat = request ? m_data.getChat(request->chatId) : nullptr;
|
2020-06-07 13:09:35 +02:00
|
|
|
if (chat) {
|
2020-06-07 14:07:58 +02:00
|
|
|
std::string message = getDisplayedError(object);
|
|
|
|
switch (request->type) {
|
|
|
|
case ChatActionRequest::Type::Kick:
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: In-chat error message, argument is a reason (text)
|
2020-06-07 14:07:58 +02:00
|
|
|
message = formatMessage(_("Cannot kick user: {}"), message);
|
|
|
|
break;
|
|
|
|
case ChatActionRequest::Type::Invite:
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: In-chat error message, argument is a reason (text)
|
2020-06-07 14:07:58 +02:00
|
|
|
message = formatMessage(_("Cannot add user to group: {}"), message);
|
|
|
|
break;
|
2020-06-07 15:18:22 +02:00
|
|
|
case ChatActionRequest::Type::GenerateInviteLink:
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: In-chat error message, argument is a reason (text)
|
2020-06-07 15:18:22 +02:00
|
|
|
message = formatMessage(_("Cannot generate invite link: {}"), message);
|
|
|
|
break;
|
2020-06-07 14:07:58 +02:00
|
|
|
}
|
|
|
|
showChatNotification(m_data, *chat, message.c_str());
|
2020-06-07 13:09:35 +02:00
|
|
|
}
|
2020-06-07 15:18:22 +02:00
|
|
|
} else {
|
|
|
|
if (request->type == ChatActionRequest::Type::GenerateInviteLink) {
|
|
|
|
const td::td_api::chatInviteLink &inviteLink = static_cast<const td::td_api::chatInviteLink &>(*object);
|
|
|
|
const td::td_api::chat *chat = request ? m_data.getChat(request->chatId) : nullptr;
|
|
|
|
if (chat)
|
|
|
|
showChatNotification(m_data, *chat, inviteLink.invite_link_.c_str());
|
|
|
|
}
|
2020-06-07 13:09:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 14:07:58 +02:00
|
|
|
void PurpleTdClient::addUserToChat(int purpleChatId, const char *name)
|
|
|
|
{
|
|
|
|
const td::td_api::chat *chat = m_data.getChatByPurpleId(purpleChatId);
|
|
|
|
if (!chat) {
|
|
|
|
purple_debug_warning(config::pluginId, "Unknown libpurple chat id %d\n", purpleChatId);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<const td::td_api::user *> users = getUsersByPurpleName(name, m_data, "kick user");
|
|
|
|
if (users.size() != 1) {
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: In-chat error message, appears after a colon (':')
|
2020-06-07 14:07:58 +02:00
|
|
|
const char *reason = users.empty() ? _("User not found") :
|
|
|
|
// Unlikely error message not worth translating
|
|
|
|
"More than one user found with this name";
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: In-chat error message, argument is a reason (text)
|
2020-06-07 14:07:58 +02:00
|
|
|
std::string message = formatMessage(_("Cannot add user to group: {}"), std::string(reason));
|
|
|
|
showChatNotification(m_data, *chat, message.c_str(), PURPLE_MESSAGE_NO_LOG);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
if (getBasicGroupId(*chat).valid() || getSupergroupId(*chat).valid()) {
|
2020-06-07 14:07:58 +02:00
|
|
|
auto request = td::td_api::make_object<td::td_api::addChatMember>();
|
|
|
|
request->chat_id_ = chat->id_;
|
|
|
|
request->user_id_ = users[0]->id_;
|
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(request), &PurpleTdClient::chatActionResponse);
|
2020-10-04 15:17:04 +02:00
|
|
|
m_data.addPendingRequest<ChatActionRequest>(requestId, ChatActionRequest::Type::Invite, getId(*chat));
|
2020-06-07 14:07:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 15:18:22 +02:00
|
|
|
void PurpleTdClient::showInviteLink(const std::string& purpleChatName)
|
|
|
|
{
|
2020-10-04 15:17:04 +02:00
|
|
|
ChatId chatId = getTdlibChatId(purpleChatName.c_str());
|
|
|
|
const td::td_api::chat *chat = chatId.valid() ? m_data.getChat(chatId) : nullptr;
|
2020-06-07 15:18:22 +02:00
|
|
|
if (!chat) {
|
|
|
|
purple_debug_warning(config::pluginId, "chat %s not found\n", purpleChatName.c_str());
|
|
|
|
return;
|
|
|
|
}
|
2020-10-04 15:17:04 +02:00
|
|
|
BasicGroupId basicGroupId = getBasicGroupId(*chat);
|
|
|
|
SupergroupId supergroupId = getSupergroupId(*chat);
|
|
|
|
const td::td_api::basicGroupFullInfo *basicGroupInfo = basicGroupId.valid() ? m_data.getBasicGroupInfo(basicGroupId) : nullptr;
|
|
|
|
const td::td_api::supergroupFullInfo *supergroupInfo = supergroupId.valid() ? m_data.getSupergroupInfo(supergroupId) : nullptr;
|
2020-06-07 15:18:22 +02:00
|
|
|
bool fullInfoKnown = false;
|
|
|
|
std::string inviteLink;
|
|
|
|
|
2020-10-04 15:17:04 +02:00
|
|
|
if (basicGroupId.valid()) {
|
2020-06-07 15:18:22 +02:00
|
|
|
fullInfoKnown = (basicGroupInfo != nullptr);
|
|
|
|
if (basicGroupInfo) inviteLink = basicGroupInfo->invite_link_;
|
|
|
|
}
|
2020-10-04 15:17:04 +02:00
|
|
|
if (supergroupId.valid()) {
|
2020-06-07 15:18:22 +02:00
|
|
|
fullInfoKnown = (supergroupInfo != nullptr);
|
|
|
|
if (supergroupInfo) inviteLink = supergroupInfo->invite_link_;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!inviteLink.empty())
|
|
|
|
showChatNotification(m_data, *chat, inviteLink.c_str());
|
|
|
|
else if (fullInfoKnown) {
|
|
|
|
auto linkRequest = td::td_api::make_object<td::td_api::generateChatInviteLink>(chat->id_);
|
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(linkRequest), &PurpleTdClient::chatActionResponse);
|
2020-10-04 15:17:04 +02:00
|
|
|
m_data.addPendingRequest<ChatActionRequest>(requestId, ChatActionRequest::Type::GenerateInviteLink, getId(*chat));
|
2020-06-07 15:18:22 +02:00
|
|
|
} else
|
|
|
|
// Unlikely error message not worth translating
|
|
|
|
showChatNotification(m_data, *chat, "Failed to get invite link, full info not known");
|
|
|
|
}
|
|
|
|
|
2020-06-11 00:04:29 +02:00
|
|
|
void PurpleTdClient::getGroupChatList(PurpleRoomlist *roomlist)
|
|
|
|
{
|
|
|
|
|
|
|
|
GList *fields = NULL;
|
|
|
|
PurpleRoomlistField *f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "",
|
|
|
|
getChatNameComponent(), TRUE);
|
|
|
|
fields = g_list_append (fields, f);
|
|
|
|
// "description" is hard-coded in bitlbee as possible field for chat topic
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: Groupchat infobox key
|
2020-06-11 00:04:29 +02:00
|
|
|
f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Description"), "description", FALSE);
|
|
|
|
fields = g_list_append (fields, f);
|
|
|
|
purple_roomlist_set_fields (roomlist, fields);
|
|
|
|
|
|
|
|
purple_roomlist_set_in_progress(roomlist, TRUE);
|
2020-08-27 17:23:59 +02:00
|
|
|
if (m_chatListReady) {
|
2020-06-11 00:04:29 +02:00
|
|
|
std::vector<const td::td_api::chat *> chats;
|
|
|
|
m_data.getChats(chats);
|
|
|
|
populateGroupChatList(roomlist, chats, m_data);
|
|
|
|
} else {
|
|
|
|
purple_roomlist_ref(roomlist);
|
|
|
|
m_pendingRoomLists.push_back(roomlist);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-16 00:16:06 +02:00
|
|
|
void PurpleTdClient::removeTempFile(int64_t messageId)
|
|
|
|
{
|
|
|
|
std::string path = m_data.extractTempFileUpload(messageId);
|
|
|
|
if (!path.empty()) {
|
|
|
|
purple_debug_misc(config::pluginId, "Removing temporary file %s\n", path.c_str());
|
|
|
|
remove(path.c_str());
|
|
|
|
}
|
|
|
|
}
|
2020-05-25 23:21:04 +02:00
|
|
|
|
2020-05-31 14:19:09 +02:00
|
|
|
void PurpleTdClient::setTwoFactorAuth(const char *oldPassword, const char *newPassword,
|
2020-05-25 23:21:04 +02:00
|
|
|
const char *hint, const char *email)
|
|
|
|
{
|
|
|
|
auto setPassword = td::td_api::make_object<td::td_api::setPassword>();
|
|
|
|
if (oldPassword)
|
|
|
|
setPassword->old_password_ = oldPassword;
|
|
|
|
if (newPassword)
|
|
|
|
setPassword->new_password_ = newPassword;
|
|
|
|
if (hint)
|
|
|
|
setPassword->new_hint_ = hint;
|
|
|
|
setPassword->set_recovery_email_address_ = (email && *email);
|
|
|
|
if (email)
|
|
|
|
setPassword->new_recovery_email_address_ = email;
|
|
|
|
|
2020-05-31 14:19:09 +02:00
|
|
|
m_transceiver.sendQuery(std::move(setPassword), &PurpleTdClient::setTwoFactorAuthResponse);
|
2020-05-25 23:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void inputCancelled(void *data)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::requestRecoveryEmailConfirmation(const std::string &emailInfo)
|
|
|
|
{
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: 2FA setup confirmation dialog, secondary content, argument is an e-mail description (address and code length)
|
|
|
|
std::string secondary = formatMessage(_("Password will be changed after new e-mail is confirmed\n{}"), emailInfo);
|
2020-05-25 23:21:04 +02:00
|
|
|
PurpleConnection *gc = purple_account_get_connection(m_account);
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: 2FA setup confirmation dialog, title
|
2020-05-25 23:21:04 +02:00
|
|
|
purple_request_input(gc, _("Two-factor authentication"),
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: 2FA setup confirmation dialog, primary content
|
2020-05-25 23:21:04 +02:00
|
|
|
_("Enter verification code received in the e-mail"), secondary.c_str(),
|
|
|
|
NULL, // default value
|
|
|
|
FALSE, // multiline input
|
|
|
|
FALSE, // masked input
|
|
|
|
NULL,
|
2020-08-20 19:33:45 +02:00
|
|
|
// TRANSLATOR: 2FA setup confirmation dialog, alternative is "_Cancel". The underscore marks accelerator keys, they must be different!
|
|
|
|
_("_OK"), G_CALLBACK(PurpleTdClient::verifyRecoveryEmail),
|
|
|
|
// TRANSLATOR: 2FA setup confirmation dialog, alternative is "_OK". The underscore marks accelerator keys, they must be different!
|
|
|
|
_("_Cancel"), G_CALLBACK(inputCancelled),
|
2020-05-25 23:21:04 +02:00
|
|
|
purple_connection_get_account(gc),
|
|
|
|
NULL, // buddy
|
|
|
|
NULL, // conversation
|
|
|
|
this);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void notifyPasswordChangeSuccess(PurpleAccount *account, const td::td_api::passwordState &passwordState)
|
|
|
|
{
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: 2FA success notification, title
|
2020-05-31 14:19:09 +02:00
|
|
|
purple_notify_info(account, _("Two-factor authentication"),
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: 2FA success notification, primary content
|
|
|
|
passwordState.has_password_ ? _("Password set") :
|
|
|
|
// TRANSLATOR: 2FA success notification, primary content
|
|
|
|
_("Password cleared"),
|
|
|
|
// TRANSLATOR: 2FA success notification, secondary content
|
2020-05-25 23:21:04 +02:00
|
|
|
passwordState.has_recovery_email_address_ ? _("Recovery e-mail is configured") :
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: 2FA success notification, secondary content
|
2020-05-25 23:21:04 +02:00
|
|
|
_("No recovery e-mail configured"));
|
|
|
|
}
|
|
|
|
|
2020-05-31 14:19:09 +02:00
|
|
|
void PurpleTdClient::setTwoFactorAuthResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
2020-05-25 23:21:04 +02:00
|
|
|
{
|
|
|
|
if (object && (object->get_id() == td::td_api::passwordState::ID)) {
|
|
|
|
const td::td_api::passwordState &passwordState = static_cast<const td::td_api::passwordState &>(*object);
|
|
|
|
if (passwordState.recovery_email_address_code_info_) {
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: 2FA setup confirmation dialog, e-mail description
|
2020-08-20 19:13:30 +02:00
|
|
|
std::string emailInfo = formatMessage(_("Code sent to {0} (length: {1})"),
|
2020-05-25 23:21:04 +02:00
|
|
|
{passwordState.recovery_email_address_code_info_->email_address_pattern_,
|
|
|
|
std::to_string(passwordState.recovery_email_address_code_info_->length_)});
|
|
|
|
requestRecoveryEmailConfirmation(emailInfo);
|
|
|
|
} else
|
|
|
|
notifyPasswordChangeSuccess(m_account, passwordState);
|
|
|
|
} else {
|
|
|
|
std::string errorMessage = getDisplayedError(object);
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: 2FA failure notification, title
|
|
|
|
purple_notify_error(m_account, _("Two-factor authentication"),
|
|
|
|
// TRANSLATOR: 2FA failure notification, primary content
|
|
|
|
_("Failed to set password"), errorMessage.c_str());
|
2020-05-25 23:21:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::verifyRecoveryEmail(PurpleTdClient *self, const char *code)
|
|
|
|
{
|
|
|
|
auto checkCode = td::td_api::make_object<td::td_api::checkRecoveryEmailAddressCode>();
|
|
|
|
if (code)
|
|
|
|
checkCode->code_ = code;
|
|
|
|
self->m_transceiver.sendQuery(std::move(checkCode), &PurpleTdClient::verifyRecoveryEmailResponse);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::verifyRecoveryEmailResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
if (object && (object->get_id() == td::td_api::passwordState::ID)) {
|
|
|
|
const td::td_api::passwordState &passwordState = static_cast<const td::td_api::passwordState &>(*object);
|
|
|
|
if (passwordState.recovery_email_address_code_info_) {
|
|
|
|
if (passwordState.recovery_email_address_code_info_->length_ > 0) {
|
2020-09-02 21:20:53 +02:00
|
|
|
// Not expected to happen, so not worth translating
|
|
|
|
std::string emailInfo = formatMessage("E-mail address: {}",
|
2020-05-25 23:21:04 +02:00
|
|
|
passwordState.recovery_email_address_code_info_->email_address_pattern_);
|
2020-05-31 14:19:09 +02:00
|
|
|
purple_notify_info(m_account, _("Two-factor authentication"),
|
2020-09-02 21:20:53 +02:00
|
|
|
"For some reason, new confirmation code was sent", emailInfo.c_str());
|
2020-05-25 23:21:04 +02:00
|
|
|
} else
|
2020-08-19 22:51:21 +02:00
|
|
|
// TRANSLATOR: 2FA failure notification, title
|
|
|
|
purple_notify_error(m_account, _("Two-factor authentication"),
|
|
|
|
// TRANSLATOR: 2FA failure notification, content
|
|
|
|
_("Looks like the code was wrong"), NULL);
|
2020-05-25 23:21:04 +02:00
|
|
|
} else
|
|
|
|
notifyPasswordChangeSuccess(m_account, passwordState);
|
|
|
|
} else {
|
2020-09-02 21:20:53 +02:00
|
|
|
// Shouldn't really happen, so not worth translating. The only reasonable failure is wrong
|
|
|
|
// code, which is handled elsewhere.
|
2020-05-25 23:21:04 +02:00
|
|
|
std::string errorMessage = getDisplayedError(object);
|
2020-09-02 21:20:53 +02:00
|
|
|
purple_notify_error(m_account, "Two-factor authentication",
|
|
|
|
"Failed to verify recovery e-mail", errorMessage.c_str());
|
2020-05-25 23:21:04 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-28 21:29:58 +02:00
|
|
|
|
2020-06-12 17:29:58 +02:00
|
|
|
void PurpleTdClient::sendFileToChat(PurpleXfer *xfer, const char *purpleName,
|
|
|
|
PurpleConversationType type, int purpleChatId)
|
2020-05-28 21:29:58 +02:00
|
|
|
{
|
|
|
|
const char *filename = purple_xfer_get_local_filename(xfer);
|
2020-06-06 14:20:56 +02:00
|
|
|
const td::td_api::user *privateUser = nullptr;
|
|
|
|
const td::td_api::chat *chat = nullptr;
|
2020-06-06 20:12:12 +02:00
|
|
|
|
2020-06-06 14:20:56 +02:00
|
|
|
if (type == PURPLE_CONV_TYPE_IM) {
|
2020-10-04 19:59:27 +02:00
|
|
|
SecretChatId secretChatId = purpleBuddyNameToSecretChatId(purpleName);
|
|
|
|
if (secretChatId.valid())
|
|
|
|
chat = m_data.getChatBySecretChat(secretChatId);
|
|
|
|
else {
|
|
|
|
std::vector<const td::td_api::user *> users = getUsersByPurpleName(purpleName, m_data, "send message");
|
|
|
|
if (users.size() == 1) {
|
|
|
|
privateUser = users[0];
|
|
|
|
chat = m_data.getPrivateChatByUserId(getId(*privateUser));
|
|
|
|
}
|
2020-06-06 20:12:12 +02:00
|
|
|
}
|
2020-06-12 17:29:58 +02:00
|
|
|
} else if (type == PURPLE_CONV_TYPE_CHAT)
|
|
|
|
chat = m_data.getChatByPurpleId(purpleChatId);
|
2020-05-28 21:29:58 +02:00
|
|
|
|
2020-06-06 14:20:56 +02:00
|
|
|
if (filename && chat)
|
2020-10-04 15:17:04 +02:00
|
|
|
startDocumentUpload(getId(*chat), filename, xfer, m_transceiver, m_data, &PurpleTdClient::uploadResponse);
|
2020-06-06 20:12:12 +02:00
|
|
|
else if (filename && privateUser) {
|
|
|
|
purple_debug_misc(config::pluginId, "Requesting private chat for user id %d\n", (int)privateUser->id_);
|
|
|
|
td::td_api::object_ptr<td::td_api::createPrivateChat> createChat =
|
|
|
|
td::td_api::make_object<td::td_api::createPrivateChat>(privateUser->id_, false);
|
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(createChat), &PurpleTdClient::sendMessageCreatePrivateChatResponse);
|
|
|
|
purple_xfer_ref(xfer);
|
|
|
|
m_data.addPendingRequest<NewPrivateChatForMessage>(requestId, purpleName, xfer);
|
|
|
|
} else {
|
2020-05-28 21:29:58 +02:00
|
|
|
if (!filename)
|
|
|
|
purple_debug_warning(config::pluginId, "Failed to send file, no file name\n");
|
2020-06-06 14:20:56 +02:00
|
|
|
else if (!chat)
|
2020-05-28 21:29:58 +02:00
|
|
|
purple_debug_warning(config::pluginId, "Failed to send file %s, chat not found\n", filename);
|
2020-06-06 20:12:12 +02:00
|
|
|
purple_xfer_cancel_local(xfer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::sendMessageCreatePrivateChatResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
std::unique_ptr<NewPrivateChatForMessage> request = m_data.getPendingRequest<NewPrivateChatForMessage>(requestId);
|
|
|
|
if (!request) return;
|
|
|
|
auto chat = (object && (object->get_id() == td::td_api::chat::ID)) ?
|
|
|
|
static_cast<const td::td_api::chat *>(object.get()) : nullptr;
|
|
|
|
|
|
|
|
if (request->fileUpload) {
|
|
|
|
if (purple_xfer_is_canceled(request->fileUpload)) {
|
|
|
|
// User cancelled the upload really fast
|
|
|
|
} else if (chat) {
|
|
|
|
const char *filename = purple_xfer_get_local_filename(request->fileUpload);
|
|
|
|
if (filename)
|
2020-10-04 15:17:04 +02:00
|
|
|
startDocumentUpload(getId(*chat), filename, request->fileUpload, m_transceiver, m_data,
|
2020-06-06 20:12:12 +02:00
|
|
|
&PurpleTdClient::uploadResponse);
|
|
|
|
else
|
|
|
|
purple_xfer_cancel_local(request->fileUpload);
|
|
|
|
} else {
|
|
|
|
std::string message = getDisplayedError(object);
|
|
|
|
purple_xfer_cancel_local(request->fileUpload);
|
|
|
|
purple_xfer_error(purple_xfer_get_type(request->fileUpload), m_account,
|
|
|
|
request->username.c_str(), message.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
purple_xfer_unref(request->fileUpload);
|
|
|
|
} else {
|
|
|
|
std::string errorMessage;
|
|
|
|
|
|
|
|
if (chat) {
|
2020-10-04 15:17:04 +02:00
|
|
|
int ret = transmitMessage(getId(*chat), request->message.c_str(), m_transceiver, m_data,
|
2020-06-06 20:12:12 +02:00
|
|
|
&PurpleTdClient::sendMessageResponse);
|
|
|
|
// Messages copied from libpurple
|
2020-08-19 22:51:21 +02:00
|
|
|
if (ret == -E2BIG) {
|
|
|
|
// TRANSLATOR: In-chat error message
|
2020-06-06 20:12:12 +02:00
|
|
|
errorMessage = _("Unable to send message: The message is too large.");
|
2020-08-19 22:51:21 +02:00
|
|
|
} else if (ret < 0) {
|
|
|
|
// TRANSLATOR: In-chat error message
|
2020-06-06 20:12:12 +02:00
|
|
|
errorMessage = _("Unable to send message.");
|
2020-08-19 22:51:21 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// TRANSLATOR: In-chat(?) error message, argument is an error description (text)
|
2020-06-06 20:12:12 +02:00
|
|
|
errorMessage = formatMessage(_("Failed to open chat: {}"), getDisplayedError(object));
|
2020-08-19 22:51:21 +02:00
|
|
|
}
|
2020-06-06 20:12:12 +02:00
|
|
|
|
|
|
|
if (!errorMessage.empty())
|
|
|
|
showMessageTextIm(m_data, request->username.c_str(), NULL, errorMessage.c_str(),
|
|
|
|
time(NULL), PURPLE_MESSAGE_ERROR);
|
2020-05-28 21:29:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::uploadResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
std::unique_ptr<UploadRequest> request = m_data.getPendingRequest<UploadRequest>(requestId);
|
|
|
|
const td::td_api::file *file = nullptr;
|
|
|
|
|
|
|
|
if (object && (object->get_id() == td::td_api::file::ID))
|
|
|
|
file = static_cast<const td::td_api::file *>(object.get());
|
|
|
|
|
|
|
|
if (request) {
|
|
|
|
if (file)
|
2020-06-05 20:14:17 +02:00
|
|
|
startDocumentUploadProgress(request->chatId, request->xfer, *file, m_transceiver, m_data,
|
|
|
|
&PurpleTdClient::sendMessageResponse);
|
2020-05-28 21:29:58 +02:00
|
|
|
else
|
|
|
|
uploadResponseError(request->xfer, getDisplayedError(object), m_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::cancelUpload(PurpleXfer *xfer)
|
|
|
|
{
|
|
|
|
int32_t fileId;
|
2020-06-02 18:42:19 +02:00
|
|
|
if (m_data.getFileIdForTransfer(xfer, fileId)) {
|
2020-05-28 21:29:58 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Cancelling upload of %s (file id %d)\n",
|
|
|
|
purple_xfer_get_local_filename(xfer), fileId);
|
|
|
|
auto cancelRequest = td::td_api::make_object<td::td_api::cancelUploadFile>(fileId);
|
|
|
|
m_transceiver.sendQuery(std::move(cancelRequest), nullptr);
|
2020-06-02 18:42:19 +02:00
|
|
|
m_data.removeFileTransfer(fileId);
|
2020-05-28 21:29:58 +02:00
|
|
|
purple_xfer_unref(xfer);
|
|
|
|
} else {
|
|
|
|
// This could mean that response to upload request has not come yet - when it does,
|
|
|
|
// uploadResponse will notice that the transfer is cancelled and act accordingly.
|
|
|
|
// Or it could just be that the upload got cancelled programmatically due to some error,
|
|
|
|
// in which case nothing more should be done.
|
|
|
|
}
|
|
|
|
}
|
2020-07-26 23:44:51 +02:00
|
|
|
|
|
|
|
bool PurpleTdClient::startVoiceCall(const char *buddyName)
|
|
|
|
{
|
|
|
|
std::vector<const td::td_api::user *> users = getUsersByPurpleName(buddyName, m_data, "start voice call");
|
|
|
|
if (users.size() != 1) {
|
|
|
|
// Unlikely error messages not worth translating
|
|
|
|
std::string errorMessage;
|
|
|
|
if (users.empty())
|
|
|
|
errorMessage = "User not found";
|
|
|
|
else
|
|
|
|
errorMessage = formatMessage("More than one user known with name '{}'", std::string(buddyName));
|
|
|
|
showMessageTextIm(m_data, buddyName, NULL, errorMessage.c_str(), time(NULL), PURPLE_MESSAGE_ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return initiateCall(users.front()->id_, m_data, m_transceiver);
|
|
|
|
}
|
2020-07-27 17:31:27 +02:00
|
|
|
|
|
|
|
bool PurpleTdClient::terminateCall(PurpleConversation *conv)
|
|
|
|
{
|
|
|
|
if (!m_data.hasActiveCall())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
discardCurrentCall(m_data, m_transceiver);
|
|
|
|
return true;
|
|
|
|
}
|
2020-10-04 16:52:57 +02:00
|
|
|
|
|
|
|
void PurpleTdClient::createSecretChat(const char* buddyName)
|
|
|
|
{
|
|
|
|
std::vector<const td::td_api::user *> users = getUsersByPurpleName(buddyName, m_data, "create secret chat");
|
|
|
|
if (users.size() != 1) {
|
|
|
|
// Unlikely error messages not worth translating
|
|
|
|
const char *reason = users.empty() ? "User not found" :
|
|
|
|
"More than one user found with this name";
|
2020-10-23 13:13:40 +02:00
|
|
|
std::string message = formatMessage("Cannot create secret chat: {}", std::string(reason));
|
2020-10-04 16:52:57 +02:00
|
|
|
purple_notify_error(purple_account_get_connection(m_account),
|
2020-10-23 13:13:40 +02:00
|
|
|
// TRANSLATOR: Failure notification, title
|
2020-10-04 16:52:57 +02:00
|
|
|
_("Failed to create secret chat"),
|
|
|
|
message.c_str(), NULL);
|
2020-10-23 13:13:40 +02:00
|
|
|
|
2020-10-04 16:52:57 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto request = td::td_api::make_object<td::td_api::createNewSecretChat>(getId(*users[0]).value());
|
|
|
|
m_transceiver.sendQuery(std::move(request), nullptr);
|
|
|
|
}
|