Compare commits

..

206 Commits

Author SHA1 Message Date
Andrea Cavalli
6f785fd26b Fix crash 2024-02-16 15:24:05 +01:00
Andrea Cavalli
0bc23e3830 Add custom deleteMessages implementation 2024-02-11 12:55:43 +01:00
Andrea Cavalli
dd075ae89f Add custom deleteMessages implementation 2024-02-11 12:53:28 +01:00
Andrea Cavalli
faa723716f Fix compiler warnings 2024-02-11 12:26:52 +01:00
Andrea Cavalli
ffcc85392c
Merge pull request #85 from davidgfnet/master
Rebase/merge upstream @ version 7.0
2024-02-11 12:16:02 +01:00
David Guillen Fandos
d2415a9191 Update and rebase to version 7.0
Fix a bunch of small issues here and there related to new API calls.
Custom DeleteMessages (bulk) has been deleted in favour of the upstream
implementation that allows deleting a list of messages (instead of a
range).

Documentation might need a redo :)
2024-02-03 12:34:51 +01:00
levlam
1bf69f7abb Update version to 7.0. 2024-02-03 11:45:30 +01:00
levlam
a38d9260af Add Message.users_shared field. 2024-02-03 11:45:30 +01:00
levlam
baa0546c31 Support request_users.max_quantity. 2024-02-03 11:45:30 +01:00
David Guillen Fandos
d3300e9ba3 Rebase: Support channel emoji status. 2024-02-03 11:45:24 +01:00
levlam
f19e58645e Add setMessageReaction method. 2024-02-03 11:43:56 +01:00
levlam
50bb07bc46 Add "message_reaction_count" update. 2024-02-03 11:43:56 +01:00
levlam
668ea399eb Add "message_reaction" updates. 2024-02-03 11:43:56 +01:00
levlam
fa489a4979 Add Message.giveaway_winners. 2024-02-03 11:43:56 +01:00
levlam
9aedc15f76 Add "has_public_winners" and "prize_description" giveaway fields. 2024-02-03 11:43:56 +01:00
levlam
7575257ca6 Add fields chat.profile_accent_color_id and chat.profile_background_custom_emoji_id. 2024-02-03 11:43:55 +01:00
levlam
7d84c0a0d8 Update TDLib to 1.8.23. 2024-02-03 11:43:55 +01:00
levlam
92b7a6a556 Add Chat.available_reactions. 2024-02-03 11:43:55 +01:00
levlam
81b2c0e550 Add copyMessages method. 2024-02-03 11:43:55 +01:00
levlam
9c1ecb749b Add forwardMessages method. 2024-02-03 11:43:55 +01:00
levlam
ce1474d5ab Add deleteMessages method. 2024-02-03 11:43:55 +01:00
levlam
3161f9a00b Store identifier of inaccessible pinned message. 2024-02-03 11:43:55 +01:00
levlam
e6e6cbc72f Add Message.giveaway_completed. 2024-02-03 11:43:55 +01:00
levlam
253d3acddc Support quote position in reply parameters. 2024-02-03 11:43:55 +01:00
levlam
5dce30bc20 Add class TextQuote. 2024-02-03 11:43:55 +01:00
levlam
aa0f369132 Update TDLib to 1.8.22. 2024-02-03 11:43:55 +01:00
levlam
de0d0ad75d Support chat in ReplyParameters. 2024-02-03 11:43:55 +01:00
David Guillen Fandos
94bfd307f8 Rebase: Support quote in ReplyParameters. 2024-02-03 11:43:50 +01:00
levlam
19a7da41b2 Improve error message. 2024-02-03 11:42:10 +01:00
David Guillen Fandos
e117cfa33f Rebase: Add class ReplyParameters and fields "reply_parameters". 2024-02-03 11:42:02 +01:00
David Guillen Fandos
1084ebd5c8 Rebase: Add Client::check_reply_parameters. 2024-02-03 11:37:40 +01:00
David Guillen Fandos
49df33acd7 Rebase: Simplify JsonChat usage. 2024-02-03 11:28:37 +01:00
levlam
4b13a450ae Add Message.quote and Message.quote_entities. 2024-02-03 11:24:11 +01:00
levlam
ab0f7878bb Add Message.external_reply. 2024-02-03 11:24:11 +01:00
levlam
a7f7cd0a7d Add Message.forward_origin. 2024-02-03 11:24:11 +01:00
levlam
05d9cd05b7 Store td_api::MessageOrigin in MessageInfo. 2024-02-03 11:24:11 +01:00
levlam
c3999a2144 Add message.link_preview_options. 2024-02-03 11:24:11 +01:00
levlam
ad84bfc214 Allow to specify link preview options for sent text messages. 2024-02-03 11:24:11 +01:00
levlam
34f9b8a860 Add getUserChatBoosts. 2024-02-03 11:24:11 +01:00
David Guillen Fandos
121e2d8a18 Rebase: Add "chat_boost" and "removed_chat_boost" updates. 2024-02-03 11:23:59 +01:00
levlam
d02a9fe5c3 Add Chat.background_custom_emoji_id. 2024-02-03 11:21:08 +01:00
levlam
a2a226ac42 Add Chat.accent_color_id. 2024-02-03 11:21:08 +01:00
levlam
0da8d14430 Add Chat.has_visible_history. 2024-02-03 11:21:08 +01:00
David Guillen Fandos
d7d127430f Rebase: Simplify update*FullInfo handling. 2024-02-03 11:21:00 +01:00
levlam
c57bb6830b Add Message.giveaway. 2024-02-03 11:17:36 +01:00
levlam
80406b7028 Add Message.giveaway_created. 2024-02-03 11:17:36 +01:00
levlam
8fe04fc33a Support td_api::textEntityTypeBlockQuote. 2024-02-03 11:17:36 +01:00
levlam
1a34273163 Add Client::get_same_chat_reply_to_message_id(const MessageInfo *message_info). 2024-02-03 11:17:36 +01:00
Andrea Cavalli
8990b79e9e
Merge pull request #84 from a5r0n/official-upstream-update
Official upstream update
2023-12-17 19:49:10 +01:00
a5r0n
a35ff4543b
fix merge conflicts 2023-12-16 22:29:04 +02:00
Andrea Cavalli
b3eb1acc91
Merge pull request #83 from a5r0n/fix/no-file-limit
fix no_file_limit typo
2023-12-16 20:58:44 +01:00
a5r0n
c24c0a2dae
Merge remote-tracking branch 'official-upstream/master' into official-upstream-update 2023-12-15 15:16:35 +02:00
a5r0n
bf66a41987
fix: fix no_file_limit typo 2023-12-15 11:58:46 +02:00
levlam
96d0d1c668 Update version to 6.9.2. 2023-11-09 02:32:21 +03:00
levlam
0566e21f93 Keep reply to the top thread message for external replies. 2023-11-05 22:08:06 +03:00
levlam
34ed6c3512 Store td_api::messageReplyToMessage in MessageInfo. 2023-11-05 21:54:58 +03:00
levlam
9447ce07ea Minor improvements. 2023-11-04 02:39:57 +03:00
levlam
f169ae654c Slowly recheck webhook IP addresses after loading them from database. 2023-11-01 23:01:08 +03:00
levlam
9a7a293a84 Update TDLib to 1.8.21. 2023-10-31 03:10:35 +03:00
levlam
d836f78e41 Log skipped updates. 2023-10-23 11:53:20 +03:00
levlam
f15bc7396e Update TDLib to 1.8.20. 2023-10-13 01:15:42 +03:00
levlam
9c413c7f11 Maintain last time when a file was uploaded for all requests. 2023-09-25 19:39:51 +03:00
levlam
5d88023dd1 Update TDLib and version to 6.9.1. 2023-09-23 16:39:14 +03:00
levlam
9e7b09ff0a Update version to 6.9. 2023-09-22 16:23:26 +03:00
levlam
d5783a1545 Add more fields to WriteAccessAllowed. 2023-09-19 20:37:10 +03:00
levlam
2b43e08dca Support "can_post_stories", "can_edit_stories" and "can_delete_stories" administrator rights. 2023-09-19 19:37:24 +03:00
levlam
11d19baa2e Update TDLib to 1.8.19. 2023-09-19 19:26:43 +03:00
levlam
df1fe4c05f Fail request early if message/caption/explanation text is too long. 2023-09-14 19:11:12 +03:00
levlam
1ec733a3f8 Don't update CPU statistics before returning it to avoid synchronous open of "/proc/stat". 2023-09-14 16:57:28 +03:00
levlam
4e8ba65838 Update TDLib to 1.8.18. 2023-09-13 23:14:57 +03:00
levlam
e58e8d3989 Improve query logging. 2023-09-08 18:09:08 +03:00
levlam
95ff757c73 Immediately return an error if more than 50 inline query results are provided. 2023-09-06 18:48:41 +03:00
levlam
70670d7217 Explicitly disallow message updates with "channel_chat_created" content. 2023-09-06 17:06:21 +03:00
levlam
87cdeaadb6 Update TDLib to 1.8.17. 2023-09-06 16:17:47 +03:00
levlam
18b5f287f7 Update CPU statistics on a dedicated thread. 2023-09-03 01:03:53 +03:00
levlam
1cab23c1f1 Improve ServerCpuStat. 2023-09-03 00:55:10 +03:00
levlam
89383695ed Make watchdog timeouts more precise. 2023-08-31 22:51:07 +03:00
levlam
8b2b62bd6f Update version to 6.8. 2023-08-18 18:39:30 +03:00
levlam
375b5d1b7c Update TDLib to 1.8.16. 2023-08-17 00:34:34 +03:00
levlam
980f98299f Improve logging of big queries. 2023-08-08 18:00:24 +03:00
levlam
0868ee6beb Simplify reply markup parsing. 2023-07-31 20:01:53 +03:00
levlam
a78edf0703 Use JsonObject member functions to get field values. 2023-07-31 17:53:56 +03:00
levlam
2bbaf87fea Use get_json_object_long_field to fetch "amount". 2023-07-31 14:02:50 +03:00
levlam
9f688af4fb Add dedicated threads for TQueue and webhook databases and webhook certificate processing. 2023-07-25 22:32:05 +03:00
levlam
c927614964 Improve threads usage. 2023-07-25 22:26:12 +03:00
levlam
ec8e44de5a Improve warnings for old updates. 2023-07-24 21:41:15 +03:00
levlam
f4422f5976 Add unpinAllGeneralForumTopicMessages. 2023-07-24 17:19:14 +03:00
levlam
afd30f2cfa Support messageStory as empty objects. 2023-07-24 16:14:03 +03:00
levlam
51fba26f78 Add Chat.emoji_status_expiration_date. 2023-07-21 13:33:00 +03:00
levlam
736411c113 Update TDLib to 1.8.15 and support votes by chats in polls. 2023-07-20 17:06:32 +03:00
levlam
1fa5c2c31a Improve processing of new messages. 2023-07-20 16:58:10 +03:00
levlam
9ce2f7df4c Don't drop replies to deleted messages. 2023-07-20 16:30:46 +03:00
levlam
68dc4f54a5 Don't track replies by yet unsent messages. 2023-07-20 16:25:42 +03:00
levlam
c8e50b8011 Improve replies handling. 2023-07-19 23:54:47 +03:00
levlam
a9a0140476 Keep last time when a file was uploaded. 2023-07-06 14:47:31 +03:00
levlam
84e512c2e4 Make Client::get_reply_markup static. 2023-07-06 14:01:14 +03:00
levlam
d9c00c452b Use bot identifier as token for webhook requests. 2023-06-28 20:55:38 +03:00
giuseppeM99
e18ecceee4
Merge pull request #79 from davidgfnet/master
Update to version 6.7.1
2023-05-05 11:40:29 +02:00
David Guillen Fandos
1c30dc8d3d Merge remote-tracking branch 'official-upstream/master' 2023-05-03 23:48:27 +02:00
levlam
2515892217 Update TDLib and version to 6.7.1. 2023-04-21 16:08:20 +03:00
levlam
c7253129b5 Update version to 6.7. 2023-04-21 13:21:58 +03:00
levlam
e7a61ce8f8 Add "switch_inline_query_chosen_chat" inline keyboard buttons. 2023-04-07 15:29:16 +03:00
levlam
9e87ac2bf9 Add ChatMemberUpdated.via_chat_folder_invite_link. 2023-04-07 14:55:58 +03:00
levlam
38a11d1e1f Add setMyName. 2023-04-07 14:52:10 +03:00
levlam
a894cace6b Add getMyName. 2023-04-07 14:48:34 +03:00
levlam
6561063f52 Update TDLib to 1.8.14. 2023-04-07 14:36:27 +03:00
levlam
0e5673f2dc Don't dump trace on Watchdog timeouts if log is disabled. 2023-03-27 19:05:23 +03:00
levlam
df53cfeb85 Update TDLib and version to 6.6.2. 2023-03-27 12:08:49 +03:00
levlam
8e00a8d41d Update version to 6.6.1. 2023-03-25 09:27:17 +03:00
levlam
10c5272497 Fix warning. 2023-03-23 20:18:32 +03:00
levlam
e9d32ad23d Add Query::get_peer_ip_address. 2023-03-13 18:42:35 +03:00
levlam
26854a6a3d Completely disable network statistics. 2023-03-13 17:40:07 +03:00
levlam
03bc114f93 Update TDLib to 1.8.13. 2023-03-13 17:28:01 +03:00
levlam
9bf5786bde Statically link libstdc++ and libgcc when memprof is enabled. 2023-03-12 22:39:48 +03:00
Giuseppe Marino
76e5967b72
Merge remote-tracking branch 'upstream/master' 2023-03-11 12:05:34 +01:00
levlam
c5b8e34cd3 Update version to 6.6. 2023-03-09 18:55:26 +03:00
levlam
79ea507664 Minor improvements. 2023-03-09 18:54:59 +03:00
levlam
8fd2a69378 Add setMyShortDescription. 2023-03-04 22:13:39 +03:00
levlam
8ee91caacf Add getMyShortDescription. 2023-03-04 22:12:34 +03:00
levlam
4df8df2f17 Improve BotStatActor::get_score. 2023-02-27 22:12:12 +03:00
levlam
ac28919390 Add getMyDescription. 2023-02-27 21:32:38 +03:00
levlam
f25e81c015 Add setMyDescription. 2023-02-27 02:19:10 +03:00
levlam
ab54061365 Add emoji to sendSticker. 2023-02-26 22:23:38 +03:00
levlam
1720ba3e81 Support "web_app" button in inline query results. 2023-02-22 21:01:05 +03:00
levlam
4dc418b8d3 Add optional web_app_name to WriteAccessAllowed. 2023-02-22 20:45:37 +03:00
levlam
7b2acc80be Avoid unneeded namespace qualification. 2023-02-22 19:50:53 +03:00
levlam
b81073cb1a Remove using of td::Json*. 2023-02-22 19:47:23 +03:00
levlam
f24dca312f Remove using for td::Status and td::Slice. 2023-02-22 19:40:25 +03:00
levlam
b38ce2a79d Add deleteStickerSet. 2023-02-16 16:27:58 +03:00
giuseppeM99
2ebec3893a
Update docker.yml 2023-02-15 20:55:51 +01:00
Giuseppe Marino
baf5cf1d29
Revert "Disable ghcr.io"
This reverts commit 3f27f6077f.
2023-02-15 13:23:11 +01:00
Giuseppe Marino
e0ff7b35c7
Revert "Attempt to fix docker workflow"
This reverts commit b60057560e.
2023-02-15 13:23:06 +01:00
Giuseppe Marino
b60057560e
Attempt to fix docker workflow 2023-02-14 22:14:39 +01:00
Giuseppe Marino
3f27f6077f
Disable ghcr.io 2023-02-14 22:10:30 +01:00
levlam
64591671fc Add getStickerMaskPosition. 2023-02-14 18:04:47 +03:00
levlam
116e7aab24 Add setStickerKeywords. 2023-02-14 17:54:59 +03:00
levlam
c48dfe5d4d Add setStickerEmojiList. 2023-02-14 17:43:08 +03:00
levlam
bb5f3651b2 Add Client::get_sticker_input_file. 2023-02-14 17:40:32 +03:00
levlam
401894a53b Add setStickerSetTitle. 2023-02-14 17:30:50 +03:00
levlam
0c7a7236eb Add setCustomEmojiStickerSetThumbnail. 2023-02-14 17:26:31 +03:00
levlam
84e6f5fa1d Support keywords in InputSticker. 2023-02-14 17:13:19 +03:00
levlam
d3898d9b7f Allow to preupload animated and video stickers. 2023-02-14 16:46:05 +03:00
levlam
a1d7c768bb Aloow to specify up to 50 stickers in createNewStickerSet. 2023-02-14 16:34:10 +03:00
levlam
d0b5abfc46 Support InputSticker in addStickerToSet. 2023-02-14 14:26:42 +03:00
levlam
1da3d34299 Add Sticker.needs_repainting. 2023-02-14 14:07:38 +03:00
levlam
3be3dfedf6 Add "needs_repainting" parameter in createNewStickerSet. 2023-02-14 13:37:27 +03:00
levlam
96e534bddc Rename "thumb" to "thumbnail". 2023-02-14 13:30:17 +03:00
levlam
0cf13d3bf4 Update TDLib to 1.8.12. 2023-02-13 17:25:33 +03:00
levlam
09c9db306a Fail flood-limited queries with a delay. 2023-02-13 15:52:15 +03:00
levlam
12c56318a8 Update clang-format to 16.0.0. 2023-02-13 15:44:46 +03:00
Giuseppe Marino
85763a1591
Merge remote-tracking branch 'upstream/master' 2023-02-04 15:31:24 +01:00
levlam
8180845429 Update version to 6.5. 2023-02-02 19:21:46 +03:00
levlam
0e6826b7e4 Add use_independent_chat_permissions to restrictChatMember and setChatPermissions. 2023-02-01 04:00:16 +03:00
levlam
0bc181c216 Add ChatJoinRequest.user_chat_id. 2023-02-01 03:46:27 +03:00
levlam
2c23688be6 Support separate media permissions. 2023-01-31 01:46:40 +03:00
levlam
d3846adaa2 Destroy HttpQuery on another thread. 2023-01-23 19:27:19 +03:00
levlam
70428fb762 Improve BotInfo for bots with invalid tokens. 2023-01-18 22:44:47 +03:00
levlam
82d592702d Support messageUserShared and messageChatShared. 2023-01-12 14:49:26 +03:00
levlam
c3c8f112ee Support "request_chat" keyboard buttons. 2023-01-11 20:50:43 +03:00
levlam
e636a6db45 Support "request_user" keyboard buttons. 2023-01-11 20:39:34 +03:00
levlam
c68f6a7a89 Update TDLib to 1.8.11. 2023-01-11 20:30:27 +03:00
levlam
a8f7f78fe1 Rendomize maximum webhook event delay. 2023-01-08 21:30:51 +03:00
levlam
1b741dd3b3 Update TDLib and asynchronously destroy deleted TQueue events. 2023-01-06 18:03:45 +03:00
levlam
452a190094 Reduce Watchdog timeout to 0.25. 2023-01-06 16:30:12 +03:00
levlam
75caf1b0e7 Log top bots in dump_statistics. 2023-01-06 16:29:09 +03:00
levlam
0dcec25d7e Add ClientManager::get_top_clients. 2023-01-06 15:52:53 +03:00
Giuseppe Marino
32275801c7
Merge remote-tracking branch 'upstream/master' 2023-01-01 13:43:25 +01:00
levlam
348b94bdee Update copyright year. 2023-01-01 00:31:16 +03:00
levlam
d091a730fb Update TDLib and version to 6.4.1. 2022-12-31 01:00:50 +03:00
Giuseppe Marino
8c99dfbf8c
Update login action 2022-12-30 21:56:51 +01:00
Giuseppe Marino
73c8efa528
Merge remote-tracking branch 'upstream/master'
Changes how searchChatMessages works, may break some code
2022-12-30 21:33:26 +01:00
levlam
c3b3c3033f Update version to 6.4. 2022-12-30 04:11:12 +03:00
levlam
c5ed576b1e Use public photo if full profile photo is unavailable. 2022-12-30 00:21:16 +03:00
levlam
fbc493c361 Add Chat.has_aggressive_anti_spam_enabled. 2022-12-29 21:11:42 +03:00
levlam
b972cebad4 Add Chat.has_hidden_members. 2022-12-29 21:06:38 +03:00
levlam
849d48e858 Add methods for General topic management. 2022-12-29 21:02:15 +03:00
levlam
9b47c90de9 Add Message.write_access_allowed. 2022-12-29 18:06:35 +03:00
levlam
d19413f8e6 Add Message.general_forum_topic_hidden/general_forum_topic_unhidden. 2022-12-29 17:53:55 +03:00
levlam
3704082f2a Add and use JsonEmptyObject. 2022-12-29 17:51:39 +03:00
levlam
38df7114f2 Add Message.forum_topic_edited. 2022-12-29 17:37:59 +03:00
levlam
28106d9df4 Support sending of media with spoiler. 2022-12-29 17:26:39 +03:00
levlam
30da51bc72 Dump statistics after stacktrace. 2022-12-29 16:59:38 +03:00
levlam
e5af2d3133 Add Message.has_media_spoiler field. 2022-12-29 16:56:32 +03:00
levlam
1fd451510f Add ReplyKeyboardMarkup.is_persistent field. 2022-12-29 00:54:24 +03:00
levlam
921f5c4b2e Update TDLib to 1.8.10. 2022-12-27 00:52:14 +03:00
levlam
b6f135b4c0 Add message_thread_id parameter to sendChatAction. 2022-12-15 20:05:22 +03:00
levlam
aec312006e Update version to 6.3.3. 2022-12-12 19:36:40 +03:00
levlam
5166914162 Improve authorization errors handling. 2022-12-12 19:33:35 +03:00
levlam
ed9f836977 Add and use Client::get_closing_error(). 2022-12-12 17:57:22 +03:00
levlam
9aeb8135e0 Use fail_query_closing in Client::on_closed. 2022-12-12 14:05:37 +03:00
levlam
66b5d8aa07 Add Client::get_retry_after_time helper. 2022-12-11 23:26:23 +03:00
levlam
a497eb5fcd Create SSL context asynchronously. 2022-12-11 17:17:25 +03:00
levlam
b005f42b2e Improve error messages. 2022-12-05 00:00:08 +03:00
levlam
1c0774b1a9 Log number of flood-limited requests. 2022-11-30 17:37:35 +03:00
levlam
48a5609de2 Improve limit for active queries. 2022-11-30 17:30:28 +03:00
levlam
ed0532bcf7 Use destroy_on_scheduler in WebhookActor. 2022-11-27 01:31:00 +03:00
levlam
a016d13c15 Use td::Hash instead of std::hash. 2022-11-23 23:43:05 +03:00
levlam
428f438e4f Avoid speculative message deletion from cache. 2022-11-23 14:53:43 +03:00
levlam
1d5dc9e5e8 Check for MAX_CONCURRENTLY_SENT_CHAT_MESSAGES as early as possible. 2022-11-22 21:31:04 +03:00
levlam
c8405e95f3 Improve HttpOutboundConnection parameters. 2022-11-22 16:44:30 +03:00
levlam
9842d5754f Improve flood control for new webhook connections. 2022-11-22 14:25:52 +03:00
levlam
53aa4d2f03 Reget poll messages from TDLib. 2022-11-22 13:34:16 +03:00
levlam
ce6ddc74d7 Limit the number of simultaneously uploaded files. 2022-11-14 15:35:43 +03:00
levlam
c833612414 Output log tag before stack trace. 2022-11-12 11:15:33 +03:00
levlam
84ceee07bf Use TQueue::clear. 2022-11-12 10:44:00 +03:00
levlam
a9cb897af4 Update TDLib to 1.8.9 and add SslCtx caching. 2022-11-11 15:37:07 +03:00
26 changed files with 4944 additions and 2407 deletions

