769 lines
25 KiB
C++
769 lines
25 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::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::sendTextMessageToSelf(const std::string& text) {
|
|
auto sendmsg = td::make_tl_object<td_api::sendMessage>();
|
|
sendmsg->chat_id_ = me;
|
|
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::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) {
|
|
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](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()) {
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
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_;
|
|
} |