2
0
mirror of https://github.com/ars3niy/tdlib-purple synced 2025-08-22 01:49:29 +00:00

Started adding voice call support with libtgvoip

This commit is contained in:
Arseniy Lartsev 2020-07-26 23:44:51 +02:00
parent 8368563615
commit 78846fb133
13 changed files with 196 additions and 3 deletions

View File

@ -13,6 +13,8 @@ set(NoWebp FALSE CACHE BOOL "Do not decode webp stickers")
set(NoBundledLottie FALSE CACHE BOOL "Do not use bundled rlottie library")
set(NoLottie FALSE CACHE BOOL "Disable animated sticker conversion")
set(NoTranslations FALSE CACHE BOOL "Disable translation support")
set(tgvoip_INCLUDE_DIRS "" CACHE STRING "Path to libtgvoip headers")
set(tgvoip_LIBRARIES "tgvoip" CACHE STRING "tgvoip library to link against")
set(API_ID 94575 CACHE STRING "API id")
set(API_HASH a3406de8d171bb422bb6ddf3bbd800e2 CACHE STRING "API hash")
set(STUFF "" CACHE STRING "")
@ -51,6 +53,7 @@ add_library(telegram-tdlib SHARED
format.cpp
sticker.cpp
file-transfer.cpp
call.cpp
)
# libpurple uses the deprecated glib-type `GParameter` and the deprecated glib-macro `G_CONST_RETURN`, which
@ -113,6 +116,13 @@ if (NOT NoTranslations)
target_link_libraries(telegram-tdlib PRIVATE ${Intl_LIBRARIES})
endif (NOT NoTranslations)
if (NOT tgvoip_INCLUDE_DIRS STREQUAL "")
target_include_directories(telegram-tdlib SYSTEM PRIVATE ${tgvoip_INCLUDE_DIRS})
endif (NOT tgvoip_INCLUDE_DIRS STREQUAL "")
if (NOT NoVoip)
target_link_libraries(telegram-tdlib PRIVATE ${tgvoip_LIBRARIES})
endif (NOT NoVoip)
if (DEFINED STANDARD_LIBRARIES_EXTRA)
set (CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} ${STANDARD_LIBRARIES_EXTRA}")
endif (DEFINED STANDARD_LIBRARIES_EXTRA)

View File