View File

@ -4,15 +4,36 @@ Language: Cpp
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments: true
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
@ -25,6 +46,8 @@ AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
# AttributeMacros:
# - __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
@ -33,12 +56,12 @@ BraceWrapping:
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
@ -47,24 +70,23 @@ BraceWrapping:
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAfterAttributes: Never
# BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeConceptDeclarations: true
BreakBeforeInheritanceComma: true # false
BreakInheritanceList: BeforeComma # BeforeColon
BreakBeforeConceptDeclarations: Always
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true # false
BreakConstructorInitializers: BeforeComma # BeforeColon
# BreakAfterJavaFieldAnnotations: false
BreakInheritanceList: BeforeComma # BeforeColon
BreakStringLiterals: true
ColumnLimit: 120 # 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
EmptyLineAfterAccessModifier: Never
@ -83,14 +105,21 @@ IndentCaseLabels: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequires: false
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
# InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
Decimal: 0
Hex: 0
# JavaScriptQuotes: Leave
# JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
@ -100,23 +129,31 @@ NamespaceIndentation: None
# ObjCBreakBeforeNestedBlockParam: true
# ObjCSpaceAfterProperty: false
# ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: NextLine
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Right
PointerAlignment: Right # Left
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: false # true
RemoveBracesLLVM: false
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 0 # 1
SortIncludes: CaseInsensitive # CaseSensitive
# SortJavaStaticImport: Before
SortUsingDeclarations: false # true
SortUsingDeclarations: Lexicographic # LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
@ -127,6 +164,16 @@ SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
@ -143,6 +190,5 @@ SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 100 # 8
UseCRLF: false
UseTab: Never
...

