TelegramTUI/src/tgclient.cpp

789 lines
26 KiB
C++

#include "tgclient.h"
#include <cassert>
#include <td/telegram/td_api.hpp>
#include <iostream>
#include <sstream>
#include <Log.h>
#include "config.h"
#include "typeconverter.h"
namespace pl = std::placeholders;
// overloaded
namespace detail {
template <class... Fs>
struct overload;
template <class F>
struct overload<F> : public F {
explicit overload(F f) : F(f) { }
};
template <class F, class... Fs>
struct overload<F, Fs...>
: public overload<F>
, overload<Fs...> {
overload(F f, Fs... fs) : overload<F>(f), overload<Fs...>(fs...) { }
using overload<F>::operator();
using overload<Fs...>::operator();
};
} // namespace detail
template <class... F>
auto overloaded(F... f) {
return detail::overload<F...>(f...);
}
class Placeholder : public td_api::Object {
public:
static const std::int32_t ID = 0;
};
const uint32_t TGClient::MAXCACHEDUSERCOUNT = 1024;
const uint64_t TGClient::CACHEDUSERCOUNTCLEANUPTIME = 86400;
void(TGClient::* TGClient::STATICHANDLERS [])(objptr<td_api::Object>) = {
nullptr, // ids with 0 are handled with the authorisation handler
nullptr, // zero handle
&TGClient::loadInitInternalCallback,
};
static const uint64_t HANDLER_NULL = 1;
TGClient::TGClient(std::function<void()> initDoneCallback) : initDoneCallback(initDoneCallback) {
td::ClientManager::execute(td_api::make_object<td_api::setLogVerbosityLevel>(1));
client_manager_ = std::make_unique<td::ClientManager>();
client_id_ = client_manager_->create_client_id();
send_query(td_api::make_object<td_api::getOption>("version"));
}
void TGClient::setAuthData(std::function<std::string()> getAuthCodeCallback_) {
getAuthCodeCallback = getAuthCodeCallback_;
}
void TGClient::loop() {
shouldrun = true;
while (shouldrun) {
if (need_restart_) {
restart();
} else if (!are_authorized_) {
Log::warn << "not authorized";
while(!are_authorized_) {
process_response(client_manager_->receive(1));
}
} else {
//process updates
while(true) {
auto response = client_manager_->receive(0.1);
if (response.object)
process_response(std::move(response));
else
break;
}
}
}
Log::note << "Client Loop terminated";
}
void TGClient::stop() {
shouldrun = false;
send_query(td_api::make_object<td_api::close>());
}
void TGClient::registerNewMessageHandler(MessageCallback mclb) {
messageCallback = mclb;
}
void TGClient::registerStatusUpdateHandler(StatusCallback sclb) {
statusCallback = sclb;
}
void TGClient::registerDraftHandler(DraftCallback dclb) {
draftCallback = dclb;
}
void TGClient::registerActionHandler(ActionCallback clb) {
actionCallback = clb;
}
void TGClient::registerDeleteHandler(DeleteCallback dclb) {
deleteCallback = dclb;
}
void TGClient::registerUserUpdateHandler(UserUpdateCallback uuclb) {
userupdCallback = uuclb;
}
void TGClient::registerEditMessageHandler(EditMessageCallback emclb) {
editMessageCallback = emclb;
}
void TGClient::registerIndexDoneHandle(IndexDoneCallback idclb) {
indexDoneCallback = idclb;
}
void TGClient::registerFileUpdateCallback(FileUpdateCallback fuclb) {
fileUpdateCallback = fuclb;
}
void TGClient::registerNewChatCallback(NewChatCallback ncclb) {
newChatCallback = ncclb;
}
void TGClient::registerChatFiltersCallback(ChatFiltersCallback cfclb) {
chatFiltersCallback = cfclb;
}
static auto createFormattedText(const std::string& in, std::vector<objptr<td_api::textEntity>>& entities) {
auto formattedtext = td::make_tl_object<td_api::formattedText>();
formattedtext->text_ = in;
formattedtext->entities_.reserve(entities.size());
for(uint32_t i = 0; i < entities.size(); ++i) {
auto& ref = entities.at(i);
formattedtext->entities_.push_back(td::make_tl_object<td_api::textEntity>(ref->offset_, ref->length_, std::move(ref->type_)));
}
return formattedtext;
}
static auto createMessageInputTextEntities(const std::string& in, std::vector<objptr<td_api::textEntity>>& entities) {
auto formattedText = createFormattedText(in, entities);
return td::make_tl_object<td_api::inputMessageText>(std::move(formattedText), false, false);
}
static auto createMessageInputText(const std::string& in) {
std::vector<objptr<td_api::textEntity>> entities;
return createMessageInputTextEntities(in, entities);
}
void TGClient::openChat(int64_t chatid) {
send_staticquery(td::make_tl_object<td_api::openChat>(chatid), HANDLER_NULL);
}
void TGClient::closeChat(int64_t chatid) {
send_staticquery(td::make_tl_object<td_api::closeChat>(chatid), HANDLER_NULL);
}
void TGClient::deleteMessage(int64_t chatid, int64_t messageid, bool forall) {
send_staticquery(td::make_tl_object<td_api::deleteMessages>(chatid, td_api::array<int64_t>(1, messageid), forall), HANDLER_NULL);
}
void TGClient::screenShottaken(int64_t chatid) {
td_api::array<td_api::int53> messageIds;
send_staticquery(td::make_tl_object<td_api::viewMessages>(chatid, std::move(messageIds), td::make_tl_object<td_api::messageSourceScreenshot>(), false), HANDLER_NULL);
}
void TGClient::reply(const objptr<td_api::message>& rplyto, const std::string& awnser) {
reply(rplyto->chat_id_, rplyto->id_, awnser);
}
void TGClient::reply(int64_t chat, int64_t msg, const std::string& awnser) {
auto inputmsgCont = createMessageInputText(awnser);
auto sendMsg = td::make_tl_object<td_api::sendMessage>();
sendMsg->chat_id_ = chat;
sendMsg->reply_to_ = td_api::make_object<td_api::messageReplyToMessage>(chat, msg);
sendMsg->input_message_content_ = std::move(inputmsgCont);
send_staticquery(std::move(sendMsg), HANDLER_NULL);
}
void TGClient::editMessage(const objptr<td_api::message>& toEdit, const std::string& newText) {
std::vector<objptr<td_api::textEntity>> entities;
editMessage(toEdit, newText, entities);
}
void TGClient::editMessage(const objptr<td_api::message>& toEdit, const std::string& newText, std::vector<objptr<td_api::textEntity>>& entities) {
auto editMsg = td::make_tl_object<td_api::editMessageText>();
editMsg->chat_id_ = toEdit->chat_id_;
editMsg->message_id_ = toEdit->id_;
editMsg->input_message_content_ = createMessageInputTextEntities(newText, entities);
send_staticquery(std::move(editMsg), HANDLER_NULL);
}
void TGClient::editMessageCaption(const objptr<td_api::message>& toEdit, const std::string& newText, std::vector<objptr<td_api::textEntity>>& entities ) {
auto editMsg = td::make_tl_object<td_api::editMessageCaption>();
editMsg->chat_id_ = toEdit->chat_id_;
editMsg->message_id_ = toEdit->id_;
editMsg->caption_ = createFormattedText(newText, entities);
send_staticquery(std::move(editMsg), HANDLER_NULL);
}
void TGClient::sendCallbackQuery(int64_t chat, int64_t messageid, const std::string& payload) {
Log::info << "sendCallbackQuery: " << chat << " " << messageid << " " << payload;
auto payloadobj = td::make_tl_object<td_api::callbackQueryPayloadData>();
payloadobj->data_ = payload;
auto sendCallback = td::make_tl_object<td_api::getCallbackQueryAnswer>();
sendCallback->chat_id_ = chat;
sendCallback->message_id_ = messageid;
sendCallback->payload_ = std::move(payloadobj);
send_wrappedquery<td_api::callbackQueryAnswer>(std::move(sendCallback), [](objptr<td_api::callbackQueryAnswer> clb) {
Log::info << "CallbackAwnser - text: " << clb->text_ << " showAlert: " << clb->show_alert_ << " url: " << clb->url_;
});
}
void TGClient::muteChat(int64_t chatid, int32_t mutefor) {
auto settings = td::make_tl_object<td_api::chatNotificationSettings>();
settings->use_default_mute_for_ = false;
settings->mute_for_ = mutefor;
settings->use_default_sound_ = true;
settings->use_default_show_preview_ = true;
settings->use_default_disable_pinned_message_notifications_ = true;
settings->use_default_disable_mention_notifications_ = true;
send_staticquery(td::make_tl_object<td_api::setChatNotificationSettings>(chatid, std::move(settings)), HANDLER_NULL);
}
void TGClient::addChatToChatListFilter(int64_t chatid, int32_t chatfilterid) {
send_staticquery(td::make_tl_object<td_api::addChatToList>(chatid, td::make_tl_object<td_api::chatListFolder>(chatfilterid)), HANDLER_NULL);
}
void TGClient::indexChat(int64_t chatid) {
requestMessages(chatid, 0, indexDoneCallback, editMessageCallback);
}
void TGClient::getAllTextMessages(int64_t chatid, std::function<void(std::map<int64_t, std::string>)> f) {
if(!f) return;
// only works without multithreading!
// with multithreading onDone could be processed before the last update -> segfault
auto map = new std::map<int64_t, std::string>();
requestMessages(chatid, 0, [this, map, f](int64_t chat) {
f(*map);
delete map;
}, [this, map](objptr<td_api::message> msg) {
// try to get text
bool isText = false;
// text does not need to be freed, because it has a owning pointer in msg
td_api::formattedText* text = getMessageFormattedText(*msg, isText);
if(msg->sender_id_->get_id() == td_api::messageSenderUser::ID) {
auto sender = (td_api::messageSenderUser*) (msg->sender_id_).get();
if(sender->user_id_ != me) {
Log::warn << "ignore non-me-config-message from: " << sender->user_id_;
return;
}
if(text) {
(*map)[msg->id_] = text->text_;
}
}
});
}
void TGClient::getLastMessages(int64_t chatid, std::function<void(const std::vector<std::shared_ptr<Message>>&)> f) {
std::vector<std::shared_ptr<Message>>* vec = new std::vector<std::shared_ptr<Message>>();
requestMessages(chatid, 0, [vec, f](int64_t chat) {
f(*vec);
delete vec;
}, [vec](objptr<td_api::message> msg) {
std::shared_ptr<Message> msgOut = std::make_shared<Message>();
convertMessage(*msg, *msgOut);
vec->push_back(std::move(msgOut));
}, false);
}
void TGClient::downloadFile(int32_t file_id) {
Log::info << "start download for file: " << file_id;
send_staticquery(td::make_tl_object<td_api::downloadFile>(file_id, /* prio */ 15, 0, 0, false), HANDLER_NULL);
}
void TGClient:: sendTextMessage(int64_t chatid, const std::string& text) {
auto sendmsg = td::make_tl_object<td_api::sendMessage>();
sendmsg->chat_id_ = chatid;
sendmsg->message_thread_id_ = 0;
sendmsg->reply_to_ = nullptr;
sendmsg->options_ = nullptr;
sendmsg->reply_markup_ = nullptr;
std::vector<objptr<td_api::textEntity>> entities;
sendmsg->input_message_content_ = createMessageInputTextEntities(text, entities);
send_staticquery(std::move(sendmsg), HANDLER_NULL);
}
void TGClient::sendTextMessageToSelf(const std::string& text) {
sendTextMessage(me, text);
}
void TGClient::getMessage(int64_t chatid, int64_t messageid, std::function<void(objptr<td_api::message>)> f) {
send_wrappedquery<td_api::message>(td::make_tl_object<td_api::getMessage>(chatid, messageid), f);
}
void TGClient::setDraft(int64_t chat, const std::string& text, int64_t replyto, int64_t messageThread) {
auto itext = createMessageInputText(text);
objptr<td_api::draftMessage> dmsg = td_api::make_object<td_api::draftMessage>(replyto, time(nullptr), std::move(itext));
send_staticquery(td_api::make_object<td_api::setChatDraftMessage>(chat, messageThread, std::move(dmsg)), HANDLER_NULL);
drafts.erase(chat); //if there was a user draft, its now gone
}
void TGClient::clearDraft(int64_t chat, int64_t messageThread) {
objptr<td_api::draftMessage> dmsg;
send_staticquery(td_api::make_object<td_api::setChatDraftMessage>(chat, messageThread, std::move(dmsg)), HANDLER_NULL);
drafts.erase(chat);
}
bool TGClient::hasDraft(int64_t chat) const {
return drafts.find(chat) != drafts.end();
}
const User* TGClient::getCachedUser(int64_t userid) {
auto it = usercache.find(userid);
if(it == usercache.end()) {
// no user found
return nullptr;
}
//update accesstimes
accessUser(it);
return &it->second;
}
void TGClient::accessUser(std::map<int64_t, CachedUser>::iterator it) {
//change last access time
uint64_t lastacc = it->second.lastaccessed;
//remove old entry
auto it2 = usercache_timeout.lower_bound(lastacc);
auto it2end = usercache_timeout.upper_bound(lastacc);
for( ;it2 != it2end; ++it2) {
if(it2->second == it->first) {
usercache_timeout.erase(it2);
break;
}
}
uint64_t newacc = time(0);
//insert new value
usercache_timeout.insert({newacc, it->first});
it->second.lastaccessed = newacc;
}
void TGClient::addUser(CachedUser& u) {
//check for existing user
auto it = usercache.find(u.tgid);
if(it != usercache.end()) {
//remove old user
removeUser(it);
}
//add user
uint64_t accesstime = time(0);
u.lastaccessed = accesstime;
usercache.insert({u.tgid, u});
//add timing information
usercache_timeout.insert({accesstime, u.tgid});
//remove old entrys
checkUserCache();
}
void TGClient::removeUser(std::map<int64_t, CachedUser>::iterator it) {
if(it == usercache.end()) return;
uint64_t accesstime = it->second.lastaccessed;
int64_t id = it->second.tgid;
//remove from timeout cache
auto it2 = usercache_timeout.lower_bound(accesstime);
auto it2end = usercache_timeout.upper_bound(accesstime);
for( ;it2 != it2end; ++it2) {
if(it2->second == id) {
usercache_timeout.erase(it2);
break;
}
}
//remove from real cache
usercache.erase(it);
}
void TGClient::checkUserCache() {
if(usercache.size() > MAXCACHEDUSERCOUNT) {
uint64_t lastallowed = time(0) - CACHEDUSERCOUNTCLEANUPTIME;
auto it = usercache_timeout.begin();
auto itend = usercache_timeout.upper_bound(lastallowed);
while(it != itend) {
if(it->first == (uint64_t) me) {
++it;
continue; // never remove self from cache
}
auto itcopy = it;
++itcopy;
auto todeleteit = usercache.find(it->second);
removeUser(todeleteit);
it = itcopy;
}
}
}
void TGClient::requestMessages(int64_t chatid, int64_t from_message_id, std::function<void(int64_t)> onDone, std::function<void(td_api::object_ptr<td_api::message>)> forMessage, bool recurse) {
Log::debug << "requestMessages " << chatid << " from: " << from_message_id;
send_wrappedquery<td_api::messages>(td_api::make_object<td_api::getChatHistory>(chatid, from_message_id, 0, 100, false), [this, chatid, onDone, forMessage, recurse](objptr<td_api::messages> m) {
//request next chunk
td_api::array<td_api::object_ptr<td_api::message>>& arr = m->messages_;
Log::trace << "got " << arr.size() << " messages";
if(arr.size() == 0) {
// indexing done
Log::info << "Chatindex done";
if(onDone) {
onDone(chatid);
}
return;
}
//issue next request bevore processing -> requires reverse iteration to find the last message id
int64_t smallestid = std::numeric_limits<int64_t>::max();
for(td_api::array<td_api::object_ptr<td_api::message>>::const_reverse_iterator it = arr.rbegin(); it != arr.rend(); ++it) {
const td_api::object_ptr<td_api::message>& obj = *it;
if(obj) {
int64_t id = obj->id_;
if(id < smallestid) {
smallestid = id;
break; //the first element should be good, because they should be ordered
}
}
}
//start next request
if(shouldrun && smallestid != std::numeric_limits<int64_t>::max() && recurse) {
requestMessages(chatid, smallestid, onDone, forMessage);
}
//process the messages
if(forMessage) {
for(auto it = arr.begin(); it != arr.end(); ++it) {
if(*it) {
forMessage(std::move(*it));
}
}
}
if(!recurse && onDone) {
onDone(chatid);
}
});
}
void TGClient::setOption(const std::string& name, objptr<td_api::OptionValue> val) {
client_manager_->execute(td_api::make_object<td_api::setOption>(name, std::move(val)));
}
void TGClient::setOptions() {
setOption("disable_persistent_network_statistics", td_api::make_object<td_api::optionValueBoolean>(true));
setOption("disable_sent_scheduled_message_notifications", td_api::make_object<td_api::optionValueBoolean>(true));
setOption("disable_time_adjustment_protection", td_api::make_object<td_api::optionValueBoolean>(true));
setOption("disable_top_chats", td_api::make_object<td_api::optionValueBoolean>(true));
setOption("ignore_background_updates", td_api::make_object<td_api::optionValueBoolean>(false));
setOption("ignore_inline_thumbnails", td_api::make_object<td_api::optionValueBoolean>(true));
setOption("ignore_platform_restrictions", td_api::make_object<td_api::optionValueBoolean>(true));
}
void TGClient::loadInit() {
send_wrappedquery<td_api::user>(td_api::make_object<td_api::getMe>(), [this](objptr<td_api::user> meu) {
CachedUser cachedme;
convertUser(*meu, cachedme);
me = cachedme.tgid;
addUser(cachedme);
//load chat list
td_api::object_ptr<td_api::ChatList> mainlist = td_api::make_object<td_api::chatListMain>();
send_staticquery(td_api::make_object<td_api::getChats>(std::move(mainlist), std::numeric_limits<int32_t>::max()), 2);
});
}
void TGClient::loadInitInternalCallback(Object o) {
(void) o;
Log::note << "init done, chats loaded";
if(initDoneCallback)
initDoneCallback();
initDone = true;
}
void TGClient::restart() {
client_manager_.reset();
authorization_state_.reset();
(this)->~TGClient();
new (this) TGClient();
}
void TGClient::send_query(td_api::object_ptr<td_api::Function> f, std::function<void(Object)> handler) {
auto query_id = next_query_id();
if (handler) {
handlers_.emplace(query_id, std::move(handler));
}
client_manager_->send(client_id_, query_id, std::move(f));
}
void TGClient::send_staticquery(td_api::object_ptr<td_api::Function> f, uint64_t handlerid) {
assert(handlerid < STATICHANDLERCOUNT);
client_manager_->send(client_id_, handlerid, std::move(f));
}
template<typename T>
void TGClient::send_wrappedquery(td_api::object_ptr<td_api::Function> f, std::function<void(td::tl_object_ptr<T>)> handler, std::function<void()> onError, bool printError) {
send_query(std::move(f), [handler, onError, printError](Object o) {
if(catchErrors<T>(o, onError, printError) && handler) {
auto casted = td::move_tl_object_as<T>(o);
handler(std::move(casted));
}
});
}
template<typename T>
bool TGClient::catchErrors(const Object& o, std::function<void()> onError, bool printError) {
if(!o) return false;
if(o->get_id() == td_api::error::ID) {
if(printError) {
const objptr<td_api::error>& err = (const objptr<td_api::error>&) o;
Log::warn << "error: " << err->code_ << " " << err->message_;
}
if(onError)
onError();
return false;
}
if(T::ID != Placeholder::ID && o->get_id() != T::ID) {
if(printError)
Log::warn << "function did not return required type: returned_id: " << o->get_id() << " required_id: " << T::ID;
if(onError)
onError();
return false;
}
return true;
}
template<typename RequestType, typename ResponseType, typename... Args>
void TGClient::send_inplace(Args... args, std::function<void(td::tl_object_ptr<ResponseType>)> handler, bool printError) {
send_wrappedquery<ResponseType>(td_api::make_object<RequestType>(std::forward<Args>(args)...), handler, printError);
}
void TGClient::process_response(td::ClientManager::Response response) {
if (!response.object) {
return;
}
// update
if (response.request_id == 0) {
return process_update(std::move(response.object));
}
if(response.request_id < STATICHANDLERCOUNT) {
auto handler = STATICHANDLERS[response.request_id];
if(!catchErrors<Placeholder>(response.object) || !handler) return; // nullptr handler
(this->*handler)(std::move(response.object));
return;
}
auto it = handlers_.find(response.request_id);
if (it != handlers_.end()) {
it->second(std::move(response.object));
handlers_.erase(it);
}
}
void TGClient::process_update(td_api::object_ptr<td_api::Object> update) {
td_api::downcast_call(
*update, overloaded(
[this](td_api::updateAuthorizationState& update_authorization_state) {
authorization_state_ = std::move(update_authorization_state.authorization_state_);
on_authorization_state_update();
},
[this](td_api::updateNewMessage& update_new_message) {
if(messageCallback) {
messageCallback(std::move(update_new_message.message_));
}
},
[this](td_api::updateUserStatus& updateStatus) {
if(statusCallback)
statusCallback(updateStatus);
},
[this](td_api::updateChatDraftMessage& draft) {
if(draftCallback) {
const objptr<td_api::draftMessage>& draftmsg = draft.draft_message_;
std::string text;
if(draftmsg) {
const objptr<td_api::InputMessageContent>& imc = draftmsg->input_message_text_;
if(imc->get_id() == td_api::inputMessageText::ID) {
text = ((const td_api::inputMessageText&) *imc).text_->text_;
}
}
draftCallback(draft.chat_id_, text);
}
},
[this](td_api::updateDeleteMessages& dele) {
if(deleteCallback && dele.is_permanent_ && !dele.from_cache_) {
int64_t chatid = dele.chat_id_;
const std::vector<int64_t>& list = dele.message_ids_;
//trigger callback
for(int64_t msgid : list) {
deleteCallback(chatid, msgid);
}
}
},
[this](td_api::updateMessageEdited& edit) {
if(editMessageCallback) {
//request full message
send_wrappedquery<td_api::message>(td_api::make_object<td_api::getMessage>(edit.chat_id_, edit.message_id_), editMessageCallback);
}
},
[this](td_api::updateChatAction& action) {
if(actionCallback) {
int64_t chatid = action.chat_id_, userid = action.chat_id_;
ChatAction::ChatAction act = ChatAction::getAction(action.action_->get_id());
actionCallback(chatid, userid, act);
}
},
[this](td_api::updateUser& user) {
if(userupdCallback) {
const td_api::user& ou = *user.user_;
CachedUser u;
convertUser(ou, u);
//cache user
addUser(u);
//trigger event
userupdCallback(u);
}
},
[this](td_api::updateFile& fileupd) {
if(fileUpdateCallback) {
fileUpdateCallback(std::move(fileupd.file_));
}
},
[this](td_api::updateNewChat& newChat) {
if(newChatCallback) {
newChatCallback(std::move(newChat.chat_));
}
},
[this](td_api::updateChatFolders& chatFilters) {
if(chatFiltersCallback) {
std::map<int32_t, std::string> out;
auto& arr = chatFilters.chat_folders_;
for(auto& it : arr) {
out[it->id_] = it->title_;
}
chatFiltersCallback(out);
}
},
[](auto& u) {})); //default
}
auto TGClient::create_authentication_query_handler() {
return [this, id = authentication_query_id_](Object object) {
if (id == authentication_query_id_) {
check_authentication_error(std::move(object));
}
};
}
void TGClient::on_authorization_state_update() {
Log::trace << "update auth state " << authorization_state_->get_id();
authentication_query_id_++;
td_api::downcast_call(
*authorization_state_,
overloaded(
[this](td_api::authorizationStateReady&) {
are_authorized_ = true;
Log::note << "Got authorization";
loadInit();
},
[this](td_api::authorizationStateLoggingOut&) {
are_authorized_ = false;
std::cout << "Logging out" << std::endl;
},
[this](td_api::authorizationStateClosing&) { std::cout << "Closing" << std::endl; },
[this](td_api::authorizationStateClosed&) {
are_authorized_ = false;
need_restart_ = true;
std::cout << "Terminated" << std::endl;
},
[this](td_api::authorizationStateWaitCode&) {
if(!getAuthCodeCallback) {
Log::error << "no authCode Callback registered";
return;
}
std::string code = getAuthCodeCallback();
send_query(td_api::make_object<td_api::checkAuthenticationCode>(code),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitRegistration&) {
std::string first_name;
std::string last_name;
std::cout << "Enter your first name: " << std::flush;
std::cin >> first_name;
std::cout << "Enter your last name: " << std::flush;
std::cin >> last_name;
send_query(td_api::make_object<td_api::registerUser>(first_name, last_name),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitPassword&) {
send_query(td_api::make_object<td_api::checkAuthenticationPassword>(Config::config.tgCloudPassword),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitOtherDeviceConfirmation& state) {
std::cout << "Confirm this login link on another device: " << state.link_ << std::endl;
},
[this](td_api::authorizationStateWaitPhoneNumber&) {
if(Config::config.phoneNumber.empty()) {
Log::fatal << "empty phone number";
return;
}
send_query(td_api::make_object<td_api::setAuthenticationPhoneNumber>(Config::config.phoneNumber, nullptr),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitEmailAddress&) {
send_query(td_api::make_object<td_api::setAuthenticationEmailAddress>(""),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitEmailCode&) {
send_query(td_api::make_object<td_api::checkAuthenticationCode>(""),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitTdlibParameters&) {
auto parameters = td_api::make_object<td_api::setTdlibParameters>();
parameters->database_directory_ = "tdlib";
parameters->use_test_dc_ = false;
parameters->use_file_database_ = true;
parameters->use_chat_info_database_ = true;
parameters->use_message_database_ = true;
parameters->use_secret_chats_ = false;
parameters->ignore_file_names_ = false;
parameters->api_id_ = Config::config.apiid;
parameters->api_hash_ = Config::config.apihash;
Log::info << "using api: " << Config::config.apiid << " " << Config::config.apihash;
parameters->system_language_code_ = "en";
parameters->device_model_ = "Desktop";
parameters->application_version_ = "1.0";
parameters->enable_storage_optimizer_ = true;
send_query(std::move(parameters),
create_authentication_query_handler());
setOptions();
}));
}
void TGClient::check_authentication_error(Object object) {
if (object->get_id() == td_api::error::ID) {
auto error = td::move_tl_object_as<td_api::error>(object);
std::cout << "Error: " << to_string(error) << std::flush;
on_authorization_state_update();
}
}
std::uint64_t TGClient::next_query_id() {
return ++current_query_id_;
}