implement inline mode
This commit is contained in:
parent
f049304b1d
commit
910a2e6bd9
115
commands.cpp
115
commands.cpp
@ -1,11 +1,17 @@
|
|||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
|
#include "common.h"
|
||||||
#include "curl/curl.h"
|
#include "curl/curl.h"
|
||||||
#include "curl/easy.h"
|
#include "curl/easy.h"
|
||||||
#include "td/telegram/td_api.h"
|
#include "td/telegram/td_api.h"
|
||||||
|
#include "td/tl/TlObject.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <random>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
void cmd::handle_regular_message(context *ctx, td_api::message &msg) {
|
void cmd::handle_regular_message(context *ctx, td_api::message &msg) {
|
||||||
@ -51,6 +57,109 @@ void cmd::handle_regular_message(context *ctx, td_api::message &msg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmd::handle_inline_query(context *ctx, td_api::updateNewInlineQuery &query) {
|
||||||
|
// TODO check URL validity
|
||||||
|
|
||||||
|
uint64_t pendingQueryId = new_pending_query_id();
|
||||||
|
ctx->inlineQueries.insert(std::pair<uint64_t, pending_inline_query>(pendingQueryId, {query.query_, std::chrono::steady_clock::now() + std::chrono::hours(5)}));
|
||||||
|
|
||||||
|
std::vector<td_api::object_ptr<td_api::InputInlineQueryResult>> results;
|
||||||
|
results.reserve(1);
|
||||||
|
auto messageButton = td_api::make_object<td_api::inlineKeyboardButton>(
|
||||||
|
"press to shorten",
|
||||||
|
static_cast<td_api::object_ptr<td_api::InlineKeyboardButtonType>>(td_api::make_object<td_api::inlineKeyboardButtonTypeCallback>(std::to_string(pendingQueryId)))
|
||||||
|
);
|
||||||
|
|
||||||
|
std::vector<decltype(messageButton)> messageButtonRow;
|
||||||
|
messageButtonRow.push_back(std::move(messageButton));
|
||||||
|
|
||||||
|
std::vector<decltype(messageButtonRow)> messageButtonRows;
|
||||||
|
messageButtonRows.push_back(std::move(messageButtonRow));
|
||||||
|
|
||||||
|
results.push_back(static_cast<td_api::object_ptr<td_api::InputInlineQueryResult>>(td_api::make_object<td_api::inputInlineQueryResultArticle>(
|
||||||
|
"shorten",
|
||||||
|
"",
|
||||||
|
true, // hide_url
|
||||||
|
"Shorten!",
|
||||||
|
"...",
|
||||||
|
"https://slavasil.ru/favicon.ico",
|
||||||
|
48, 48,
|
||||||
|
static_cast<td_api::object_ptr<td_api::ReplyMarkup>>(td_api::make_object<td_api::replyMarkupInlineKeyboard>(
|
||||||
|
std::move(messageButtonRows)
|
||||||
|
)),
|
||||||
|
static_cast<td_api::object_ptr<td_api::InputMessageContent>>(td_api::make_object<td_api::inputMessageText>(
|
||||||
|
td_api::make_object<td_api::formattedText>(
|
||||||
|
query.query_,
|
||||||
|
std::move(std::vector<td_api::object_ptr<td_api::textEntity>>())
|
||||||
|
),
|
||||||
|
td_api::make_object<td_api::linkPreviewOptions>(true, "", false, false, false),
|
||||||
|
false // clear_draft
|
||||||
|
))
|
||||||
|
)));
|
||||||
|
ctx->tg->send_query(td_api::make_object<td_api::answerInlineQuery>(
|
||||||
|
query.id_,
|
||||||
|
false, // is_personal
|
||||||
|
nullptr, // button
|
||||||
|
std::move(results),
|
||||||
|
600, // cache_time
|
||||||
|
"" // next_offset
|
||||||
|
), {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd::handle_callback_query(context *ctx, td_api::updateNewInlineCallbackQuery &query) {
|
||||||
|
spdlog::info("callback query");
|
||||||
|
if (query.payload_->get_id() != td_api::callbackQueryPayloadData::ID) return;
|
||||||
|
|
||||||
|
auto payload = td::move_tl_object_as<td_api::callbackQueryPayloadData>(query.payload_);
|
||||||
|
ctx->tg->send_query(td_api::make_object<td_api::answerCallbackQuery>(
|
||||||
|
query.id_,
|
||||||
|
payload->data_,
|
||||||
|
false,
|
||||||
|
"",
|
||||||
|
0
|
||||||
|
), [](TgObject obj){
|
||||||
|
if (obj->get_id() == td_api::error::ID) {
|
||||||
|
spdlog::error("answerCallbackQuery error: {}", static_cast<td_api::error*>(obj.get())->message_);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
uint64_t queryId = std::stoull(payload->data_);
|
||||||
|
spdlog::debug("is query in cache? {}", ctx->inlineQueries.contains(queryId));
|
||||||
|
try {
|
||||||
|
pending_inline_query &queryInfo = ctx->inlineQueries.at(queryId);
|
||||||
|
shorten_link(queryInfo.text, ctx, [ctx, inlineMessageId = query.inline_message_id_](std::string url){
|
||||||
|
ctx->tg->send_query(td_api::make_object<td_api::editInlineMessageText>(
|
||||||
|
inlineMessageId,
|
||||||
|
nullptr, // reply_markup
|
||||||
|
static_cast<td_api::object_ptr<td_api::InputMessageContent>>(td_api::make_object<td_api::inputMessageText>(
|
||||||
|
td_api::make_object<td_api::formattedText>(
|
||||||
|
url,
|
||||||
|
std::move(std::vector<td_api::object_ptr<td_api::textEntity>>())
|
||||||
|
),
|
||||||
|
td_api::make_object<td_api::linkPreviewOptions>(true, "", false, false, false),
|
||||||
|
//nullptr,
|
||||||
|
false
|
||||||
|
))
|
||||||
|
), {});
|
||||||
|
});
|
||||||
|
ctx->inlineQueries.erase(queryId);
|
||||||
|
} catch (std::out_of_range) {
|
||||||
|
spdlog::warn("a user tried to use an expired pending query");
|
||||||
|
ctx->tg->send_query(td_api::make_object<td_api::editInlineMessageText>(
|
||||||
|
query.inline_message_id_,
|
||||||
|
nullptr, // reply_markup
|
||||||
|
static_cast<td_api::object_ptr<td_api::InputMessageContent>>(td_api::make_object<td_api::inputMessageText>(
|
||||||
|
td_api::make_object<td_api::formattedText>(
|
||||||
|
"<сообщение устарело>",
|
||||||
|
std::move(std::vector<td_api::object_ptr<td_api::textEntity>>())
|
||||||
|
),
|
||||||
|
nullptr,
|
||||||
|
false
|
||||||
|
))
|
||||||
|
), {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool cmd::shorten_link(std::string link, context *ctx, std::function<void(std::string)> cb) {
|
bool cmd::shorten_link(std::string link, context *ctx, std::function<void(std::string)> cb) {
|
||||||
spdlog::debug("creating CURL request");
|
spdlog::debug("creating CURL request");
|
||||||
CURL *req = curl_easy_init();
|
CURL *req = curl_easy_init();
|
||||||
@ -75,3 +184,9 @@ bool cmd::shorten_link(std::string link, context *ctx, std::function<void(std::s
|
|||||||
CURLMcode r = curl_multi_add_handle(ctx->curl, req);
|
CURLMcode r = curl_multi_add_handle(ctx->curl, req);
|
||||||
return r == CURLM_OK;
|
return r == CURLM_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t cmd::new_pending_query_id() {
|
||||||
|
static std::mt19937 rng;
|
||||||
|
static std::uniform_int_distribution<uint64_t> dist(0, 0xFFFFFFFFFFFFFFFFULL);
|
||||||
|
return dist(rng);
|
||||||
|
}
|
@ -6,5 +6,9 @@
|
|||||||
|
|
||||||
namespace cmd {
|
namespace cmd {
|
||||||
void handle_regular_message(context *ctx, td_api::message &msg);
|
void handle_regular_message(context *ctx, td_api::message &msg);
|
||||||
|
void handle_inline_query(context *ctx, td_api::updateNewInlineQuery &query);
|
||||||
|
void handle_callback_query(context *ctx, td_api::updateNewInlineCallbackQuery &query);
|
||||||
bool shorten_link(std::string link, context *ctx, std::function<void(std::string)> cb);
|
bool shorten_link(std::string link, context *ctx, std::function<void(std::string)> cb);
|
||||||
|
|
||||||
|
uint64_t new_pending_query_id();
|
||||||
}
|
}
|
7
common.h
7
common.h
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "curl/multi.h"
|
#include "curl/multi.h"
|
||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -55,12 +56,18 @@ struct active_request {
|
|||||||
active_request(std::function<void(active_request&)> doneCallback): doneCallback(doneCallback) {}
|
active_request(std::function<void(active_request&)> doneCallback): doneCallback(doneCallback) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct pending_inline_query {
|
||||||
|
std::string text;
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> ttl;
|
||||||
|
};
|
||||||
|
|
||||||
struct context {
|
struct context {
|
||||||
TelegramClient *tg;
|
TelegramClient *tg;
|
||||||
ApiCreds apiCreds;
|
ApiCreds apiCreds;
|
||||||
CURLM *curl;
|
CURLM *curl;
|
||||||
uv_timer_t curlTimer;
|
uv_timer_t curlTimer;
|
||||||
std::map<CURL*, active_request> requests;
|
std::map<CURL*, active_request> requests;
|
||||||
|
std::map<uint64_t, pending_inline_query> inlineQueries;
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t curl_receive_cb(char *ptr, size_t size, size_t nmemb, CURL *ctx);
|
size_t curl_receive_cb(char *ptr, size_t size, size_t nmemb, CURL *ctx);
|
32
main.cpp
32
main.cpp
@ -1,8 +1,11 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <sys/select.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -31,6 +34,7 @@ int curl_timeout_cb(CURLM*, long, context*);
|
|||||||
void uv_socket_cb(uv_poll_t *handle, int status, int events);
|
void uv_socket_cb(uv_poll_t *handle, int status, int events);
|
||||||
void uv_curl_timeout_cb(uv_timer_t *handle);
|
void uv_curl_timeout_cb(uv_timer_t *handle);
|
||||||
void check_curl_multi_info(context *ctx);
|
void check_curl_multi_info(context *ctx);
|
||||||
|
void gc_pending_queries(uv_timer_t *handle);
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
CommandLineParams cmdLineParams;
|
CommandLineParams cmdLineParams;
|
||||||
@ -72,6 +76,11 @@ int main(int argc, char **argv) {
|
|||||||
uv_idle_init(defaultLoop, &startupHandle);
|
uv_idle_init(defaultLoop, &startupHandle);
|
||||||
uv_idle_start(&startupHandle, async_startup);
|
uv_idle_start(&startupHandle, async_startup);
|
||||||
|
|
||||||
|
uv_timer_t pendingQueryGcTimer;
|
||||||
|
uv_timer_init(defaultLoop, &pendingQueryGcTimer);
|
||||||
|
pendingQueryGcTimer.data = &ctx;
|
||||||
|
uv_timer_start(&pendingQueryGcTimer, gc_pending_queries, 60000, 60000);
|
||||||
|
|
||||||
auto signalHandles = configure_shutdown_signals(defaultLoop, &ctx);
|
auto signalHandles = configure_shutdown_signals(defaultLoop, &ctx);
|
||||||
|
|
||||||
termios termSettings;
|
termios termSettings;
|
||||||
@ -128,7 +137,10 @@ void async_startup(uv_idle_t *h) {
|
|||||||
cmd::handle_regular_message(ctx, *upd.message_);
|
cmd::handle_regular_message(ctx, *upd.message_);
|
||||||
},
|
},
|
||||||
[ctx](td_api::updateNewInlineQuery &upd) {
|
[ctx](td_api::updateNewInlineQuery &upd) {
|
||||||
|
cmd::handle_inline_query(ctx, upd);
|
||||||
|
},
|
||||||
|
[ctx](td_api::updateNewInlineCallbackQuery &upd) {
|
||||||
|
cmd::handle_callback_query(ctx, upd);
|
||||||
},
|
},
|
||||||
[](td_api::Object &obj){}
|
[](td_api::Object &obj){}
|
||||||
));
|
));
|
||||||
@ -307,3 +319,21 @@ size_t curl_receive_cb(char *ptr, size_t size, size_t nmemb, CURL *curl) {
|
|||||||
|
|
||||||
return nmemb;
|
return nmemb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gc_pending_queries(uv_timer_t *handle) {
|
||||||
|
context *ctx = reinterpret_cast<context*>(handle->data);
|
||||||
|
|
||||||
|
std::map<uint64_t, pending_inline_query> oldQueries(ctx->inlineQueries);
|
||||||
|
std::map<uint64_t, pending_inline_query> newQueries;
|
||||||
|
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
for (auto i = oldQueries.begin(); i != oldQueries.end(); i++) {
|
||||||
|
if (i->second.ttl > now) {
|
||||||
|
newQueries.insert(*i);
|
||||||
|
} else {
|
||||||
|
spdlog::debug("deleting a pending query {} that expired {} seconds ago", i->first, std::chrono::duration_cast<std::chrono::seconds>(now - i->second.ttl).count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->inlineQueries = newQueries;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user