View File

@ -29,13 +29,13 @@ jobs:
# Strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
# Strip "v" prefix from tag name
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
# Use Docker `latest` tag convention
[ "$VERSION" == "master" ] && VERSION=latest
# Convert IMAGE_TAG, HASH_VERSION and VERSION to lowercase (repository name must be lowercase)
IMAGE_TAG=$(echo "$IMAGE_TAG" | awk '{print tolower($0)}')
IMAGE_TAG_DH=$(echo "$IMAGE_TAG_DH" | awk '{print tolower($0)}')
@ -69,10 +69,10 @@ jobs:
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Cache Docker layers
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ env.SAFE_ARCH }}-${{ github.sha }}
@ -80,7 +80,7 @@ jobs:
${{ runner.os }}-buildx-${{ env.SAFE_ARCH }}-
- name: Login to ghcr registry
uses: docker/login-action@v1
uses: docker/login-action@v2
if: ${{ github.event_name != 'pull_request' }}
with:
registry: ghcr.io
@ -88,7 +88,7 @@ jobs:
password: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Login to Docker Hub registry
uses: docker/login-action@v1
uses: docker/login-action@v2
if: ${{ github.event_name != 'pull_request' }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
@ -158,14 +158,14 @@ jobs:
docker load --input image_linuxppc64le/linuxppc64le.tar
- name: Login to ghcr registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ secrets.GH_USERNAME }}
password: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Login to Docker Hub registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }}
@ -180,7 +180,7 @@ jobs:
--amend ${{ env.IMAGE_TAG }}:${{ env.HASH_VERSION }}-linuxarmv7 \
--amend ${{ env.IMAGE_TAG }}:${{ env.HASH_VERSION }}-linuxarm64 \
--amend ${{ env.IMAGE_TAG }}:${{ env.HASH_VERSION }}-linuxppc64le
docker manifest push ${{ env.IMAGE_TAG }}:${{ env.HASH_VERSION }}
#docker manifest push ${{ env.IMAGE_TAG }}:${{ env.HASH_VERSION }}
# Tag images as VERSION (like 'latest')
docker tag ${{ env.IMAGE_TAG }}:${{ env.HASH_VERSION }}-linux386 ${{ env.IMAGE_TAG }}:${{ env.VERSION }}-linux386
@ -197,7 +197,7 @@ jobs:
--amend ${{ env.IMAGE_TAG }}:${{ env.VERSION }}-linuxarmv7 \
--amend ${{ env.IMAGE_TAG }}:${{ env.VERSION }}-linuxarm64 \
--amend ${{ env.IMAGE_TAG }}:${{ env.VERSION }}-linuxppc64le
docker manifest push ${{ env.IMAGE_TAG }}:${{ env.VERSION }}
#docker manifest push ${{ env.IMAGE_TAG }}:${{ env.VERSION }}
# -- Push to Docker Hub
docker tag ${{ env.IMAGE_TAG }}:${{ env.HASH_VERSION }}-linux386 ${{ env.IMAGE_TAG_DH }}:${{ env.VERSION }}-linux386

View File

