2020-02-16 17:46:52 +01:00
|
|
|
#include "td-client.h"
|
2020-02-16 17:59:19 +01:00
|
|
|
#include "config.h"
|
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-02-16 17:46:52 +01:00
|
|
|
class UpdateHandler {
|
|
|
|
public:
|
|
|
|
UpdateHandler(PurpleTdClient *owner) : m_owner(owner) {}
|
|
|
|
|
2020-02-22 12:08:02 +01:00
|
|
|
void operator()(td::td_api::updateAuthorizationState &update_authorization_state) const {
|
2020-02-16 17:59:19 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: authorization state\n");
|
2020-02-22 13:23:01 +01:00
|
|
|
m_owner->m_lastAuthState = update_authorization_state.authorization_state_->get_id();
|
2020-02-16 17:46:52 +01:00
|
|
|
td::td_api::downcast_call(*update_authorization_state.authorization_state_, *m_owner->m_authUpdateHandler);
|
|
|
|
}
|
|
|
|
|
2020-02-22 15:04:06 +01:00
|
|
|
void operator()(td::td_api::updateConnectionState &connectionUpdate) const {
|
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)
|
|
|
|
m_owner->connectionReady();
|
|
|
|
else if (connectionUpdate.state_->get_id() == td::td_api::connectionStateConnecting::ID)
|
|
|
|
g_idle_add(PurpleTdClient::setPurpleConnectionInProgress, m_owner);
|
|
|
|
else if (connectionUpdate.state_->get_id() == td::td_api::connectionStateUpdating::ID)
|
|
|
|
g_idle_add(PurpleTdClient::setPurpleConnectionUpdating, m_owner);
|
|
|
|
}
|
2020-02-22 14:58:32 +01:00
|
|
|
}
|
|
|
|
|
2020-02-22 19:03:14 +01:00
|
|
|
void operator()(td::td_api::updateUser &userUpdate) const {
|
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: update user\n");
|
2020-03-08 23:27:58 +01:00
|
|
|
m_owner->updateUser(std::move(userUpdate.user_));
|
2020-02-22 19:03:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void operator()(td::td_api::updateNewChat &newChat) const {
|
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: new chat\n");
|
2020-02-23 14:02:47 +01:00
|
|
|
TdAccountData::Lock lock(m_owner->m_data);
|
2020-05-02 12:35:31 +02:00
|
|
|
m_owner->m_data.addChat(std::move(newChat.chat_));
|
2020-02-22 19:03:14 +01:00
|
|
|
}
|
|
|
|
|
2020-02-27 21:16:12 +01:00
|
|
|
void operator()(td::td_api::updateNewMessage &newMessageUpdate) const {
|
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: new message\n");
|
|
|
|
if (newMessageUpdate.message_)
|
|
|
|
m_owner->onIncomingMessage(std::move(newMessageUpdate.message_));
|
|
|
|
else
|
|
|
|
purple_debug_warning(config::pluginId, "Received null new message\n");
|
|
|
|
}
|
|
|
|
|
2020-02-27 22:27:56 +01:00
|
|
|
void operator()(td::td_api::updateUserStatus &updateStatus) const {
|
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: user status\n");
|
|
|
|
if (updateStatus.status_)
|
|
|
|
m_owner->updateUserStatus(updateStatus.user_id_, std::move(updateStatus.status_));
|
|
|
|
}
|
|
|
|
|
2020-03-15 21:47:47 +01:00
|
|
|
void operator()(td::td_api::updateUserChatAction &updateChatAction) const {
|
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: chat action %d\n",
|
|
|
|
updateChatAction.action_->get_id());
|
|
|
|
m_owner->handleUserChatAction(updateChatAction);
|
|
|
|
}
|
|
|
|
|
2020-02-22 12:08:02 +01:00
|
|
|
void operator()(auto &update) const {
|
2020-02-16 17:59:19 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update: ignorig ID=%d\n", update.get_id());
|
|
|
|
}
|
2020-02-16 17:46:52 +01:00
|
|
|
private:
|
|
|
|
PurpleTdClient *m_owner;
|
|
|
|
};
|
|
|
|
|
|
|
|
class AuthUpdateHandler {
|
|
|
|
public:
|
|
|
|
AuthUpdateHandler(PurpleTdClient *owner) : m_owner(owner) {}
|
|
|
|
|
|
|
|
void operator()(td::td_api::authorizationStateWaitEncryptionKey &) const {
|
2020-02-16 17:59:19 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: encriytion key requested\n");
|
2020-05-02 12:44:07 +02:00
|
|
|
m_owner->m_transceiver.sendQuery(td::td_api::make_object<td::td_api::checkDatabaseEncryptionKey>(""),
|
|
|
|
&PurpleTdClient::authResponse);
|
2020-02-16 17:46:52 +01:00
|
|
|
}
|
|
|
|
|
2020-02-22 12:08:02 +01:00
|
|
|
void operator()(td::td_api::authorizationStateWaitTdlibParameters &) const {
|
2020-02-22 11:10:30 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: TDLib parameters requested\n");
|
|
|
|
m_owner->sendTdlibParameters();
|
|
|
|
}
|
|
|
|
|
2020-02-22 12:08:02 +01:00
|
|
|
void operator()(td::td_api::authorizationStateWaitPhoneNumber &) const {
|
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: phone number requested\n");
|
|
|
|
m_owner->sendPhoneNumber();
|
|
|
|
}
|
|
|
|
|
2020-02-22 14:58:32 +01:00
|
|
|
void operator()(td::td_api::authorizationStateWaitCode &codeState) const {
|
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: authentication code requested\n");
|
|
|
|
m_owner->m_authCodeInfo = std::move(codeState.code_info_);
|
|
|
|
g_idle_add(PurpleTdClient::requestAuthCode, m_owner);
|
|
|
|
}
|
|
|
|
|
|
|
|
void operator()(td::td_api::authorizationStateReady &) const {
|
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: ready\n");
|
|
|
|
}
|
|
|
|
|
2020-02-16 17:59:19 +01:00
|
|
|
void operator()(auto &update) const {
|
|
|
|
purple_debug_misc(config::pluginId, "Authorization state update: ignorig ID=%d\n", update.get_id());
|
|
|
|
}
|
2020-02-16 17:46:52 +01:00
|
|
|
private:
|
|
|
|
PurpleTdClient *m_owner;
|
|
|
|
};
|
|
|
|
|
2020-02-22 11:42:04 +01:00
|
|
|
PurpleTdClient::PurpleTdClient(PurpleAccount *acct)
|
2020-05-02 12:44:07 +02:00
|
|
|
: m_transceiver(this, &PurpleTdClient::processUpdate)
|
2020-02-16 17:46:52 +01:00
|
|
|
{
|
2020-02-22 11:42:04 +01:00
|
|
|
m_account = acct;
|
2020-02-16 17:46:52 +01:00
|
|
|
m_updateHandler = std::make_unique<UpdateHandler>(this);
|
|
|
|
m_authUpdateHandler = std::make_unique<AuthUpdateHandler>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
PurpleTdClient::~PurpleTdClient()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-02-17 23:30:24 +01: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-05-02 12:44:07 +02:00
|
|
|
void PurpleTdClient::processUpdate(TdObjectPtr object)
|
2020-02-16 17:46:52 +01:00
|
|
|
{
|
2020-05-02 12:44:07 +02:00
|
|
|
purple_debug_misc(config::pluginId, "Incoming update\n");
|
|
|
|
td::td_api::downcast_call(*object, *m_updateHandler);
|
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:
|
|
|
|
return "Telegram message (length: " +
|
|
|
|
std::to_string(static_cast<const td::td_api::authenticationCodeTypeTelegramMessage &>(codeType).length_) +
|
|
|
|
")";
|
|
|
|
case td::td_api::authenticationCodeTypeSms::ID:
|
|
|
|
return "SMS (length: " +
|
|
|
|
std::to_string(static_cast<const td::td_api::authenticationCodeTypeSms &>(codeType).length_) +
|
|
|
|
")";
|
|
|
|
case td::td_api::authenticationCodeTypeCall::ID:
|
|
|
|
return "Phone call (length: " +
|
|
|
|
std::to_string(static_cast<const td::td_api::authenticationCodeTypeCall &>(codeType).length_) +
|
|
|
|
")";
|
|
|
|
case td::td_api::authenticationCodeTypeFlashCall::ID:
|
|
|
|
return "Poor man's phone call (pattern: " +
|
|
|
|
static_cast<const td::td_api::authenticationCodeTypeFlashCall &>(codeType).pattern_ +
|
|
|
|
")";
|
|
|
|
default:
|
|
|
|
return "Pigeon post";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int PurpleTdClient::requestAuthCode(gpointer user_data)
|
|
|
|
{
|
|
|
|
PurpleTdClient *self = static_cast<PurpleTdClient *>(user_data);
|
|
|
|
std::string message = "Enter authentication code\n";
|
|
|
|
|
|
|
|
if (self->m_authCodeInfo) {
|
|
|
|
if (self->m_authCodeInfo->type_)
|
|
|
|
message += "Code sent via: " + getAuthCodeDesc(*self->m_authCodeInfo->type_) + "\n";
|
|
|
|
if (self->m_authCodeInfo->next_type_)
|
|
|
|
message += "Next code will be: " + getAuthCodeDesc(*self->m_authCodeInfo->next_type_) + "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!purple_request_input (purple_account_get_connection(self->m_account),
|
|
|
|
(char *)"Login code",
|
|
|
|
message.c_str(),
|
|
|
|
NULL, // secondary message
|
|
|
|
NULL, // default value
|
|
|
|
FALSE, // multiline input
|
|
|
|
FALSE, // masked input
|
|
|
|
(char *)"the code",
|
|
|
|
(char *)"OK", G_CALLBACK(requestCodeEntered),
|
|
|
|
(char *)"Cancel", G_CALLBACK(requestCodeCancelled),
|
|
|
|
self->m_account,
|
|
|
|
NULL, // buddy
|
|
|
|
NULL, // conversation
|
|
|
|
self))
|
|
|
|
{
|
|
|
|
purple_connection_set_state (purple_account_get_connection(self->m_account), PURPLE_CONNECTED);
|
|
|
|
PurpleConversation *conv = purple_conversation_new (PURPLE_CONV_TYPE_IM, self->m_account, "Telegram");
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE; // This idle handler will not be called again
|
|
|
|
}
|
|
|
|
|
|
|
|
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),
|
|
|
|
"Authentication code required");
|
|
|
|
}
|
|
|
|
|
2020-02-22 13:23:01 +01:00
|
|
|
void PurpleTdClient::authResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
if (object->get_id() == td::td_api::error::ID) {
|
2020-02-22 14:58:32 +01:00
|
|
|
td::td_api::object_ptr<td::td_api::error> error = td::move_tl_object_as<td::td_api::error>(object);
|
2020-02-22 13:23:01 +01:00
|
|
|
purple_debug_misc(config::pluginId, "Authentication error on query %lu (auth step %d): code %d (%s)\n",
|
|
|
|
(unsigned long)requestId, (int)m_lastAuthState, (int)error->code_,
|
|
|
|
error->message_.c_str());
|
2020-02-22 14:58:32 +01:00
|
|
|
m_authError = std::move(error);
|
2020-02-22 13:23:01 +01:00
|
|
|
g_idle_add(notifyAuthError, this);
|
|
|
|
} else
|
|
|
|
purple_debug_misc(config::pluginId, "Authentication success on query %lu\n", (unsigned long)requestId);
|
|
|
|
}
|
|
|
|
|
2020-03-21 14:32:33 +01:00
|
|
|
static std::string getDisplayedError(const td::td_api::error &error)
|
|
|
|
{
|
|
|
|
return "code " + std::to_string(error.code_) + " (" + error.message_ + ")";
|
|
|
|
}
|
|
|
|
|
2020-02-22 13:23:01 +01:00
|
|
|
int PurpleTdClient::notifyAuthError(gpointer user_data)
|
|
|
|
{
|
|
|
|
PurpleTdClient *self = static_cast<PurpleTdClient *>(user_data);
|
|
|
|
|
|
|
|
std::string message;
|
|
|
|
switch (self->m_lastAuthState) {
|
|
|
|
case td::td_api::authorizationStateWaitEncryptionKey::ID:
|
|
|
|
message = "Error applying database encryption key";
|
|
|
|
break;
|
|
|
|
case td::td_api::authorizationStateWaitPhoneNumber::ID:
|
|
|
|
message = "Authentication error after sending phone number";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
message = "Authentication error";
|
|
|
|
}
|
|
|
|
|
2020-02-22 14:58:32 +01:00
|
|
|
if (self->m_authError) {
|
2020-03-21 14:32:33 +01:00
|
|
|
message += ": " + getDisplayedError(*self->m_authError);
|
2020-02-22 14:58:32 +01:00
|
|
|
self->m_authError.reset();
|
|
|
|
}
|
|
|
|
|
2020-02-22 13:23:01 +01:00
|
|
|
purple_connection_error(purple_account_get_connection(self->m_account), message.c_str());
|
|
|
|
return FALSE; // This idle handler will not be called again
|
|
|
|
}
|
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-03-25 16:49:26 +01:00
|
|
|
// This query ensures an updateUser for every contact
|
2020-05-02 12:44:07 +02:00
|
|
|
m_transceiver.sendQuery(td::td_api::make_object<td::td_api::getContacts>(),
|
|
|
|
&PurpleTdClient::getContactsResponse);
|
2020-02-22 15:53:07 +01:00
|
|
|
}
|
|
|
|
|
2020-02-29 15:30:24 +01:00
|
|
|
int PurpleTdClient::setPurpleConnectionInProgress(gpointer user_data)
|
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");
|
|
|
|
PurpleTdClient *self = static_cast<PurpleTdClient *>(user_data);
|
|
|
|
PurpleConnection *gc = purple_account_get_connection(self->m_account);
|
2020-02-22 14:58:32 +01:00
|
|
|
|
2020-02-29 15:30:24 +01:00
|
|
|
purple_connection_set_state (gc, PURPLE_CONNECTING);
|
|
|
|
purple_blist_remove_account(self->m_account);
|
|
|
|
purple_connection_update_progress(gc, "Connecting", 1, 3);
|
|
|
|
|
|
|
|
return FALSE; // This idle handler will not be called again
|
|
|
|
}
|
|
|
|
|
|
|
|
int PurpleTdClient::setPurpleConnectionUpdating(gpointer user_data)
|
|
|
|
{
|
|
|
|
purple_debug_misc(config::pluginId, "Updating account status\n");
|
|
|
|
PurpleTdClient *self = static_cast<PurpleTdClient *>(user_data);
|
|
|
|
PurpleConnection *gc = purple_account_get_connection(self->m_account);
|
|
|
|
|
|
|
|
purple_connection_update_progress(gc, "Updating status", 2, 3);
|
2020-02-22 14:58:32 +01:00
|
|
|
|
|
|
|
return FALSE; // This idle handler will not be called again
|
|
|
|
}
|
2020-02-22 19:03:14 +01:00
|
|
|
|
2020-03-25 16:49:26 +01:00
|
|
|
void PurpleTdClient::getContactsResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
purple_debug_misc(config::pluginId, "getChats response to request %llu\n", (unsigned long long)requestId);
|
|
|
|
if (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);
|
|
|
|
{
|
|
|
|
TdAccountData::Lock lock(m_data);
|
|
|
|
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-03-25 16:49:26 +01:00
|
|
|
} else {
|
|
|
|
m_authError = td::td_api::make_object<td::td_api::error>(0, "Strange response to getContacts");
|
|
|
|
g_idle_add(notifyAuthError, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-22 19:03:14 +01:00
|
|
|
void PurpleTdClient::getChatsResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
purple_debug_misc(config::pluginId, "getChats response to request %llu\n", (unsigned long long)requestId);
|
|
|
|
if (object->get_id() == td::td_api::chats::ID) {
|
|
|
|
td::td_api::object_ptr<td::td_api::chats> chats = td::move_tl_object_as<td::td_api::chats>(object);
|
|
|
|
{
|
2020-02-23 14:02:47 +01:00
|
|
|
TdAccountData::Lock lock(m_data);
|
|
|
|
m_data.setActiveChats(std::move(chats->chat_ids_));
|
2020-05-02 12:35:31 +02:00
|
|
|
m_data.getContactsWithNoChat(m_usersForNewPrivateChats);
|
2020-02-22 19:03:14 +01:00
|
|
|
}
|
2020-05-02 12:35:31 +02:00
|
|
|
requestMissingPrivateChats();
|
2020-03-25 16:49:26 +01:00
|
|
|
} else {
|
|
|
|
m_authError = td::td_api::make_object<td::td_api::error>(0, "Strange response to getChats");
|
|
|
|
g_idle_add(notifyAuthError, this);
|
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");
|
|
|
|
g_idle_add(updatePurpleChatListAndReportConnected, this);
|
|
|
|
} 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)
|
|
|
|
{
|
|
|
|
if (object->get_id() != td::td_api::chat::ID) {
|
|
|
|
td::td_api::object_ptr<td::td_api::chat> chat = td::move_tl_object_as<td::td_api::chat>(object);
|
|
|
|
purple_debug_misc(config::pluginId, "Requested private chat received: id %lld\n",
|
|
|
|
(long long)chat->id_);
|
|
|
|
TdAccountData::Lock lock(m_data);
|
|
|
|
m_data.addChat(std::move(chat));
|
|
|
|
} else
|
|
|
|
purple_debug_misc(config::pluginId, "Failed to get requested private chat\n");
|
|
|
|
requestMissingPrivateChats();
|
|
|
|
}
|
|
|
|
|
2020-02-22 19:03:14 +01:00
|
|
|
static const char *getPurpleStatusId(const td::td_api::UserStatus &tdStatus)
|
|
|
|
{
|
|
|
|
if (tdStatus.get_id() == td::td_api::userStatusOnline::ID)
|
|
|
|
return purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
|
|
|
|
else
|
|
|
|
return purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE);
|
|
|
|
}
|
|
|
|
|
2020-02-27 22:27:56 +01:00
|
|
|
static const char *getPurpleUserName(const td::td_api::user &user)
|
|
|
|
{
|
|
|
|
return user.phone_number_.c_str();
|
|
|
|
}
|
|
|
|
|
2020-02-29 15:30:24 +01:00
|
|
|
int PurpleTdClient::updatePurpleChatListAndReportConnected(gpointer user_data)
|
2020-02-22 19:03:14 +01:00
|
|
|
{
|
|
|
|
PurpleTdClient *self = static_cast<PurpleTdClient *>(user_data);
|
|
|
|
|
2020-02-29 15:30:24 +01:00
|
|
|
purple_connection_set_state (purple_account_get_connection(self->m_account), PURPLE_CONNECTED);
|
|
|
|
purple_blist_add_account(self->m_account);
|
|
|
|
|
2020-02-22 19:03:14 +01:00
|
|
|
// Only populate the list from scratch
|
2020-02-23 14:02:47 +01:00
|
|
|
TdAccountData::Lock lock(self->m_data);
|
|
|
|
|
|
|
|
std::vector<PrivateChat> privateChats;
|
|
|
|
self->m_data.getPrivateChats(privateChats);
|
|
|
|
// lock must be held for as long as references from privateChats elements are used
|
|
|
|
|
|
|
|
for (const PrivateChat &c: privateChats) {
|
2020-02-27 22:27:56 +01:00
|
|
|
const td::td_api::chat &chat = c.chat;
|
|
|
|
const td::td_api::user &user = c.user;
|
|
|
|
const char *purpleUserName = getPurpleUserName(user);
|
2020-02-23 14:02:47 +01:00
|
|
|
|
2020-02-27 22:27:56 +01:00
|
|
|
PurpleBuddy *buddy = purple_find_buddy(self->m_account, purpleUserName);
|
2020-02-23 14:02:47 +01:00
|
|
|
if (buddy == NULL) {
|
|
|
|
purple_debug_misc(config::pluginId, "Adding new buddy %s for chat id %lld\n",
|
|
|
|
chat.title_.c_str(), (long long)chat.id_);
|
2020-02-27 22:27:56 +01:00
|
|
|
buddy = purple_buddy_new(self->m_account, purpleUserName, chat.title_.c_str());
|
2020-02-23 14:02:47 +01:00
|
|
|
purple_blist_add_buddy(buddy, NULL, NULL, NULL);
|
2020-03-08 23:29:45 +01: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",
|
|
|
|
purpleUserName, oldName, chat.title_.c_str());
|
|
|
|
purple_blist_remove_buddy(buddy);
|
|
|
|
buddy = purple_buddy_new(self->m_account, purpleUserName, chat.title_.c_str());
|
|
|
|
purple_blist_add_buddy(buddy, NULL, NULL, NULL);
|
|
|
|
}
|
2020-02-22 19:03:14 +01:00
|
|
|
}
|
|
|
|
|
2020-02-27 22:27:56 +01:00
|
|
|
purple_prpl_got_user_status(self->m_account, purpleUserName, getPurpleStatusId(*user.status_), NULL);
|
2020-02-22 23:15:43 +01:00
|
|
|
}
|
|
|
|
|
2020-03-08 23:27:58 +01:00
|
|
|
const td::td_api::user *selfInfo = self->m_data.getUserByPhone(purple_account_get_username(self->m_account));
|
|
|
|
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());
|
|
|
|
purple_account_set_alias(self->m_account, alias.c_str());
|
|
|
|
} else
|
|
|
|
purple_debug_warning(config::pluginId, "Did not receive user information for self (%s) at login\n",
|
|
|
|
purple_account_get_username(self->m_account));
|
|
|
|
|
|
|
|
// TODO: try to have same code handle pre-login and post-login updates
|
|
|
|
// For now, discard accumulated user updates because they have just been handled above
|
|
|
|
std::vector<UserUpdate> updates;
|
|
|
|
self->m_data.getUpdatedUsers(updates);
|
|
|
|
|
2020-02-22 23:15:43 +01:00
|
|
|
return FALSE; // This idle handler will not be called again
|
|
|
|
}
|
|
|
|
|
|
|
|
int PurpleTdClient::showUnreadMessages(gpointer user_data)
|
|
|
|
{
|
|
|
|
PurpleTdClient *self = static_cast<PurpleTdClient *>(user_data);
|
2020-02-23 14:02:47 +01:00
|
|
|
std::vector<UnreadChat> chats;
|
|
|
|
TdAccountData::Lock lock(self->m_data);
|
2020-02-22 23:15:43 +01:00
|
|
|
|
2020-02-23 14:02:47 +01:00
|
|
|
self->m_data.getUnreadChatMessages(chats);
|
|
|
|
|
2020-02-23 15:47:18 +01:00
|
|
|
for (const UnreadChat &unreadChat: chats)
|
|
|
|
if (!unreadChat.messages.empty()) {
|
|
|
|
td::td_api::object_ptr<td::td_api::viewMessages> viewMessagesReq = td::td_api::make_object<td::td_api::viewMessages>();
|
|
|
|
viewMessagesReq->chat_id_ = unreadChat.chatId;
|
|
|
|
viewMessagesReq->force_read_ = true; // no idea what "closed chats" are at this point
|
|
|
|
for (const auto &pMessage: unreadChat.messages)
|
|
|
|
viewMessagesReq->message_ids_.push_back(pMessage->id_);
|
2020-05-02 12:44:07 +02:00
|
|
|
self->m_transceiver.sendQuery(std::move(viewMessagesReq), nullptr);
|
2020-02-23 15:47:18 +01:00
|
|
|
|
2020-02-27 21:16:12 +01:00
|
|
|
for (const auto &pMessage: unreadChat.messages)
|
|
|
|
self->showMessage(*pMessage);
|
2020-02-23 15:47:18 +01:00
|
|
|
}
|
2020-02-23 14:02:47 +01:00
|
|
|
|
2020-02-22 19:03:14 +01:00
|
|
|
return FALSE; // This idle handler will not be called again
|
|
|
|
}
|
2020-02-22 23:15:43 +01:00
|
|
|
|
|
|
|
static const char *getText(const td::td_api::message &message)
|
|
|
|
{
|
|
|
|
if (message.content_) {
|
|
|
|
if ((message.content_->get_id() == td::td_api::messageText::ID)) {
|
|
|
|
const td::td_api::messageText &text = static_cast<const td::td_api::messageText &>(*message.content_);
|
|
|
|
if (text.text_)
|
|
|
|
return text.text_->text_.c_str();
|
|
|
|
} else if ((message.content_->get_id() == td::td_api::messagePhoto::ID)) {
|
|
|
|
const td::td_api::messagePhoto &photo = static_cast<const td::td_api::messagePhoto &>(*message.content_);
|
|
|
|
if (photo.caption_)
|
|
|
|
return photo.caption_->text_.c_str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::showMessage(const td::td_api::message &message)
|
|
|
|
{
|
|
|
|
// Skip unsupported content
|
|
|
|
const char *text = getText(message);
|
|
|
|
if (text == nullptr) {
|
|
|
|
purple_debug_misc(config::pluginId, "Skipping message: no supported content\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// m_dataMutex already locked
|
2020-02-23 14:02:47 +01:00
|
|
|
const td::td_api::chat *chat = m_data.getChat(message.chat_id_);
|
|
|
|
if (!chat) {
|
2020-02-22 23:15:43 +01:00
|
|
|
purple_debug_warning(config::pluginId, "Received message with unknown chat id %lld\n",
|
|
|
|
(long long)message.chat_id_);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-23 14:02:47 +01:00
|
|
|
if (chat->type_->get_id() == td::td_api::chatTypePrivate::ID) {
|
|
|
|
int32_t userId = static_cast<const td::td_api::chatTypePrivate &>(*chat->type_).user_id_;
|
|
|
|
const td::td_api::user *user = m_data.getUser(userId);
|
|
|
|
if (user) {
|
2020-03-08 23:42:12 +01:00
|
|
|
const char *who = getPurpleUserName(*user);
|
|
|
|
if (message.is_outgoing_) {
|
|
|
|
// serv_got_im seems to work for messages sent from another client, but not for
|
|
|
|
// echoed messages from this client. Therefore, this (code snippet from facebook plugin).
|
|
|
|
PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, m_account);
|
|
|
|
if (conv == NULL)
|
|
|
|
conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, m_account, who);
|
|
|
|
purple_conversation_write(conv, purple_account_get_alias(m_account), text,
|
|
|
|
PURPLE_MESSAGE_SEND, // TODO: maybe set PURPLE_MESSAGE_REMOTE_SEND when appropriate
|
|
|
|
message.date_);
|
|
|
|
} else
|
|
|
|
serv_got_im(purple_account_get_connection(m_account), who, text,
|
|
|
|
PURPLE_MESSAGE_RECV, message.date_);
|
2020-02-22 23:15:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-27 21:16:12 +01:00
|
|
|
|
|
|
|
void PurpleTdClient::onIncomingMessage(td::td_api::object_ptr<td::td_api::message> message)
|
|
|
|
{
|
|
|
|
// Pass it to the main thread
|
|
|
|
TdAccountData::Lock lock(m_data);
|
|
|
|
m_data.addNewMessage(std::move(message));
|
|
|
|
g_idle_add(showUnreadMessages, this);
|
|
|
|
}
|
2020-02-27 21:49:02 +01:00
|
|
|
|
|
|
|
int PurpleTdClient::sendMessage(const char *buddyName, const char *message)
|
|
|
|
{
|
2020-02-27 22:27:56 +01:00
|
|
|
TdAccountData::Lock lock(m_data);
|
2020-02-27 21:49:02 +01:00
|
|
|
const td::td_api::user *tdUser = m_data.getUserByPhone(buddyName);
|
|
|
|
if (tdUser == nullptr) {
|
|
|
|
purple_debug_warning(config::pluginId, "No user with phone '%s'\n", buddyName);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
const td::td_api::chat *tdChat = m_data.getPrivateChatByUserId(tdUser->id_);
|
|
|
|
if (tdChat == nullptr) {
|
2020-03-25 16:49:26 +01:00
|
|
|
purple_debug_warning(config::pluginId, "No chat with user %s\n", tdUser->phone_number_.c_str());
|
2020-02-27 21:49:02 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
td::td_api::object_ptr<td::td_api::sendMessage> send_message = td::td_api::make_object<td::td_api::sendMessage>();
|
|
|
|
send_message->chat_id_ = tdChat->id_;
|
|
|
|
td::td_api::object_ptr<td::td_api::inputMessageText> message_content = td::td_api::make_object<td::td_api::inputMessageText>();
|
|
|
|
message_content->text_ = td::td_api::make_object<td::td_api::formattedText>();
|
|
|
|
message_content->text_->text_ = message;
|
|
|
|
send_message->input_message_content_ = std::move(message_content);
|
|
|
|
|
2020-05-02 12:44:07 +02:00
|
|
|
m_transceiver.sendQuery(std::move(send_message), nullptr);
|
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)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
TdAccountData::Lock lock(m_data);
|
|
|
|
m_data.updateUserStatus(userId, std::move(status));
|
|
|
|
}
|
2020-03-08 23:27:58 +01:00
|
|
|
g_idle_add(showUserUpdates, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::updateUser(td::td_api::object_ptr<td::td_api::user> user)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
TdAccountData::Lock lock(m_data);
|
|
|
|
m_data.updateUser(std::move(user));
|
|
|
|
}
|
|
|
|
if (purple_connection_get_state (purple_account_get_connection(m_account)) == PURPLE_CONNECTED)
|
|
|
|
g_idle_add(showUserUpdates, this);
|
2020-02-27 22:27:56 +01:00
|
|
|
}
|
|
|
|
|
2020-03-08 23:27:58 +01:00
|
|
|
int PurpleTdClient::showUserUpdates(gpointer user_data)
|
2020-02-27 22:27:56 +01:00
|
|
|
{
|
|
|
|
PurpleTdClient *self = static_cast<PurpleTdClient *>(user_data);
|
|
|
|
std::vector<UserUpdate> userUpdates;
|
|
|
|
|
|
|
|
{
|
|
|
|
TdAccountData::Lock lock(self->m_data);
|
|
|
|
self->m_data.getUpdatedUsers(userUpdates);
|
|
|
|
}
|
|
|
|
for (const UserUpdate &updateInfo: userUpdates) {
|
|
|
|
const td::td_api::user *user;
|
|
|
|
{
|
|
|
|
TdAccountData::Lock lock(self->m_data);
|
|
|
|
user = self->m_data.getUser(updateInfo.userId);
|
|
|
|
}
|
|
|
|
if (user == nullptr)
|
|
|
|
continue;
|
|
|
|
|
2020-03-08 23:27:58 +01:00
|
|
|
if (updateInfo.updates.status)
|
|
|
|
purple_prpl_got_user_status(self->m_account, getPurpleUserName(*user),
|
|
|
|
getPurpleStatusId(*user->status_), NULL);
|
|
|
|
// TODO: handle other updates
|
2020-02-27 22:27:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE; // This idle handler will not be called again
|
|
|
|
}
|
2020-03-15 21:47:47 +01:00
|
|
|
|
|
|
|
void PurpleTdClient::handleUserChatAction(const td::td_api::updateUserChatAction &updateChatAction)
|
|
|
|
{
|
|
|
|
const td::td_api::chat *chat;
|
|
|
|
{
|
|
|
|
TdAccountData::Lock lock(m_data);
|
|
|
|
chat = m_data.getChat(updateChatAction.chat_id_);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!chat)
|
|
|
|
purple_debug_warning(config::pluginId, "Got user chat action for unknown chat %lld\n",
|
|
|
|
(long long)updateChatAction.chat_id_);
|
|
|
|
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_)
|
|
|
|
purple_debug_warning(config::pluginId, "Got user action for private chat %lld (with user %d) for another user %d\n",
|
2020-03-21 12:47:47 +01:00
|
|
|
(long long)updateChatAction.chat_id_, privType.user_id_,
|
|
|
|
updateChatAction.user_id_);
|
2020-03-15 21:47:47 +01:00
|
|
|
else if (updateChatAction.action_) {
|
2020-03-21 12:47:47 +01:00
|
|
|
TdAccountData::Lock lock(m_data);
|
|
|
|
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_);
|
|
|
|
m_data.addUserAction(updateChatAction.user_id_, false);
|
|
|
|
} 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_);
|
|
|
|
m_data.addUserAction(updateChatAction.user_id_, false);
|
|
|
|
} else {
|
|
|
|
purple_debug_misc(config::pluginId, "User (id %d) started chat action (id %d)\n",
|
|
|
|
updateChatAction.user_id_, updateChatAction.action_->get_id());
|
|
|
|
m_data.addUserAction(updateChatAction.user_id_, true);
|
|
|
|
}
|
|
|
|
g_idle_add(PurpleTdClient::showUserChatActions, this);
|
2020-03-15 21:47:47 +01:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
purple_debug_misc(config::pluginId, "Ignoring user chat action for non-private chat %lld\n",
|
|
|
|
(long long)updateChatAction.chat_id_);
|
|
|
|
}
|
2020-03-21 12:47:47 +01:00
|
|
|
|
|
|
|
int PurpleTdClient::showUserChatActions(gpointer user_data)
|
|
|
|
{
|
|
|
|
PurpleTdClient *self = static_cast<PurpleTdClient *>(user_data);
|
|
|
|
std::vector<UserAction> actions;
|
|
|
|
TdAccountData::Lock lock(self->m_data);
|
|
|
|
|
|
|
|
self->m_data.getNewUserActions(actions);
|
|
|
|
|
|
|
|
for (const UserAction &action: actions) {
|
|
|
|
const td::td_api::user *user = self->m_data.getUser(action.userId);
|
|
|
|
if (user) {
|
|
|
|
if (action.isTyping)
|
|
|
|
serv_got_typing(purple_account_get_connection(self->m_account),
|
|
|
|
getPurpleUserName(*user), REMOTE_TYPING_NOTICE_TIMEOUT,
|
|
|
|
PURPLE_TYPING);
|
|
|
|
else
|
|
|
|
serv_got_typing_stopped(purple_account_get_connection(self->m_account),
|
|
|
|
getPurpleUserName(*user));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-21 14:32:33 +01:00
|
|
|
return FALSE; // This idle handler will not be called again
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurpleTdClient::addContact(const char *phoneNumber, const char *alias)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
TdAccountData::Lock lock(m_data);
|
|
|
|
if (m_data.getUserByPhone(phoneNumber)) {
|
|
|
|
purple_debug_info(config::pluginId, "User with phone number %s already exists\n", phoneNumber);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
{
|
|
|
|
TdAccountData::Lock lock(m_data);
|
|
|
|
m_data.addNewContactRequest(requestId, phoneNumber);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-23 22:56:16 +01:00
|
|
|
void PurpleTdClient::importContactResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
std::string phoneNumber;
|
2020-03-24 00:14:19 +01:00
|
|
|
int32_t dummy;
|
2020-03-23 22:56:16 +01:00
|
|
|
TdAccountData::Lock lock(m_data);
|
2020-03-24 00:14:19 +01:00
|
|
|
if (! m_data.extractContactRequest(requestId, phoneNumber, dummy))
|
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];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (userId) {
|
|
|
|
td::td_api::object_ptr<td::td_api::contact> contact =
|
|
|
|
td::td_api::make_object<td::td_api::contact>(phoneNumber, "Beh", "Meh", "", userId);
|
|
|
|
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-03-24 00:14:19 +01:00
|
|
|
m_data.addNewContactRequest(newRequestId, phoneNumber.c_str(), userId);
|
2020-03-23 22:56:16 +01:00
|
|
|
} else {
|
|
|
|
m_data.addFailedContact(std::move(phoneNumber), nullptr);
|
|
|
|
g_idle_add(&PurpleTdClient::notifyFailedContacts, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-21 14:32:33 +01:00
|
|
|
void PurpleTdClient::addContactResponse(uint64_t requestId, td::td_api::object_ptr<td::td_api::Object> object)
|
|
|
|
{
|
|
|
|
std::string phoneNumber;
|
2020-03-24 00:14:19 +01:00
|
|
|
int32_t userId;
|
2020-03-21 14:32:33 +01:00
|
|
|
TdAccountData::Lock lock(m_data);
|
2020-03-24 00:14:19 +01:00
|
|
|
if (! m_data.extractContactRequest(requestId, phoneNumber, userId))
|
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 =
|
|
|
|
td::td_api::make_object<td::td_api::createPrivateChat>(userId, false);
|
2020-05-02 12:44:07 +02:00
|
|
|
uint64_t newRequestId = m_transceiver.sendQuery(std::move(createChat),
|
|
|
|
&PurpleTdClient::addContactCreatePrivateChatResponse);
|
2020-03-24 00:14:19 +01:00
|
|
|
m_data.addNewContactRequest(newRequestId, phoneNumber.c_str(), userId);
|
2020-03-23 22:56:16 +01:00
|
|
|
} else {
|
2020-03-24 00:14:19 +01:00
|
|
|
td::td_api::object_ptr<td::td_api::error> error;
|
2020-03-23 22:56:16 +01:00
|
|
|
if (object->get_id() == td::td_api::error::ID) {
|
|
|
|
error = td::move_tl_object_as<td::td_api::error>(object);
|
|
|
|
purple_debug_misc(config::pluginId, "Failed to add contact (%s): code %d (%s)\n",
|
|
|
|
phoneNumber.c_str(), (int)error->code_, error->message_.c_str());
|
|
|
|
} else
|
2020-03-24 00:14:19 +01:00
|
|
|
error = td::td_api::make_object<td::td_api::error>(0, "Strange reply to adding contact");
|
|
|
|
m_data.addFailedContact(std::move(phoneNumber), std::move(error));
|
|
|
|
g_idle_add(&PurpleTdClient::notifyFailedContacts, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
std::string phoneNumber;
|
|
|
|
int32_t userId;
|
|
|
|
TdAccountData::Lock lock(m_data);
|
|
|
|
if (! m_data.extractContactRequest(requestId, phoneNumber, userId))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (object->get_id() != td::td_api::chat::ID) {
|
|
|
|
td::td_api::object_ptr<td::td_api::error> error;
|
|
|
|
if (object->get_id() == td::td_api::error::ID) {
|
|
|
|
error = td::move_tl_object_as<td::td_api::error>(object);
|
|
|
|
purple_debug_misc(config::pluginId, "Failed to create private chat (to %s): code %d (%s)\n",
|
|
|
|
phoneNumber.c_str(), (int)error->code_, error->message_.c_str());
|
|
|
|
} else
|
|
|
|
error = td::td_api::make_object<td::td_api::error>(0, "Strange reply to creating private chat");
|
2020-03-21 14:32:33 +01:00
|
|
|
m_data.addFailedContact(std::move(phoneNumber), std::move(error));
|
|
|
|
g_idle_add(&PurpleTdClient::notifyFailedContacts, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int PurpleTdClient::notifyFailedContacts(gpointer user_data)
|
|
|
|
{
|
|
|
|
PurpleTdClient *self = static_cast<PurpleTdClient *>(user_data);
|
|
|
|
std::vector<FailedContact> failedContacts;
|
|
|
|
{
|
|
|
|
TdAccountData::Lock lock(self->m_data);
|
|
|
|
self->m_data.getFailedContacts(failedContacts);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!failedContacts.empty()) {
|
|
|
|
std::string message;
|
|
|
|
for (const FailedContact &contact: failedContacts) {
|
|
|
|
if (!message.empty())
|
|
|
|
message += " ";
|
|
|
|
message += "Failed to add contact (";
|
|
|
|
message += contact.phoneNumber;
|
|
|
|
message += "): ";
|
2020-03-23 22:56:16 +01:00
|
|
|
if (contact.error)
|
|
|
|
message += getDisplayedError(*contact.error);
|
|
|
|
else
|
|
|
|
message += "User not found";
|
2020-03-21 14:32:33 +01:00
|
|
|
|
|
|
|
PurpleBuddy *buddy = purple_find_buddy(self->m_account, contact.phoneNumber.c_str());
|
|
|
|
if (buddy)
|
|
|
|
purple_blist_remove_buddy(buddy);
|
|
|
|
}
|
|
|
|
purple_notify_error(purple_account_get_connection(self->m_account),
|
|
|
|
"Failed to add contact", message.c_str(), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-21 12:47:47 +01:00
|
|
|
return FALSE; // This idle handler will not be called again
|
|
|
|
}
|