@ -47,6 +47,8 @@ make install DESTDIR=/path/to/tdlib
```
Also see [building](https://github.com/tdlib/td#building) for additional details on TDLib building.
libtgvoip is required for voice calls.
Building this plugin:
```
mkdir build
@ -68,6 +70,13 @@ Building without animated sticker decoding: `-DNoLottie=True`
Building without localization: `-DNoTranslations=True`
Building without voice call support: `-DNoVoip=True`
If libtgvoip is not installed in include/library path then build with
```
-Dtgvoip_INCLUDE_DIRS=/path/to/tgvoip/include -Dtgvoip_LIBRARIES=/path/to/libtgvoip.a
```
## Proper user names in bitlbee
```

View File

@ -702,3 +702,24 @@ TdAccountData::getBasicGroupsWithMember(int32_t userId)
return result;
}
bool TdAccountData::hasActiveCall()
{
return (m_callData != nullptr);
}
void TdAccountData::setActiveCall()
{
if (!m_callData)
m_callData = std::make_unique<tgvoip::VoIPController>();
}
tgvoip::VoIPController *TdAccountData::getCallData()
{
return m_callData.get();
}
void TdAccountData::removeActiveCall()
{
m_callData.reset();
}

View File

@ -2,11 +2,21 @@
#define _ACCOUNT_DATA_H
#include <td/telegram/td_api.h>
#include <map>
#include <mutex>
#include <set>
#include <purple.h>
#ifndef NoVoip
#include <tgvoip/VoIPController.h>
#else
namespace tgvoip {
struct VoIPController {};
}
#endif
bool isPhoneNumber(const char *s);
const char *getCanonicalPhoneNumber(const char *s);
int32_t stringToUserId(const char *s);
@ -277,6 +287,11 @@ public:
auto getBasicGroupsWithMember(int32_t userId) ->
std::vector<std::pair<int32_t, const td::td_api::basicGroupFullInfo *>>;
bool hasActiveCall();
void setActiveCall();
tgvoip::VoIPController *getCallData();
void removeActiveCall();
private:
TdAccountData(const TdAccountData &other) = delete;
TdAccountData &operator=(const TdAccountData &other) = delete;
@ -341,6 +356,9 @@ private:
// Currently active file transfers for which PurpleXfer is used
std::vector<FileTransferInfo> m_fileTransfers;
// Voice call data
std::unique_ptr<tgvoip::VoIPController> m_callData;
std::unique_ptr<PendingRequest> getPendingRequestImpl(uint64_t requestId);
PendingRequest * findPendingRequestImpl(uint64_t requestId);
};

View File

@ -11,4 +11,6 @@
#cmakedefine NoTranslations
#cmakedefine NoVoip
#endif

48
call.cpp Normal file
View File

@ -0,0 +1,48 @@
#include "call.h"
#include "client-utils.h"
#include "config.h"
#include "buildopt.h"
static td::td_api::object_ptr<td::td_api::callProtocol> getCallProtocol()
{
auto protocol = td::td_api::make_object<td::td_api::callProtocol>();
protocol->udp_p2p_ = true;
protocol->udp_reflector_ = true;
protocol->min_layer_ = 65;
protocol->max_layer_ = 65;
return protocol;
}
bool initiateCall(int32_t userId, TdAccountData &account, TdTransceiver &transceiver)
{
if (account.hasActiveCall())
return false;
td::td_api::object_ptr<td::td_api::createCall> callRequest = td::td_api::make_object<td::td_api::createCall>();
callRequest->user_id_ = userId;
callRequest->protocol_ = getCallProtocol();
return false;
}
void updateCall(const td::td_api::call &call, TdAccountData &account, TdTransceiver &transceiver)
{
PurpleMediaManager *mediaManager = purple_media_manager_get();
PurpleMediaCaps capabilities = purple_media_manager_get_ui_caps(mediaManager);
if (!(capabilities & PURPLE_MEDIA_CAPS_AUDIO) && !(capabilities & PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION)) {
purple_debug_misc(config::pluginId, "Ignoring incoming call: no audio capabilities\n");
return;
}
if (!call.is_outgoing_) {
/*if (not already in this call && call.state_ && (call.state_->get_id() == td::td_api::callStatePending::ID)) {
const td::td_api::user *user = account.getUser(call.user_id_);
if (!user) return;
request call
if accepted:
td::td_api::object_ptr<td::td_api::acceptCall> acceptReq = td::td_api::make_object<td::td_api::acceptCall>();
acceptReq->call_id_ = GPOINTER_TO_INT(purple_media_get_prpl_data(media));
acceptReq->protocol_ = getCallProtocol();
transceiver->sendQuery(std::move(acceptReq), nullptr);
}*/
}
}

10
call.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _CALL_H
#define _CALL_H
#include "transceiver.h"
#include "account-data.h"
bool initiateCall(int32_t userId, TdAccountData &account, TdTransceiver &transceiver);
void updateCall(const td::td_api::call &call, TdAccountData &account, TdTransceiver &transceiver);
#endif

View File

@ -85,6 +85,7 @@ cmake -DCMAKE_SYSTEM_NAME=Windows \
-DIntl_LIBRARY=$PWD/../../deps/win32-dev/gtk_2_0-2.14/lib/libintl.dll.a \
-DGLIB_LIBRARIES="$PWD/../../deps/win32-dev/gtk_2_0-2.14/lib/libglib-2.0.dll.a;$PWD/../../deps/win32-dev/gtk_2_0-2.14/lib/libgthread-2.0.dll.a" \
-DSTANDARD_LIBRARIES_EXTRA="-Wl,-Bstatic -lpthread -Wl,-Bdynamic" \
-DNoVoip=True \
-DCMAKE_BUILD_TYPE=Release ..
make
@ -119,5 +120,5 @@ WS2_32.dll
## Regression test
```
WINEPATH=$PWD/../deps/win32-dev/gtk_2_0-2.14/bin wine test/tests
WINEPATH=$PWD/../../deps/win32-dev/gtk_2_0-2.14/bin wine test/tests
```

View File

@ -4,6 +4,7 @@
#include "format.h"
#include "sticker.h"
#include "file-transfer.h"
#include "call.h"
#include <unistd.h>
#include <stdlib.h>
#include <algorithm>
@ -195,6 +196,18 @@ void PurpleTdClient::processUpdate(td::td_api::Object &update)
break;
};
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;
};
default:
purple_debug_misc(config::pluginId, "Incoming update: ignorig ID=%d\n", update.get_id());
break;
@ -2412,3 +2425,20 @@ void PurpleTdClient::cancelUpload(PurpleXfer *xfer)
// in which case nothing more should be done.
}
}
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);
}

View File

@ -47,6 +47,7 @@ public:
void sendFileToChat(PurpleXfer *xfer, const char *purpleName, PurpleConversationType type,
int purpleChatId);
void cancelUpload(PurpleXfer *xfer);
bool startVoiceCall(const char *buddyName);
private:
using TdObjectPtr = td::td_api::object_ptr<td::td_api::Object>;
using ResponseCb = void (PurpleTdClient::*)(uint64_t requestId, TdObjectPtr object);

View File

@ -599,6 +599,31 @@ static PurpleCmdRet tgprpl_cmd_kick(PurpleConversation *conv, const gchar *cmd,
return PURPLE_CMD_RET_OK;
}
static PurpleMediaCaps getMediaCaps(PurpleAccount *account, const char *who)
{
#ifndef NoVoip
return PURPLE_MEDIA_CAPS_AUDIO;
#else
return PURPLE_MEDIA_CAPS_NONE;
#endif
}
gboolean initiateMedia(PurpleAccount *account, const char *who, PurpleMediaSessionType type)
{
#ifndef NoVoip
if (!(type & PURPLE_MEDIA_AUDIO))
return FALSE;
PurpleTdClient *tdClient = getTdClient(account);
if (tdClient)
return tdClient->startVoiceCall(who) ? TRUE : FALSE;
else
return FALSE;
#else
return FALSE;
#endif
}
static char png[] = "png";
static PurplePluginProtocolInfo prpl_info = {
@ -679,8 +704,8 @@ static PurplePluginProtocolInfo prpl_info = {
.get_attention_types = NULL,
.struct_size = sizeof(PurplePluginProtocolInfo),
.get_account_text_table = tgprpl_get_account_text_table,
.initiate_media = NULL,
.get_media_caps = NULL,
.initiate_media = initiateMedia,
.get_media_caps = getMediaCaps,
.get_moods = NULL,
.set_public_alias = NULL,
.get_public_alias = NULL,

View File

@ -30,6 +30,7 @@ add_executable(tests EXCLUDE_FROM_ALL
../format.cpp
../sticker.cpp
../file-transfer.cpp
../call.cpp
)
set_property(TARGET tests PROPERTY CXX_STANDARD 14)
@ -58,4 +59,11 @@ if (NOT NoTranslations)
target_link_libraries(tests PRIVATE ${Intl_LIBRARIES})
endif (NOT NoTranslations)
if (NOT tgvoip_INCLUDE_DIRS STREQUAL "")
target_include_directories(tests SYSTEM PRIVATE ${tgvoip_INCLUDE_DIRS})
endif (NOT tgvoip_INCLUDE_DIRS STREQUAL "")
if (NOT NoVoip)
target_link_libraries(tests PRIVATE ${tgvoip_LIBRARIES})
endif (NOT NoVoip)
add_custom_target(run-tests ${CMAKE_CURRENT_BINARY_DIR}/tests DEPENDS tests)

View File

@ -1421,4 +1421,14 @@ PurpleCmdId purple_cmd_register(const gchar *cmd, const gchar *args, PurpleCmdPr
return 0;
}
PurpleMediaManager *purple_media_manager_get(void)
{
return NULL;
}
PurpleMediaCaps purple_media_manager_get_ui_caps(PurpleMediaManager *manager)
{
return PURPLE_MEDIA_CAPS_NONE;
}
};