@ -6,7 +6,7 @@ if (POLICY CMP0065)
cmake_policy(SET CMP0065 NEW)
endif()
project(TelegramBotApi VERSION 6.3.2 LANGUAGES CXX)
project(TelegramBotApi VERSION 7.0 LANGUAGES CXX)
if (POLICY CMP0069)
option(TELEGRAM_BOT_API_ENABLE_LTO "Use \"ON\" to enable Link Time Optimization.")
@ -73,6 +73,9 @@ if (CLANG OR GCC)
elseif (APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-no_pie")
endif()
include(AddCXXCompilerFlag)
add_cxx_compiler_flag("-static-libstdc++")
add_cxx_compiler_flag("-static-libgcc")
endif()
endif()

View File

@ -54,17 +54,6 @@ Get the member list of a supergroup or channel
###### Returns `ChatMember`
##### Method `deleteMessages`
Delete all the messages with message_id in range between `start` and `end`.
The `start` parameter MUST be less than the `end` parameter
Both `start` and `end` must be positive non zero numbers
The method will always return `true` as a result, even if the messages cannot be deleted
This method does not work on private chat or normal groups
It is not suggested to delete more than 200 messages per call
**NOTE**
The maximum number of messages to be deleted in a single batch is determined by the `max-batch-operations` parameter and is 10000 by default
###### Parameters
- `chat_id` Chat id
- `start` First message id to delete
@ -122,6 +111,17 @@ _For Docker containers, `$TELEGRAM_VERBOSITY` can be set._
##### Method `getChat`
The command `getChat` will also try to resolve the username online, if it can't be found locally
##### Method `deleteMessages`
The command `deleteMessages` can also delete all the messages with message_id in range between `start` and `end`.
The `start` parameter MUST be less than the `end` parameter
Both `start` and `end` must be positive non-zero numbers
The method will always return `true` as a result, even if the messages cannot be deleted
This method does not work on private chat or normal groups
It is not suggested to delete more than 200 messages per call
**NOTE**
The maximum number of messages to be deleted in a single batch is determined by the `max-batch-operations` parameter and is 10000 by default
##### Object `Message`
The `Message` object now has two new fields:
- `views`: how many views has the message (usually the views are shown only for channel messages)

2
td

@ -1 +1 @@
Subproject commit 7eba19887ad834fd731b6b07b53c2426fe4beb59
Subproject commit 27c3eaeb4964bd5f18d8488e354abde1a4383e49

View File

@ -547,16 +547,23 @@ paths:
/deleteMessages:
post:
tags:
- added
- modified
description: |-
Delete all the messages with message_id in range between start and end.
The start parameter MUST be less than the end parameter
Both start and end must be positive non zero numbers
The method will always return true as a result, even if the messages cannot be deleted
This method does not work on private chat or normal groups It is not suggested to delete more than 200 messages per call.
Use this method to delete multiple messages simultaneously.
This method can delete a set of message ids, or a range of message ids.
If you specify "message_ids", this method tries to delete the specified set of ids:
If some of the specified messages can't be found, they are skipped.
Returns True on success.
*NOTE*
The maximum number of messages to be deleted in a single batch is determined by the max-batch-operations parameter and is 10000 by default.
If you specify "start" and "end", this method deletes all the messages with message_id in range between start and end:
The start parameter MUST be less than the end parameter
Both start and end must be positive non zero numbers
The method will always return true as a result, even if the messages cannot be deleted
This method does not work on private chat or normal groups It is not suggested to delete more than 200 messages per call.
*NOTE*
The maximum number of messages to be deleted in a single batch is determined by the max-batch-operations parameter and is 10000 by default.
requestBody:
content:
application/x-www-form-urlencoded:
@ -568,6 +575,10 @@ paths:
anyOf:
- type: integer
- type: string
message_ids:
type: array
items:
type: integer
start:
description: First message id to delete
type: integer
@ -576,8 +587,6 @@ paths:
type: integer
required:
- chat_id
- start
- end
multipart/form-data:
schema:
type: object
@ -587,6 +596,10 @@ paths:
anyOf:
- type: integer
- type: string
message_ids:
type: array
items:
type: integer
start:
description: First message id to delete
type: integer
@ -595,8 +608,6 @@ paths:
type: integer
required:
- chat_id
- start
- end
application/json:
schema:
type: object
@ -606,6 +617,10 @@ paths:
anyOf:
- type: integer
- type: string
message_ids:
type: array
items:
type: integer
start:
description: First message id to delete
type: integer
@ -614,12 +629,10 @@ paths:
type: integer
required:
- chat_id
- start
- end
required: true
responses:
'200':
description: ''
description: 'Request was successful, the result is returned.'
content:
application/json:
schema:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -177,13 +177,7 @@ void ClientManager::user_login(PromisedQueryPtr query) {
}
bool ClientManager::check_flood_limits(PromisedQueryPtr &query, bool is_user_login) {
td::string ip_address;
if (query->peer_address().is_valid() && !query->peer_address().is_reserved()) { // external connection
ip_address = query->peer_address().get_ip_str().str();
} else {
// invalid peer address or connection from the local network
ip_address = query->get_header("x-real-ip").str();
}
td::string ip_address = query->get_peer_ip_address();
if (!ip_address.empty()) {
td::IPAddress tmp;
tmp.init_host_port(ip_address, 0).ignore();
@ -192,7 +186,7 @@ bool ClientManager::check_flood_limits(PromisedQueryPtr &query, bool is_user_log
ip_address = tmp.get_ip_str().str();
}
}
LOG(DEBUG) << "Receive incoming query for new bot " << query->token() << " from " << query->peer_address();
LOG(DEBUG) << "Receive incoming query for new bot " << query->token() << " from " << ip_address;
if (!ip_address.empty()) {
LOG(DEBUG) << "Check Client creation flood control for IP address " << ip_address;
if (is_user_login) {
@ -209,18 +203,51 @@ bool ClientManager::check_flood_limits(PromisedQueryPtr &query, bool is_user_log
flood_control.add_limit(60 * 60, 600); // 600 in an hour
}
}
auto now = static_cast<td::uint32>(td::Time::now());
td::uint32 wakeup_at = flood_control.get_wakeup_at();
auto now = td::Time::now();
auto wakeup_at = flood_control.get_wakeup_at();
if (wakeup_at > now) {
LOG(INFO) << "Failed to create Client from IP address " << ip_address;
query->set_retry_after_error(static_cast<int>(wakeup_at - now) + 1);
return false;
}
flood_control.add_event(static_cast<td::int32>(now));
flood_control.add_event(now);
}
return true;
}
ClientManager::TopClients ClientManager::get_top_clients(std::size_t max_count, td::Slice token_filter) {
auto now = td::Time::now();
TopClients result;
td::vector<std::pair<td::int64, td::uint64>> top_client_ids;
for (auto id : clients_.ids()) {
auto *client_info = clients_.get(id);
CHECK(client_info);
if (client_info->stat_.is_active(now)) {
result.active_count++;
}
if (!td::begins_with(client_info->token_, token_filter)) {
continue;
}
auto score = static_cast<td::int64>(client_info->stat_.get_score(now) * -1e9);
if (score == 0 && top_client_ids.size() >= max_count) {
continue;
}
top_client_ids.emplace_back(score, id);
}
if (top_client_ids.size() < max_count) {
max_count = top_client_ids.size();
}
std::partial_sort(top_client_ids.begin(), top_client_ids.begin() + max_count, top_client_ids.end());
result.top_client_ids.reserve(max_count);
for (std::size_t i = 0; i < max_count; i++) {
result.top_client_ids.push_back(top_client_ids[i].second);
}
return result;
}
void ClientManager::get_stats(td::Promise<td::BufferSlice> promise,
td::vector<std::pair<td::string, td::string>> args,
bool as_json) {
@ -266,35 +293,10 @@ void ClientManager::get_stats(td::Promise<td::BufferSlice> promise,
}
auto now = td::Time::now();
td::int32 active_bot_count = 0;
td::vector<std::pair<td::int64, td::uint64>> top_bot_ids;
size_t max_bots = 50;
for (auto id : clients_.ids()) {
auto *client_info = clients_.get(id);
CHECK(client_info);
if (client_info->stat_.is_active(now)) {
active_bot_count++;
}
if (!td::begins_with(client_info->token_, id_filter)) {
continue;
}
auto score = static_cast<td::int64>(client_info->stat_.get_score(now) * -1e9);
if (score == 0 && top_bot_ids.size() >= max_bots) {
continue;
}
top_bot_ids.emplace_back(score, id);
}
if (top_bot_ids.size() < max_bots) {
max_bots = top_bot_ids.size();
}
std::partial_sort(top_bot_ids.begin(), top_bot_ids.begin() + max_bots, top_bot_ids.end());
top_bot_ids.resize(max_bots);
auto top_clients = get_top_clients(50, id_filter);
if(!as_json) {
sb << stat_.get_description() << '\n';
sb << BotStatActor::get_description() << '\n';
}
if (id_filter.empty()) {
if(as_json) {
@ -308,9 +310,9 @@ void ClientManager::get_stats(td::Promise<td::BufferSlice> promise,
sb << "bot_count\t" << clients_.size() << '\n';
}
if(as_json) {
jb_root("active_bot_count", td::JsonInt(active_bot_count));
jb_root("active_bot_count", td::JsonInt(top_clients.active_count));
} else {
sb << "active_bot_count\t" << active_bot_count << '\n';
sb << "active_bot_count\t" << top_clients.active_count << '\n';
}
auto r_mem_stat = td::mem_stat();
if (r_mem_stat.is_ok()) {
@ -344,13 +346,13 @@ void ClientManager::get_stats(td::Promise<td::BufferSlice> promise,
if(as_json) {
jb_root("buffer_memory", JsonStatsSize(td::BufferAllocator::get_buffer_mem()));
jb_root("active_webhook_connections", td::JsonLong(WebhookActor::get_total_connections_count()));
jb_root("active_requests", td::JsonLong(parameters_->shared_data_->query_count_.load()));
jb_root("active_webhook_connections", td::JsonLong(WebhookActor::get_total_connection_count()));
jb_root("active_requests", td::JsonLong(parameters_->shared_data_->query_count_.load(std::memory_order_relaxed)));
jb_root("active_network_queries", td::JsonLong(td::get_pending_network_query_count(*parameters_->net_query_stats_)));
} else {
sb << "buffer_memory\t" << td::format::as_size(td::BufferAllocator::get_buffer_mem()) << '\n';
sb << "active_webhook_connections\t" << WebhookActor::get_total_connections_count() << '\n';
sb << "active_requests\t" << parameters_->shared_data_->query_count_.load() << '\n';
sb << "active_webhook_connections\t" << WebhookActor::get_total_connection_count() << '\n';
sb << "active_requests\t" << parameters_->shared_data_->query_count_.load(std::memory_order_relaxed) << '\n';
sb << "active_network_queries\t" << td::get_pending_network_query_count(*parameters_->net_query_stats_) << '\n';
}
if(as_json) {
@ -364,27 +366,29 @@ void ClientManager::get_stats(td::Promise<td::BufferSlice> promise,
if(as_json) {
td::vector<JsonStatsBotAdvanced> bots;
for (std::pair<td::int64, td::uint64> top_bot_id : top_bot_ids) {
auto client_info = clients_.get(top_bot_id.second);
for (auto top_client_id : top_clients.top_client_ids) {
auto client_info = clients_.get(top_client_id);
CHECK(client_info);
ServerBotInfo bot_info = client_info->client_.get_actor_unsafe()->get_bot_info();
auto active_request_count = client_info->stat_.get_active_request_count();
auto active_file_upload_bytes = client_info->stat_.get_active_file_upload_bytes();
auto active_file_upload_count = client_info->stat_.get_active_file_upload_count();
auto stats = client_info->stat_.as_json_ready_vector(now);
JsonStatsBotAdvanced bot(
std::move(top_bot_id), std::move(bot_info), active_request_count, active_file_upload_bytes, std::move(stats), parameters_->stats_hide_sensible_data_, now
std::move(top_client_id), std::move(bot_info), active_request_count, active_file_upload_bytes, active_file_upload_count, std::move(stats), parameters_->stats_hide_sensible_data_, now
);
bots.push_back(bot);
}
auto bot_count = bots.size();
jb_root("bots", JsonStatsBots(std::move(bots), bot_count > 100));
} else {
for (auto top_bot_id : top_bot_ids) {
auto *client_info = clients_.get(top_bot_id.second);
for (auto top_client_id : top_clients.top_client_ids) {
auto *client_info = clients_.get(top_client_id);
CHECK(client_info);
auto bot_info = client_info->client_.get_actor_unsafe()->get_bot_info();
auto active_request_count = client_info->stat_.get_active_request_count();
auto active_file_upload_bytes = client_info->stat_.get_active_file_upload_bytes();
auto active_file_upload_count = client_info->stat_.get_active_file_upload_count();
sb << '\n';
sb << "id\t" << bot_info.id_ << '\n';
sb << "uptime\t" << now - bot_info.start_time_ << '\n';
@ -398,6 +402,9 @@ void ClientManager::get_stats(td::Promise<td::BufferSlice> promise,
if (active_file_upload_bytes != 0) {
sb << "active_file_upload_bytes\t" << active_file_upload_bytes << '\n';
}
if (active_file_upload_count != 0) {
sb << "active_file_upload_count\t" << active_file_upload_count << '\n';
}
if (!bot_info.webhook_.empty()) {
if (!parameters_->stats_hide_sensible_data_) {
sb << "webhook\t" << bot_info.webhook_ << '\n';
@ -441,9 +448,6 @@ td::int64 ClientManager::get_tqueue_id(td::int64 user_id, bool is_test_dc) {
}
void ClientManager::start_up() {
//NB: the same scheduler as for database in Td
auto scheduler_id = 1;
// init tqueue
{
auto load_start_time = td::Time::now();
@ -471,7 +475,8 @@ void ClientManager::start_up() {
}
}
auto concurrent_binlog = std::make_shared<td::ConcurrentBinlog>(std::move(binlog), scheduler_id);
auto concurrent_binlog =
std::make_shared<td::ConcurrentBinlog>(std::move(binlog), SharedData::get_binlog_scheduler_id());
auto concurrent_tqueue_binlog = td::make_unique<td::TQueueBinlog<td::BinlogInterface>>();
concurrent_tqueue_binlog->set_binlog(std::move(concurrent_binlog));
tqueue->set_callback(std::move(concurrent_tqueue_binlog));
@ -486,18 +491,18 @@ void ClientManager::start_up() {
// init webhook_db and user_db
auto concurrent_webhook_db = td::make_unique<td::BinlogKeyValue<td::ConcurrentBinlog>>();
auto status = concurrent_webhook_db->init(parameters_->working_directory_ + "webhooks_db.binlog", td::DbKey::empty(),
scheduler_id);
SharedData::get_binlog_scheduler_id());
LOG_IF(FATAL, status.is_error()) << "Can't open webhooks_db.binlog " << status;
parameters_->shared_data_->webhook_db_ = std::move(concurrent_webhook_db);
auto concurrent_user_db = td::make_unique<td::BinlogKeyValue<td::ConcurrentBinlog>>();
status = concurrent_user_db->init(parameters_->working_directory_ + "user_db.binlog", td::DbKey::empty(), scheduler_id);
status = concurrent_user_db->init(parameters_->working_directory_ + "user_db.binlog", td::DbKey::empty(), SharedData::get_binlog_scheduler_id());
LOG_IF(FATAL, status.is_error()) << "Can't open user_db.binlog " << status.error();
parameters_->shared_data_->user_db_ = std::move(concurrent_user_db);
auto &webhook_db = *parameters_->shared_data_->webhook_db_;
auto &user_db = *parameters_->shared_data_->user_db_;
for (auto key_value : webhook_db.get_all()) {
for (const auto &key_value : webhook_db.get_all()) {
if (!token_range_(td::to_integer<td::uint64>(key_value.first))) {
LOG(WARNING) << "DROP WEBHOOK: " << key_value.first << " ---> " << key_value.second;
webhook_db.erase(key_value.first);
@ -509,8 +514,8 @@ void ClientManager::start_up() {
}
// launch watchdog
watchdog_id_ = td::create_actor_on_scheduler<Watchdog>(
"ManagerWatchdog", td::Scheduler::instance()->sched_count() - 3, td::this_thread::get_id(), WATCHDOG_TIMEOUT);
watchdog_id_ = td::create_actor_on_scheduler<Watchdog>("ManagerWatchdog", SharedData::get_watchdog_scheduler_id(),
td::this_thread::get_id(), WATCHDOG_TIMEOUT);
set_timeout_in(600.0);
}
@ -520,7 +525,7 @@ PromisedQueryPtr ClientManager::get_webhook_restore_query(td::Slice token, bool
td::vector<td::BufferSlice> containers;
auto add_string = [&containers](td::Slice str) {
containers.emplace_back(str);
return containers.back().as_slice();
return containers.back().as_mutable_slice();
};
token = add_string(token);
@ -626,6 +631,37 @@ void ClientManager::dump_statistics() {
}
td::dump_pending_network_queries(*parameters_->net_query_stats_);
auto now = td::Time::now();
auto top_clients = get_top_clients(10, {});
for (auto top_client_id : top_clients.top_client_ids) {
auto *client_info = clients_.get(top_client_id);
CHECK(client_info);
auto bot_info = client_info->client_.get_actor_unsafe()->get_bot_info();
td::string update_count;
td::string request_count;
auto replace_tabs = [](td::string &str) {
for (auto &c : str) {
if (c == '\t') {
c = ' ';
}
}
};
auto stats = client_info->stat_.as_vector(now);
for (auto &stat : stats) {
if (stat.key_ == "update_count") {
replace_tabs(stat.value_);
update_count = std::move(stat.value_);
}
if (stat.key_ == "request_count") {
replace_tabs(stat.value_);
request_count = std::move(stat.value_);
}
}
LOG(WARNING) << td::tag("id", bot_info.id_) << td::tag("update_count", update_count)
<< td::tag("request_count", request_count);
}
}
void ClientManager::raw_event(const td::Event::Raw &event) {
@ -646,7 +682,7 @@ void ClientManager::raw_event(const td::Event::Raw &event) {
void ClientManager::timeout_expired() {
send_closure(watchdog_id_, &Watchdog::kick);
set_timeout_in(WATCHDOG_TIMEOUT / 2);
set_timeout_in(WATCHDOG_TIMEOUT / 10);
double now = td::Time::now();
if (now > next_tqueue_gc_time_) {

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -79,13 +79,19 @@ class ClientManager final : public td::Actor {
td::int64 tqueue_deleted_events_ = 0;
td::int64 last_tqueue_deleted_events_ = 0;
static constexpr double WATCHDOG_TIMEOUT = 0.5;
static constexpr double WATCHDOG_TIMEOUT = 0.25;
static td::int64 get_tqueue_id(td::int64 user_id, bool is_test_dc);
static PromisedQueryPtr get_webhook_restore_query(td::Slice token, bool is_user, td::Slice webhook_info,
std::shared_ptr<SharedData> shared_data);
struct TopClients {
td::int32 active_count = 0;
td::vector<td::uint64> top_client_ids;
};
TopClients get_top_clients(std::size_t max_count, td::Slice token_filter);
void start_up() final;
void raw_event(const td::Event::Raw &event) final;
void timeout_expired() final;

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -53,6 +53,55 @@ struct SharedData {
}
return static_cast<td::int32>(result);
}
static td::int32 get_file_gc_scheduler_id() {
// the same scheduler as for file GC in Td
return 2;
}
static td::int32 get_client_scheduler_id() {
// the thread for ClientManager and all Clients
return 4;
}
static td::int32 get_watchdog_scheduler_id() {
// the thread for watchdogs
return 5;
}
static td::int32 get_slow_incoming_http_scheduler_id() {
// the thread for slow incoming HTTP connections
return 6;
}
static td::int32 get_slow_outgoing_http_scheduler_id() {
// the thread for slow outgoing HTTP connections
return 7;
}
static td::int32 get_dns_resolver_scheduler_id() {
// the thread for DNS resolving
return 8;
}
static td::int32 get_binlog_scheduler_id() {
// the thread for TQueue and webhook binlogs
return 9;
}
static td::int32 get_webhook_certificate_scheduler_id() {
// the thread for webhook certificate processing
return 10;
}
static td::int32 get_statistics_thread_id() {
// the thread for CPU usage updating
return 11;
}
static td::int32 get_thread_count() {
return 12;
}
};
struct ClientParameters {
@ -62,7 +111,7 @@ struct ClientParameters {
bool local_mode_ = false;
bool allow_http_ = false;
bool use_relative_path_ = false;
bool no_file_limit_ = true;
bool no_file_limit_ = false;
bool allow_users_ = false;
bool allow_users_registration_ = false;
bool stats_hide_sensible_data_ = false;

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,17 +1,20 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "telegram-bot-api/ClientParameters.h"
#include "td/net/HttpInboundConnection.h"
#include "td/net/TcpListener.h"
#include "td/actor/actor.h"
#include "td/utils/BufferedFd.h"
#include "td/utils/common.h"
#include "td/utils/FloodControlFast.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
@ -46,8 +49,8 @@ class HttpServer final : public td::TcpListener::Callback {
set_timeout_at(wakeup_at);
return;
}
flood_control_.add_event(static_cast<td::int32>(now));
LOG(INFO) << "Create tcp listener " << td::tag("address", ip_address_) << td::tag("port", port_);
flood_control_.add_event(now);
LOG(INFO) << "Create TCP listener " << td::tag("address", ip_address_) << td::tag("port", port_);
listener_ = td::create_actor<td::TcpListener>(
PSLICE() << "TcpListener" << td::tag("address", ip_address_) << td::tag("port", port_), port_,
actor_shared(this, 1), ip_address_);
@ -60,13 +63,8 @@ class HttpServer final : public td::TcpListener::Callback {
}
void accept(td::SocketFd fd) final {
auto scheduler_count = td::Scheduler::instance()->sched_count();
auto scheduler_id = scheduler_count - 1;
if (scheduler_id > 0) {
scheduler_id--;
}
td::create_actor<td::HttpInboundConnection>("HttpInboundConnection", td::BufferedFd<td::SocketFd>(std::move(fd)), 0,
20, 500, creator_(), scheduler_id)
50, 500, creator_(), SharedData::get_slow_incoming_http_scheduler_id())
.release();
}

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -26,10 +26,10 @@ td::FlatHashMap<td::string, td::unique_ptr<td::VirtuallyJsonable>> empty_paramet
Query::Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_user, bool is_test_dc, td::MutableSlice method,
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&args,
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&headers, td::vector<td::HttpFile> &&files,
std::shared_ptr<SharedData> shared_data, const td::IPAddress &peer_address, bool is_internal)
std::shared_ptr<SharedData> shared_data, const td::IPAddress &peer_ip_address, bool is_internal)
: state_(State::Query)
, shared_data_(shared_data)
, peer_address_(peer_address)
, peer_ip_address_(peer_ip_address)
, container_(std::move(container))
, token_(token)
, is_user_(is_user)
@ -44,7 +44,7 @@ Query::Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_u
}
td::to_lower_inplace(method_);
start_timestamp_ = td::Time::now();
LOG(INFO) << "QUERY: create " << td::tag("ptr", this) << *this;
LOG(INFO) << "Query " << this << ": " << *this;
if (shared_data_) {
shared_data_->query_count_.fetch_add(1, std::memory_order_relaxed);
if (method_ != "getupdates") {
@ -54,6 +54,15 @@ Query::Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_u
}
}
td::string Query::get_peer_ip_address() const {
if (peer_ip_address_.is_valid() && !peer_ip_address_.is_reserved()) { // external connection
return peer_ip_address_.get_ip_str().str();
} else {
// invalid peer IP address or connection from the local network
return get_header("x-real-ip").str();
}
}
td::int64 Query::query_size() const {
return std::accumulate(
container_.begin(), container_.end(), td::int64{0},
@ -77,7 +86,7 @@ void Query::set_stat_actor(td::ActorId<BotStatActor> stat_actor) {
void Query::set_ok(td::BufferSlice result) {
CHECK(state_ == State::Query);
LOG(INFO) << "QUERY: got ok " << td::tag("ptr", this) << td::tag("text", result.as_slice());
LOG(INFO) << "Query " << this << ": " << td::tag("method", method_) << td::tag("text", result.as_slice());
answer_ = std::move(result);
state_ = State::OK;
http_status_code_ = 200;
@ -85,7 +94,7 @@ void Query::set_ok(td::BufferSlice result) {
}
void Query::set_error(int http_status_code, td::BufferSlice result) {
LOG(INFO) << "QUERY: got error " << td::tag("ptr", this) << td::tag("code", http_status_code)
LOG(INFO) << "Query " << this << ": " << td::tag("method", method_) << td::tag("code", http_status_code)
<< td::tag("text", result.as_slice());
CHECK(state_ == State::Query);
answer_ = std::move(result);
@ -107,9 +116,25 @@ td::StringBuilder &operator<<(td::StringBuilder &sb, const Query &query) {
auto padded_time =
td::lpad(PSTRING() << td::format::as_time(td::Time::now_cached() - query.start_timestamp()), 10, ' ');
sb << "[bot" << td::rpad(query.token().str(), 46, ' ') << "][time:" << padded_time << ']'
<< td::tag("method", td::lpad(query.method().str(), 20, ' '));
<< td::tag("method", td::lpad(query.method().str(), 25, ' '));
if (!query.args().empty()) {
sb << td::oneline(PSLICE() << query.args());
sb << '{';
for (const auto &arg : query.args()) {
sb << '[';
if (arg.first.size() > 128) {
sb << '<' << arg.first.size() << '>' << td::oneline(arg.first.substr(0, 128)) << "...";
} else {
sb << td::oneline(arg.first);
}
sb << ':';
if (arg.second.size() > 4096) {
sb << '<' << arg.second.size() << '>' << td::oneline(arg.second.substr(0, 4096)) << "...";
} else {
sb << td::oneline(arg.second);
}
sb << ']';
}
sb << '}';
}
if (!query.files().empty()) {
sb << query.files();
@ -136,7 +161,7 @@ void Query::send_response_stat() const {
return;
}
send_closure(stat_actor_, &BotStatActor::add_event<ServerBotStat::Response>,
ServerBotStat::Response{state_ == State::OK, answer_.size(), files_size()}, now);
ServerBotStat::Response{state_ == State::OK, answer_.size(), file_count(), files_size()}, now);
}
} // namespace telegram_bot_api

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -38,44 +38,53 @@ class Query final : public td::ListNode {
td::Slice token() const {
return token_;
}
bool is_user() const {
return is_user_;
}
bool is_test_dc() const {
return is_test_dc_;
}
td::Slice method() const {
return method_;
}
bool has_arg(td::Slice key) const {
auto it = std::find_if(args_.begin(), args_.end(),
[&key](const std::pair<td::MutableSlice, td::MutableSlice> &s) { return s.first == key; });
return it != args_.end();
}
td::MutableSlice arg(td::Slice key) const {
auto it = std::find_if(args_.begin(), args_.end(),
[&key](const std::pair<td::MutableSlice, td::MutableSlice> &s) { return s.first == key; });
return it == args_.end() ? td::MutableSlice() : it->second;
}
const td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &args() const {
return args_;
}
td::Slice get_header(td::Slice key) const {
auto it = std::find_if(headers_.begin(), headers_.end(),
[&key](const std::pair<td::MutableSlice, td::MutableSlice> &s) { return s.first == key; });
return it == headers_.end() ? td::Slice() : it->second;
}
const td::HttpFile *file(td::Slice key) const {
auto it = std::find_if(files_.begin(), files_.end(), [&key](const td::HttpFile &f) { return f.field_name == key; });
return it == files_.end() ? nullptr : &*it;
}
const td::vector<td::HttpFile> &files() const {
return files_;
}
const td::IPAddress &peer_address() const {
return peer_address_;
}
td::int64 files_size() const;
td::string get_peer_ip_address() const;
td::BufferSlice &answer() {
return answer_;
@ -106,7 +115,7 @@ class Query final : public td::ListNode {
Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_user, bool is_test_dc, td::MutableSlice method,
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&args,
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&headers, td::vector<td::HttpFile> &&files,
std::shared_ptr<SharedData> shared_data, const td::IPAddress &peer_address, bool is_internal);
std::shared_ptr<SharedData> shared_data, const td::IPAddress &peer_ip_address, bool is_internal);
Query(const Query &) = delete;
Query &operator=(const Query &) = delete;
Query(Query &&) = delete;
@ -117,6 +126,8 @@ class Query final : public td::ListNode {
if (!empty()) {
shared_data_->query_list_size_.fetch_sub(1, std::memory_order_relaxed);
}
td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), container_, args_,
headers_, files_, answer_);
}
}
@ -130,7 +141,7 @@ class Query final : public td::ListNode {
State state_;
std::shared_ptr<SharedData> shared_data_;
double start_timestamp_;
td::IPAddress peer_address_;
td::IPAddress peer_ip_address_;
td::ActorId<BotStatActor> stat_actor_;
// request
@ -156,8 +167,6 @@ class Query final : public td::ListNode {
td::int64 query_size() const;
td::int64 files_size() const;
td::int64 files_max_size() const;
void send_request_stat() const;

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -7,6 +7,7 @@
#include "telegram-bot-api/Stats.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/port/thread.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/StringBuilder.h"
@ -19,17 +20,24 @@ ServerCpuStat::ServerCpuStat() {
}
}
void ServerCpuStat::add_event(const td::CpuStat &cpu_stat, double now) {
std::lock_guard<std::mutex> guard(mutex_);
for (auto &stat : stat_) {
stat.add_event(cpu_stat, now);
void ServerCpuStat::update(double now) {
auto r_cpu_stat = td::cpu_stat();
if (r_cpu_stat.is_error()) {
return;
}
auto &cpu_stat = instance();
std::lock_guard<std::mutex> guard(cpu_stat.mutex_);
for (auto &stat : cpu_stat.stat_) {
stat.add_event(r_cpu_stat.ok(), now);
}
LOG(WARNING) << "CPU usage: " << cpu_stat.stat_[1].get_stat(now).as_vector()[0].value_;
}
td::string ServerCpuStat::get_description() const {
td::string ServerCpuStat::get_description() {
td::string res = "DURATION";
for (auto &descr : DESCR) {
res += "\t";
res += '\t';
res += descr;
}
return res;
@ -37,7 +45,7 @@ td::string ServerCpuStat::get_description() const {
static td::string to_percentage(td::uint64 ticks, td::uint64 total_ticks) {
static double multiplier = 100.0 * (td::thread::hardware_concurrency() ? td::thread::hardware_concurrency() : 1);
return PSTRING() << (static_cast<double>(ticks) / static_cast<double>(total_ticks) * multiplier) << "%";
return PSTRING() << (static_cast<double>(ticks) / static_cast<double>(total_ticks) * multiplier) << '%';
}
td::vector<StatItem> CpuStat::as_vector() const {
@ -167,7 +175,7 @@ td::vector<ServerBotStat> BotStatActor::as_json_ready_vector(double now) {
return res;
}
td::string BotStatActor::get_description() const {
td::string BotStatActor::get_description() {
td::string res = "DURATION";
for (auto &descr : DESCR) {
res += "\t";
@ -187,12 +195,26 @@ td::vector<td::string> BotStatActor::get_jsonable_description() const {
double BotStatActor::get_score(double now) {
auto minute_stat = stat_[2].stat_duration(now);
double result = minute_stat.first.request_count_ + minute_stat.first.update_count_;
double minute_score = minute_stat.first.request_count_ + minute_stat.first.update_count_;
if (minute_stat.second != 0) {
minute_score /= minute_stat.second;
}
auto all_time_stat = stat_[0].stat_duration(now);
double all_time_score = 0.01 * (all_time_stat.first.request_count_ + all_time_stat.first.update_count_);
if (all_time_stat.second != 0) {
all_time_score /= all_time_stat.second;
}
auto active_request_score = static_cast<double>(td::max(get_active_request_count() - 10, static_cast<td::int64>(0)));
auto active_file_upload_score = static_cast<double>(get_active_file_upload_bytes()) * 1e-8;
return minute_score + all_time_score + active_request_score + active_file_upload_score;
}
double BotStatActor::get_minute_update_count(double now) {
auto minute_stat = stat_[2].stat_duration(now);
double result = minute_stat.first.update_count_;
if (minute_stat.second != 0) {
result /= minute_stat.second;
}
result += td::max(static_cast<double>(get_active_request_count() - 10), static_cast<double>(0));
result += static_cast<double>(get_active_file_upload_bytes()) * 1e-8;
return result;
}
@ -204,6 +226,10 @@ td::int64 BotStatActor::get_active_file_upload_bytes() const {
return active_file_upload_bytes_;
}
td::int64 BotStatActor::get_active_file_upload_count() const {
return active_file_upload_count_;
}
bool BotStatActor::is_active(double now) const {
return last_activity_timestamp_ > now - 86400;
}

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -9,7 +9,6 @@
#include "td/actor/actor.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/port/Stat.h"
#include "td/utils/Time.h"
#include "td/utils/TimedStat.h"
@ -49,16 +48,10 @@ class ServerCpuStat {
static ServerCpuStat stat;
return stat;
}
static void update(double now) {
auto r_event = td::cpu_stat();
if (r_event.is_error()) {
return;
}
instance().add_event(r_event.ok(), now);
LOG(WARNING) << "CPU usage: " << instance().stat_[1].get_stat(now).as_vector()[0].value_;
}
td::string get_description() const;
static void update(double now);
static td::string get_description();
td::vector<StatItem> as_vector(double now);
td::vector<td::vector<StatItem>> as_json_ready_vector(double now);
@ -72,8 +65,6 @@ class ServerCpuStat {
td::TimedStat<CpuStat> stat_[SIZE];
ServerCpuStat();
void add_event(const td::CpuStat &stat, double now);
};
class ServerBotInfo {
@ -116,6 +107,7 @@ struct ServerBotStat {
struct Response {
bool ok_;
size_t size_;
td::int64 file_count_;
td::int64 files_size_;
};
void on_event(const Response &response) {
@ -156,7 +148,7 @@ class BotStatActor final : public td::Actor {
}
BotStatActor(const BotStatActor &) = delete;
BotStatActor &operator=(const BotStatActor &other) = delete;
BotStatActor &operator=(const BotStatActor &) = delete;
BotStatActor(BotStatActor &&) = default;
BotStatActor &operator=(BotStatActor &&other) noexcept {
if (!empty()) {
@ -183,15 +175,20 @@ class BotStatActor final : public td::Actor {
td::vector<StatItem> as_vector(double now);
td::vector<ServerBotStat> as_json_ready_vector(double now);
td::string get_description() const;
td::vector<td::string> get_jsonable_description() const;
static td::string get_description();
double get_score(double now);
double get_minute_update_count(double now);
td::int64 get_active_request_count() const;
td::int64 get_active_file_upload_bytes() const;
td::int64 get_active_file_upload_count() const;
bool is_active(double now) const;
static constexpr std::size_t SIZE = 4;
@ -203,12 +200,14 @@ class BotStatActor final : public td::Actor {
double last_activity_timestamp_ = -1e9;
td::int64 active_request_count_ = 0;
td::int64 active_file_upload_bytes_ = 0;
td::int64 active_file_upload_count_ = 0;
void on_event(const ServerBotStat::Update &update) {
}
void on_event(const ServerBotStat::Response &response) {
active_request_count_--;
active_file_upload_count_ -= response.file_count_;
active_file_upload_bytes_ -= response.files_size_;
CHECK(active_request_count_ >= 0);
CHECK(active_file_upload_bytes_ >= 0);
@ -216,6 +215,7 @@ class BotStatActor final : public td::Actor {
void on_event(const ServerBotStat::Request &request) {
active_request_count_++;
active_file_upload_count_ += request.file_count_;
active_file_upload_bytes_ += request.files_size_;
}
};

View File

@ -123,16 +123,15 @@ class JsonStatsCpu : public td::Jsonable {
class JsonStatsBot : public td::Jsonable {
public:
explicit JsonStatsBot(std::pair<td::int64, td::uint64> score_id_pair) : score_id_pair_(std::move(score_id_pair)) {
explicit JsonStatsBot(td::uint64 client_id) : client_id_(client_id) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("score", td::JsonLong(score_id_pair_.first));
object("internal_id", td::JsonLong(score_id_pair_.second));
object("client_id", td::JsonLong(client_id_));
}
protected:
const std::pair<td::int64, td::uint64> score_id_pair_;
const td::uint64 client_id_;
};
class JsonStatsBotStatDouble : public td::Jsonable {
@ -198,23 +197,23 @@ class JsonStatsBotStats : public td::Jsonable {
class JsonStatsBotAdvanced : public JsonStatsBot {
public:
explicit JsonStatsBotAdvanced(std::pair<td::int64, td::uint64> score_id_pair,
explicit JsonStatsBotAdvanced(td::uint64 client_id,
ServerBotInfo bot,
td::int64 active_request_count,
td::int64 active_file_upload_bytes,
td::int64 active_file_upload_count,
td::vector<ServerBotStat> stats,
const bool hide_sensible_data,
const double now)
: JsonStatsBot(std::move(score_id_pair)), bot_(std::move(bot)), active_request_count_(active_request_count),
active_file_upload_bytes_(active_file_upload_bytes), stats_(std::move(stats)),
hide_sensible_data_(hide_sensible_data), now_(now) {
: JsonStatsBot(client_id), bot_(std::move(bot)), active_request_count_(active_request_count),
active_file_upload_bytes_(active_file_upload_bytes), active_file_upload_count_(active_file_upload_count),
stats_(std::move(stats)), hide_sensible_data_(hide_sensible_data), now_(now) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("id", td::JsonLong(td::to_integer<td::int64>(bot_.id_)));
object("uptime", now_ - bot_.start_time_);
object("score", td::JsonLong(score_id_pair_.first));
object("internal_id", td::JsonLong(score_id_pair_.second));
object("client_id", td::JsonLong(client_id_));
if (!hide_sensible_data_) {
object("token", td::JsonString(bot_.token_));
}
@ -240,6 +239,7 @@ class JsonStatsBotAdvanced : public JsonStatsBot {
ServerBotInfo bot_;
td::int64 active_request_count_;
td::int64 active_file_upload_bytes_;
td::int64 active_file_upload_count_;
td::vector<ServerBotStat> stats_;
const bool hide_sensible_data_;
const double now_;

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -13,7 +13,7 @@ namespace telegram_bot_api {
void Watchdog::kick() {
auto now = td::Time::now();
if (now >= last_kick_time_ + timeout_ && last_kick_time_ > 0) {
if (now >= last_kick_time_ + timeout_ && last_kick_time_ > 0 && GET_VERBOSITY_LEVEL() >= VERBOSITY_NAME(ERROR)) {
LOG(ERROR) << get_name() << " timeout expired after " << now - last_kick_time_ << " seconds";
td::thread::send_real_time_signal(main_thread_id_, 2);
}

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -11,11 +11,8 @@
#include "td/net/GetHostByNameActor.h"
#include "td/net/HttpHeaderCreator.h"
#include "td/net/HttpProxy.h"
#include "td/net/SslStream.h"
#include "td/net/TransparentProxy.h"
#include "td/actor/actor.h"
#include "td/utils/base64.h"
#include "td/utils/buffer.h"
#include "td/utils/common.h"
@ -32,13 +29,11 @@
#include "td/utils/Span.h"
#include "td/utils/Time.h"
#include <limits>
namespace telegram_bot_api {
static int VERBOSITY_NAME(webhook) = VERBOSITY_NAME(DEBUG);
std::atomic<td::uint64> WebhookActor::total_connections_count_{0};
std::atomic<td::uint64> WebhookActor::total_connection_count_{0};
WebhookActor::WebhookActor(td::ActorShared<Callback> callback, td::int64 tqueue_id, td::HttpUrl url,
td::string cert_path, td::int32 max_connections, bool from_db_flag,
@ -74,6 +69,11 @@ WebhookActor::WebhookActor(td::ActorShared<Callback> callback, td::int64 tqueue_
<< ", max_connections = " << max_connections_;
}
WebhookActor::~WebhookActor() {
td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), update_map_, queue_updates_,
queues_, ssl_ctx_);
}
void WebhookActor::relax_wakeup_at(double wakeup_at, const char *source) {
if (wakeup_at_ == 0 || wakeup_at < wakeup_at_) {
VLOG(webhook) << "Wake up in " << wakeup_at - td::Time::now() << " from " << source;
@ -114,8 +114,9 @@ void WebhookActor::on_resolved_ip_address(td::Result<td::IPAddress> r_ip_address
return on_error(r_ip_address.move_as_error());
}
auto new_ip_address = r_ip_address.move_as_ok();
if (!check_ip_address(new_ip_address)) {
return on_error(td::Status::Error(PSLICE() << "IP address " << new_ip_address.get_ip_str() << " is reserved"));
auto check_status = check_ip_address(new_ip_address);
if (check_status.is_error()) {
return on_error(std::move(check_status));
}
if (!(ip_address_ == new_ip_address)) {
VLOG(webhook) << "IP address has changed: " << ip_address_ << " --> " << new_ip_address;
@ -128,24 +129,25 @@ void WebhookActor::on_resolved_ip_address(td::Result<td::IPAddress> r_ip_address
VLOG(webhook) << "IP address was verified";
}
td::Status WebhookActor::create_connection() {
if (!ip_address_.is_valid()) {
VLOG(webhook) << "Can't create connection: IP address is not ready";
return td::Status::Error("IP address is not ready");
void WebhookActor::on_ssl_context_created(td::Result<td::SslCtx> r_ssl_ctx) {
if (r_ssl_ctx.is_error()) {
create_webhook_error("Can't create an SSL context", r_ssl_ctx.move_as_error(), true);
loop();
return;
}
ssl_ctx_ = r_ssl_ctx.move_as_ok();
VLOG(webhook) << "SSL context was created";
loop();
}
td::Status WebhookActor::create_connection() {
CHECK(ip_address_.is_valid());
if (parameters_->webhook_proxy_ip_address_.is_valid()) {
auto r_proxy_socket_fd = td::SocketFd::open(parameters_->webhook_proxy_ip_address_);
if (r_proxy_socket_fd.is_error()) {
td::Slice error_message = "Can't connect to the webhook proxy";
auto error = td::Status::Error(PSLICE() << error_message << ": " << r_proxy_socket_fd.error());
VLOG(webhook) << error;
on_webhook_error(error_message);
on_error(td::Status::Error(error_message));
return error;
return create_webhook_error("Can't connect to the webhook proxy", r_proxy_socket_fd.move_as_error(), false);
}
if (!was_checked_) {
TRY_STATUS(create_ssl_stream()); // check certificate
// verify webhook even we can't establish connection to the webhook
was_checked_ = true;
on_webhook_verified();
@ -188,29 +190,33 @@ td::Status WebhookActor::create_connection() {
auto r_fd = td::SocketFd::open(ip_address_);
if (r_fd.is_error()) {
td::Slice error_message = "Can't connect to the webhook";
auto error = td::Status::Error(PSLICE() << error_message << ": " << r_fd.error());
VLOG(webhook) << error;
on_webhook_error(error_message);
on_error(r_fd.move_as_error());
return error;
return create_webhook_error("Can't connect to the webhook", r_fd.move_as_error(), false);
}
return create_connection(td::BufferedFd<td::SocketFd>(r_fd.move_as_ok()));
}
td::Status WebhookActor::create_webhook_error(td::Slice error_message, td::Status &&result, bool is_public) {
CHECK(result.is_error());
auto error = td::Status::Error(PSLICE() << error_message << ": " << result);
VLOG(webhook) << error;
if (is_public) {
on_webhook_error(PSLICE() << error_message << ": " << result.public_message());
} else {
on_webhook_error(error_message);
}
on_error(std::move(result));
return error;
}
td::Result<td::SslStream> WebhookActor::create_ssl_stream() {
if (url_.protocol_ == td::HttpUrl::Protocol::Http) {
return td::SslStream();
}
auto r_ssl_stream = td::SslStream::create(url_.host_, cert_path_, td::SslStream::VerifyPeer::On, !cert_path_.empty());
CHECK(ssl_ctx_);
auto r_ssl_stream = td::SslStream::create(url_.host_, ssl_ctx_, !cert_path_.empty());
if (r_ssl_stream.is_error()) {
td::Slice error_message = "Can't create an SSL connection";
auto error = td::Status::Error(PSLICE() << error_message << ": " << r_ssl_stream.error());
VLOG(webhook) << error;
on_webhook_error(PSLICE() << error_message << ": " << r_ssl_stream.error().public_message());
on_error(r_ssl_stream.move_as_error());
return std::move(error);
return create_webhook_error("Can't create an SSL connection", r_ssl_stream.move_as_error(), true);
}
return r_ssl_stream.move_as_ok();
}
@ -221,13 +227,14 @@ td::Status WebhookActor::create_connection(td::BufferedFd<td::SocketFd> fd) {
auto id = connections_.create(Connection());
auto *conn = connections_.get(id);
conn->actor_id_ = td::create_actor<td::HttpOutboundConnection>(
PSLICE() << "Connect:" << id, std::move(fd), std::move(ssl_stream), std::numeric_limits<size_t>::max(), 20, 60,
td::ActorShared<td::HttpOutboundConnection::Callback>(actor_id(this), id));
PSLICE() << "Connect:" << id, std::move(fd), std::move(ssl_stream), 0, 50, 60,
td::ActorShared<td::HttpOutboundConnection::Callback>(actor_id(this), id),
SharedData::get_slow_outgoing_http_scheduler_id());
conn->ip_generation_ = ip_generation_;
conn->event_id_ = {};
conn->id_ = id;
ready_connections_.put(conn->to_list_node());
total_connections_count_.fetch_add(1, std::memory_order_relaxed);
total_connection_count_.fetch_add(1, std::memory_order_relaxed);
if (!was_checked_) {
was_checked_ = true;
@ -251,6 +258,15 @@ void WebhookActor::on_socket_ready_async(td::Result<td::BufferedFd<td::SocketFd>
}
void WebhookActor::create_new_connections() {
if (!ip_address_.is_valid()) {
VLOG(webhook) << "Can't create new connections: IP address is not ready";
return;
}
if (url_.protocol_ != td::HttpUrl::Protocol::Http && !ssl_ctx_) {
VLOG(webhook) << "Can't create new connections: SSL context is not ready";
return;
}
size_t need_connections = queue_updates_.size();
if (need_connections > static_cast<size_t>(max_connections_)) {
need_connections = max_connections_;
@ -287,7 +303,7 @@ void WebhookActor::create_new_connections() {
<< td::tag("after", td::format::as_time(wakeup_at - now));
break;
}
flood->add_event(static_cast<td::int32>(now));
flood->add_event(now);
if (create_connection().is_error()) {
relax_wakeup_at(now + 1.0, "create_new_connections error");
return;
@ -382,7 +398,7 @@ void WebhookActor::load_updates() {
CHECK(update.id.is_valid());
auto &dest_ptr = update_map_[update.id];
if (dest_ptr != nullptr) {
LOG(ERROR) << "Receive duplicated event " << update.id << " from TQueue";
LOG(ERROR) << "Receive duplicate event " << update.id << " from TQueue";
continue;
}
dest_ptr = td::make_unique<Update>();
@ -487,7 +503,8 @@ void WebhookActor::on_update_error(td::TQueue::EventId event_id, td::Slice error
int next_delay = update.delay_;
int next_effective_delay = retry_after;
if (retry_after == 0 && update.fail_count_ > 0) {
next_delay = td::min(WEBHOOK_MAX_RESEND_TIMEOUT, next_delay * 2);
auto max_timeout = td::Random::fast(WEBHOOK_MAX_RESEND_TIMEOUT, WEBHOOK_MAX_RESEND_TIMEOUT * 2);
next_delay = td::min(max_timeout, next_delay * 2);
next_effective_delay = next_delay;
}
if (parameters_->shared_data_->get_unix_time(now) + next_effective_delay > update.expires_at_) {
@ -572,6 +589,11 @@ void WebhookActor::send_updates() {
}
void WebhookActor::handle(td::unique_ptr<td::HttpQuery> response) {
SCOPE_EXIT {
bool dummy = false;
td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), response, dummy);
};
auto connection_id = get_link_token();
if (response) {
VLOG(webhook) << "Got response from connection " << connection_id;
@ -600,10 +622,12 @@ void WebhookActor::handle(td::unique_ptr<td::HttpQuery> response) {
if (!method.empty() && method != "deletewebhook" && method != "setwebhook" && method != "close" &&
method != "logout" && !td::begins_with(method, "get")) {
VLOG(webhook) << "Receive request " << method << " in response to webhook";
auto query = td::make_unique<Query>(std::move(response->container_), td::MutableSlice(), false, false,
td::MutableSlice(), std::move(response->args_),
std::move(response->headers_), std::move(response->files_),
parameters_->shared_data_, response->peer_address_, false);
response->container_.emplace_back(PSLICE() << (tqueue_id_ & ((static_cast<td::int64>(1) << 54) - 1)));
auto token = response->container_.back().as_slice();
auto query = td::make_unique<Query>(
std::move(response->container_), token, tqueue_id_ >= (static_cast<td::int64>(1) << 54), false,
td::MutableSlice(), std::move(response->args_), std::move(response->headers_),
std::move(response->files_), parameters_->shared_data_, response->peer_address_, false);
auto promised_query = PromisedQueryPtr(query.release(), PromiseDeleter(td::Promise<td::unique_ptr<Query>>()));
send_closure(callback_, &Callback::send, std::move(promised_query));
}
@ -652,7 +676,7 @@ void WebhookActor::handle(td::unique_ptr<td::HttpQuery> response) {
if (need_close || close_connection) {
VLOG(webhook) << "Close connection " << connection_id;
connections_.erase(connection_ptr->id_);
total_connections_count_.fetch_sub(1, std::memory_order_relaxed);
total_connection_count_.fetch_sub(1, std::memory_order_relaxed);
} else {
ready_connections_.put(connection_ptr->to_list_node());
}
@ -667,11 +691,16 @@ void WebhookActor::handle(td::unique_ptr<td::HttpQuery> response) {
void WebhookActor::start_up() {
max_loaded_updates_ = max_connections_ * 2;
next_ip_address_resolve_time_ = last_success_time_ = td::Time::now() - 3600;
active_new_connection_flood_.add_limit(1, 10 * max_connections_);
active_new_connection_flood_.add_limit(5, 20 * max_connections_);
last_success_time_ = td::Time::now() - 2 * IP_ADDRESS_CACHE_TIME;
if (from_db_flag_) {
next_ip_address_resolve_time_ = td::Time::now() + td::Random::fast(0, IP_ADDRESS_CACHE_TIME);
} else {
next_ip_address_resolve_time_ = last_success_time_;
}
pending_new_connection_flood_.add_limit(1, 1);
active_new_connection_flood_.add_limit(0.5, 10);
pending_new_connection_flood_.add_limit(2, 1);
if (!parameters_->local_mode_) {
if (url_.protocol_ == td::HttpUrl::Protocol::Https || (parameters_->allow_http_ && url_.protocol_ == td::HttpUrl::Protocol::Http)) {
@ -682,15 +711,14 @@ void WebhookActor::start_up() {
} else {
CHECK(url_.protocol_ == td::HttpUrl::Protocol::Http);
VLOG(webhook) << "Can't create connection: HTTP is forbidden";
on_error(td::Status::Error("HTTPS url must be provided for webhook"));
on_error(td::Status::Error("An HTTPS URL must be provided for webhook"));
}
}
if (fix_ip_address_ && !stop_flag_) {
if (!ip_address_.is_valid()) {
on_error(td::Status::Error("Invalid IP address specified"));
} else if (!check_ip_address(ip_address_)) {
on_error(td::Status::Error(PSLICE() << "IP address " << ip_address_.get_ip_str() << " is reserved"));
auto check_status = check_ip_address(ip_address_);
if (check_status.is_error()) {
return on_error(std::move(check_status));
}
}
@ -699,6 +727,16 @@ void WebhookActor::start_up() {
on_webhook_verified();
}
if (url_.protocol_ != td::HttpUrl::Protocol::Http && !stop_flag_) {
// asynchronously create SSL context
td::Scheduler::instance()->run_on_scheduler(SharedData::get_webhook_certificate_scheduler_id(),
[actor_id = actor_id(this), cert_path = cert_path_](td::Unit) mutable {
send_closure(
actor_id, &WebhookActor::on_ssl_context_created,
td::SslCtx::create(cert_path, td::SslCtx::VerifyPeer::On));
});
}
yield();
}
@ -720,7 +758,7 @@ void WebhookActor::close() {
}
void WebhookActor::tear_down() {
total_connections_count_.fetch_sub(connections_.size(), std::memory_order_relaxed);
total_connection_count_.fetch_sub(connections_.size(), std::memory_order_relaxed);
}
void WebhookActor::on_webhook_verified() {
@ -731,24 +769,26 @@ void WebhookActor::on_webhook_verified() {
send_closure(callback_, &Callback::webhook_verified, std::move(ip_address_str));
}
bool WebhookActor::check_ip_address(const td::IPAddress &addr) const {
td::Status WebhookActor::check_ip_address(const td::IPAddress &addr) const {
if (!addr.is_valid()) {
return false;
return td::Status::Error("Invalid IP address specified");
}
if (parameters_->local_mode_) {
// allow any valid IP address
return true;
return td::Status::OK();
}
if (!addr.is_ipv4()) {
VLOG(webhook) << "Bad IP address (not IPv4): " << addr;
return false;
return td::Status::Error("IPv6-only addresses are not allowed");
}
return !addr.is_reserved();
if (addr.is_reserved()) {
return td::Status::Error(PSLICE() << "IP address " << addr.get_ip_str() << " is reserved");
}
return td::Status::OK();
}
void WebhookActor::on_error(td::Status status) {
VLOG(webhook) << "Receive webhook error " << status;
if (!was_checked_) {
if (!was_checked_ && !stop_flag_) {
CHECK(!callback_.empty());
send_closure(std::move(callback_), &Callback::webhook_closed, std::move(status));
stop_flag_ = true;

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -12,6 +12,7 @@
#include "td/net/HttpOutboundConnection.h"
#include "td/net/HttpQuery.h"
#include "td/net/SslCtx.h"
#include "td/net/SslStream.h"
#include "td/actor/actor.h"
@ -21,6 +22,7 @@
#include "td/utils/Container.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FloodControlFast.h"
#include "td/utils/HashTableUtils.h"
#include "td/utils/HttpUrl.h"
#include "td/utils/JsonBuilder.h"
#include "td/utils/List.h"
@ -31,7 +33,6 @@
#include "td/utils/VectorQueue.h"
#include <atomic>
#include <functional>
#include <memory>
#include <set>
#include <tuple>
@ -54,13 +55,18 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback {
WebhookActor(td::ActorShared<Callback> callback, td::int64 tqueue_id, td::HttpUrl url, td::string cert_path,
td::int32 max_connections, bool from_db_flag, td::string cached_ip_address, bool fix_ip_address,
td::string secret_token, std::shared_ptr<const ClientParameters> parameters);
WebhookActor(const WebhookActor &) = delete;
WebhookActor &operator=(const WebhookActor &) = delete;
WebhookActor(WebhookActor &&) = delete;
WebhookActor &operator=(WebhookActor &&) = delete;
~WebhookActor();
void update();
void close();
static td::int64 get_total_connections_count() {
return total_connections_count_;
static td::int64 get_total_connection_count() {
return total_connection_count_;
}
private:
@ -69,14 +75,14 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback {
static constexpr int WEBHOOK_MAX_RESEND_TIMEOUT = 60;
static constexpr int WEBHOOK_DROP_TIMEOUT = 60 * 60 * 23;
static std::atomic<td::uint64> total_connections_count_;
static std::atomic<td::uint64> total_connection_count_;
td::ActorShared<Callback> callback_;
td::int64 tqueue_id_;
bool tqueue_empty_ = false;
std::size_t last_pending_update_count_ = MIN_PENDING_UPDATES_WARNING;
td::HttpUrl url_;
td::string cert_path_;
const td::string cert_path_;
std::shared_ptr<const ClientParameters> parameters_;
double last_error_time_ = 0;
@ -122,8 +128,8 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback {
td::TQueue::EventId tqueue_offset_;
std::size_t max_loaded_updates_ = 0;
struct EventIdHash {
std::size_t operator()(td::TQueue::EventId event_id) const {
return std::hash<td::int32>()(event_id.value());
td::uint32 operator()(td::TQueue::EventId event_id) const {
return td::Hash<td::int32>()(event_id.value());
}
};
td::FlatHashMap<td::TQueue::EventId, td::unique_ptr<Update>, EventIdHash> update_map_;
@ -133,6 +139,7 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback {
double first_error_410_time_ = 0;
td::SslCtx ssl_ctx_;
td::IPAddress ip_address_;
td::int32 ip_generation_ = 0;
double next_ip_address_resolve_time_ = 0;
@ -176,6 +183,10 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback {
void resolve_ip_address();
void on_resolved_ip_address(td::Result<td::IPAddress> r_ip_address);
void on_ssl_context_created(td::Result<td::SslCtx> r_ssl_ctx);
td::Status create_webhook_error(td::Slice error_message, td::Status &&result, bool is_public);
td::Result<td::SslStream> create_ssl_stream();
td::Status create_connection() TD_WARN_UNUSED_RESULT;
td::Status create_connection(td::BufferedFd<td::SocketFd> fd) TD_WARN_UNUSED_RESULT;
@ -202,7 +213,7 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback {
void start_up() final;
bool check_ip_address(const td::IPAddress &addr) const;
td::Status check_ip_address(const td::IPAddress &addr) const;
void on_error(td::Status status);
void on_connection_error(td::Status error) final;

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -67,6 +67,7 @@ static void quit_signal_handler(int sig) {
static td::MemoryLog<1 << 20> memory_log;
void print_log() {
td::LogGuard log_guard;
auto buf = memory_log.get_buffer();
auto pos = memory_log.get_pos();
size_t tail_length = buf.size() - pos;
@ -85,19 +86,30 @@ void print_log() {
static std::atomic_bool has_failed{false};
static std::atomic_flag need_dump_statistics;
static void dump_stacktrace_signal_handler(int sig) {
if (has_failed) {
return;
}
td::LogGuard log_guard;
if (LOG_TAG != nullptr && *LOG_TAG) {
td::signal_safe_write(td::Slice(LOG_TAG));
td::signal_safe_write(td::Slice("\n"), false);
}
td::Stacktrace::print_to_stderr();
need_dump_statistics.clear();
}
static void fail_signal_handler(int sig) {
has_failed = true;
td::signal_safe_write_signal_number(sig);
td::Stacktrace::PrintOptions options;
options.use_gdb = true;
td::Stacktrace::print_to_stderr(options);
{
td::LogGuard log_guard;
td::signal_safe_write_signal_number(sig);
td::Stacktrace::PrintOptions options;
options.use_gdb = true;
td::Stacktrace::print_to_stderr(options);
}
print_log();
_Exit(EXIT_FAILURE);
}
@ -130,6 +142,7 @@ int main(int argc, char *argv[]) {
need_reopen_log.test_and_set();
need_quit.test_and_set();
need_change_verbosity_level.test_and_set();
need_dump_statistics.test_and_set();
need_dump_log.test_and_set();
td::Stacktrace::init();
@ -152,7 +165,7 @@ int main(int argc, char *argv[]) {
auto start_time = td::Time::now();
auto shared_data = std::make_shared<SharedData>();
auto parameters = std::make_unique<ClientParameters>();
parameters->version_ = "6.3.2";
parameters->version_ = "7.0";
parameters->shared_data_ = shared_data;
parameters->start_time_ = start_time;
auto net_query_stats = td::create_net_query_stats();
@ -291,7 +304,7 @@ int main(int argc, char *argv[]) {
options.add_checked_option('\0', "file-expiration-time",
PSLICE() << "downloaded files expire after this amount of seconds of not being used (defaults to " << parameters->file_expiration_timeout_seconds_ << ")",
td::OptionParser::parse_integer(parameters->file_expiration_timeout_seconds_));
options.add_checked_option('\0', "proxy",
"HTTP proxy server for outgoing webhook requests in the format http://host:port",
[&](td::Slice address) {
@ -461,29 +474,24 @@ int main(int argc, char *argv[]) {
// LOG(WARNING) << "Bot API server with commit " << td::GitInfo::commit() << ' '
// << (td::GitInfo::is_dirty() ? "(dirty)" : "") << " started";
LOG(WARNING) << "Bot API " << parameters->version_ << " server started";
LOG(WARNING) << "TDLight Bot API " << parameters->version_ << " server started";
// +3 threads for Td
// one thread for ClientManager and all Clients
// one thread for watchdogs
// one thread for slow HTTP connections
// one thread for DNS resolving
const int thread_count = 7;
td::ConcurrentScheduler sched(thread_count, cpu_affinity);
td::ConcurrentScheduler sched(SharedData::get_thread_count() - 1, cpu_affinity);
td::GetHostByNameActor::Options get_host_by_name_options;
get_host_by_name_options.scheduler_id = thread_count;
get_host_by_name_options.scheduler_id = SharedData::get_dns_resolver_scheduler_id();
parameters->get_host_by_name_actor_id_ =
sched.create_actor_unsafe<td::GetHostByNameActor>(0, "GetHostByName", std::move(get_host_by_name_options))
.release();
auto client_manager =
sched.create_actor_unsafe<ClientManager>(thread_count - 3, "ClientManager", std::move(parameters), token_range)
.release();
auto client_manager = sched
.create_actor_unsafe<ClientManager>(SharedData::get_client_scheduler_id(), "ClientManager",
std::move(parameters), token_range)
.release();
sched
.create_actor_unsafe<HttpServer>(
thread_count - 3, "HttpServer", http_ip_address, http_port,
SharedData::get_client_scheduler_id(), "HttpServer", http_ip_address, http_port,
[client_manager, shared_data] {
return td::ActorOwn<td::HttpInboundConnection::Callback>(
td::create_actor<HttpConnection>("HttpConnection", client_manager, shared_data));
@ -493,7 +501,7 @@ int main(int argc, char *argv[]) {
if (http_stat_port != 0) {
sched
.create_actor_unsafe<HttpServer>(
thread_count - 3, "HttpStatsServer", http_stat_ip_address, http_stat_port,
SharedData::get_client_scheduler_id(), "HttpStatsServer", http_stat_ip_address, http_stat_port,
[client_manager] {
return td::ActorOwn<td::HttpInboundConnection::Callback>(
td::create_actor<HttpStatConnection>("HttpStatConnection", client_manager));
@ -501,9 +509,9 @@ int main(int argc, char *argv[]) {
.release();
}
constexpr double WATCHDOG_TIMEOUT = 0.5;
auto watchdog_id =
sched.create_actor_unsafe<Watchdog>(thread_count - 2, "Watchdog", td::this_thread::get_id(), WATCHDOG_TIMEOUT);
constexpr double WATCHDOG_TIMEOUT = 0.25;
auto watchdog_id = sched.create_actor_unsafe<Watchdog>(SharedData::get_watchdog_scheduler_id(), "Watchdog",
td::this_thread::get_id(), WATCHDOG_TIMEOUT);
sched.start();
@ -556,8 +564,7 @@ int main(int argc, char *argv[]) {
if (!need_dump_log.test_and_set()) {
print_log();
auto guard = sched.get_main_guard();
send_closure(client_manager, &ClientManager::dump_statistics);
need_dump_statistics.clear();
}
double now = td::Time::now();
@ -566,16 +573,18 @@ int main(int argc, char *argv[]) {
next_cron_time = now;
}
next_cron_time += 1.0;
ServerCpuStat::update(now);
auto guard = sched.get_main_guard();
td::Scheduler::instance()->run_on_scheduler(SharedData::get_statistics_thread_id(),
[](td::Unit) { ServerCpuStat::update(td::Time::now()); });
}
if (now >= start_time + 600) {
auto guard = sched.get_main_guard();
send_closure(watchdog_id, &Watchdog::kick);
next_watchdog_kick_time = now + WATCHDOG_TIMEOUT / 2;
next_watchdog_kick_time = now + WATCHDOG_TIMEOUT / 10;
}
if (now > last_dump_time + 300.0) {
if (!need_dump_statistics.test_and_set() || now > last_dump_time + 300.0) {
last_dump_time = now;
auto guard = sched.get_main_guard();
send_closure(client_manager, &ClientManager::dump_statistics);