2020-02-16 17:46:52 +01:00
|
|
|
#include "td-client.h"
|
2020-05-09 14:07:21 +02:00
|
|
|
#include "chat-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-05-13 22:22:55 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
2020-05-12 15:21:16 +02:00
|
|
|
|
|
|
|
static char *_(const char *s) { return const_cast<char *>(s); }
|
2020-02-16 17:46:52 +01: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-05-08 17:01:12 +02:00
|
|
|
FILE_DOWNLOAD_PRIORITY = 1
|
2020-03-21 12:47:47 +01:00
|
|
|
};
|
|
|
|
|
2020-05-10 16:32:13 +02:00
|
|
|
PurpleTdClient::PurpleTdClient(PurpleAccount *acct, ITransceiverBackend *testBackend)
|
|
|
|
: m_transceiver(this, &PurpleTdClient::processUpdate, testBackend)
|
|
|
|
{
|
|
|
|
m_account = acct;
|
|
|
|
}
|
|
|
|
|
|
|
|
PurpleTdClient::~PurpleTdClient()
|
|
|
|
{
|
|
|
|
}
|
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)});
|
|
|
|
}
|
|
|
|
|
|
|
|
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::updateConnectionState::ID: {
|
|
|
|
auto &connectionUpdate = static_cast<td::td_api::updateConnectionState &>(update);
|
2020-02-22 14:58:32 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: connection state\n");
|
2020-02-29 15:30:24 +01:00
|
|
|
if (connectionUpdate.state_) {
|
|
|
|
if (connectionUpdate.state_->get_id() == td::td_api::connectionStateReady::ID)
|
2020-05-10 16:32:13 +02:00
|
|
|
connectionReady();
|
2020-02-29 15:30:24 +01:00
|
|
|
else if (connectionUpdate.state_->get_id() == td::td_api::connectionStateConnecting::ID)
|
2020-05-10 16:32:13 +02:00
|
|
|
setPurpleConnectionInProgress();
|
2020-02-29 15:30:24 +01:00
|
|
|
else if (connectionUpdate.state_->get_id() == td::td_api::connectionStateUpdating::ID)
|
2020-05-10 16:32:13 +02:00
|
|
|
setPurpleConnectionUpdating();
|
2020-02-29 15:30:24 +01:00
|
|
|
}
|
2020-05-10 16:32:13 +02:00
|
|
|
break;
|
2020-02-22 14:58:32 +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-05-10 16:32:13 +02:00
|
|
|
updateUserStatus(updateStatus.user_id_, std::move(updateStatus.status_));
|
|
|
|
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-05-16 00:16:06 +02:00
|
|
|
case td::td_api::updateMessageSendSucceeded::ID: {
|
|
|
|
auto &sendSucceeded = static_cast<const td::td_api::updateMessageSendSucceeded &>(update);
|
|
|
|
removeTempFile(sendSucceeded.old_message_id_);
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
|
|
|
case td::td_api::updateMessageSendFailed::ID: {
|
|
|
|
auto &sendFailed = static_cast<const td::td_api::updateMessageSendFailed &>(update);
|
|
|
|
removeTempFile(sendFailed.old_message_id_);
|
|
|
|
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-10 16:32:13 +02:00
|
|
|
sendTdlibParameters();
|
|
|
|
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-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-05-13 13:14:38 +02:00
|
|
|
if (m_connectionReady)
|
|
|
|
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
|
|
|
|
|
|
|
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);
|
|
|
|
parameters->database_directory_ = std::string(purple_user_dir()) + G_DIR_SEPARATOR_S +
|
|
|
|
config::configSubdir + G_DIR_SEPARATOR_S + username;
|
|
|
|
purple_debug_misc(config::pluginId, "Account %s using database directory %s\n",
|
|
|
|
username, parameters->database_directory_.c_str());
|
|
|
|
parameters->use_message_database_ = true;
|
|
|
|
parameters->use_secret_chats_ = true;
|
|
|
|
parameters->api_id_ = 94575;
|
|
|
|
parameters->api_hash_ = "a3406de8d171bb422bb6ddf3bbd800e2";
|
|
|
|
parameters->system_language_code_ = "en";
|
|
|
|
parameters->device_model_ = "Desktop";
|
|
|
|
parameters->system_version_ = "Unknown";
|
|
|
|
parameters->application_version_ = "1.0";
|
|
|
|
parameters->enable_storage_optimizer_ = true;
|
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-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-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-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-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:
|
|
|
|
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-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) {
|
|
|
|
if (codeInfo->type_)
|
|
|
|
message += formatMessage(_("Code sent via: {}"), getAuthCodeDesc(*codeInfo->type_)) + "\n";
|
|
|
|
if (codeInfo->next_type_)
|
|
|
|
message += formatMessage(_("Next code will be: {}"), getAuthCodeDesc(*codeInfo->next_type_)) + "\n";
|
2020-02-22 14:58:32 +01:00
|
|
|
}
|
|
|
|
|
2020-05-02 13:40:48 +02:00
|
|
|
if (!purple_request_input (purple_account_get_connection(m_account),
|
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-05-12 15:21:16 +02:00
|
|
|
_("the code"),
|
|
|
|
_("OK"), G_CALLBACK(requestCodeEntered),
|
|
|
|
_("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-05-02 13:40:48 +02:00
|
|
|
this))
|
2020-02-22 14:58:32 +01:00
|
|
|
{
|
2020-05-02 13:40:48 +02:00
|
|
|
purple_connection_set_state (purple_account_get_connection(m_account), PURPLE_CONNECTED);
|
|
|
|
PurpleConversation *conv = purple_conversation_new (PURPLE_CONV_TYPE_IM, m_account, "Telegram");
|
2020-02-22 14:58:32 +01:00
|
|
|
purple_conversation_write (conv, "Telegram",
|
|
|
|
"Authentication code needs to be entered but this libpurple won't cooperate",
|
|
|
|
(PurpleMessageFlags)(PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_SYSTEM), 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
if (firstName.empty() && lastName.empty())
|
|
|
|
purple_connection_error(purple_account_get_connection(m_account),
|
|
|
|
_("Account alias (your name) must be set to register new user"));
|
|
|
|
else
|
|
|
|
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-02-22 14:58:32 +01:00
|
|
|
void PurpleTdClient::requestCodeEntered(PurpleTdClient *self, const gchar *code)
|
|
|
|
{
|
|
|
|
purple_debug_misc(config::pluginId, "Authentication code entered: '%s'\n", code);
|
2020-05-02 12:44:07 +02:00
|
|
|
self->m_transceiver.sendQuery(td::td_api::make_object<td::td_api::checkAuthenticationCode>(code),
|
|
|
|
&PurpleTdClient::authResponse);
|
2020-02-22 14:58:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::requestCodeCancelled(PurpleTdClient *self)
|
|
|
|
{
|
|
|
|
purple_connection_error(purple_account_get_connection(self->m_account),
|
2020-05-14 15:00:17 +02:00
|
|
|
_("Authentication code required"));
|
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
|
|
|
static std::string getDisplayedError(const td::td_api::object_ptr<td::td_api::Object> &object)
|
2020-03-21 14:32:33 +01:00
|
|
|
{
|
2020-05-12 16:00:07 +02:00
|
|
|
if (!object)
|
|
|
|
return _("No response received");
|
|
|
|
else if (object->get_id() == td::td_api::error::ID) {
|
|
|
|
const td::td_api::error &error = static_cast<const td::td_api::error &>(*object);
|
|
|
|
return formatMessage("code {} ({})", {std::to_string(error.code_), error.message_});
|
|
|
|
} else
|
|
|
|
return _("Unexpected response");
|
2020-03-21 14:32:33 +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-05-12 15:21:16 +02:00
|
|
|
message = _("Error applying database encryption key: {}");
|
2020-02-22 13:23:01 +01:00
|
|
|
break;
|
|
|
|
case td::td_api::authorizationStateWaitPhoneNumber::ID:
|
2020-05-12 15:21:16 +02:00
|
|
|
message = _("Authentication error after sending phone number: {}");
|
2020-02-22 13:23:01 +01:00
|
|
|
break;
|
|
|
|
default:
|
2020-05-12 15:21:16 +02:00
|
|
|
message = _("Authentication error: {}");
|
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-02-22 15:53:07 +01:00
|
|
|
void PurpleTdClient::connectionReady()
|
|
|
|
{
|
2020-02-29 15:30:24 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Connection ready\n");
|
2020-05-13 13:14:38 +02:00
|
|
|
m_connectionReady = true;
|
|
|
|
if (m_lastAuthState == td::td_api::authorizationStateReady::ID)
|
|
|
|
onLoggedIn();
|
2020-02-22 15:53:07 +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-13 13:14:38 +02:00
|
|
|
m_connectionReady = false;
|
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);
|
|
|
|
purple_connection_update_progress(gc, "Connecting", 1, 3);
|
|
|
|
}
|
|
|
|
|
2020-05-02 13:40:48 +02:00
|
|
|
void PurpleTdClient::setPurpleConnectionUpdating()
|
2020-02-29 15:30:24 +01:00
|
|
|
{
|
|
|
|
purple_debug_misc(config::pluginId, "Updating account status\n");
|
2020-05-13 13:14:38 +02:00
|
|
|
m_connectionReady = false;
|
2020-05-02 13:40:48 +02:00
|
|
|
PurpleConnection *gc = purple_account_get_connection(m_account);
|
2020-02-29 15:30:24 +01:00
|
|
|
|
|
|
|
purple_connection_update_progress(gc, "Updating status", 2, 3);
|
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()
|
|
|
|
{
|
|
|
|
// 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-05-02 12:35:31 +02:00
|
|
|
td::td_api::object_ptr<td::td_api::users> users = td::move_tl_object_as<td::td_api::users>(object);
|
2020-05-02 12:53:25 +02:00
|
|
|
m_data.setContacts(users->user_ids_);
|
2020-03-25 16:49:26 +01:00
|
|
|
// td::td_api::chats response will be preceded by a string of updateNewChat for all chats
|
|
|
|
// apparently even if td::td_api::getChats has limit_ of like 1
|
2020-05-02 12:44:07 +02:00
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::getChats>(
|
|
|
|
nullptr, std::numeric_limits<std::int64_t>::max(), 0, 200),
|
|
|
|
&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-05-02 12:53:25 +02:00
|
|
|
m_data.setActiveChats(std::move(chats->chat_ids_));
|
|
|
|
m_data.getContactsWithNoChat(m_usersForNewPrivateChats);
|
2020-05-02 12:35:31 +02:00
|
|
|
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-05-02 13:40:48 +02:00
|
|
|
updatePurpleChatListAndReportConnected();
|
2020-05-02 12:35:31 +02:00
|
|
|
} else {
|
|
|
|
int32_t userId = m_usersForNewPrivateChats.back();
|
|
|
|
m_usersForNewPrivateChats.pop_back();
|
|
|
|
purple_debug_misc(config::pluginId, "Requesting private chat for user id %d\n", (int)userId);
|
|
|
|
td::td_api::object_ptr<td::td_api::createPrivateChat> createChat =
|
|
|
|
td::td_api::make_object<td::td_api::createPrivateChat>(userId, 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-04 13:35:16 +02:00
|
|
|
if (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-13 15:59:45 +12:00
|
|
|
purple_debug_misc(config::pluginId, "Requested private chat received: id %" G_GUINT64_FORMAT "\n",
|
|
|
|
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-05-09 14:07:21 +02:00
|
|
|
void PurpleTdClient::updatePrivateChat(const td::td_api::chat &chat, const td::td_api::user &user)
|
2020-05-04 14:08:57 +02:00
|
|
|
{
|
2020-05-16 20:38:58 +02:00
|
|
|
std::string purpleUserName = getPurpleBuddyName(user);
|
2020-05-04 14:08:57 +02:00
|
|
|
|
2020-05-13 11:21:19 +02:00
|
|
|
PurpleBuddy *buddy = purple_find_buddy(m_account, purpleUserName.c_str());
|
2020-05-04 14:08:57 +02:00
|
|
|
if (buddy == NULL) {
|
2020-05-13 15:59:45 +12:00
|
|
|
purple_debug_misc(config::pluginId, "Adding new buddy %s for user %s, chat id %" G_GUINT64_FORMAT "\n",
|
|
|
|
chat.title_.c_str(), purpleUserName.c_str(), chat.id_);
|
2020-05-13 12:31:03 +02:00
|
|
|
|
|
|
|
const ContactRequest *contactReq = m_data.findContactRequest(user.id_);
|
|
|
|
PurpleGroup *group = (contactReq && !contactReq->groupName.empty()) ?
|
|
|
|
purple_find_group(contactReq->groupName.c_str()) : NULL;
|
|
|
|
if (group)
|
|
|
|
purple_debug_misc(config::pluginId, "Adding into group %s\n", purple_group_get_name(group));
|
|
|
|
|
2020-05-13 11:21:19 +02:00
|
|
|
buddy = purple_buddy_new(m_account, purpleUserName.c_str(), chat.title_.c_str());
|
2020-05-13 12:31:03 +02:00
|
|
|
purple_blist_add_buddy(buddy, NULL, group, NULL);
|
2020-05-13 11:21:19 +02:00
|
|
|
// If a new buddy has been added here, it means that there was updateNewChat with the private
|
|
|
|
// chat. This means either we added them to contacts or started messaging them, or they
|
|
|
|
// messaged us. Either way, there is no need to for any extra notification about new contact
|
|
|
|
// because the user will be aware anyway.
|
2020-05-04 14:08:57 +02:00
|
|
|
} else {
|
|
|
|
const char *oldName = purple_buddy_get_alias_only(buddy);
|
|
|
|
if (chat.title_ != oldName) {
|
|
|
|
purple_debug_misc(config::pluginId, "Renaming buddy %s '%s' to '%s'\n",
|
2020-05-13 11:21:19 +02:00
|
|
|
purpleUserName.c_str(), oldName, chat.title_.c_str());
|
2020-05-04 17:07:53 +02:00
|
|
|
PurpleGroup *group = purple_buddy_get_group(buddy);
|
2020-05-04 14:08:57 +02:00
|
|
|
purple_blist_remove_buddy(buddy);
|
2020-05-13 11:21:19 +02:00
|
|
|
buddy = purple_buddy_new(m_account, purpleUserName.c_str(), chat.title_.c_str());
|
2020-05-04 17:07:53 +02:00
|
|
|
purple_blist_add_buddy(buddy, NULL, group, NULL);
|
2020-05-04 14:08:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-09 14:07:21 +02:00
|
|
|
void PurpleTdClient::updateBasicGroupChat(int32_t groupId)
|
|
|
|
{
|
|
|
|
const td::td_api::basicGroup *group = m_data.getBasicGroup(groupId);
|
|
|
|
const td::td_api::chat *chat = m_data.getBasicGroupChatByGroup(groupId);
|
|
|
|
|
|
|
|
if (!group)
|
|
|
|
purple_debug_misc(config::pluginId, "Basic group %d does not exist yet\n", groupId);
|
|
|
|
else if (!chat)
|
|
|
|
purple_debug_misc(config::pluginId, "Chat for basic group %d does not exist yet\n", groupId);
|
|
|
|
else if (!isGroupMember(group->status_))
|
|
|
|
purple_debug_misc(config::pluginId, "Skipping basic group %d because we are not a member\n",
|
|
|
|
group->id_);
|
|
|
|
else {
|
|
|
|
std::string chatName = getChatName(*chat);
|
|
|
|
PurpleChat *purpleChat = purple_blist_find_chat(m_account, chatName.c_str());
|
|
|
|
if (!purpleChat) {
|
|
|
|
purple_debug_misc(config::pluginId, "Adding new chat for basic group %d (%s)\n",
|
|
|
|
group->id_, chat->title_.c_str());
|
|
|
|
purpleChat = purple_chat_new(m_account, chat->title_.c_str(), getChatComponents(*chat));
|
|
|
|
purple_blist_add_chat(purpleChat, NULL, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::updateSupergroupChat(int32_t groupId)
|
|
|
|
{
|
|
|
|
const td::td_api::supergroup *group = m_data.getSupergroup(groupId);
|
|
|
|
const td::td_api::chat *chat = m_data.getSupergroupChatByGroup(groupId);
|
|
|
|
|
|
|
|
if (!group)
|
|
|
|
purple_debug_misc(config::pluginId, "Supergroup %d does not exist yet\n", groupId);
|
|
|
|
else if (!chat)
|
|
|
|
purple_debug_misc(config::pluginId, "Chat for supergroup %d does not exist yet\n", groupId);
|
|
|
|
else if (!isGroupMember(group->status_))
|
|
|
|
purple_debug_misc(config::pluginId, "Skipping supergroup %d because we are not a member\n",
|
|
|
|
group->id_);
|
|
|
|
else {
|
|
|
|
std::string chatName = getChatName(*chat);
|
|
|
|
PurpleChat *purpleChat = purple_blist_find_chat(m_account, chatName.c_str());
|
|
|
|
if (!purpleChat) {
|
|
|
|
purple_debug_misc(config::pluginId, "Adding new chat for supergroup %d (%s)\n",
|
|
|
|
group->id_, chat->title_.c_str());
|
|
|
|
purpleChat = purple_chat_new(m_account, chat->title_.c_str(), getChatComponents(*chat));
|
|
|
|
purple_blist_add_chat(purpleChat, NULL, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-11 19:35:27 +02:00
|
|
|
void PurpleTdClient::requestBasicGroupMembers(int32_t groupId)
|
|
|
|
{
|
|
|
|
uint64_t requestId = m_transceiver.sendQuery(td::td_api::make_object<td::td_api::getBasicGroupFullInfo>(groupId),
|
|
|
|
&PurpleTdClient::groupInfoResponse);
|
|
|
|
m_data.addPendingRequest<GroupInfoRequest>(requestId, groupId);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
const td::td_api::chat *chat = m_data.getBasicGroupChatByGroup(request->groupId);
|
|
|
|
|
|
|
|
if (chat) {
|
2020-05-16 19:25:01 +02:00
|
|
|
PurpleConvChat *purpleChat = findChatConversation(m_account, *chat);
|
2020-05-11 19:35:27 +02:00
|
|
|
if (purpleChat)
|
|
|
|
setChatMembers(purpleChat, *groupInfo, m_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_data.updateBasicGroupInfo(request->groupId, std::move(groupInfo));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-02 13:40:48 +02:00
|
|
|
void PurpleTdClient::updatePurpleChatListAndReportConnected()
|
2020-02-22 19:03:14 +01:00
|
|
|
{
|
2020-05-02 13:40:48 +02:00
|
|
|
purple_connection_set_state (purple_account_get_connection(m_account), PURPLE_CONNECTED);
|
2020-02-29 15:30:24 +01:00
|
|
|
|
2020-05-09 15:12:32 +02:00
|
|
|
std::vector<const td::td_api::chat *> chats;
|
|
|
|
m_data.getActiveChats(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-09 13:37:38 +02:00
|
|
|
if (user) {
|
2020-05-09 14:07:21 +02:00
|
|
|
updatePrivateChat(*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-05-09 15:12:32 +02:00
|
|
|
|
|
|
|
int32_t groupId = getBasicGroupId(*chat);
|
2020-05-11 19:35:27 +02:00
|
|
|
if (groupId) {
|
|
|
|
requestBasicGroupMembers(groupId);
|
2020-05-09 15:12:32 +02:00
|
|
|
updateBasicGroupChat(groupId);
|
2020-05-11 19:35:27 +02:00
|
|
|
}
|
2020-05-09 15:12:32 +02:00
|
|
|
groupId = getSupergroupId(*chat);
|
|
|
|
if (groupId)
|
|
|
|
updateSupergroupChat(groupId);
|
2020-02-22 23:15:43 +01:00
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
std::string alias = selfInfo->first_name_ + " " + selfInfo->last_name_;
|
|
|
|
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-05-13 19:15:15 +02:00
|
|
|
void PurpleTdClient::showTextMessage(const td::td_api::chat &chat, const TgMessageInfo &message,
|
2020-05-08 13:10:51 +02:00
|
|
|
const td::td_api::messageText &text)
|
|
|
|
{
|
|
|
|
if (text.text_)
|
2020-05-13 19:15:15 +02:00
|
|
|
showMessageText(m_account, chat, message, text.text_->text_.c_str(), NULL, m_data);
|
2020-05-08 13:10:51 +02:00
|
|
|
}
|
|
|
|
|
2020-05-08 17:01:12 +02:00
|
|
|
static const td::td_api::file *selectPhotoSize(const td::td_api::messagePhoto &photo)
|
|
|
|
{
|
|
|
|
const td::td_api::photoSize *selectedSize = nullptr;
|
|
|
|
if (photo.photo_)
|
|
|
|
for (const auto &newSize: photo.photo_->sizes_)
|
|
|
|
if (newSize && newSize->photo_ && (!selectedSize || (newSize->width_ > selectedSize->width_)))
|
|
|
|
selectedSize = newSize.get();
|
|
|
|
|
|
|
|
if (selectedSize)
|
|
|
|
purple_debug_misc(config::pluginId, "Selected size %dx%d for photo\n",
|
|
|
|
(int)selectedSize->width_, (int)selectedSize->height_);
|
|
|
|
else
|
|
|
|
purple_debug_warning(config::pluginId, "No file found for a photo\n");
|
|
|
|
|
|
|
|
return selectedSize ? selectedSize->photo_.get() : nullptr;
|
|
|
|
}
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
void PurpleTdClient::showPhotoMessage(const td::td_api::chat &chat, const TgMessageInfo &message,
|
2020-05-09 18:58:04 +02:00
|
|
|
const td::td_api::messagePhoto &photo)
|
2020-05-08 13:10:51 +02:00
|
|
|
{
|
2020-05-08 17:01:12 +02:00
|
|
|
const td::td_api::file *file = selectPhotoSize(photo);
|
2020-05-12 10:57:32 +02:00
|
|
|
const char *notice;
|
2020-05-08 17:01:12 +02:00
|
|
|
|
2020-05-12 10:57:32 +02:00
|
|
|
if (!file)
|
|
|
|
notice = "Faulty image";
|
|
|
|
else if (file->local_ && file->local_->is_downloading_completed_)
|
|
|
|
notice = NULL;
|
|
|
|
else
|
|
|
|
notice = "Downloading image";
|
|
|
|
|
2020-05-15 19:31:03 +02:00
|
|
|
const char *caption = photo.caption_ ? photo.caption_->text_.c_str() : NULL;
|
|
|
|
|
|
|
|
if (notice)
|
|
|
|
showMessageText(m_account, chat, message, caption, notice, m_data);
|
2020-05-12 10:57:32 +02:00
|
|
|
|
|
|
|
if (file)
|
2020-05-15 19:31:03 +02:00
|
|
|
showImage(chat, message, *file, caption);
|
2020-05-12 10:57:32 +02:00
|
|
|
}
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
void PurpleTdClient::requestDownload(int32_t fileId, int64_t chatId, const TgMessageInfo &message,
|
2020-05-12 13:01:18 +02:00
|
|
|
td::td_api::object_ptr<td::td_api::file> thumbnail,
|
|
|
|
TdTransceiver::ResponseCb responseCb)
|
|
|
|
{
|
|
|
|
td::td_api::object_ptr<td::td_api::downloadFile> downloadReq =
|
|
|
|
td::td_api::make_object<td::td_api::downloadFile>();
|
|
|
|
downloadReq->file_id_ = fileId;
|
|
|
|
downloadReq->priority_ = FILE_DOWNLOAD_PRIORITY;
|
|
|
|
downloadReq->offset_ = 0;
|
|
|
|
downloadReq->limit_ = 0;
|
|
|
|
downloadReq->synchronous_ = true;
|
|
|
|
|
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(downloadReq), responseCb);
|
2020-05-13 19:15:15 +02:00
|
|
|
m_data.addPendingRequest<DownloadRequest>(requestId, chatId, message, thumbnail.release());
|
2020-05-12 13:01:18 +02:00
|
|
|
}
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
void PurpleTdClient::showImage(const td::td_api::chat &chat, const TgMessageInfo &message,
|
2020-05-15 19:31:03 +02:00
|
|
|
const td::td_api::file &file, const char *caption)
|
2020-05-12 10:57:32 +02:00
|
|
|
{
|
|
|
|
if (file.local_ && file.local_->is_downloading_completed_)
|
2020-05-15 19:31:03 +02:00
|
|
|
showDownloadedImage(chat.id_, message, file.local_->path_, caption);
|
2020-05-12 10:57:32 +02:00
|
|
|
else {
|
|
|
|
purple_debug_misc(config::pluginId, "Downloading image (file id %d)\n", (int)file.id_);
|
2020-05-13 19:15:15 +02:00
|
|
|
requestDownload(file.id_, chat.id_, message, nullptr, &PurpleTdClient::imageDownloadResponse);
|
2020-05-08 17:01:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-12 13:01:18 +02:00
|
|
|
static std::string getDownloadPath(const td::td_api::Object *object)
|
2020-05-08 17:01:12 +02:00
|
|
|
{
|
2020-05-12 13:01:18 +02:00
|
|
|
if (!object)
|
|
|
|
purple_debug_misc(config::pluginId, "No response after downloading file\n");
|
|
|
|
else if (object->get_id() == td::td_api::file::ID) {
|
2020-05-08 17:01:12 +02:00
|
|
|
const td::td_api::file &file = static_cast<const td::td_api::file &>(*object);
|
|
|
|
if (!file.local_)
|
2020-05-12 13:01:18 +02:00
|
|
|
purple_debug_misc(config::pluginId, "No local file info after downloading\n");
|
2020-05-08 17:01:12 +02:00
|
|
|
else if (!file.local_->is_downloading_completed_)
|
2020-05-12 13:01:18 +02:00
|
|
|
purple_debug_misc(config::pluginId, "File not completely downloaded\n");
|
2020-05-08 17:01:12 +02:00
|
|
|
else
|
2020-05-12 13:01:18 +02:00
|
|
|
return file.local_->path_;
|
2020-05-08 17:01:12 +02:00
|
|
|
} else
|
2020-05-12 13:01:18 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Unexpected response to downloading file: id %d\n",
|
2020-05-08 17:01:12 +02:00
|
|
|
(int)object->get_id());
|
|
|
|
|
2020-05-12 13:01:18 +02:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::imageDownloadResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
std::string path = getDownloadPath(object.get());
|
|
|
|
std::unique_ptr<DownloadRequest> request = m_data.getPendingRequest<DownloadRequest>(requestId);
|
|
|
|
|
|
|
|
if (request && !path.empty()) {
|
2020-05-12 10:57:32 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Image downloaded, path: %s\n", path.c_str());
|
2020-05-15 19:31:03 +02:00
|
|
|
// For image that needed downloading, caption was shown as soon as message was received
|
|
|
|
showDownloadedImage(request->chatId, request->message, path, NULL);
|
2020-05-08 17:01:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
void PurpleTdClient::showDownloadedImage(int64_t chatId, const TgMessageInfo &message,
|
2020-05-15 19:31:03 +02:00
|
|
|
const std::string &filePath, const char *caption)
|
2020-05-08 17:01:12 +02:00
|
|
|
{
|
|
|
|
const td::td_api::chat *chat = m_data.getChat(chatId);
|
2020-05-09 20:48:31 +02:00
|
|
|
if (chat) {
|
2020-05-15 19:31:03 +02:00
|
|
|
std::string text;
|
|
|
|
const char *notice = NULL;
|
|
|
|
gchar *data = NULL;
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
if (g_file_get_contents (filePath.c_str(), &data, &len, NULL)) {
|
|
|
|
int id = purple_imgstore_add_with_id (data, len, NULL);
|
|
|
|
text = "\n<img id=\"" + std::to_string(id) + "\">";
|
|
|
|
} else if (filePath.find('"') == std::string::npos)
|
|
|
|
text = "<img src=\"file://" + filePath + "\">";
|
|
|
|
else
|
|
|
|
notice = "Cannot show photo: file path contains quotes";
|
|
|
|
|
|
|
|
if (caption && *caption) {
|
|
|
|
if (!text.empty())
|
|
|
|
text += "\n";
|
|
|
|
text += caption;
|
2020-05-08 17:01:12 +02:00
|
|
|
}
|
2020-05-15 19:31:03 +02:00
|
|
|
|
|
|
|
showMessageText(m_account, *chat, message, text.empty() ? NULL : text.c_str(), notice, m_data,
|
|
|
|
PURPLE_MESSAGE_IMAGES);
|
2020-05-08 17:01:12 +02:00
|
|
|
}
|
2020-05-08 13:10:51 +02:00
|
|
|
}
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
void PurpleTdClient::showDocument(const td::td_api::chat &chat, const TgMessageInfo &message,
|
2020-05-08 13:10:51 +02:00
|
|
|
const td::td_api::messageDocument &document)
|
|
|
|
{
|
|
|
|
std::string description = "Sent a file";
|
|
|
|
if (document.document_)
|
|
|
|
description = description + ": " + document.document_->file_name_ + " [" +
|
|
|
|
document.document_->mime_type_ + "]";
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
showMessageText(m_account, chat, message,
|
2020-05-09 18:58:04 +02:00
|
|
|
document.caption_ ? document.caption_->text_.c_str() : NULL,
|
2020-05-13 19:15:15 +02:00
|
|
|
description.c_str(), m_data);
|
2020-05-08 13:10:51 +02:00
|
|
|
}
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
void PurpleTdClient::showVideo(const td::td_api::chat &chat, const TgMessageInfo &message,
|
2020-05-08 13:10:51 +02:00
|
|
|
const td::td_api::messageVideo &video)
|
|
|
|
{
|
|
|
|
std::string description = "Sent a video";
|
|
|
|
if (video.video_)
|
|
|
|
description = description + ": " + video.video_->file_name_ + " [" +
|
|
|
|
std::to_string(video.video_->width_) + "x" + std::to_string(video.video_->height_) + ", " +
|
|
|
|
std::to_string(video.video_->duration_) + "s]";
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
showMessageText(m_account, chat, message, video.caption_ ? video.caption_->text_.c_str() : NULL,
|
|
|
|
description.c_str(), m_data);
|
2020-05-08 13:10:51 +02:00
|
|
|
}
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
void PurpleTdClient::showSticker(const td::td_api::chat &chat, const TgMessageInfo &message,
|
2020-05-12 11:45:49 +02:00
|
|
|
td::td_api::messageSticker &stickerContent)
|
2020-05-12 10:57:32 +02:00
|
|
|
{
|
|
|
|
if (!stickerContent.sticker_) return;
|
2020-05-12 11:45:49 +02:00
|
|
|
td::td_api::sticker &sticker = *stickerContent.sticker_;
|
2020-05-12 10:57:32 +02:00
|
|
|
|
2020-05-12 13:01:18 +02:00
|
|
|
if (sticker.sticker_) {
|
2020-05-13 19:15:15 +02:00
|
|
|
auto thumbnail = sticker.thumbnail_ ? std::move(sticker.thumbnail_->photo_) : nullptr;
|
2020-05-12 13:01:18 +02:00
|
|
|
|
|
|
|
if (sticker.sticker_->local_ && sticker.sticker_->local_->is_downloading_completed_)
|
2020-05-13 19:15:15 +02:00
|
|
|
showDownloadedSticker(chat.id_, message, sticker.sticker_->local_->path_,
|
|
|
|
std::move(thumbnail));
|
2020-05-12 13:01:18 +02:00
|
|
|
else {
|
|
|
|
purple_debug_misc(config::pluginId, "Downloading sticker (file id %d)\n", (int)sticker.sticker_->id_);
|
2020-05-13 19:15:15 +02:00
|
|
|
requestDownload(sticker.sticker_->id_, chat.id_, message, std::move(thumbnail),
|
2020-05-12 13:01:18 +02:00
|
|
|
&PurpleTdClient::stickerDownloadResponse);
|
|
|
|
}
|
|
|
|
}
|
2020-05-12 10:57:32 +02:00
|
|
|
}
|
|
|
|
|
2020-05-12 13:01:18 +02:00
|
|
|
static bool isTgs(const std::string &path)
|
2020-05-12 10:57:32 +02:00
|
|
|
{
|
2020-05-12 13:01:18 +02:00
|
|
|
size_t dot = path.rfind('.');
|
|
|
|
if (dot != std::string::npos)
|
|
|
|
return !strcmp(path.c_str() + dot + 1, "tgs");
|
|
|
|
|
|
|
|
return false;
|
2020-05-12 10:57:32 +02:00
|
|
|
}
|
|
|
|
|
2020-05-12 13:01:18 +02:00
|
|
|
|
|
|
|
void PurpleTdClient::stickerDownloadResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
2020-05-12 10:57:32 +02:00
|
|
|
{
|
2020-05-12 13:01:18 +02:00
|
|
|
std::string path = getDownloadPath(object.get());
|
2020-05-12 10:57:32 +02:00
|
|
|
std::unique_ptr<DownloadRequest> request = m_data.getPendingRequest<DownloadRequest>(requestId);
|
|
|
|
|
2020-05-12 13:01:18 +02:00
|
|
|
if (request && !path.empty())
|
2020-05-13 19:15:15 +02:00
|
|
|
showDownloadedSticker(request->chatId, request->message, path, std::move(request->thumbnail));
|
2020-05-12 13:01:18 +02:00
|
|
|
}
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
void PurpleTdClient::showDownloadedSticker(int64_t chatId, const TgMessageInfo &message,
|
|
|
|
const std::string &filePath,
|
2020-05-12 13:01:18 +02:00
|
|
|
td::td_api::object_ptr<td::td_api::file> thumbnail)
|
|
|
|
{
|
|
|
|
if (isTgs(filePath) && thumbnail) {
|
|
|
|
if (thumbnail->local_ && thumbnail->local_->is_downloading_completed_)
|
2020-05-13 19:15:15 +02:00
|
|
|
showDownloadedInlineFile(chatId, message, thumbnail->local_->path_, "Sticker");
|
2020-05-12 10:57:32 +02:00
|
|
|
else
|
2020-05-13 19:15:15 +02:00
|
|
|
requestDownload(thumbnail->id_, chatId, message, nullptr,
|
2020-05-12 13:01:18 +02:00
|
|
|
&PurpleTdClient::stickerDownloadResponse);
|
2020-05-12 10:57:32 +02:00
|
|
|
} else
|
2020-05-13 19:15:15 +02:00
|
|
|
showDownloadedInlineFile(chatId, message, filePath, "Sticker");
|
2020-05-12 13:01:18 +02:00
|
|
|
}
|
2020-05-12 10:57:32 +02:00
|
|
|
|
2020-05-12 13:01:18 +02:00
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
void PurpleTdClient::showInlineFile(const td::td_api::chat &chat, const TgMessageInfo &message,
|
2020-05-12 13:01:18 +02:00
|
|
|
const td::td_api::file &file)
|
|
|
|
{
|
|
|
|
if (file.local_ && file.local_->is_downloading_completed_)
|
2020-05-13 19:15:15 +02:00
|
|
|
showDownloadedInlineFile(chat.id_, message, file.local_->path_, "Sent file");
|
2020-05-12 13:01:18 +02:00
|
|
|
else {
|
|
|
|
purple_debug_misc(config::pluginId, "Downloading file (id %d)\n", (int)file.id_);
|
2020-05-13 19:15:15 +02:00
|
|
|
requestDownload(file.id_, chat.id_, message, nullptr,
|
2020-05-12 13:01:18 +02:00
|
|
|
&PurpleTdClient::fileDownloadResponse);
|
2020-05-12 10:57:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-12 13:01:18 +02:00
|
|
|
void PurpleTdClient::fileDownloadResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
2020-05-12 11:45:49 +02:00
|
|
|
{
|
2020-05-12 13:01:18 +02:00
|
|
|
std::string path = getDownloadPath(object.get());
|
|
|
|
std::unique_ptr<DownloadRequest> request = m_data.getPendingRequest<DownloadRequest>(requestId);
|
2020-05-12 11:45:49 +02:00
|
|
|
|
2020-05-12 13:01:18 +02:00
|
|
|
if (request && !path.empty()) {
|
|
|
|
purple_debug_misc(config::pluginId, "File downloaded, path: %s\n", path.c_str());
|
2020-05-13 19:15:15 +02:00
|
|
|
showDownloadedInlineFile(request->chatId, request->message, path, "Sent file");
|
2020-05-12 13:01:18 +02:00
|
|
|
}
|
2020-05-12 11:45:49 +02:00
|
|
|
}
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
void PurpleTdClient::showDownloadedInlineFile(int64_t chatId, const TgMessageInfo &message,
|
|
|
|
const std::string &filePath, const char *label)
|
2020-05-12 10:57:32 +02:00
|
|
|
{
|
|
|
|
const td::td_api::chat *chat = m_data.getChat(chatId);
|
|
|
|
if (chat) {
|
|
|
|
if (filePath.find('"') != std::string::npos)
|
2020-05-13 19:15:15 +02:00
|
|
|
showMessageText(m_account, *chat, message, NULL,
|
|
|
|
"Cannot show file: path contains quotes", m_data);
|
2020-05-12 10:57:32 +02:00
|
|
|
else {
|
|
|
|
std::string text = "<a href=\"file://" + filePath + "\">" + label + "</a>";
|
2020-05-13 19:15:15 +02:00
|
|
|
showMessageText(m_account, *chat, message, text.c_str(), NULL, m_data);
|
2020-05-12 10:57:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-12 11:45:49 +02:00
|
|
|
void PurpleTdClient::showMessage(const td::td_api::chat &chat, td::td_api::message &message)
|
2020-02-22 23:15:43 +01:00
|
|
|
{
|
2020-05-05 18:35:06 +02:00
|
|
|
td::td_api::object_ptr<td::td_api::viewMessages> viewMessagesReq = td::td_api::make_object<td::td_api::viewMessages>();
|
|
|
|
viewMessagesReq->chat_id_ = message.chat_id_;
|
|
|
|
viewMessagesReq->force_read_ = true; // no idea what "closed chats" are at this point
|
|
|
|
viewMessagesReq->message_ids_.push_back(message.id_);
|
|
|
|
m_transceiver.sendQuery(std::move(viewMessagesReq), nullptr);
|
|
|
|
|
2020-05-08 13:10:51 +02:00
|
|
|
if (!message.content_)
|
|
|
|
return;
|
|
|
|
|
2020-05-13 19:15:15 +02:00
|
|
|
TgMessageInfo messageInfo;
|
2020-05-16 13:38:00 +02:00
|
|
|
messageInfo.sender = getSenderPurpleName(chat, message, m_data);
|
|
|
|
messageInfo.timestamp = message.date_;
|
|
|
|
messageInfo.outgoing = message.is_outgoing_;
|
|
|
|
messageInfo.repliedMessageId = message.reply_to_message_id_;
|
2020-05-13 19:15:15 +02:00
|
|
|
|
2020-05-16 15:32:56 +02:00
|
|
|
if (message.forward_info_)
|
|
|
|
messageInfo.forwardedFrom = getForwardSource(*message.forward_info_, m_data);
|
|
|
|
|
2020-05-16 14:44:29 +02:00
|
|
|
if (message.ttl_ != 0)
|
|
|
|
showMessageText(m_account, chat, messageInfo, NULL,
|
|
|
|
_("Received self-destructing message, not displayed due to lack of support"), m_data);
|
|
|
|
|
2020-05-08 13:10:51 +02:00
|
|
|
switch (message.content_->get_id()) {
|
2020-05-12 10:57:32 +02:00
|
|
|
case td::td_api::messageText::ID:
|
2020-05-13 19:15:15 +02:00
|
|
|
showTextMessage(chat, messageInfo, static_cast<const td::td_api::messageText &>(*message.content_));
|
2020-05-08 13:10:51 +02:00
|
|
|
break;
|
2020-05-12 10:57:32 +02:00
|
|
|
case td::td_api::messagePhoto::ID:
|
2020-05-13 19:15:15 +02:00
|
|
|
showPhotoMessage(chat, messageInfo, static_cast<const td::td_api::messagePhoto &>(*message.content_));
|
2020-05-08 13:10:51 +02:00
|
|
|
break;
|
2020-05-12 10:57:32 +02:00
|
|
|
case td::td_api::messageDocument::ID:
|
2020-05-13 19:15:15 +02:00
|
|
|
showDocument(chat, messageInfo, static_cast<const td::td_api::messageDocument &>(*message.content_));
|
2020-05-08 13:10:51 +02:00
|
|
|
break;
|
2020-05-12 10:57:32 +02:00
|
|
|
case td::td_api::messageVideo::ID:
|
2020-05-13 19:15:15 +02:00
|
|
|
showVideo(chat, messageInfo, static_cast<const td::td_api::messageVideo &>(*message.content_));
|
2020-05-12 10:57:32 +02:00
|
|
|
break;
|
|
|
|
case td::td_api::messageSticker::ID:
|
2020-05-13 19:15:15 +02:00
|
|
|
showSticker(chat, messageInfo, static_cast<td::td_api::messageSticker &>(*message.content_));
|
2020-05-08 13:10:51 +02:00
|
|
|
break;
|
2020-05-08 13:22:21 +02:00
|
|
|
default: {
|
|
|
|
std::string notice = "Received unsupported message type " +
|
|
|
|
messageTypeToString(*message.content_);
|
2020-05-13 19:15:15 +02:00
|
|
|
showMessageText(m_account, chat, messageInfo, NULL, notice.c_str(), m_data);
|
2020-05-08 13:22:21 +02:00
|
|
|
}
|
2020-02-22 23:15:43 +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-05-05 18:35:06 +02:00
|
|
|
const td::td_api::chat *chat = m_data.getChat(message->chat_id_);
|
2020-02-23 14:02:47 +01:00
|
|
|
if (!chat) {
|
2020-05-13 15:59:45 +12:00
|
|
|
purple_debug_warning(config::pluginId, "Received message with unknown chat id %" G_GUINT64_FORMAT "\n",
|
|
|
|
message->chat_id_);
|
2020-02-22 23:15:43 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-05-09 18:58:04 +02:00
|
|
|
showMessage(*chat, *message);
|
2020-05-16 14:44:29 +02:00
|
|
|
if (message->ttl_ == 0)
|
|
|
|
m_data.saveMessage(std::move(message));
|
2020-02-22 23:15:43 +01:00
|
|
|
}
|
2020-02-27 21:16:12 +01:00
|
|
|
|
2020-02-27 21:49:02 +01:00
|
|
|
int PurpleTdClient::sendMessage(const char *buddyName, const char *message)
|
|
|
|
{
|
2020-05-13 11:21:19 +02:00
|
|
|
int32_t userId = stringToUserId(buddyName);
|
|
|
|
if (userId == 0) {
|
|
|
|
purple_debug_warning(config::pluginId, "'%s' is not a valid user id\n", buddyName);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
const td::td_api::user *tdUser = m_data.getUser(userId);
|
2020-02-27 21:49:02 +01:00
|
|
|
if (tdUser == nullptr) {
|
2020-05-13 11:21:19 +02:00
|
|
|
purple_debug_warning(config::pluginId, "No user with id %s\n", buddyName);
|
2020-02-27 21:49:02 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
const td::td_api::chat *tdChat = m_data.getPrivateChatByUserId(tdUser->id_);
|
|
|
|
if (tdChat == nullptr) {
|
2020-05-13 11:21:19 +02:00
|
|
|
purple_debug_warning(config::pluginId, "No chat with user %s\n", buddyName);
|
2020-02-27 21:49:02 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-05-16 00:16:06 +02:00
|
|
|
transmitMessage(tdChat->id_, message, m_transceiver, m_data, &PurpleTdClient::sendMessageResponse);
|
2020-02-27 21:49:02 +01:00
|
|
|
|
|
|
|
// Message shall not be echoed: tdlib will shortly present it as a new message and it will be displayed then
|
|
|
|
return 0;
|
|
|
|
}
|
2020-02-27 22:27:56 +01:00
|
|
|
|
|
|
|
void PurpleTdClient::updateUserStatus(uint32_t userId, td::td_api::object_ptr<td::td_api::UserStatus> status)
|
|
|
|
{
|
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-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-05-13 11:21:19 +02:00
|
|
|
int32_t userId = userInfo->id_;
|
|
|
|
purple_debug_misc(config::pluginId, "Update user: %d '%s' '%s'\n", (int)userId,
|
|
|
|
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
|
|
|
const td::td_api::user *user = m_data.getUser(userId);
|
|
|
|
const td::td_api::chat *chat = m_data.getPrivateChatByUserId(userId);
|
2020-05-05 18:35:06 +02:00
|
|
|
|
2020-05-13 11:21:19 +02:00
|
|
|
// In case user gets renamed in another client maybe?
|
|
|
|
// For chats, find_chat doesn't work if account is not yet connected, so just in case, don't
|
|
|
|
// user find_buddy either
|
|
|
|
if (user && chat && purple_account_is_connected(m_account))
|
|
|
|
updatePrivateChat(*chat, *user);
|
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_);
|
|
|
|
|
|
|
|
int32_t id = group->id_;
|
|
|
|
m_data.updateBasicGroup(std::move(group));
|
|
|
|
|
|
|
|
// purple_blist_find_chat doesn't work if account is not connected
|
|
|
|
if (purple_account_is_connected(m_account))
|
|
|
|
updateBasicGroupChat(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
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_);
|
|
|
|
|
|
|
|
int32_t id = group->id_;
|
|
|
|
m_data.updateSupergroup(std::move(group));
|
|
|
|
|
|
|
|
// purple_blist_find_chat doesn't work if account is not connected
|
|
|
|
if (purple_account_is_connected(m_account))
|
|
|
|
updateSupergroupChat(id);
|
|
|
|
}
|
|
|
|
|
2020-05-08 19:50:55 +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;
|
|
|
|
}
|
|
|
|
|
|
|
|
const td::td_api::user *privateChatUser = m_data.getUserByPrivateChat(*chat);
|
2020-05-09 14:07:21 +02:00
|
|
|
int32_t basicGroupId = getBasicGroupId(*chat);
|
|
|
|
int32_t supergroupId = getSupergroupId(*chat);
|
|
|
|
|
|
|
|
purple_debug_misc(config::pluginId, "Add chat: '%s' private=%d basic group=%d supergroup=%d\n",
|
|
|
|
chat->title_.c_str(), privateChatUser ? privateChatUser->id_ : 0,
|
|
|
|
basicGroupId, supergroupId);
|
2020-05-08 19:50:55 +02:00
|
|
|
|
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
|
|
|
|
// user find_buddy either
|
2020-05-13 11:21:19 +02:00
|
|
|
if (privateChatUser && purple_account_is_connected(m_account))
|
2020-05-09 13:37:38 +02:00
|
|
|
{
|
2020-05-09 14:07:21 +02:00
|
|
|
updatePrivateChat(*chat, *privateChatUser);
|
2020-05-09 13:37:38 +02:00
|
|
|
}
|
2020-05-08 19:50:55 +02:00
|
|
|
|
|
|
|
m_data.addChat(std::move(chat));
|
2020-05-09 14:07:21 +02:00
|
|
|
|
|
|
|
// purple_blist_find_chat doesn't work if account is not connected
|
2020-05-11 19:35:27 +02:00
|
|
|
if (basicGroupId && purple_account_is_connected(m_account)) {
|
|
|
|
requestBasicGroupMembers(basicGroupId);
|
2020-05-09 14:07:21 +02:00
|
|
|
updateBasicGroupChat(basicGroupId);
|
2020-05-11 19:35:27 +02:00
|
|
|
}
|
2020-05-09 14:07:21 +02:00
|
|
|
if (supergroupId && purple_account_is_connected(m_account))
|
|
|
|
updateSupergroupChat(supergroupId);
|
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)
|
|
|
|
{
|
|
|
|
const td::td_api::chat *chat;
|
2020-05-02 12:53:25 +02:00
|
|
|
chat = m_data.getChat(updateChatAction.chat_id_);
|
2020-03-15 21:47:47 +01:00
|
|
|
|
|
|
|
if (!chat)
|
2020-05-13 15:59:45 +12:00
|
|
|
purple_debug_warning(config::pluginId, "Got user chat action for unknown chat %" G_GUINT64_FORMAT "\n",
|
|
|
|
updateChatAction.chat_id_);
|
2020-03-15 21:47:47 +01:00
|
|
|
else if (chat->type_->get_id() == td::td_api::chatTypePrivate::ID) {
|
|
|
|
const td::td_api::chatTypePrivate &privType = static_cast<const td::td_api::chatTypePrivate &>(*chat->type_);
|
|
|
|
if (privType.user_id_ != updateChatAction.user_id_)
|
2020-05-13 15:59:45 +12:00
|
|
|
purple_debug_warning(config::pluginId, "Got user action for private chat %" G_GUINT64_FORMAT " (with user %d) for another user %d\n",
|
|
|
|
updateChatAction.chat_id_, privType.user_id_,
|
2020-03-21 12:47:47 +01:00
|
|
|
updateChatAction.user_id_);
|
2020-03-15 21:47:47 +01:00
|
|
|
else if (updateChatAction.action_) {
|
2020-03-21 12:47:47 +01:00
|
|
|
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-05-02 13:40:48 +02:00
|
|
|
showUserChatAction(updateChatAction.user_id_, false);
|
2020-03-21 12:47:47 +01: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-05-02 13:40:48 +02:00
|
|
|
showUserChatAction(updateChatAction.user_id_, false);
|
2020-03-21 12:47:47 +01:00
|
|
|
} else {
|
|
|
|
purple_debug_misc(config::pluginId, "User (id %d) started chat action (id %d)\n",
|
|
|
|
updateChatAction.user_id_, updateChatAction.action_->get_id());
|
2020-05-02 13:40:48 +02:00
|
|
|
showUserChatAction(updateChatAction.user_id_, true);
|
2020-03-21 12:47:47 +01:00
|
|
|
}
|
2020-03-15 21:47:47 +01:00
|
|
|
}
|
|
|
|
} else
|
2020-05-13 15:59:45 +12:00
|
|
|
purple_debug_misc(config::pluginId, "Ignoring user chat action for non-private chat %" G_GUINT64_FORMAT "\n",
|
|
|
|
updateChatAction.chat_id_);
|
2020-03-15 21:47:47 +01:00
|
|
|
}
|
2020-03-21 12:47:47 +01:00
|
|
|
|
2020-05-02 13:40:48 +02:00
|
|
|
void PurpleTdClient::showUserChatAction(int32_t 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-13 12:31:03 +02:00
|
|
|
void PurpleTdClient::addContact(const std::string &phoneNumber, const std::string &alias,
|
|
|
|
const std::string &groupName)
|
2020-03-21 14:32:33 +01:00
|
|
|
{
|
2020-05-13 11:21:19 +02:00
|
|
|
if (m_data.getUserByPhone(phoneNumber.c_str())) {
|
|
|
|
purple_debug_info(config::pluginId, "User with phone number %s already exists\n", phoneNumber.c_str());
|
2020-05-02 12:53:25 +02:00
|
|
|
return;
|
2020-03-21 14:32:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
td::td_api::object_ptr<td::td_api::contact> contact =
|
2020-03-23 22:56:16 +01:00
|
|
|
td::td_api::make_object<td::td_api::contact>(phoneNumber, "", "", "", 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));
|
2020-05-02 12:44:07 +02:00
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(importReq),
|
|
|
|
&PurpleTdClient::importContactResponse);
|
2020-03-21 14:32:33 +01:00
|
|
|
|
2020-05-13 12:31:03 +02:00
|
|
|
m_data.addPendingRequest<ContactRequest>(requestId, phoneNumber, alias, groupName, 0);
|
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;
|
|
|
|
|
|
|
|
int32_t userId = 0;
|
|
|
|
if (object->get_id() == td::td_api::importedContacts::ID) {
|
|
|
|
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())
|
|
|
|
userId = reply->user_ids_[0];
|
|
|
|
}
|
|
|
|
|
2020-05-04 17:32:33 +02:00
|
|
|
// For whatever reason, complaining at an earlier stage leads to error message not being shown in pidgin
|
2020-05-13 12:31:03 +02:00
|
|
|
if (!isPhoneNumber(request->phoneNumber.c_str()))
|
|
|
|
notifyFailedContact(request->phoneNumber, _("Not a valid phone number"));
|
2020-05-04 17:07:53 +02:00
|
|
|
else if (userId) {
|
2020-03-23 22:56:16 +01:00
|
|
|
td::td_api::object_ptr<td::td_api::contact> contact =
|
2020-05-13 12:31:03 +02:00
|
|
|
td::td_api::make_object<td::td_api::contact>(request->phoneNumber, request->alias, "", "", userId);
|
2020-03-23 22:56:16 +01:00
|
|
|
td::td_api::object_ptr<td::td_api::addContact> addContact =
|
|
|
|
td::td_api::make_object<td::td_api::addContact>(std::move(contact), true);
|
2020-05-02 12:44:07 +02:00
|
|
|
uint64_t newRequestId = m_transceiver.sendQuery(std::move(addContact),
|
|
|
|
&PurpleTdClient::addContactResponse);
|
2020-05-13 12:31:03 +02:00
|
|
|
m_data.addPendingRequest<ContactRequest>(newRequestId, request->phoneNumber, request->alias,
|
|
|
|
request->groupName, userId);
|
2020-05-02 13:40:48 +02:00
|
|
|
} else
|
2020-05-13 12:31:03 +02:00
|
|
|
notifyFailedContact(request->phoneNumber, _("User not found"));
|
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-03-23 22:56:16 +01:00
|
|
|
if (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-05-13 12:31:03 +02:00
|
|
|
td::td_api::make_object<td::td_api::createPrivateChat>(request->userId, 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-13 12:31:03 +02:00
|
|
|
notifyFailedContact(request->phoneNumber, 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-05-12 16:00:07 +02:00
|
|
|
if (!object || (object->get_id() != td::td_api::chat::ID)) {
|
|
|
|
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());
|
|
|
|
notifyFailedContact(request->phoneNumber, getDisplayedError(object));
|
2020-03-21 14:32:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-04 17:07:53 +02:00
|
|
|
void PurpleTdClient::notifyFailedContact(const std::string &phoneNumber, const std::string &errorMessage)
|
2020-03-21 14:32:33 +01:00
|
|
|
{
|
2020-05-12 15:21:16 +02:00
|
|
|
std::string message = formatMessage(_("Failed to add contact (phone number {}): {}"),
|
|
|
|
{phoneNumber, errorMessage});
|
2020-03-21 14:32:33 +01:00
|
|
|
|
2020-05-02 13:40:48 +02:00
|
|
|
purple_notify_error(purple_account_get_connection(m_account),
|
2020-05-12 15:21:16 +02:00
|
|
|
_("Failed to add contact"), message.c_str(), NULL);
|
2020-03-21 12:47:47 +01:00
|
|
|
}
|
2020-05-10 12:05:55 +02:00
|
|
|
|
|
|
|
bool PurpleTdClient::joinChat(const char *chatName)
|
|
|
|
{
|
|
|
|
int64_t id = getTdlibChatId(chatName);
|
|
|
|
const td::td_api::chat *chat = m_data.getChat(id);
|
|
|
|
int32_t purpleId = m_data.getPurpleChatId(id);
|
|
|
|
PurpleConvChat *conv = NULL;
|
|
|
|
|
2020-05-10 14:24:15 +02:00
|
|
|
if (!chat)
|
|
|
|
purple_debug_warning(config::pluginId, "No telegram chat found for purple name %s\n", chatName);
|
|
|
|
else if (!m_data.isGroupChatWithMembership(*chat))
|
|
|
|
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-11 19:35:27 +02:00
|
|
|
conv = getChatConversation(m_account, *chat, purpleId, m_data);
|
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))
|
|
|
|
purple_debug_warning(config::pluginId, "purple id %d (chat %s) is not a group we a member of\n",
|
|
|
|
purpleChatId, chat->title_.c_str());
|
|
|
|
else {
|
2020-05-16 00:16:06 +02:00
|
|
|
transmitMessage(chat->id_, message, m_transceiver, m_data, &PurpleTdClient::sendMessageResponse);
|
2020-05-10 14:24:15 +02:00
|
|
|
// Message shall not be echoed: tdlib will shortly present it as a new message and it will be displayed then
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2020-05-12 19:37:59 +02:00
|
|
|
|
|
|
|
bool PurpleTdClient::joinChatByLink(const char *inviteLink)
|
|
|
|
{
|
|
|
|
auto request = td::td_api::make_object<td::td_api::joinChatByInviteLink>(inviteLink);
|
|
|
|
uint64_t requestId = m_transceiver.sendQuery(std::move(request), &PurpleTdClient::joinChatByLinkResponse);
|
|
|
|
m_data.addPendingRequest<GroupJoinRequest>(requestId, inviteLink);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::joinChatByLinkResponse(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)) {
|
|
|
|
// 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) {
|
|
|
|
std::vector<PurpleChat *> obsoleteChats = findChatsByInviteLink(request->inviteLink);
|
|
|
|
for (PurpleChat *chat: obsoleteChats)
|
|
|
|
purple_blist_remove_chat(chat);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
std::string message = formatMessage(_("Failed to join chat: {}"), getDisplayedError(object));
|
|
|
|
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
|
|
|
|
|
|
|
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)) {
|
|
|
|
const td::td_api::message &message = static_cast<td::td_api::message &>(*object);
|
|
|
|
m_data.addTempFileUpload(message.id_, request->tempFile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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-16 20:38:58 +02:00
|
|
|
|
|
|
|
void PurpleTdClient::getUsers(const char *username, std::vector<const td::td_api::user *> &users)
|
|
|
|
{
|
|
|
|
getUsersByPurpleName(username, users, m_data);
|
|
|
|
}
|