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