· 4 years ago · Jul 27, 2021, 03:40 PM
1#ifndef VK_MISC_CPPDEFS_HPP
2#define VK_MISC_CPPDEFS_HPP
3
4#if defined(__GNUC__)
5 #define _DECL_VK_EXPORT __attribute__((visibility("default")))
6 #define _DECL_VK_IMPORT __attribute__((visibility("default")))
7 #define _DECL_VK_HIDDEN __attribute__((visibility("hidden")))
8#elif defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(WIN32) || defined(_WIN32)
9 #define _DECL_VK_EXPORT __declspec(dllexport)
10 #define _DECL_VK_IMPORT __declspec(dllimport)
11 #define _DECL_VK_HIDDEN
12#else
13 #warning Unknown dynamic link import/export semantics.
14 #define _DECL_VK_EXPORT
15 #define _DECL_VK_IMPORT
16 #define _DECL_VK_HIDDEN
17#endif
18
19#if defined(VK_MISC_CPPDEFS_HPP)
20 #define vk_export _DECL_VK_EXPORT
21#else
22 #define vk_export _DECL_VK_IMPORT
23#endif
24#define vk_hidden _DECL_VK_HIDDEN
25
26#define VK_PRAGMA(P) _Pragma(#P)
27#define VK_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
28#define VK_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
29#define VK_DISABLE_GCC_WARNING(WARNING) VK_PRAGMA(GCC diagnostic ignored #WARNING)
30#define VK_PUSH_DISABLE_ALL_WARNINGS \
31 VK_PUSH_DISABLE_WARNINGS \
32 VK_DISABLE_GCC_WARNING(-Weffc++) \
33 VK_DISABLE_GCC_WARNING(-Wall) \
34 VK_DISABLE_GCC_WARNING(-Wconversion) \
35 VK_DISABLE_GCC_WARNING(-Wold - style - cast) \
36 VK_DISABLE_GCC_WARNING(-Wextra) \
37 VK_DISABLE_GCC_WARNING(-Wattributes) \
38 VK_DISABLE_GCC_WARNING(-Wimplicit - fallthrough) \
39 VK_DISABLE_GCC_WARNING(-Wnon - virtual - dtor) \
40 VK_DISABLE_GCC_WARNING(-Wreturn - type) \
41 VK_DISABLE_GCC_WARNING(-Wshadow) \
42 VK_DISABLE_GCC_WARNING(-Wunused - parameter) \
43 VK_DISABLE_GCC_WARNING(-Wunused - variable)
44
45#define VK_REALLY_INLINE inline __attribute__((always_inline))
46#define VK_NEVER_INLINE inline __attribute__((noinline))
47
48#define VK_LIKELY(expr) __builtin_expect(!!(expr), 1)
49#define VK_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
50
51#define VK_UNUSED(expr) (void)expr
52
53#define VK_DISABLE_COPY(x) \
54 x(const x&) = delete; \
55 x& operator=(const x&) = delete;
56
57#define VK_DISABLE_MOVE(x) \
58 x(x&&) = delete; \
59 x& operator=(x&&) = delete;
60
61#define VK_DISABLE_COPY_MOVE(x) \
62 VK_DISABLE_COPY(x) \
63 VK_DISABLE_MOVE(x)
64
65#endif// VK_MISC_CPPDEFS_HPP
66#ifndef VK_CONFIG_LOADER_HPP
67#define VK_CONFIG_LOADER_HPP
68
69#include "misc/cppdefs.hpp"
70
71#include <string>
72
73namespace vk {
74namespace config {
75/*!
76 * @brief Config class with its helper functions.
77 *
78 * Implemented as singleton to avoid multiply and senseless JSON file read.
79 * Please, don't try to use this class directly, use non-member functions
80 * instead.
81 */
82class loader
83{
84public:
85 VK_DISABLE_COPY_MOVE(loader)
86
87 static loader* load(std::string_view path);
88 static loader* get();
89
90 std::string& user_token_instance() noexcept { return m_user_token; }
91 const std::string& username() const noexcept { return m_username; }
92 const std::string& password() const noexcept { return m_password; }
93 const std::string& user_token() const noexcept { return m_user_token; }
94 const std::string& access_token() const noexcept { return m_access_token; }
95 const std::string& error_log_path() const noexcept { return m_error_log_path; }
96 const std::string& event_log_path() const noexcept { return m_event_log_path; }
97 int64_t num_workers() const noexcept { return m_num_workers; }
98
99private:
100 loader(std::string_view path);
101
102 std::string m_username{};
103 std::string m_password{};
104 std::string m_user_token{};
105 std::string m_access_token{};
106 std::string m_error_log_path{};
107 std::string m_event_log_path{};
108 int64_t m_num_workers{};
109
110 static loader* instance;
111};
112
113inline void load(std::string_view path) { loader::load(path); }
114inline void set_user_token(std::string_view token) { loader::get()->user_token_instance() = token; }
115inline std::string password() noexcept { return loader::get()->password(); }
116inline std::string username() noexcept { return loader::get()->username(); }
117inline std::string user_token() noexcept { return loader::get()->user_token(); }
118inline std::string access_token() noexcept { return loader::get()->access_token(); }
119inline std::string error_logpath() noexcept { return loader::get()->error_log_path(); }
120inline std::string event_logpath() noexcept { return loader::get()->event_log_path(); }
121inline int64_t num_workers() noexcept { return loader::get()->num_workers(); }
122
123}// namespace config
124}// namespace vk
125
126#endif// VK_CONFIG_LOADER_HPP
127#ifndef VK_METHODS_DOCS_HPP
128#define VK_METHODS_DOCS_HPP
129
130#include "document/common.hpp"
131#include "methods/utility/constructor.hpp"
132
133namespace vk {
134namespace method {
135/*!
136 * @brief The docs methods representation.
137 *
138 * Please, inherit this class to add new methods.
139 */
140class docs
141{
142public:
143 explicit docs();
144 explicit docs(std::string_view user_token);
145 ~docs();
146
147 void edit(int64_t owner_id, int64_t doc_id, std::string_view title, std::initializer_list<std::string> tags = {}) const;
148 void remove(int64_t owner_id, int64_t doc_id) const;
149 std::string get_upload_server(int64_t group_id) const;
150 std::string get_wall_upload_server(int64_t group_id) const;
151 std::string get_messages_upload_server(std::string_view type, int64_t peer_id) const;
152 vk::attachment::attachments_t search(std::string_view query, int64_t count) const;
153 std::shared_ptr<vk::attachment::audio_message> save_audio_message(std::string_view file, std::string_view raw_server) const;
154
155protected:
156 std::shared_ptr<simdjson::dom::parser> m_parser;
157 mutable method::group_constructor m_group_constructor;
158 mutable method::user_constructor m_user_constructor;
159 document::common m_document;
160};
161
162}// namespace method
163}// namespace vk
164
165#endif// VK_METHODS_DOCS_HPP
166#ifndef VK_METHODS_AUDIO_HPP
167#define VK_METHODS_AUDIO_HPP
168
169#include "document/common.hpp"
170#include "methods/utility/constructor.hpp"
171
172namespace vk {
173namespace method {
174/*!
175 * @brief The audio methods representation.
176 *
177 * Please, inherit this class to add new methods.
178 */
179class audio
180{
181public:
182 explicit audio();
183 explicit audio(std::string_view user_token);
184 ~audio();
185
186 std::string get_upload_server() const;
187 void save(std::string_view artist, std::string_view title, std::string_view filename, std::string_view raw_server) const;
188
189protected:
190 std::shared_ptr<simdjson::dom::parser> m_parser;
191 document::common m_document;
192 mutable method::group_constructor m_group_constructor;
193 mutable method::user_constructor m_user_constructor;
194};
195
196}// namespace method
197}// namespace vk
198
199#endif// VK_METHODS_AUDIO_HPP
200#ifndef VK_METHODS_UTILITY_CONSTRUCTOR_HPP
201#define VK_METHODS_UTILITY_CONSTRUCTOR_HPP
202
203#include "utility.hpp"
204#include "misc/cppdefs.hpp"
205#include "net/network.hpp"
206
207namespace vk {
208namespace method {
209namespace policy {
210
211class group_api
212{
213public:
214 static std::string execute(const vk::method::utility& method_util, std::string_view method, std::map<std::string, std::string>& params)
215 {
216 return method_util.call(method, method_util.group_args(params));
217 }
218};
219
220class user_api
221{
222public:
223 static std::string execute(const vk::method::utility& method_util, std::string_view method, std::map<std::string, std::string>& params)
224 {
225 return method_util.call(method, method_util.user_args(params));
226 }
227};
228
229class do_not_use_api_link
230{
231public:
232 static std::string execute(const vk::method::utility& method_util, std::string_view method, std::map<std::string, std::string>& params)
233 {
234 VK_UNUSED(method_util);
235 return vk::network::request(method, params);
236 }
237};
238
239} // namespace policy
240
241template <typename ExecutionPolicy>
242class constructor
243{
244public:
245 explicit constructor()
246 : m_method_util() {}
247
248 explicit constructor(std::string_view user_token)
249 : m_method_util(user_token) {}
250
251 constructor& method(std::string_view method)
252 {
253 m_method = method;
254 return *this;
255 }
256
257 constructor& param(std::string_view key, std::string_view value)
258 {
259 m_params.emplace(key, value);
260 return *this;
261 }
262
263 constructor& append_map(std::map<std::string, std::string> additional_params)
264 {
265 m_params.merge(std::move(additional_params));
266 return *this;
267 }
268
269 std::string perform_request()
270 {
271 const std::string result = ExecutionPolicy::execute(m_method_util, m_method, m_params);
272 m_params.clear();
273 return result;
274 }
275
276private:
277 vk::method::utility m_method_util;
278 std::string m_method{};
279 std::map<std::string, std::string> m_params{};
280};
281
282using user_constructor = constructor<policy::user_api>;
283using group_constructor = constructor<policy::group_api>;
284using raw_constructor = constructor<policy::do_not_use_api_link>;
285
286}// namespace method
287}// namespace vk
288
289#endif// VK_METHODS_UTILITY_CONSTRUCTOR_HPP
290#ifndef VK_METHODS_UTILITY_HPP
291#define VK_METHODS_UTILITY_HPP
292
293#include <map>
294#include <memory>
295#include <string>
296
297namespace simdjson {
298namespace dom {
299class object;
300class parser;
301}// namespace dom
302}// namespace simdjson
303
304namespace vk {
305namespace method {
306/*!
307 * @brief The container of common functions and constants needed by various methods.
308 */
309class utility
310{
311public:
312 utility();
313 utility(std::string_view user_token);
314 ~utility();
315
316 std::map<std::string, std::string>& user_args(std::map<std::string, std::string>& params) const;
317 std::map<std::string, std::string>& group_args(std::map<std::string, std::string>& params) const;
318 /*!
319 * Move or copy is your choice.
320 */
321 std::string call(std::string_view method, std::map<std::string, std::string> params) const;
322 std::string append_url(std::string_view method) const;
323
324 static inline const int64_t chat_id_constant = 2000000000;
325
326private:
327 std::string m_user_token;
328 std::string m_access_token;
329 mutable std::shared_ptr<simdjson::dom::parser> m_parser;
330};
331
332}// namespace method
333}// namespace vk
334
335#endif// VK_METHODS_UTILITY_HPP
336#ifndef VK_METHODS_UTILITY_MESSAGE_CONSTRUCTOR_HPP
337#define VK_METHODS_UTILITY_MESSAGE_CONSTRUCTOR_HPP
338
339#include "attachment/attachment.hpp"
340#include "methods/utility/constructor.hpp"
341
342namespace vk {
343namespace method {
344/*!
345 * @brief Helper to work with message.send method.
346 */
347class message_constructor
348{
349public:
350 static inline bool disable_mentions = true;
351 static inline bool enable_mentions = false;
352
353 message_constructor(bool disable_mentions_flag);
354
355 message_constructor& param(std::string_view lhs, std::string_view rhs);
356 /*!
357 * Move or copy is your choice.
358 */
359 message_constructor& append_map(std::map<std::string, std::string> additional_params);
360 message_constructor& attachments(attachment::attachments_t attachments);
361 std::string execute();
362
363private:
364 std::string append_attachments_impl(attachment::attachments_t attachments) const;
365
366 group_constructor m_constructor;
367};
368
369}// namespace method
370}// namespace vk
371
372#endif// VK_METHODS_UTILITY_MESSAGE_CONSTRUCTOR_HPP
373#ifndef VK_METHODS_MESSAGES_HPP
374#define VK_METHODS_MESSAGES_HPP
375
376#include "document/common.hpp"
377#include "methods/utility/constructor.hpp"
378
379namespace vk {
380namespace keyboard {
381class layout;
382}// namespace keyboard
383}// namespace vk
384
385namespace vk {
386struct conversation_member
387{
388 std::string first_name;
389 std::string last_name;
390 int64_t id;
391 bool online;
392};
393
394using conversation_member_list = std::vector<conversation_member>;
395
396namespace method {
397/*!
398 * @brief The messages methods representation.
399 *
400 * Please, inherit this class to add new methods.
401 */
402class messages
403{
404public:
405 explicit messages(bool disable_mentions_flag);
406 messages() = delete;
407 ~messages();
408
409 static inline bool disable_mentions = true;
410 static inline bool enable_mentions = false;
411
412 void send(int64_t peer_id, std::string_view text) const;
413 void send(int64_t peer_id, std::string_view text, attachment::attachments_t list) const;
414 void send(int64_t peer_id, std::string_view text, std::map<std::string, std::string> raw_parameters) const;
415 void send(int64_t peer_id, std::string_view text, std::string_view layout) const;
416 void remove_chat_user(int64_t chat_id, int64_t user_id) const;
417 void edit_chat(int64_t chat_id, std::string_view new_title) const;
418 void create_chat(std::string_view title, int64_t group_id, std::vector<size_t> user_ids);
419 void add_chat_user(int64_t chat_id, int64_t user_id);
420 void delete_chat_photo(int64_t chat_id, int64_t group_id) const;
421 void pin(int64_t peer_id, int64_t message_id, int64_t conversation_message_id) const;
422 void set_chat_photo(std::string_view filename, std::string_view raw_server) const;
423 conversation_member_list get_conversation_members(int64_t peer_id) const;
424
425protected:
426 bool m_disable_mentions_flag;
427 std::shared_ptr<simdjson::dom::parser> m_parser;
428 document::common m_document;
429 mutable method::group_constructor m_group_constructor;
430 mutable method::user_constructor m_user_constructor;
431};
432
433}// namespace method
434}// namespace vk
435
436#endif// VK_METHODS_MESSAGES_HPP
437#ifndef VK_METHODS_UTILS_HPP
438#define VK_METHODS_UTILS_HPP
439
440#include "methods/utility/constructor.hpp"
441
442namespace vk {
443namespace method {
444/*!
445 * @brief The utils methods representation.
446 *
447 * Please, inherit this class to add new methods.
448 */
449class utils
450{
451public:
452 utils();
453 ~utils();
454
455 bool check_link(std::string_view url) const;
456 std::string get_short_link(std::string_view url) const;
457 int64_t resolve_screen_name(std::string_view screen_name) const;
458
459protected:
460 std::shared_ptr<simdjson::dom::parser> m_parser;
461 mutable method::group_constructor m_group_constructor;
462};
463
464}// namespace method
465}// namespace vk
466
467#endif// VK_METHODS_UTILS_HPP
468#ifndef VK_METHODS_PHOTOS_HPP
469#define VK_METHODS_PHOTOS_HPP
470
471#include "document/common.hpp"
472#include "methods/utility/constructor.hpp"
473
474namespace vk {
475namespace method {
476/*!
477 * @brief The photos methods representation.
478 *
479 * Please, inherit this class to add new methods.
480 */
481class photos
482{
483public:
484 explicit photos();
485 explicit photos(std::string_view user_token);
486 ~photos();
487
488 std::string get_messages_upload_server(int64_t peer_id) const;
489 std::string get_chat_upload_server(int64_t chat_id, int64_t crop = 512) const;
490 vk::attachment::attachments_t search(std::string_view query, int64_t count) const;
491 std::shared_ptr<vk::attachment::photo> save_messages_photo(std::string_view filename, std::string_view raw_server) const;
492
493protected:
494 std::shared_ptr<simdjson::dom::parser> m_parser;
495 mutable method::group_constructor m_group_constructor{};
496 mutable method::user_constructor m_user_constructor{};
497 document::common m_document;
498};
499
500}// namespace method
501}// namespace vk
502
503#endif// VK_METHODS_PHOTOS_HPP
504#ifndef VK_METHODS_VIDEO_HPP
505#define VK_METHODS_VIDEO_HPP
506
507#include "document/common.hpp"
508#include "methods/utility/constructor.hpp"
509
510namespace vk {
511namespace method {
512/*!
513 * @brief The video methods representation.
514 *
515 * Please, inherit this class to add new methods.
516 */
517class video
518{
519public:
520 explicit video();
521 explicit video(std::string_view user_token);
522 ~video();
523
524 vk::attachment::attachments_t search(std::string_view query, int64_t count) const;
525 void save_by_link(std::string_view url) const;
526
527protected:
528 mutable method::user_constructor m_user_constructor;
529 document::common m_document;
530};
531
532}// namespace method
533}// namespace vk
534
535#endif// VK_METHODS_VIDEO_HPP
536#ifndef VK_METHODS_GROUPS_HPP
537#define VK_METHODS_GROUPS_HPP
538
539#include "methods/utility/constructor.hpp"
540
541#include <memory>
542
543namespace vk {
544namespace method {
545/*!
546 * @brief The groups methods representation.
547 *
548 * Please, inherit this class to add new methods.
549 */
550class groups
551{
552public:
553 explicit groups();
554 ~groups();
555
556 int64_t get_by_id() const;
557 simdjson::dom::object get_long_poll_server(int64_t group_id) const;
558
559protected:
560 mutable method::group_constructor m_group_constructor;
561 std::shared_ptr<simdjson::dom::parser> m_parser;
562};
563
564}// namespace method
565}// namespace vk
566
567#endif// VK_METHODS_GROUPS_HPP
568#ifndef VK_LONG_POLL_API_HPP
569#define VK_LONG_POLL_API_HPP
570
571#include "events/common_event.hpp"
572#include "long_poll/data.hpp"
573#include "methods/groups.hpp"
574#include "processing/task_queue.hpp"
575
576namespace vk {
577namespace long_poll {
578/*!
579 * @brief Class to interact with long poll mechanism.
580 */
581class api
582{
583public:
584 api(int64_t update_interval = 600);
585 ~api() = default;
586
587 VK_DISABLE_COPY_MOVE(api)
588
589 using events_t = std::vector<std::unique_ptr<event::common>>;
590 /*!
591 * @brief Get long poll server.
592 * @return parsed data.
593 */
594 data server() const;
595 /*!
596 * @brief Try get updates.
597 * @return vector with `common` update objects.
598 *
599 * In the case, when no updates were returned, the request is executed again.
600 */
601 events_t listen(data& lp_data, int8_t timeout = 60) const;
602
603 template <typename ExecutionPolicy>
604 void on_event(std::string_view event_type, const event::common& event, ExecutionPolicy executor)
605 {
606 if (event.on_type(event_type))
607 {
608 queue(executor);
609 }
610 }
611 /*!
612 * @brief Push task to thread pool queue.
613 */
614 template <typename Function>
615 void queue(Function&& function)
616 {
617 m_task_queue.push_void_task(std::move(function));
618 }
619 /*!
620 * @brief Pop and execute task from thread pool queue.
621 */
622 void run();
623
624private:
625 std::shared_ptr<simdjson::dom::parser> m_parser;
626 /*!
627 * @brief Class with group long poll methods.
628 */
629 processing::task_queue m_task_queue;
630 mutable method::raw_constructor m_raw_method;
631 method::groups m_groups;
632 int64_t m_group_id;
633 int64_t m_update_interval;
634};
635
636}// namespace long_poll
637}// namespace vk
638
639#endif// VK_LONG_POLL_API_HPP
640#ifndef VK_LONG_POLL_DATA_HPP
641#define VK_LONG_POLL_DATA_HPP
642
643#include <string>
644
645namespace vk {
646namespace long_poll {
647/*!
648 * @brief Wrapper for data returned by group long poll.
649 */
650struct data
651{
652 std::string key;
653 std::string server;
654 std::string ts;
655};
656
657}// namespace long_poll
658}// namespace vk
659
660#endif// VK_LONG_POLL_DATA_HPP
661#ifndef VK_NET_NETWORK_HPP
662#define VK_NET_NETWORK_HPP
663
664#include <map>
665#include <string_view>
666
667namespace vk {
668namespace network {
669/*!
670 * @brief Execute HTTP POST request.
671 * @return response output.
672 *
673 * @example request("https://www.example.com?", {{"q","text"},{"length","50"}});
674 */
675std::string request(std::string_view host, const std::map<std::string, std::string>& target = {});
676/*!
677 * @brief Download file from server to filename.
678 * @return -1 in case, when file was not created or opened, 0 otherwise.
679 */
680size_t download(std::string_view filename, std::string_view server);
681/*!
682 * @brief Upload file from filename to server.
683 * @return upload response.
684 */
685std::string upload(std::string_view field_name, std::string_view filename, std::string_view server);
686/*!
687 * @brief Execute HTTP POST request with text data.
688 * @return response output.
689 */
690std::string request_data(std::string_view host, std::string_view data);
691
692}// namespace network
693}// namespace vk
694
695#endif// VK_NET_NETWORK_HPP
696#ifndef VK_DOCUMENT_COMMON_HPP
697#define VK_DOCUMENT_COMMON_HPP
698
699#include "attachment/attachment.hpp"
700#include "methods/utility/constructor.hpp"
701
702namespace simdjson {
703namespace dom {
704class parser;
705class object;
706}// namespace dom
707}// namespace simdjson
708
709namespace vk {
710namespace document {
711/*!
712 * @brief The base class for @ref vk::docs, @ref vk::photos and @ref vk::video.
713 */
714class common
715{
716public:
717 explicit common();
718 explicit common(std::string_view user_token);
719 ~common();
720
721 /*!
722 * @brief Upload file to server.
723 * @return parsed JSON response.
724 */
725 simdjson::dom::object upload(std::string_view filename, std::string_view server, std::string_view field_name) const;
726 /*!
727 * @brief Search attachments of requested type.
728 * @param method - method name (photos.search, video.search or docs.search)
729 * @param query - search query
730 * @param count - maximum count of documents to search
731 * @return vector of attachments.
732 */
733 vk::attachment::attachments_t search(std::string_view method, std::string_view query, int64_t count) const;
734
735private:
736 std::shared_ptr<simdjson::dom::parser> m_parser;
737 mutable method::group_constructor m_group_constructor;
738};
739
740}// namespace document
741}// namespace vk
742
743#endif// VK_DOCUMENT_COMMON_HPP
744#ifndef VK_ATTACHMENT_HPP
745#define VK_ATTACHMENT_HPP
746
747#include "exception/exception.hpp"
748#include "string_utils/string_utils.hpp"
749
750#include <memory>
751#include <string>
752#include <vector>
753
754namespace vk {
755/*!
756 * @namespace Different attachment types and cast function to it.
757 */
758namespace attachment {
759
760class base
761{
762public:
763 explicit base(std::string_view type, int32_t owner_id, int32_t id)
764 : m_attachment_type(type)
765 , m_owner_id(owner_id)
766 , m_id(id) {}
767
768 virtual ~base() = default;
769
770 std::string value() const
771 {
772 return string_utils::format("{}{}_{}", m_attachment_type, m_owner_id, m_id);
773 }
774
775 const std::string& type() const noexcept
776 {
777 return m_attachment_type;
778 }
779
780private:
781 std::string m_attachment_type;
782 int32_t m_owner_id;
783 int32_t m_id;
784};
785
786class photo : public base
787{
788public:
789 explicit photo(int32_t owner_id, int32_t id)
790 : base("photo", owner_id, id) {}
791};
792
793class video : public base
794{
795public:
796 explicit video(int32_t owner_id, int32_t id)
797 : base("video", owner_id, id) {}
798};
799
800class audio : public base
801{
802public:
803 explicit audio(int32_t owner_id, int32_t id)
804 : base("audio", owner_id, id) {}
805};
806
807class document : public base
808{
809public:
810 explicit document(int32_t owner_id, int32_t id, std::string_view url)
811 : base("doc", owner_id, id)
812 , m_raw_url(url) {}
813
814 const std::string& raw_url() const noexcept
815 {
816 return m_raw_url;
817 }
818
819private:
820 std::string m_raw_url;
821};
822
823class wall : public base
824{
825public:
826 explicit wall(int32_t id, int32_t from_id)
827 : base("wall", from_id, id) {}
828};
829
830class audio_message : public base
831{
832public:
833 explicit audio_message(int32_t owner_id, int32_t id, std::string_view raw_ogg, std::string_view raw_mp3)
834 : base("audio_message", owner_id, id)
835 , m_raw_ogg(raw_ogg)
836 , m_raw_mp3(raw_mp3) {}
837
838 const std::string& raw_ogg() const noexcept
839 {
840 return m_raw_ogg;
841 }
842
843 const std::string& raw_mp3() const noexcept
844 {
845 return m_raw_mp3;
846 }
847
848private:
849 std::string m_raw_ogg;
850 std::string m_raw_mp3;
851};
852
853template <typename Attachment>
854std::shared_ptr<Attachment> cast(const std::shared_ptr<base>& pointer)
855{
856 if (auto* casted = static_cast<Attachment*>(pointer.get())) {
857 return std::shared_ptr<Attachment>(pointer, casted);
858 }
859
860 throw exception::bad_cast_error<base, Attachment>();
861}
862
863using attachments_t = std::vector<std::shared_ptr<attachment::base>>;
864
865}// namespace attachment
866}// namespace vk
867
868#endif// VK_ATTACHMENT_HPP
869#ifndef VK_KEYBOARD_BUTTONS_OPEN_APP_HPP
870#define VK_KEYBOARD_BUTTONS_OPEN_APP_HPP
871
872#include "string_utils/string_utils.hpp"
873
874namespace vk {
875namespace keyboard {
876namespace button {
877
878class open_app
879{
880public:
881 open_app(int64_t app_id, int64_t owner_id, std::string_view hash, std::string_view label)
882 : m_app_id(app_id)
883 , m_owner_id(owner_id)
884 , m_hash(hash)
885 , m_label(label) {}
886
887 std::string serialize() const
888 {
889 return string_utils::format(
890 R"__({"action":{"type":"open_app","app_id":{},"owner_id":{},"hash":"{}","label":"{}"}})__",
891 m_app_id,
892 m_owner_id,
893 m_hash,
894 m_label);
895 }
896
897private:
898 int64_t m_app_id;
899 int64_t m_owner_id;
900 std::string m_hash;
901 std::string m_label;
902};
903
904}// namespace button
905}// namespace keyboard
906}// namespace vk
907
908#endif// VK_KEYBOARD_BUTTONS_OPEN_APP_HPP
909#ifndef VK_KEYBOARD_BUTTONS_TEXT_HPP
910#define VK_KEYBOARD_BUTTONS_TEXT_HPP
911
912#include "keyboard/colors.hpp"
913#include "string_utils/string_utils.hpp"
914
915namespace vk {
916namespace keyboard {
917namespace button {
918
919class text
920{
921public:
922 text(color selected_color, std::string_view payload_data)
923 : m_selected_color(selected_color)
924 , m_payload_data(payload_data) {}
925
926 std::string serialize() const
927 {
928 const char* color = keyboard::get_color(m_selected_color);
929 return string_utils::format(
930 R"__({"action":{"type":"text","payload":"{\"button\":\"1\"}","label":"{}"},"color":"{}"})__",
931 m_payload_data,
932 color);
933 }
934
935private:
936 color m_selected_color = color::none;
937 std::string m_payload_data;
938};
939
940}// namespace button
941}// namespace keyboard
942}// namespace vk
943
944#endif// VK_KEYBOARD_BUTTONS_TEXT_HPP
945#ifndef VK_KEYBOARD_BUTTONS_LOCATION_HPP
946#define VK_KEYBOARD_BUTTONS_LOCATION_HPP
947
948#include <string>
949
950namespace vk {
951namespace keyboard {
952namespace button {
953
954class location
955{
956public:
957 const char* serialize() const noexcept
958 {
959 return R"__({"action":{"type":"location","payload":"{\"button\":\"1\"}"}})__";
960 }
961};
962
963}// namespace button
964}// namespace keyboard
965}// namespace vk
966
967#endif// VK_KEYBOARD_BUTTONS_LOCATION_HPP
968#ifndef VK_KEYBOARD_BUTTONS_VK_PAY_HPP
969#define VK_KEYBOARD_BUTTONS_VK_PAY_HPP
970
971#include "string_utils/string_utils.hpp"
972
973namespace vk {
974namespace keyboard {
975namespace button {
976
977class vk_pay
978{
979public:
980 vk_pay(std::string_view hash)
981 : m_hash(hash) {}
982
983 std::string serialize() const
984 {
985 return string_utils::format(R"({"action":{"type":"vkpay","hash":"{}"}})", m_hash);
986 }
987
988private:
989 std::string m_hash;
990};
991}// namespace button
992}// namespace keyboard
993}// namespace vk
994
995#endif// VK_KEYBOARD_BUTTONS_VK_PAY_HPP
996#ifndef VK_KEYBOARD_LAYOUT_HPP
997#define VK_KEYBOARD_LAYOUT_HPP
998
999#include "keyboard/buttons/location.hpp"
1000#include "keyboard/buttons/open_app.hpp"
1001#include "keyboard/buttons/text.hpp"
1002#include "keyboard/buttons/vk_pay.hpp"
1003#include "keyboard/flags.hpp"
1004
1005#include <variant>
1006
1007namespace vk {
1008namespace keyboard {
1009
1010using any_button = std::variant<button::text, button::vk_pay, button::open_app, button::location>;
1011
1012/*!
1013 * @brief The buttons grid representation.
1014 */
1015class layout
1016{
1017public:
1018 layout() = default;
1019 layout(keyboard::flag flags);
1020
1021 /*!
1022 * @brief Add row by passing `std::vector`.
1023 */
1024 void add_row(const std::vector<any_button>& row);
1025 /*!
1026 * @brief Convert buttons data to JSON schema.
1027 * @return JSON representation.
1028 */
1029 void serialize();
1030 std::string get() const noexcept;
1031 bool has_flag(keyboard::flag flag) const noexcept;
1032
1033private:
1034 std::string m_serialized{};
1035 /*!
1036 * @brief Button grid container.
1037 *
1038 * Example: for 2x2 layout: [[button1,button2],[button3,button4]].
1039 */
1040 std::vector<std::vector<any_button>> m_buttons{};
1041 flag m_flags = vk::keyboard::flag::none;
1042};
1043
1044}// namespace keyboard
1045}// namespace vk
1046
1047#endif// VK_KEYBOARD_LAYOUT_HPP
1048#ifndef VK_KEYBOARD_FLAGS_HPP
1049#define VK_KEYBOARD_FLAGS_HPP
1050
1051namespace vk {
1052namespace keyboard {
1053
1054enum class flag : unsigned char
1055{
1056 none = (1 << 0),
1057 in_line = (1 << 1),
1058 one_time = (1 << 2)
1059};
1060
1061constexpr unsigned char operator & (flag lhs, flag rhs) noexcept
1062{
1063 return static_cast<unsigned char>(lhs) & static_cast<unsigned char>(rhs);
1064}
1065
1066}// namespace keyboard
1067}// namespace vk
1068
1069#endif// VK_KEYBOARD_FLAGS_HPP
1070#ifndef VK_KEYBOARD_COLORS_HPP
1071#define VK_KEYBOARD_COLORS_HPP
1072
1073namespace vk {
1074namespace keyboard {
1075namespace colors {
1076
1077static inline constexpr char red[] = "negative";
1078static inline constexpr char green[] = "positive";
1079static inline constexpr char blue[] = "primary";
1080static inline constexpr char white[] = "secondary";
1081}// namespace color_data
1082
1083enum class color : unsigned char
1084{
1085 none = (1 << 0),
1086 red = (1 << 1),
1087 green = (1 << 2),
1088 blue = (1 << 3),
1089 white = (1 << 4)
1090};
1091
1092constexpr const char* get_color(color c) noexcept
1093{
1094 switch (c) {
1095 case color::red: return colors::red;
1096 case color::green: return colors::green;
1097 case color::blue: return colors::blue;
1098 default: return colors::white;
1099 }
1100}
1101
1102}// namespace keyboard
1103}// namespace vk
1104
1105#endif// VK_KEYBOARD_COLORS_HPP
1106#ifndef VK_PROCESSING_TASK_QUEUE_HPP
1107#define VK_PROCESSING_TASK_QUEUE_HPP
1108
1109#include "misc/cppdefs.hpp"
1110
1111#include <condition_variable>
1112#include <deque>
1113#include <future>
1114#include <iostream>
1115#include <sstream>
1116#include <vector>
1117
1118namespace vk {
1119namespace processing {
1120
1121class task_queue
1122{
1123 enum class stop_policy
1124 {
1125 wait_for_queue_completion,
1126 stop_after_current_task
1127 };
1128
1129public:
1130 VK_DISABLE_COPY_MOVE(task_queue)
1131
1132 using exception_callback_t = std::function<void(std::exception_ptr)>;
1133
1134 explicit task_queue(size_t num_workers = std::thread::hardware_concurrency());
1135
1136 task_queue& set_exception_handler(exception_callback_t handler);
1137
1138 task_queue& set_num_workers(size_t num);
1139
1140 void start();
1141 void stop();
1142
1143 template <typename Function, typename... Args>
1144 bool push_void_task(Function&& fn, Args&&... args);
1145
1146 template <typename Function, typename... Args, typename InvokeTaskType = std::invoke_result_t<Function, Args...>>
1147 std::pair<bool, std::future<InvokeTaskType>> push_future_task(Function&& fn, Args&&... args);
1148
1149 void wait_for_completion();
1150
1151 ~task_queue();
1152
1153private:
1154 void set_default_exception_handler();
1155 void init_workers(size_t num);
1156
1157 std::condition_variable m_notifier;
1158 std::deque<std::function<void()>> m_tasks;
1159 exception_callback_t m_on_exception;
1160 std::mutex m_locker;
1161 std::vector<std::thread> m_workers;
1162 size_t m_num_workers;
1163 stop_policy m_stop_policy = stop_policy::stop_after_current_task;
1164 std::atomic<bool> m_running;
1165 std::atomic<bool> m_stop_requested;
1166};
1167
1168inline task_queue::task_queue(size_t num_workers)
1169 : m_num_workers(num_workers)
1170 , m_running(false)
1171 , m_stop_requested(false)
1172{
1173 set_default_exception_handler();
1174}
1175
1176inline task_queue& task_queue::set_exception_handler(task_queue::exception_callback_t handler)
1177{
1178 m_on_exception = std::move(handler);
1179 return *this;
1180}
1181
1182inline task_queue& task_queue::set_num_workers(size_t num)
1183{
1184 if (m_running) {
1185 throw std::runtime_error("Attempt to change number of workers while running");
1186 }
1187 m_num_workers = num;
1188 return *this;
1189}
1190
1191inline void task_queue::start()
1192{
1193 if (m_running) {
1194 return;
1195 }
1196 m_running = true;
1197 init_workers(m_num_workers);
1198}
1199
1200inline void task_queue::stop()
1201{
1202 if (!m_running) {
1203 return;
1204 }
1205 m_stop_requested = true;
1206 m_running = false;
1207 m_notifier.notify_all();
1208 for (auto& w : m_workers) {
1209 w.join();
1210 }
1211 m_workers.clear();
1212 m_stop_requested = false;
1213}
1214
1215template <typename Function, typename... Args>
1216bool task_queue::push_void_task(Function&& fn, Args&&... args)
1217{
1218 if (m_stop_requested) {
1219 return false;
1220 }
1221
1222 std::unique_lock<decltype(m_locker)> lock(m_locker);
1223 m_tasks.emplace_back([this, fn = std::forward<Function>(fn), tp = std::make_tuple(std::forward<Args>(args)...)]() mutable {
1224 try {
1225 std::apply(std::forward<Function>(fn), std::forward<decltype(tp)>(tp));
1226 } catch (...) {
1227 m_on_exception(std::current_exception());
1228 }
1229 });
1230 m_locker.unlock();
1231 m_notifier.notify_one();
1232
1233 return true;
1234}
1235
1236inline void task_queue::wait_for_completion()
1237{
1238 if (!m_running) {
1239 return;
1240 }
1241 m_stop_policy = stop_policy::wait_for_queue_completion;
1242 stop();
1243 m_stop_policy = stop_policy::stop_after_current_task;
1244 start();
1245}
1246
1247inline task_queue::~task_queue()
1248{
1249 stop();
1250}
1251
1252void task_queue::set_default_exception_handler()
1253{
1254 m_on_exception = [](std::exception_ptr ex_ptr) -> void {
1255 if (!ex_ptr) {
1256 return;
1257 }
1258
1259 std::ostringstream stream;
1260 stream << "[task_queue] Exception from thread (" << std::this_thread::get_id() << "): ";
1261
1262 try {
1263 std::rethrow_exception(ex_ptr);
1264 } catch (std::exception& ex) {
1265 stream << ex.what();
1266 } catch (...) {
1267 stream << "Unknown exception";
1268 }
1269
1270 std::cerr << stream.str() << std::endl;
1271 };
1272}
1273
1274inline void task_queue::init_workers(size_t num)
1275{
1276 for (size_t i = 0; i < num; ++i) {
1277 m_workers.emplace_back([this]() {
1278 while (true) {
1279 std::unique_lock<decltype(m_locker)> locker(m_locker);
1280 m_notifier.wait(locker, [this] {
1281 return !m_tasks.empty() || m_stop_requested;
1282 });
1283 if (m_stop_requested) {
1284 if (m_tasks.empty() || m_stop_policy == stop_policy::stop_after_current_task) {
1285 return;
1286 }
1287 }
1288 auto task = std::move(m_tasks.front());
1289 m_tasks.pop_front();
1290 locker.unlock();
1291 task();
1292 }
1293 });
1294 }
1295}
1296
1297template <typename Function, typename... Args, typename InvokeTaskType>
1298inline std::pair<bool, std::future<InvokeTaskType>> task_queue::push_future_task(Function&& fn, Args&&... args)
1299{
1300 if (m_stop_requested) {
1301 return {false, std::future<InvokeTaskType>()};
1302 }
1303 auto result_promise = std::make_shared<std::promise<InvokeTaskType>>();
1304 auto result_future = result_promise->get_future();
1305 std::unique_lock<decltype(m_locker)> lock(m_locker);
1306
1307 m_tasks.emplace_back([fn = std::forward<Function>(fn),
1308 tp = std::make_tuple(std::forward<Args>(args)...),
1309 promise = std::move(result_promise)]() mutable {
1310 if constexpr (std::is_void_v<InvokeTaskType>) {
1311 try {
1312 std::apply(std::forward<Function>(fn), std::forward<decltype(tp)>(tp));
1313 promise->set_value();
1314 } catch (...) {
1315 promise->set_exception(std::current_exception());
1316 }
1317 } else {
1318 try {
1319 promise->set_value(std::apply(std::forward<Function>(fn), std::forward<decltype(tp)>(tp)));
1320 } catch (...) {
1321 promise->set_exception(std::current_exception());
1322 }
1323 }
1324 });
1325 lock.unlock();
1326 m_notifier.notify_one();
1327 return {true, std::move(result_future)};
1328}
1329
1330}// namespace processing
1331}// namespace vk
1332
1333
1334#endif// VK_PROCESSING_TASK_QUEUE_HPP
1335#ifndef VK_EVENTS_MESSAGE_NEW_HPP
1336#define VK_EVENTS_MESSAGE_NEW_HPP
1337
1338#include "handlers/attachment_handler.hpp"
1339#include "handlers/message_action_handler.hpp"
1340
1341namespace simdjson {
1342namespace dom {
1343class object;
1344class array;
1345}// namespace dom
1346}// namespace simdjson
1347
1348namespace vk {
1349namespace event {
1350/*!
1351 * @brief The `message_new` event representation.
1352 *
1353 * Internal information accessed in a "lazy way".
1354 * It means, that no data is extracted from JSON until the user wants to access
1355 * it, and there's meaningless to construct all attachment, reply and forwarded
1356 * messages objects in the case you only need message text.
1357 */
1358class message_new
1359{
1360public:
1361 message_new(simdjson::dom::object&& event);
1362 ~message_new();
1363 /*!
1364 * @throws vk::exception::access_error in case, when reply pointer is not set.
1365 */
1366 std::shared_ptr<message_new> reply() const;
1367 /*!
1368 * @throws vk::exception::access_error in case, when forward messages vector is empty.
1369 */
1370 std::vector<std::unique_ptr<message_new>> fwd_messages() const;
1371 /*!
1372 * @throws vk::exception::access error in case, when there's no actions setted.
1373 */
1374 action::any_action_t action() const;
1375 /*!
1376 * @throws exception::access_error in case, when object hasn't attachments.
1377 * @note In case, when no attachments were provided, empty vector returned.
1378 */
1379 attachment::attachments_t attachments() const;
1380
1381 bool on_action(std::string_view action_type) const noexcept;
1382
1383 int64_t conversation_message_id() const noexcept;
1384 std::string text() const noexcept;
1385 int64_t from_id() const noexcept;
1386 int64_t peer_id() const noexcept;
1387 bool has_reply() const noexcept;
1388 bool has_fwd_messages() const noexcept;
1389 bool has_action() const noexcept;
1390
1391private:
1392 void try_get_actions();
1393 simdjson::dom::object& get_event() const;
1394
1395 std::shared_ptr<simdjson::dom::object> m_event_json;
1396 action::any_action_t m_action;
1397 attachment_handler m_attachment_handler;
1398 bool m_has_action = false;
1399 bool m_has_reply = false;
1400 bool m_has_fwd_messages = false;
1401 bool m_has_attachments = false;
1402};
1403
1404}// namespace event
1405}// namespace vk
1406
1407std::ostream& operator<<(std::ostream& ostream, const vk::event::message_new& event);
1408
1409#endif// VK_EVENTS_MESSAGE_NEW_HPP
1410#ifndef VK_EVENTS_WALL_REPLY_NEW_HPP
1411#define VK_EVENTS_WALL_REPLY_NEW_HPP
1412
1413#include "events/handlers/attachment_handler.hpp"
1414
1415namespace simdjson {
1416namespace dom {
1417class object;
1418}// namespace dom
1419}// namespace simdjson
1420
1421namespace vk {
1422namespace event {
1423/*!
1424 * @brief The `wall_reply_new` event representation.
1425 *
1426 * Internal information accessed in a "lazy way".
1427 * It means, that no data is extracted from JSON until the user wants to access it.
1428 */
1429class wall_reply_new
1430{
1431public:
1432 explicit wall_reply_new(simdjson::dom::object&& event);
1433 ~wall_reply_new();
1434
1435 int64_t id() const noexcept;
1436 int64_t from_id() const noexcept;
1437 int64_t post_id() const noexcept;
1438 int64_t owner_id() const noexcept;
1439 std::string text() const noexcept;
1440 bool has_attachments() const noexcept;
1441 attachment::attachments_t attachments() const;
1442
1443private:
1444 simdjson::dom::object& get_event() const;
1445
1446 std::shared_ptr<simdjson::dom::object> m_event_json;
1447 attachment_handler m_attachment_handler;
1448 bool m_has_attachments = false;
1449};
1450
1451}// namespace event
1452}// namespace vk
1453
1454std::ostream& operator<<(std::ostream& ostream, const vk::event::wall_reply_new& reply);
1455
1456#endif// VK_EVENTS_WALL_REPLY_NEW_HPP
1457#ifndef VK_EVENTS_WALL_POST_NEW_HPP
1458#define VK_EVENTS_WALL_POST_NEW_HPP
1459
1460#include "events/handlers/attachment_handler.hpp"
1461#include "events/wall_repost.hpp"
1462
1463namespace simdjson {
1464namespace dom {
1465class object;
1466}// namespace dom
1467}// namespace simdjson
1468
1469namespace vk {
1470namespace event {
1471/*!
1472 * @brief The `wall_post_new` event representation.
1473 *
1474 * Internal information accessed in a "lazy way".
1475 * It means, that no data is extracted from JSON until the user wants to access it.
1476 */
1477class wall_post_new
1478{
1479public:
1480 ~wall_post_new();
1481
1482 wall_post_new(simdjson::dom::object&& event);
1483
1484 int64_t id() const noexcept;
1485 int64_t from_id() const noexcept;
1486 int64_t owner_id() const noexcept;
1487 int64_t created_by() const noexcept;
1488 std::string text() const noexcept;
1489 bool can_edit() const noexcept;
1490 bool can_delete() const noexcept;
1491 bool marked_as_ads() const noexcept;
1492 bool has_attachments() const noexcept;
1493 bool has_repost() const noexcept;
1494 /*!
1495 * @throws vk::exception::access_error in case, when _repost pointer is not set.
1496 */
1497 std::shared_ptr<wall_repost> repost() const;
1498 /*!
1499 * @brief Get attachments vector.
1500 * @note In case, when no attachments were provided, empty vector returned.
1501 */
1502 attachment::attachments_t attachments() const;
1503
1504private:
1505 simdjson::dom::object& get_event() const;
1506
1507 std::shared_ptr<simdjson::dom::object> m_event_json;
1508 attachment_handler m_attachment_handler;
1509 bool m_has_attachments = false;
1510 bool m_has_repost = false;
1511};
1512
1513}// namespace event
1514}// namespace vk
1515
1516std::ostream& operator<<(std::ostream& ostream, const vk::event::wall_post_new& event);
1517
1518#endif// VK_EVENTS_WALL_POST_NEW_HPP
1519#ifndef VK_EVENTS_HANDLERS_MESSAGE_ACTION_HANDLER_HPP
1520#define VK_EVENTS_HANDLERS_MESSAGE_ACTION_HANDLER_HPP
1521
1522#include <string>
1523#include <variant>
1524
1525namespace vk {
1526namespace action {
1527
1528struct chat_invite_user
1529{
1530 int64_t member_id;
1531};
1532
1533struct chat_kick_user
1534{
1535 int64_t member_id;
1536};
1537
1538struct chat_pin_message
1539{
1540 int64_t member_id;
1541 int64_t conversation_member_id;
1542 std::string message;
1543};
1544
1545struct chat_unpin_message
1546{
1547 int64_t member_id;
1548 int64_t conversation_member_id;
1549};
1550
1551struct chat_photo_update
1552{
1553 // Empty.
1554};
1555
1556struct chat_title_update
1557{
1558 std::string text;
1559};
1560
1561using any_action_t = std::variant<
1562 chat_invite_user,
1563 chat_kick_user,
1564 chat_pin_message,
1565 chat_unpin_message,
1566 chat_photo_update,
1567 chat_title_update
1568>;
1569
1570}// namespace action
1571}// namespace vk
1572
1573#endif// VK_EVENTS_HANDLERS_MESSAGE_ACTION_HANDLER_HPP
1574#ifndef VK_EVENTS_HANDLERS_ATTACHMENT_HANDLER_HPP
1575#define VK_EVENTS_HANDLERS_ATTACHMENT_HANDLER_HPP
1576
1577#include "attachment/attachment.hpp"
1578
1579namespace simdjson {
1580namespace dom {
1581class array;
1582}// namespace dom
1583}// namespace simdjson
1584
1585namespace vk {
1586namespace event {
1587class message_new;
1588class wall_reply_new;
1589class wall_post_new;
1590}// namespace event
1591}// namespace vk
1592
1593namespace vk {
1594namespace event {
1595
1596class attachment_handler
1597{
1598private:
1599 attachment::attachments_t try_get(const simdjson::dom::array& attachments) const;
1600
1601 friend class message_new;
1602 friend class wall_reply_new;
1603 friend class wall_post_new;
1604};
1605
1606}// namespace event
1607}// namespace vk
1608
1609#endif// VK_EVENTS_HANDLERS_ATTACHMENT_HANDLER_HPP
1610
1611#ifndef VK_EVENTS_WALL_REPOST_HPP
1612#define VK_EVENTS_WALL_REPOST_HPP
1613
1614#include "attachment/attachment.hpp"
1615
1616namespace vk {
1617namespace event {
1618/*!
1619 * @brief The `wall_post_new` repost representation.
1620 *
1621 * Internal information accessed in a "lazy way".
1622 * It means, that no data is extracted from JSON until the user wants to access
1623 * it.
1624 */
1625class wall_repost
1626{
1627public:
1628 wall_repost(int64_t id, int64_t from_id, int64_t owner_id, std::string text);
1629
1630 void construct_attachments(attachment::attachments_t&& attachments);
1631
1632 int64_t id() const noexcept;
1633 int64_t from_id() const noexcept;
1634 int64_t owner_id() const noexcept;
1635 const std::string& text() const noexcept;
1636 const attachment::attachments_t& attachments() const noexcept;
1637
1638private:
1639 int64_t m_id;
1640 int64_t m_from_id;
1641 int64_t m_owner_id;
1642 std::string m_text;
1643 attachment::attachments_t m_attachments;
1644};
1645
1646}// namespace event
1647}// namespace vk
1648
1649#endif// VK_EVENTS_WALL_REPOST_HPP
1650#ifndef VK_EVENTS_COMMON_EVENT_HPP
1651#define VK_EVENTS_COMMON_EVENT_HPP
1652
1653#include "events/message_new.hpp"
1654#include "events/wall_post_new.hpp"
1655#include "events/wall_reply_new.hpp"
1656
1657namespace simdjson {
1658namespace dom {
1659class object;
1660}// namespace dom
1661}// namespace simdjson
1662
1663namespace vk {
1664namespace event {
1665/*!
1666 * @brief Temporary update container.
1667 */
1668class common
1669{
1670public:
1671 common(std::string_view ts, simdjson::dom::object&& event);
1672 ~common();
1673
1674 std::string type() const noexcept;
1675 std::string ts() const noexcept;
1676 std::string dump() const noexcept;
1677
1678 /*!
1679 * @brief Check if event matches @param type.
1680 * @param type
1681 */
1682 bool on_type(std::string_view type) const noexcept;
1683
1684 message_new get_message_event() const;
1685 wall_post_new get_wall_post_event() const;
1686 wall_reply_new get_wall_reply_event() const;
1687
1688private:
1689 simdjson::dom::object& get_event() const noexcept;
1690
1691 std::string m_ts;
1692 std::string m_update_type;
1693 std::shared_ptr<simdjson::dom::object> m_event;
1694};
1695
1696}// namespace event
1697}// namespace vk
1698
1699#endif// VK_EVENTS_COMMON_EVENT_HPP
1700#ifndef VK_STRING_UTILS_HPP
1701#define VK_STRING_UTILS_HPP
1702
1703#include "string_utils/implementation/convert_ascii.hpp"
1704#include "string_utils/implementation/convert_utf8.hpp"
1705#include "string_utils/implementation/format.hpp"
1706#include "string_utils/implementation/join.hpp"
1707#include "string_utils/implementation/lazy_split.hpp"
1708#include "string_utils/implementation/split.hpp"
1709
1710namespace vk {
1711namespace string_utils {
1712
1713template <typename T, typename Container>
1714std::string join(Container&& elements, char delimiter)
1715{
1716 return join_impl<T>::create(std::forward<Container>(elements), delimiter);
1717}
1718
1719template <typename T>
1720std::string join(std::initializer_list<T> elements, char delimiter)
1721{
1722 return join_impl<T>::create(elements, delimiter);
1723}
1724
1725template <typename... Args>
1726std::string format(std::string_view data, Args&&... args)
1727{
1728 return format_impl<Args...>::create(data, std::forward<Args>(args)...);
1729}
1730
1731inline std::vector<std::string_view> split(std::string_view data, char delimiter)
1732{
1733 return split_impl::create(data, delimiter);
1734}
1735
1736inline auto lazy_split(std::string_view data, std::string_view delimiter)
1737{
1738 return split_range<std::string_view>(data, delimiter);
1739}
1740
1741inline std::string utf8_to_lower(std::string_view data)
1742{
1743 return utf8_convert_impl::create_lower(data);
1744}
1745
1746inline std::string utf8_to_upper(std::string_view data)
1747{
1748 return utf8_convert_impl::create_upper(data);
1749}
1750
1751inline std::string ascii_to_lower(std::string_view data)
1752{
1753 return ascii_convert_impl::create_lower(data);
1754}
1755
1756inline std::string ascii_to_upper(std::string_view data)
1757{
1758 return ascii_convert_impl::create_upper(data);
1759}
1760
1761}// namespace string_utils
1762}// namespace vk
1763
1764#endif// VK_STRING_UTILS_HPP
1765#ifndef VK_STRING_UTILS_UNICODE_OSTREAM_HPP
1766#define VK_STRING_UTILS_UNICODE_OSTREAM_HPP
1767
1768#include <codecvt>
1769#include <locale>
1770
1771inline std::ostream& operator<<(std::ostream& os, std::wstring_view s)
1772{
1773 return os << std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(s.data());
1774}
1775
1776inline std::ostream& operator<<(std::ostream& os, std::u16string_view s)
1777{
1778 return os << std::wstring_convert<std::codecvt_utf8<char16_t>, char16_t>().to_bytes(s.data());
1779}
1780
1781inline std::ostream& operator<<(std::ostream& os, std::u32string_view s)
1782{
1783 return os << std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t>().to_bytes(s.data());
1784}
1785
1786#endif// VK_STRING_UTILS_UNICODE_OSTREAM_HPP
1787#ifndef VK_STRING_UTILS_IMPLEMENTATION_CONVERT_ASCII_HPP
1788#define VK_STRING_UTILS_IMPLEMENTATION_CONVERT_ASCII_HPP
1789
1790#include <codecvt>
1791#include <locale>
1792
1793namespace vk {
1794namespace string_utils {
1795std::string ascii_to_lower(std::string_view data);
1796std::string ascii_to_upper(std::string_view data);
1797}// namespace string_utils
1798}// namespace vk
1799
1800namespace vk {
1801namespace string_utils {
1802
1803struct ascii_convert_impl
1804{
1805private:
1806 static char internal_to_lower_ascii_char(char c) noexcept
1807 {
1808 if (c <= 'Z' && c >= 'A') {
1809 return c - ('Z' - 'z');
1810 }
1811
1812 return c;
1813 }
1814
1815 static char internal_to_upper_ascii_char(char c) noexcept
1816 {
1817 if (c <= 'z' && c >= 'a') {
1818 return c + ('Z' - 'z');
1819 }
1820
1821 return c;
1822 }
1823
1824 template <typename ExecutionPolicy>
1825 static std::string internal_ascii_convert_implementation(std::string_view data, ExecutionPolicy policy)
1826 {
1827 std::string converted;
1828 converted.reserve(data.length());
1829
1830 for (auto& c : data) {
1831 converted += policy(c);
1832 }
1833
1834 return converted;
1835 }
1836
1837 static std::string create_lower(std::string_view data)
1838 {
1839 return internal_ascii_convert_implementation(data, [](auto& c) {
1840 return internal_to_lower_ascii_char(c);
1841 });
1842 }
1843
1844 static std::string create_upper(std::string_view data)
1845 {
1846 return internal_ascii_convert_implementation(data, [](auto& c) {
1847 return internal_to_upper_ascii_char(c);
1848 });
1849 }
1850
1851 friend std::string string_utils::ascii_to_lower(std::string_view data);
1852 friend std::string string_utils::ascii_to_upper(std::string_view data);
1853};
1854
1855}// namespace string_utils
1856}// namespace vk
1857
1858#endif// VK_STRING_UTILS_IMPLEMENTATION_CONVERT_ASCII_HPP
1859#ifndef VK_STRING_UTILS_IMPLEMENTATION_CONVERT_UTF8_HPP
1860#define VK_STRING_UTILS_IMPLEMENTATION_CONVERT_UTF8_HPP
1861
1862#include <codecvt>
1863#include <locale>
1864
1865namespace vk {
1866namespace string_utils {
1867std::string utf8_to_lower(std::string_view data);
1868std::string utf8_to_upper(std::string_view data);
1869}// namespace string_utils
1870}// namespace vk
1871
1872namespace vk {
1873namespace string_utils {
1874
1875const std::locale utf8("en_US.UTF-8");
1876
1877struct utf8_convert_impl
1878{
1879private:
1880 static std::wstring internal_to_wstring(const std::string& s)
1881 {
1882 std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
1883 return conv.from_bytes(s);
1884 }
1885 static std::string internal_to_string(const std::wstring& s)
1886 {
1887 std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
1888 return conv.to_bytes(s);
1889 }
1890 template <typename ExecutionPolicy>
1891 static std::string internal_create(std::string_view data, ExecutionPolicy converter)
1892 {
1893 auto ss = internal_to_wstring(data.data());
1894 for (auto& c : ss) {
1895 c = converter(c);
1896 }
1897 return internal_to_string(ss);
1898 }
1899 static std::string create_upper(std::string_view data)
1900 {
1901 return internal_create(data, [](auto& c) {
1902 return std::toupper(c, utf8);
1903 });
1904 }
1905 static std::string create_lower(std::string_view data)
1906 {
1907 return internal_create(data, [](auto& c) {
1908 return std::tolower(c, utf8);
1909 });
1910 }
1911 friend std::string string_utils::utf8_to_lower(std::string_view data);
1912 friend std::string string_utils::utf8_to_upper(std::string_view data);
1913};
1914
1915}// namespace string_utils
1916}// namespace vk
1917
1918#endif// VK_STRING_UTILS_IMPLEMENTATION_CONVERT_UTF8_HPP
1919#ifndef VK_STRING_UTILS_IMPLEMENTATION_JOIN_HPP
1920#define VK_STRING_UTILS_IMPLEMENTATION_JOIN_HPP
1921
1922#include <numeric>
1923#include <string>
1924
1925namespace vk {
1926namespace string_utils {
1927
1928template <typename T, typename Container>
1929std::string join(Container&& elements, char delimiter = ',');
1930
1931template <typename T>
1932std::string join(std::initializer_list<T> elements, char delimiter = ',');
1933
1934}// namespace string_utils
1935}// namespace vk
1936
1937namespace vk {
1938namespace string_utils {
1939
1940template <typename T>
1941struct join_impl
1942{
1943public:
1944 join_impl() = delete;
1945
1946private:
1947 template <typename Container, typename BinaryOperation>
1948 static std::string implementation(Container&& elements, BinaryOperation operation)
1949 {
1950 return std::accumulate(elements.begin(), elements.end(), std::string(), operation);
1951 }
1952
1953 template <typename Container>
1954 static std::string common_create(Container&& elements, char delimiter)
1955 {
1956 return implementation(elements, [&delimiter](std::string& accumlator, T element) {
1957 if constexpr (std::is_integral_v<T>) {
1958 return accumlator.empty() ? std::to_string(element) : std::move(accumlator) + delimiter + std::to_string(element);
1959 } else {
1960 return accumlator.empty() ? std::string(element) : std::move(accumlator) + delimiter + std::string(element);
1961 }
1962 });
1963 }
1964
1965 template <typename Container>
1966 static std::string create(Container&& elements, char delimiter)
1967 {
1968 return common_create<Container&&>(std::forward<Container>(elements), delimiter);
1969 }
1970
1971 template <typename mT, typename Container>
1972 friend std::string string_utils::join(Container&& elements, char delimiter);
1973
1974 template <typename mT>
1975 friend std::string string_utils::join(std::initializer_list<mT> elements, char delimiter);
1976};
1977
1978}// namespace string_utils
1979}// namespace vk
1980
1981#endif// VK_STRING_UTILS_IMPLEMENTATION_JOIN_HPP
1982#ifndef VK_STRING_UTILS_IMPLEMENTATION_FORMAT_HPP
1983#define VK_STRING_UTILS_IMPLEMENTATION_FORMAT_HPP
1984
1985#include <array>
1986#include <sstream>
1987
1988namespace vk {
1989namespace string_utils {
1990template <typename... Args>
1991std::string format(std::string_view data, Args&&... args);
1992}// namespace string_utils
1993}// namespace vk
1994
1995namespace vk {
1996namespace string_utils {
1997
1998template <typename... Args>
1999struct format_impl
2000{
2001public:
2002 format_impl() = delete;
2003
2004private:
2005 static constexpr size_t average_word_size = 7;
2006
2007 static std::string create(std::string_view data, Args&&... args)
2008 {
2009 if (data.empty()) { return {}; }
2010
2011 std::string formatted;
2012 formatted.reserve(data.size() + (average_word_size * sizeof...(args)));
2013 auto pack_one = [](auto&& argument) {
2014 if constexpr (std::is_integral_v<std::decay_t<decltype(argument)>>) {
2015 return std::to_string(argument);
2016 } else {
2017 return std::string(argument);
2018 }
2019 };
2020 std::array<std::string, sizeof...(Args)> elements{pack_one(args)...};
2021 size_t curr = 0;
2022 for (size_t i = 0; i < data.size(); i++) {
2023 // If we're have '{}' token, insert parameter at this place.
2024 if (data[i] == '{' && data[i + 1] == '}') {
2025 formatted += elements[curr++];
2026 // Add all characters from source string except '{}' token.
2027 } else if (data[i - 1] != '{' || data[i] != '}') {
2028 formatted += data[i];
2029 }
2030 }
2031 return formatted;
2032 }
2033
2034 template <typename... mArgs>
2035 friend std::string vk::string_utils::format(std::string_view data, mArgs&&... args);
2036};
2037
2038}// namespace string_utils
2039}// namespace vk
2040
2041#endif// VK_STRING_UTILS_IMPLEMENTATION_FORMAT_HPP
2042#ifndef VK_STRING_UTILS_IMPLEMENTATION_LAZY_SPLIT_HPP
2043#define VK_STRING_UTILS_IMPLEMENTATION_LAZY_SPLIT_HPP
2044
2045// Thanks some-coder366 for the idea.
2046// https://github.com/some-coder366/cpp-lazy-split/blob/master/split_iterators.cpp
2047
2048#include <string_view>
2049
2050namespace vk {
2051namespace string_utils {
2052
2053struct end_split_iterator {};
2054
2055template <typename StringType>
2056class split_iterator
2057{
2058public:
2059 split_iterator(StringType&& source, StringType&& delim)
2060 : src(std::forward<StringType>(source))
2061 , delimiter(std::forward<StringType>(delim))
2062 , first(0)
2063 , last(src.find(delimiter)) {}
2064
2065 split_iterator& operator++() noexcept
2066 {
2067 first = last + delimiter.size();
2068 last = src.find(delimiter, first);
2069 return *this;
2070 }
2071 StringType operator*() const noexcept
2072 {
2073 if (last != StringType::npos) {
2074 return src.substr(first, last - first);
2075 }
2076
2077 finished = true;
2078
2079 return src.substr(first, src.size() - first);
2080 }
2081 friend bool operator!=(const split_iterator& iterator, end_split_iterator) noexcept
2082 {
2083 return !iterator.finished;
2084 }
2085 friend bool operator!=(end_split_iterator, const split_iterator& iterator) noexcept
2086 {
2087 return !iterator.finished;
2088 }
2089
2090private:
2091 StringType src;
2092 StringType delimiter;
2093 size_t first;
2094 size_t last;
2095 mutable bool finished = false;
2096};
2097
2098template <typename StringType>
2099class split_range
2100{
2101public:
2102 split_range(StringType source, StringType delim) noexcept
2103 : src(source)
2104 , delimiter(delim) {}
2105
2106 auto begin() const noexcept
2107 {
2108 return split_iterator<StringType>(src, delimiter);
2109 }
2110
2111 auto end() const noexcept
2112 {
2113 return end_split_iterator();
2114 }
2115
2116private:
2117 StringType src;
2118 StringType delimiter;
2119};
2120
2121}// namespace string_utils
2122}// namespace vk
2123
2124#endif// VK_STRING_UTILS_IMPLEMENTATION_LAZY_SPLIT_HPP
2125#ifndef VK_STRING_UTILS_IMPLEMENTATION_SPLIT_HPP
2126#define VK_STRING_UTILS_IMPLEMENTATION_SPLIT_HPP
2127
2128#include <string_view>
2129#include <vector>
2130
2131namespace vk {
2132namespace string_utils {
2133std::vector<std::string_view> split(std::string_view text, char delimiter);
2134}// namespace string_utils
2135}// namespace vk
2136
2137namespace vk {
2138namespace string_utils {
2139
2140struct split_impl
2141{
2142public:
2143 split_impl() = delete;
2144
2145private:
2146 static std::vector<std::string_view> create(std::string_view data, char delimiter)
2147 {
2148 std::vector<std::string_view> splitted;
2149 splitted.reserve(data.length() / 4);
2150 size_t pos = 0;
2151 while (pos != std::string_view::npos) {
2152 pos = data.find_first_not_of(delimiter);
2153 if (pos == std::string_view::npos) {
2154 return splitted;
2155 }
2156 data = data.substr(pos);
2157 pos = data.find(delimiter);
2158 splitted.emplace_back(data.substr(0, pos));
2159 data = data.substr(pos == std::string_view::npos ? 0 : pos);
2160 }
2161 splitted.shrink_to_fit();
2162 return splitted;
2163 }
2164
2165 friend std::vector<std::string_view> string_utils::split(std::string_view text, char delimiter);
2166};
2167
2168}// namespace string_utils
2169}// namespace vk
2170
2171#endif// VK_STRING_UTILS_IMPLEMENTATION_SPLIT_HPP
2172#ifndef VK_OAUTH_CLIENT_HPP
2173#define VK_OAUTH_CLIENT_HPP
2174
2175#include <memory>
2176#include <string>
2177
2178namespace simdjson {
2179namespace dom {
2180class parser;
2181}// namespace dom
2182}// namespace simdjson
2183
2184namespace vk {
2185namespace oauth {
2186enum class target_client : uint8_t
2187{
2188 android = (1 << 0),
2189 iphone = (1 << 1),
2190 windows = (1 << 2)
2191};
2192
2193/*!
2194 * @brief VK Oauth client.
2195 *
2196 * Example usage:
2197 *
2198 * @code
2199 * int main() {
2200 * vk::oauth::client client("phone number", "password", vk::oauth::target_client::windows);
2201 * client.pull();
2202 * vk::messages messages(client.token());
2203 * }
2204 * @endcode
2205 */
2206class client
2207{
2208public:
2209 explicit client(std::string_view username_, std::string_view password_, target_client client_type_);
2210
2211 ~client();
2212 /*!
2213 * @brief Try get user data.
2214 * @throws vk::exception::access_error with detailed description in case, when
2215 * wrong data were provided.
2216 */
2217 void pull();
2218 std::string token() const noexcept;
2219 int64_t user_id() const noexcept;
2220
2221private:
2222 static inline const std::string_view m_oauth_link = "https://oauth.vk.com/";
2223 static inline const std::string_view m_android_app_client_secret = "hHbZxrka2uZ6jB1inYsH";
2224 static inline const std::string_view m_iphone_app_client_secret = "VeWdmVclDCtn6ihuP1nt";
2225 static inline const std::string_view m_windows_app_client_secret = "AlVXZFMUqyrnABp8ncuU";
2226
2227 static inline const int32_t m_android_app_client_id = 2274003;
2228 static inline const int32_t m_windows_app_client_id = 3697615;
2229 static inline const int32_t m_iphone_app_client_id = 3140623;
2230
2231 target_client m_client_type;
2232 std::string m_username;
2233 std::string m_password;
2234
2235 std::string m_target_client_secret;
2236 int64_t m_target_client_id;
2237
2238 std::string m_pulled_token;
2239 int64_t m_pulled_user_id;
2240
2241 std::shared_ptr<simdjson::dom::parser> m_parser;
2242};
2243
2244}// namespace oauth
2245}// namespace vk
2246
2247#endif// VK_OAUTH_CLIENT_HPP
2248/*!
2249 * @mainpage C++ VK library
2250 *
2251 * Please, check the [README.md](https://github.com/duonumerouno/cpp_vk_lib/tree/feature).
2252 */
2253
2254#ifndef VK_COMMON_HPP
2255#define VK_COMMON_HPP
2256
2257#include "attachment/attachment.hpp"
2258#include "config/loader.hpp"
2259#include "document/common.hpp"
2260#include "events/common_event.hpp"
2261#include "events/handlers/attachment_handler.hpp"
2262#include "events/handlers/message_action_handler.hpp"
2263#include "events/message_new.hpp"
2264#include "events/wall_post_new.hpp"
2265#include "events/wall_reply_new.hpp"
2266#include "exception/error-inl.hpp"
2267#include "keyboard/colors.hpp"
2268#include "keyboard/flags.hpp"
2269#include "keyboard/layout.hpp"
2270#include "long_poll/api.hpp"
2271#include "methods/audio.hpp"
2272#include "methods/docs.hpp"
2273#include "methods/groups.hpp"
2274#include "methods/messages.hpp"
2275#include "methods/photos.hpp"
2276#include "methods/utils.hpp"
2277#include "methods/video.hpp"
2278#include "misc/cppdefs.hpp"
2279#include "net/network.hpp"
2280#include "oauth/client.hpp"
2281#include "processing/backtrace.hpp"
2282#include "processing/task_queue.hpp"
2283#include "string_utils/string_utils.hpp"
2284
2285#endif// VK_COMMON_HPP
2286#ifndef VK_EXCEPTION_HPP
2287#define VK_EXCEPTION_HPP
2288
2289#include "string_utils/string_utils.hpp"
2290
2291#include <stdexcept>
2292
2293namespace vk {
2294namespace exception {
2295
2296/*!
2297 * @brief General exception of VK method classes.
2298 */
2299class common_exception : public std::exception
2300{
2301public:
2302 const char* what() const noexcept override
2303 {
2304 return m_error.what();
2305 }
2306
2307protected:
2308 explicit common_exception(std::string_view what_arg)
2309 : m_error(what_arg.data()) {}
2310
2311 std::string create(size_t id, const char* error_name, const char* arg) const
2312 {
2313 return string_utils::format("[vk.exception.{}.{}]: {}", error_name, id, arg);
2314 }
2315
2316private:
2317 std::runtime_error m_error;
2318};
2319
2320class upload_error : public common_exception
2321{
2322public:
2323 explicit upload_error(size_t id, const char* what_arg)
2324 : common_exception(create(id, "upload_error", what_arg)) {}
2325};
2326
2327class access_error : public common_exception
2328{
2329public:
2330 explicit access_error(size_t id, const char* what_arg)
2331 : common_exception(create(id, "access_error", what_arg)) {}
2332};
2333
2334class invalid_parameter_error : public common_exception
2335{
2336public:
2337 explicit invalid_parameter_error(size_t id, const char* what_arg)
2338 : common_exception(create(id, "invalid_parameter_error", what_arg)) {}
2339};
2340
2341class runtime_error : public common_exception
2342{
2343public:
2344 explicit runtime_error(size_t id, const char* what_arg)
2345 : common_exception(create(id, "runtime_error", what_arg)) {}
2346};
2347
2348template <typename Base, typename Derived>
2349class bad_cast_error : public common_exception
2350{
2351public:
2352 explicit bad_cast_error()
2353 : common_exception(create(0, "bad_cast_error", "Cast error")) {}
2354};
2355
2356}// namespace exception
2357}// namespace vk
2358
2359#endif// VK_EXCEPTION_HPP
2360#ifndef VK_EXCEPTION_ERROR_INL_HPP
2361#define VK_EXCEPTION_ERROR_INL_HPP
2362
2363#include "exception/exception.hpp"
2364
2365#include "spdlog/spdlog.h"
2366
2367namespace vk {
2368namespace exception {
2369
2370enum class error_type : size_t
2371{
2372 upload_error,
2373 access_error,
2374 invalid_parameter_error,
2375 runtime_error,
2376};
2377
2378struct error_code
2379{
2380 const char* message;
2381 error_type type;
2382};
2383
2384static const std::unordered_map<size_t, error_code> errors = {
2385 { 1, { "Unknown error occurred", error_type::runtime_error }},
2386 { 2, { "Application is disabled. Enable your application or use test mode", error_type::runtime_error }},
2387 { 3, { "Unknown method passed", error_type::runtime_error }},
2388 { 4, { "Incorrect signature", error_type::invalid_parameter_error }},
2389 { 5, { "User authorization failed", error_type::access_error }},
2390 { 6, { "Too many requests per second", error_type::runtime_error }},
2391 { 7, { "Permission to perform this action is denied", error_type::access_error }},
2392 { 8, { "Invalid request", error_type::invalid_parameter_error }},
2393 { 9, { "Flood control", error_type::runtime_error }},
2394 { 10, { "Internal server error", error_type::runtime_error }},
2395 { 11, { "In test mode application should be disabled or user should be authorized", error_type::access_error }},
2396 { 14, { "Captcha needed", error_type::runtime_error }},
2397 { 15, { "Access denied", error_type::access_error }},
2398 { 16, { "HTTP authorization failed", error_type::runtime_error }},
2399 { 17, { "Validation required", error_type::access_error }},
2400 { 18, { "User was deleted or banned", error_type::access_error }},
2401 { 20, { "Permission to perform this action is denied for non-standalone applications", error_type::access_error }},
2402 { 21, { "Permission to perform this action is allowed only for Standalone and OpenAPI applications", error_type::access_error }},
2403 { 23, { "This method was disabled", error_type::access_error }},
2404 { 24, { "Confirmation required", error_type::access_error }},
2405 { 27, { "Group authorization failed", error_type::access_error }},
2406 { 28, { "Application authorization failed", error_type::access_error }},
2407 { 29, { "Rate limit reached", error_type::runtime_error }},
2408 { 30, { "This profile is private", error_type::access_error }},
2409 { 33, { "Not implemented yet", error_type::access_error }},
2410 { 37, { "User was banned", error_type::access_error }},
2411 { 38, { "Unknown application", error_type::runtime_error }},
2412 { 39, { "Unknown user", error_type::runtime_error }},
2413 { 40, { "Unknown group", error_type::runtime_error }},
2414 { 41, { "Additional signup required", error_type::runtime_error }},
2415 { 100, { "One of the parameters specified was missing or invalid", error_type::invalid_parameter_error }},
2416 { 101, { "Invalid application API ID", error_type::invalid_parameter_error }},
2417 { 103, { "Out of limits", error_type::runtime_error }},
2418 { 113, { "Invalid user id", error_type::invalid_parameter_error }},
2419 { 150, { "Invalid timestamp", error_type::invalid_parameter_error }},
2420 { 200, { "Access to album denied", error_type::access_error }},
2421 { 201, { "Access to audio denied", error_type::access_error }},
2422 { 203, { "Access to group denied", error_type::access_error }},
2423 { 300, { "This album is full", error_type::runtime_error }},
2424 { 500, { "Permission denied. You must enable votes processing in application settings", error_type::access_error }},
2425 { 600, { "Permission denied. You have no access to operations specified with given object(s)", error_type::access_error }},
2426 { 925, { "You are not admin of this chat", error_type::access_error }},
2427 { 932, { "Your community can't interact with this peer", error_type::access_error }},
2428 { 936, { "Contact not found", error_type::runtime_error }},
2429 { 939, { "Message request already sent", error_type::runtime_error }},
2430 { 945, { "Chat was disabled", error_type::access_error }},
2431 { 946, { "Chat not supported", error_type::runtime_error }},
2432 { 947, { "Can't add user to chat, because user has no access to group", error_type::access_error }},
2433 { 603, { "Some ads error occured", error_type::runtime_error }},
2434 { 3300, { "Recaptcha needed", error_type::access_error }},
2435 { 3301, { "Phone validation needed", error_type::access_error }},
2436 { 3302, { "Password validation needed", error_type::access_error }},
2437 { 3303, { "Otp app validation needed", error_type::access_error }},
2438 { 3304, { "Email confirmation needed", error_type::access_error }},
2439 { 3305, { "Assert votes", error_type::access_error }},
2440 { 3609, { "Token extension required", error_type::access_error }},
2441 { 3610, { "User is deactivated", error_type::access_error }},
2442 { 3611, { "Service is deactivated for user", error_type::access_error }}
2443};
2444
2445inline constexpr bool log_before_throw = true;
2446
2447inline void dispatch_error_by_code(size_t error_code, bool enable_logging_before_throw = false)
2448{
2449 const auto& error = errors.at(error_code);
2450
2451 if (enable_logging_before_throw) {
2452 auto dispatch_error_name = [](error_type err) -> const char* {
2453 switch (err) {
2454 case error_type::access_error: return "access error";
2455 case error_type::runtime_error: return "runtime error";
2456 case error_type::invalid_parameter_error: return "invalid parameter";
2457 default:
2458 return "unknown error type";
2459 }
2460 };
2461
2462 std::string error_type_name = dispatch_error_name(error.type);
2463
2464 spdlog::error("{}: {}", error_type_name, error.message);
2465 }
2466
2467 switch (error.type) {
2468 case error_type::access_error: throw access_error(error_code, error.message);
2469 case error_type::runtime_error: throw runtime_error(error_code, error.message);
2470 case error_type::invalid_parameter_error: throw invalid_parameter_error(error_code, error.message);
2471 default:
2472 throw runtime_error(-1, "Unknown error");
2473 }
2474}
2475
2476} // namespace exception
2477} // namespace vk
2478
2479#endif // VK_EXCEPTION_ERROR_INL_HPP
2480#include "config/loader.hpp"
2481
2482#include "simdjson.h"
2483
2484vk::config::loader* vk::config::loader::instance = nullptr;
2485
2486vk::config::loader* vk::config::loader::load(std::string_view path)
2487{
2488 if (!instance) {
2489 instance = new loader(path);
2490 }
2491
2492 return instance;
2493}
2494
2495vk::config::loader* vk::config::loader::get()
2496{
2497 if (!instance) {
2498 throw std::runtime_error("Please, load config first.");
2499 }
2500
2501 return instance;
2502}
2503
2504vk::config::loader::loader(std::string_view path)
2505{
2506 simdjson::dom::parser parser;
2507 const simdjson::dom::element element = parser.load(path.data());
2508
2509 m_username = element["oauth"]["login"];
2510 m_password = element["oauth"]["password"];
2511 m_user_token = element["api"]["user_token"].get_c_str().take_value();
2512 m_access_token = element["api"]["access_token"].get_c_str().take_value();
2513 m_error_log_path = element["environment"]["error_log_path"].get_c_str().take_value();
2514 m_event_log_path = element["environment"]["event_log_path"].get_c_str().take_value();
2515 m_num_workers = element["environment"]["num_workers"].get_int64();
2516}
2517#include "methods/utils.hpp"
2518
2519#include "exception/error-inl.hpp"
2520#include "simdjson.h"
2521
2522vk::method::utils::utils()
2523 : m_parser(std::make_shared<simdjson::dom::parser>())
2524 , m_group_constructor() {}
2525
2526vk::method::utils::~utils() = default;
2527
2528bool vk::method::utils::check_link(std::string_view url) const
2529{
2530 std::string raw_response = m_group_constructor
2531 .method("utils.checkLink")
2532 .param("url", url)
2533 .perform_request();
2534
2535 const simdjson::dom::object response = m_parser->parse(raw_response);
2536
2537 if (response.begin().key() == "error") {
2538 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
2539 }
2540
2541 const std::string_view status = response["response"]["status"];
2542
2543 return status == "not_banned";
2544}
2545
2546std::string vk::method::utils::get_short_link(std::string_view url) const
2547{
2548 const std::string raw_response = m_group_constructor
2549 .method("utils.getShortLink")
2550 .param("url", url)
2551 .perform_request();
2552
2553 const simdjson::dom::object response = m_parser->parse(raw_response);
2554
2555 if (response.begin().key() == "error") {
2556 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
2557 }
2558
2559 const std::string_view short_url_response = response["response"]["short_url"];
2560 std::string short_url;
2561
2562 return short_url.assign(short_url_response.data(), short_url_response.size());
2563}
2564
2565int64_t vk::method::utils::resolve_screen_name(std::string_view screen_name) const
2566{
2567 const std::string raw_response = m_group_constructor
2568 .method("utils.resolveScreenName")
2569 .param("screen_name", screen_name)
2570 .perform_request();
2571
2572 const simdjson::dom::object response = m_parser->parse(raw_response);
2573
2574 if (response["response"].get_array().size() == 0) {
2575 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
2576 }
2577
2578 return response["response"]["object_id"].get_int64();
2579}
2580#include "methods/photos.hpp"
2581
2582#include "exception/error-inl.hpp"
2583#include "simdjson.h"
2584
2585vk::method::photos::photos()
2586 : m_parser(std::make_shared<simdjson::dom::parser>())
2587 , m_document() {}
2588
2589vk::method::photos::photos(std::string_view user_token)
2590 : m_parser(std::make_shared<simdjson::dom::parser>())
2591 , m_document(user_token.data()) {}
2592
2593vk::method::photos::~photos() = default;
2594
2595vk::attachment::attachments_t vk::method::photos::search(std::string_view query, int64_t count) const
2596{
2597 return m_document.search("photos.search", query, count);
2598}
2599
2600std::string vk::method::photos::get_messages_upload_server(int64_t peer_id) const
2601{
2602 return m_group_constructor
2603 .method("photos.getMessagesUploadServer")
2604 .param("peer_id", std::to_string(peer_id))
2605 .perform_request();
2606}
2607
2608std::string vk::method::photos::get_chat_upload_server(int64_t chat_id, int64_t crop) const
2609{
2610 return m_group_constructor
2611 .method("photos.getChatUploadServer")
2612 .param("crop_x", std::to_string(crop))
2613 .param("crop_y", std::to_string(crop))
2614 .param("chat_id", std::to_string(chat_id - vk::method::utility::chat_id_constant))
2615 .perform_request();
2616}
2617
2618static std::map<std::string, std::string> save_messages_photo_args(simdjson::dom::object upload_response)
2619{
2620 return {
2621 {"photo", upload_response["photo"].get_c_str().take_value()},
2622 {"hash", upload_response["hash"].get_c_str().take_value()},
2623 {"server", std::to_string(upload_response["server"].get_int64())}};
2624}
2625
2626std::shared_ptr<vk::attachment::photo> vk::method::photos::save_messages_photo(std::string_view filename, std::string_view raw_server) const
2627{
2628 const simdjson::dom::object response = m_document.upload(filename, raw_server, "file");
2629
2630 if (response["photo"].get_string().take_value() == "[]" || response["photo"].get_string().take_value().empty()) {
2631 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
2632 }
2633
2634 const std::string raw_response = m_group_constructor
2635 .method("photos.saveMessagesPhoto")
2636 .append_map(save_messages_photo_args(response))
2637 .perform_request();
2638
2639 const simdjson::dom::object uploaded = m_parser->parse(raw_response)["response"].at(0);
2640
2641 const int64_t owner_id = uploaded["owner_id"].get_int64();
2642 const int64_t id = uploaded["id"].get_int64();
2643
2644 return std::make_shared<vk::attachment::photo>(owner_id, id);
2645}
2646#include "methods/video.hpp"
2647
2648vk::method::video::video()
2649 : m_user_constructor()
2650 , m_document() {}
2651
2652vk::method::video::video(std::string_view user_token)
2653 : m_user_constructor(user_token.data())
2654 , m_document(user_token.data()) {}
2655
2656vk::method::video::~video() = default;
2657
2658vk::attachment::attachments_t vk::method::video::search(std::string_view query, int64_t count) const
2659{
2660 return m_document.search("video.search", query, count);
2661}
2662
2663void vk::method::video::save_by_link(std::string_view url) const
2664{
2665 m_user_constructor
2666 .method("video.save")
2667 .param("link", url)
2668 .perform_request();
2669}
2670#include "methods/audio.hpp"
2671
2672#include "exception/error-inl.hpp"
2673#include "simdjson.h"
2674
2675vk::method::audio::audio()
2676 : m_parser(std::make_shared<simdjson::dom::parser>())
2677 , m_document()
2678 , m_group_constructor()
2679 , m_user_constructor() {}
2680
2681vk::method::audio::audio(std::string_view user_token)
2682 : m_parser(std::make_shared<simdjson::dom::parser>())
2683 , m_document(user_token.data())
2684 , m_group_constructor()
2685 , m_user_constructor(user_token) {}
2686
2687vk::method::audio::~audio() = default;
2688
2689std::string vk::method::audio::get_upload_server() const
2690{
2691 const std::string response = m_user_constructor
2692 .method("audio.getUploadServer")
2693 .perform_request();
2694
2695 return response;
2696}
2697
2698void vk::method::audio::save(std::string_view artist, std::string_view title, std::string_view filename, std::string_view raw_server) const
2699{
2700 const simdjson::dom::object response = m_document.upload(filename, raw_server, "file");
2701
2702 if (response.begin().key() == "error") {
2703 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
2704 }
2705
2706 m_group_constructor
2707 .method("audio.save")
2708 .param("server", std::to_string(response["server"].get_int64()))
2709 .param("audio", std::string(response["audio"].get_c_str()))
2710 .param("hash", std::string(response["hash"].get_c_str()))
2711 .param("artist", artist)
2712 .param("title", title)
2713 .perform_request();
2714}
2715#include "methods/utility/utility.hpp"
2716#include "net/network.hpp"
2717
2718#include "config/loader.hpp"
2719#include "simdjson.h"
2720
2721vk::method::utility::utility()
2722 : m_user_token(config::user_token())
2723 , m_access_token(config::access_token())
2724 , m_parser(std::make_shared<simdjson::dom::parser>()) {}
2725
2726vk::method::utility::utility(std::string_view user_token_)
2727 : m_user_token(user_token_.data())
2728 , m_access_token(config::access_token())
2729 , m_parser(std::make_shared<simdjson::dom::parser>()) {}
2730
2731vk::method::utility::~utility() = default;
2732
2733std::string vk::method::utility::append_url(std::string_view method) const
2734{
2735 return "https://api.vk.com/method/" + std::string(method) + '?';
2736}
2737
2738std::map<std::string, std::string>& vk::method::utility::user_args(std::map<std::string, std::string>& params) const
2739{
2740 params.insert({{"access_token", m_user_token}, {"v", API_V}});
2741 return params;
2742}
2743
2744std::map<std::string, std::string>& vk::method::utility::group_args(std::map<std::string, std::string>& params) const
2745{
2746 params.insert({{"access_token", m_access_token}, {"v", API_V}});
2747 return params;
2748}
2749
2750std::string vk::method::utility::call(std::string_view method, std::map<std::string, std::string> params) const
2751{
2752 return network::request(append_url(method), std::move(params));
2753}
2754#include "methods/utility/message_constructor.hpp"
2755
2756vk::method::message_constructor::message_constructor(bool disable_mentions_flag)
2757{
2758 m_constructor.method("messages.send");
2759
2760 param("random_id", "0");
2761
2762 if (disable_mentions_flag) {
2763 param("disable_mentions", "1");
2764 } else {
2765 param("disable_mentions", "0");
2766 }
2767}
2768
2769vk::method::message_constructor& vk::method::message_constructor::param(std::string_view lhs, std::string_view rhs)
2770{
2771 m_constructor.param(lhs, rhs);
2772 return *this;
2773}
2774
2775vk::method::message_constructor& vk::method::message_constructor::append_map(std::map<std::string, std::string> additional_params)
2776{
2777 m_constructor.append_map(std::move(additional_params));
2778 return *this;
2779}
2780
2781vk::method::message_constructor& vk::method::message_constructor::attachments(attachment::attachments_t attachments)
2782{
2783 param("attachment", append_attachments_impl(std::move(attachments)).data());
2784 return *this;
2785}
2786
2787std::string vk::method::message_constructor::execute()
2788{
2789 return m_constructor.perform_request();
2790}
2791
2792std::string vk::method::message_constructor::append_attachments_impl(attachment::attachments_t attachments) const
2793{
2794 std::string result;
2795 result.reserve(attachments.size() * 20);
2796
2797 for (auto& attachment : attachments) {
2798 result += attachment->value();
2799 result += ',';
2800 }
2801
2802 return result;
2803}
2804
2805#include "methods/messages.hpp"
2806
2807#include "exception/error-inl.hpp"
2808#include "methods/utility/message_constructor.hpp"
2809#include "simdjson.h"
2810
2811vk::method::messages::messages(bool disable_mentions_flag)
2812 : m_disable_mentions_flag(disable_mentions_flag)
2813 , m_parser(std::make_shared<simdjson::dom::parser>())
2814 , m_document()
2815 , m_group_constructor()
2816 , m_user_constructor() {}
2817
2818vk::method::messages::~messages() = default;
2819
2820void vk::method::messages::send(int64_t peer_id, std::string_view text, attachment::attachments_t list) const
2821{
2822 message_constructor constructor(m_disable_mentions_flag);
2823
2824 constructor
2825 .param("peer_id", std::to_string(peer_id))
2826 .param("message", text)
2827 .attachments(std::move(list))
2828 .execute();
2829}
2830
2831void vk::method::messages::send(int64_t peer_id, std::string_view text, std::map<std::string, std::string> raw_parameters) const
2832{
2833 message_constructor constructor(m_disable_mentions_flag);
2834
2835 constructor
2836 .param("peer_id", std::to_string(peer_id))
2837 .param("message", text)
2838 .append_map(std::move(raw_parameters))
2839 .execute();
2840}
2841
2842void vk::method::messages::send(int64_t peer_id, std::string_view text, std::string_view layout) const
2843{
2844 message_constructor constructor(m_disable_mentions_flag);
2845
2846 constructor
2847 .param("peer_id", std::to_string(peer_id))
2848 .param("message", text)
2849 .param("keyboard", layout)
2850 .execute();
2851}
2852
2853void vk::method::messages::send(int64_t peer_id, std::string_view text) const
2854{
2855 message_constructor constructor(m_disable_mentions_flag);
2856
2857 constructor
2858 .param("peer_id", std::to_string(peer_id))
2859 .param("message", text)
2860 .execute();
2861}
2862
2863void vk::method::messages::remove_chat_user(int64_t chat_id, int64_t user_id) const
2864{
2865 const std::string raw_response = m_group_constructor
2866 .method("messages.removeChatUser")
2867 .param("chat_id", std::to_string(chat_id))
2868 .param("user_id", std::to_string(user_id))
2869 .param("random_id", "0")
2870 .perform_request();
2871
2872 const simdjson::dom::object response = m_parser->parse(raw_response);
2873
2874 if (response.begin().key() == "error") {
2875 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
2876 }
2877}
2878
2879void vk::method::messages::edit_chat(int64_t chat_id, std::string_view new_title) const
2880{
2881 m_group_constructor
2882 .method("messages.editChat")
2883 .param("chat_id", std::to_string(chat_id - vk::method::utility::chat_id_constant))
2884 .param("title", new_title)
2885 .param("random_id", "0")
2886 .perform_request();
2887}
2888
2889void vk::method::messages::create_chat(std::string_view title, int64_t group_id, std::vector<size_t> user_ids)
2890{
2891 m_group_constructor
2892 .method("messages.createChat")
2893 .param("title", title)
2894 .param("group_id", std::to_string(group_id))
2895 .param("user_ids", string_utils::join<size_t>(std::move(user_ids)))
2896 .perform_request();
2897}
2898
2899void vk::method::messages::add_chat_user(int64_t chat_id, int64_t user_id)
2900{
2901 const std::string raw_response = m_user_constructor
2902 .method("messages.addChatUser")
2903 .param("chat_id", std::to_string(chat_id - vk::method::utility::chat_id_constant))
2904 .param("user_id", std::to_string(user_id))
2905 .perform_request();
2906
2907 const simdjson::dom::object response = m_parser->parse(raw_response);
2908
2909 if (response.begin().key() == "error") {
2910 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
2911 }
2912}
2913
2914void vk::method::messages::delete_chat_photo(int64_t chat_id, int64_t group_id) const
2915{
2916 const std::string raw_response = m_group_constructor
2917 .method("messages.deleteChatPhoto")
2918 .param("chat_id", std::to_string(chat_id - vk::method::utility::chat_id_constant))
2919 .param("group_id", std::to_string(group_id))
2920 .perform_request();
2921
2922 const simdjson::dom::object response = m_parser->parse(raw_response);
2923
2924 if (response.begin().key() == "error") {
2925 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
2926 }
2927}
2928
2929void vk::method::messages::pin(int64_t peer_id, int64_t message_id, int64_t conversation_message_id) const
2930{
2931 const std::string raw_response = m_group_constructor
2932 .method("messages.pin")
2933 .param("peer_id", std::to_string(peer_id))
2934 .param("message_id", std::to_string(message_id))
2935 .param("conversation_message_id", std::to_string(conversation_message_id))
2936 .perform_request();
2937
2938 const simdjson::dom::object response = m_parser->parse(raw_response);
2939
2940 if (response.begin().key() == "error") {
2941 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
2942 }
2943}
2944
2945void vk::method::messages::set_chat_photo(std::string_view filename, std::string_view raw_server) const
2946{
2947 const simdjson::dom::object response = m_document.upload(filename, raw_server, "file");
2948 m_group_constructor
2949 .method("messages.setChatPhoto")
2950 .param("file", response["response"])
2951 .perform_request();
2952}
2953
2954vk::conversation_member_list vk::method::messages::get_conversation_members(int64_t peer_id) const
2955{
2956 const std::string raw_response = m_group_constructor
2957 .method("messages.pin")
2958 .param("peer_id", std::to_string(peer_id))
2959 .perform_request();
2960
2961 const simdjson::dom::object response = m_parser->parse(raw_response);
2962
2963 if (response.begin().key() == "error") {
2964 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
2965 }
2966
2967 conversation_member_list members;
2968 for (auto&& profile : response["response"]["profiles"].get_array()) {
2969 members.push_back(
2970 {profile["first_name"].get_string().take_value().data(),
2971 profile["last_name"].get_string().take_value().data(),
2972 profile["id"].get_int64(),
2973 profile["online"].get_int64() == 1});
2974 }
2975
2976 return members;
2977}
2978#include "methods/groups.hpp"
2979
2980#include "exception/error-inl.hpp"
2981#include "simdjson.h"
2982
2983vk::method::groups::groups()
2984 : m_group_constructor()
2985 , m_parser(std::make_unique<simdjson::dom::parser>()) {}
2986
2987vk::method::groups::~groups() = default;
2988
2989int64_t vk::method::groups::get_by_id() const
2990{
2991 const std::string raw_response = m_group_constructor
2992 .method("groups.getById")
2993 .perform_request();
2994
2995 const simdjson::dom::object response = m_parser->parse(raw_response);
2996
2997 if (response.begin().key() == "error") {
2998 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
2999 }
3000
3001 return response["response"].at(0)["id"];
3002}
3003
3004simdjson::dom::object vk::method::groups::get_long_poll_server(int64_t group_id) const
3005{
3006 const std::string raw_response = m_group_constructor
3007 .method("groups.getLongPollServer")
3008 .param("group_id", std::to_string(group_id))
3009 .param("random_id", "0")
3010 .perform_request();
3011
3012 const simdjson::dom::object response = m_parser->parse(raw_response);
3013
3014 if (response.begin().key() == "error") {
3015 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
3016 }
3017
3018 return response["response"];
3019}
3020#include "methods/docs.hpp"
3021
3022#include "exception/error-inl.hpp"
3023#include "simdjson.h"
3024#include "string_utils/string_utils.hpp"
3025
3026vk::method::docs::docs()
3027 : m_parser(std::make_shared<simdjson::dom::parser>())
3028 , m_group_constructor()
3029 , m_user_constructor()
3030 , m_document() {}
3031
3032vk::method::docs::docs(std::string_view user_token)
3033 : m_parser(std::make_shared<simdjson::dom::parser>())
3034 , m_group_constructor()
3035 , m_user_constructor(user_token.data())
3036 , m_document(user_token.data()) {}
3037
3038vk::method::docs::~docs() = default;
3039
3040vk::attachment::attachments_t vk::method::docs::search(std::string_view query, int64_t count) const
3041{
3042 return m_document.search("docs.search", query, count);
3043}
3044
3045void vk::method::docs::edit(int64_t owner_id, int64_t doc_id, std::string_view title, std::initializer_list<std::string> tags) const
3046{
3047 const std::string raw_response = m_user_constructor
3048 .method("docs.edit")
3049 .param("owner_id", std::to_string(owner_id))
3050 .param("doc_id", std::to_string(doc_id))
3051 .param("title", title.data())
3052 .param("tags", string_utils::join<std::string>(tags).data())
3053 .perform_request();
3054
3055 const simdjson::dom::object response = m_parser->parse(raw_response);
3056
3057 if (response.begin().key() == "error") {
3058 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
3059 }
3060}
3061
3062void vk::method::docs::remove(int64_t owner_id, int64_t doc_id) const
3063{
3064 const std::string raw_response = m_user_constructor
3065 .method("docs.delete")
3066 .param("owner_id", std::to_string(owner_id))
3067 .param("doc_id", std::to_string(doc_id))
3068 .perform_request();
3069
3070 const simdjson::dom::object response = m_parser->parse(raw_response);
3071
3072 if (response.begin().key() == "error") {
3073 exception::dispatch_error_by_code(response["error"]["error_code"].get_int64(), exception::log_before_throw);
3074 }
3075}
3076
3077std::string vk::method::docs::get_upload_server(int64_t group_id) const
3078{
3079 return m_group_constructor
3080 .method("docs.getUploadServer")
3081 .param("group_id", std::to_string(group_id))
3082 .perform_request();
3083}
3084
3085std::string vk::method::docs::get_wall_upload_server(int64_t group_id) const
3086{
3087 return m_group_constructor
3088 .method("docs.getWallUploadServer")
3089 .param("group_id", std::to_string(group_id))
3090 .perform_request();
3091}
3092
3093std::string vk::method::docs::get_messages_upload_server(std::string_view type, int64_t peer_id) const
3094{
3095 return m_group_constructor
3096 .method("docs.getMessagesUploadServer")
3097 .param("peer_id", std::to_string(peer_id))
3098 .param("type", type.data())
3099 .perform_request();
3100}
3101
3102std::shared_ptr<vk::attachment::audio_message>
3103vk::method::docs::save_audio_message(std::string_view filename, std::string_view raw_server) const
3104{
3105 const simdjson::dom::object upload_response = m_document.upload(filename, raw_server, "file");
3106
3107 if (upload_response.begin().key() != "file") {
3108 throw exception::upload_error(-1, "Can't upload file. Maybe is not an mp3 track?");
3109 }
3110
3111 const std::string file = upload_response["file"].get_c_str().take_value();
3112
3113 if (file.empty()) { return {}; }
3114
3115 const std::string raw_save_response = m_group_constructor
3116 .method("docs.save")
3117 .param("file", file)
3118 .param("title", "voice")
3119 .perform_request();
3120
3121 const simdjson::dom::object uploaded_doc = m_parser->parse(raw_save_response)["response"]["audio_message"];
3122
3123 return std::make_shared<vk::attachment::audio_message>(
3124 uploaded_doc["owner_id"].get_int64(),
3125 uploaded_doc["id"].get_int64(),
3126 uploaded_doc["link_ogg"].get_string(),
3127 uploaded_doc["link_mp3"].get_string());
3128}
3129#include "long_poll/api.hpp"
3130
3131#include "config/loader.hpp"
3132#include "simdjson.h"
3133#include "spdlog/spdlog.h"
3134
3135vk::long_poll::api::api(int64_t update_interval)
3136 : m_parser(std::make_shared<simdjson::dom::parser>())
3137 , m_task_queue(vk::config::num_workers())
3138 , m_raw_method()
3139 , m_groups()
3140 , m_group_id(m_groups.get_by_id())
3141 , m_update_interval(update_interval)
3142{
3143 spdlog::info("Long poll: group id - {}", m_group_id);
3144}
3145
3146vk::long_poll::data vk::long_poll::api::server() const
3147{
3148 const simdjson::dom::object server_object = m_groups.get_long_poll_server(m_group_id);
3149
3150 const std::string key = server_object["key"].get_c_str().take_value();
3151 const std::string server = server_object["server"].get_c_str().take_value();
3152 const std::string ts = server_object["ts"].get_c_str().take_value();
3153
3154 return { key, server, ts };
3155}
3156
3157vk::long_poll::api::events_t vk::long_poll::api::listen(vk::long_poll::data& data, int8_t timeout) const
3158{
3159 events_t event_list;
3160
3161 const std::string response = m_raw_method
3162 .method(data.server + "?")
3163 .param("act", "a_check")
3164 .param("key", data.key)
3165 .param("ts", data.ts)
3166 .param("wait", std::to_string(timeout))
3167 .perform_request();
3168
3169 const simdjson::dom::object parsed_response = m_parser->parse(response);
3170
3171 if (std::time(nullptr) % m_update_interval == 0) {
3172 data = server();
3173 }
3174
3175 for (auto&& update : parsed_response["updates"].get_array()) {
3176 event_list.push_back(std::make_unique<vk::event::common>(parsed_response["ts"].get_string(), std::move(update)));
3177 }
3178
3179 data.ts = parsed_response["ts"].get_c_str().take_value();
3180 return event_list;
3181}
3182
3183void vk::long_poll::api::run()
3184{
3185 m_task_queue.start();
3186 m_task_queue.wait_for_completion();
3187}
3188#include "net/network.hpp"
3189
3190#include "spdlog/spdlog.h"
3191
3192#include "curlpp/Easy.hpp"
3193#include "curlpp/Options.hpp"
3194#include "curlpp/cURLpp.hpp"
3195
3196#include <sstream>
3197
3198static std::string escape(std::string_view url)
3199{
3200 return curlpp::escape(url.data());
3201}
3202
3203static size_t file_write(FILE* file, char* ptr, size_t size, size_t nmemb)
3204{
3205 return fwrite(ptr, size, nmemb, file);
3206}
3207
3208static std::string create_parameters(const std::map<std::string, std::string>& body)
3209{
3210 static constexpr size_t average_word_length = 20;
3211 std::string result;
3212 result.reserve(average_word_length * body.size() * 2);
3213
3214 for (const auto& [key, value] : body) {
3215 result += key;
3216 result += '=';
3217 result += escape(value);
3218 result += '&';
3219 }
3220
3221 return result;
3222}
3223
3224#if defined VK_CURL_DEBUG
3225static void debug(std::string_view template_text, std::string_view arg)
3226{
3227 spdlog::info("network - {}: {}", template_text, arg);
3228}
3229
3230static void debug_error(std::string_view template_text, std::string_view arg)
3231{
3232 spdlog::error("network error - {}: {}", template_text, arg);
3233}
3234#else
3235static void debug(std::string_view, std::string_view) {}
3236static void debug_error(std::string_view, std::string_view) {}
3237#endif
3238
3239std::string vk::network::request(std::string_view host, const std::map<std::string, std::string>& target)
3240{
3241 std::ostringstream response;
3242 curlpp::Easy curl_easy;
3243
3244 std::string url = host.data() + create_parameters(target);
3245 debug("HTTP POST", url);
3246
3247 curl_easy.setOpt(curlpp::options::Url(url));
3248 curl_easy.setOpt(curlpp::options::WriteStream(&response));
3249 curl_easy.perform();
3250
3251 debug("HTTP RESPONSE", response.str());
3252
3253 return response.str();
3254}
3255
3256std::string vk::network::request_data(std::string_view host, std::string_view data)
3257{
3258 std::ostringstream response;
3259 curlpp::Easy curl_easy;
3260
3261 debug("HTTP POST", data);
3262
3263 curl_easy.setOpt(curlpp::options::Url(host.data()));
3264 curl_easy.setOpt(curlpp::options::PostFields(data.data()));
3265 curl_easy.setOpt(curlpp::options::PostFieldSize(data.size()));
3266 curl_easy.setOpt(curlpp::options::WriteStream(&response));
3267 curl_easy.perform();
3268
3269 debug("HTTP RESPONSE", response.str());
3270
3271 return response.str();
3272}
3273
3274#ifdef _WIN32
3275#define not !
3276#endif
3277
3278size_t vk::network::download(std::string_view filename, std::string_view server)
3279{
3280 FILE* fp = fopen(filename.data(), "w");
3281 if (not fp) {
3282 debug_error("Can't open file", filename.data());
3283 return -1;
3284 }
3285
3286 curlpp::Easy curl_easy;
3287
3288 debug("HTTP download - filename", filename);
3289
3290 auto* write_function =
3291 new curlpp::options::WriteFunction([fp](auto&& placeholder1, auto&& placeholder2, auto&& placeholder3) {
3292 return file_write(fp,
3293 std::forward<decltype(placeholder1)>(placeholder1),
3294 std::forward<decltype(placeholder2)>(placeholder2),
3295 std::forward<decltype(placeholder3)>(placeholder3));
3296 });
3297
3298 curl_easy.setOpt(write_function);
3299 curl_easy.setOpt(curlpp::options::Url(server.data()));
3300 curl_easy.perform();
3301 fclose(fp);
3302
3303 return 0;
3304}
3305
3306std::string vk::network::upload(std::string_view field_name, std::string_view filename, std::string_view server)
3307{
3308 std::ostringstream response;
3309 curlpp::Forms form_parts;
3310 curlpp::Easy curl_easy;
3311
3312 form_parts.push_back(new curlpp::FormParts::File(field_name.data(), filename.data()));
3313
3314 debug("HTTP upload", filename);
3315
3316 curl_easy.setOpt(curlpp::options::Url(server.data()));
3317 curl_easy.setOpt(curlpp::options::HttpPost(form_parts));
3318 curl_easy.setOpt(curlpp::options::WriteStream(&response));
3319
3320 try {
3321 curl_easy.perform();
3322 } catch (curlpp::RuntimeError& re) {
3323 debug_error("HTTP upload error", "");
3324 }
3325
3326 return response.str();
3327}
3328#include "document/common.hpp"
3329
3330#include "net/network.hpp"
3331
3332#include "simdjson.h"
3333
3334#include <random>
3335
3336vk::document::common::common()
3337 : m_parser(std::make_shared<simdjson::dom::parser>()) {}
3338
3339vk::document::common::common(std::string_view user_token)
3340 : m_parser(std::make_shared<simdjson::dom::parser>())
3341 , m_group_constructor(user_token.data()) {}
3342
3343vk::document::common::~common() = default;
3344
3345template <typename ExecutionPolicy>
3346static void search_attachments(int32_t items_size, ExecutionPolicy&& adding_policy)
3347{
3348 std::random_device rd;
3349 std::mt19937 generator(rd());
3350 std::uniform_int_distribution<> distribution(1, items_size);
3351
3352 for (uint8_t i = 0; i < items_size && i < 10; i++) {
3353 const size_t index = distribution(generator);
3354 adding_policy(index);
3355 }
3356}
3357
3358vk::attachment::attachments_t vk::document::common::search(std::string_view method, std::string_view query, int64_t count) const
3359{
3360 vk::attachment::attachments_t documents;
3361
3362 const std::string raw_response = m_group_constructor
3363 .method(method)
3364 .param("q", query)
3365 .param("count", std::to_string(count))
3366 .perform_request();
3367
3368 const simdjson::dom::array items = m_parser->parse(raw_response)["response"]["items"].get_array();
3369 documents.reserve(items.size());
3370
3371 if (items.size() == 0) { return {}; }
3372
3373 if (method == "photos.search") {
3374 search_attachments(static_cast<int32_t>(items.size()), [&documents, &items](size_t index) {
3375 documents.push_back(std::make_shared<vk::attachment::photo>(
3376 items.at(index)["owner_id"].get_int64(),
3377 items.at(index)["id"].get_int64()));
3378 });
3379 } else if (method == "video.search") {
3380 search_attachments(static_cast<int32_t>(items.size()), [&documents, &items](size_t index) {
3381 documents.push_back(std::make_shared<vk::attachment::video>(
3382 items.at(index)["owner_id"].get_int64(),
3383 items.at(index)["id"].get_int64()));
3384 });
3385 } else if (method == "docs.search") {
3386 search_attachments(static_cast<int32_t>(items.size()), [&documents, &items](size_t index) {
3387 documents.push_back(std::make_shared<vk::attachment::document>(
3388 items.at(index)["owner_id"].get_int64(),
3389 items.at(index)["id"].get_int64(),
3390 items.at(index)["url"].get_string()));
3391 });
3392 }
3393
3394 return documents;
3395}
3396
3397simdjson::dom::object vk::document::common::upload(std::string_view filename, std::string_view server, std::string_view field_name) const
3398{
3399 const std::string upload_server = m_parser->parse(server)["response"]["upload_url"].get_c_str().take_value();
3400 const std::string upload_response = network::upload(field_name, filename, upload_server);
3401
3402 return m_parser->parse(upload_response);
3403}
3404#include "keyboard/layout.hpp"
3405
3406#include <algorithm>
3407
3408vk::keyboard::layout::layout(vk::keyboard::flag flags)
3409 : m_serialized()
3410 , m_buttons()
3411 , m_flags(flags) {}
3412
3413void vk::keyboard::layout::add_row(const std::vector<vk::keyboard::any_button>& row)
3414{
3415 m_buttons.push_back(row);
3416}
3417
3418static std::string create_button(const vk::keyboard::any_button& any_button)
3419{
3420 if (std::holds_alternative<vk::keyboard::button::text>(any_button)) {
3421 return std::get<vk::keyboard::button::text>(any_button).serialize();
3422 }
3423
3424 if (std::holds_alternative<vk::keyboard::button::vk_pay>(any_button)) {
3425 return std::get<vk::keyboard::button::vk_pay>(any_button).serialize();
3426 }
3427
3428 if (std::holds_alternative<vk::keyboard::button::open_app>(any_button)) {
3429 return std::get<vk::keyboard::button::open_app>(any_button).serialize();
3430 }
3431
3432 if (std::holds_alternative<vk::keyboard::button::location>(any_button)) {
3433 return std::get<vk::keyboard::button::location>(any_button).serialize();
3434 }
3435
3436 return "";
3437}
3438
3439void vk::keyboard::layout::serialize()
3440{
3441 m_serialized.push_back('{');
3442
3443 if (has_flag(flag::in_line)) {
3444 m_serialized.append("\"inline\":true,");
3445 }
3446
3447 if (has_flag(flag::one_time)) {
3448 m_serialized.append("\"one_time\":true,");
3449 }
3450
3451 m_serialized.append("\"buttons\":[");
3452
3453 std::vector<std::string> serialized_rows;
3454
3455 for (const auto& row : m_buttons) {
3456 std::vector<std::string> serialized_buttons;
3457 std::transform(row.begin(), row.end(), std::back_inserter(serialized_buttons), [](const any_button& button){
3458 return create_button(button);
3459 });
3460
3461 serialized_rows.push_back('[' + string_utils::join<std::string>(serialized_buttons) + ']');
3462 }
3463
3464 m_serialized += string_utils::join<std::string>(serialized_rows);
3465
3466 m_serialized.append("]}");
3467}
3468
3469std::string vk::keyboard::layout::get() const noexcept
3470{
3471 return m_serialized;
3472}
3473
3474bool vk::keyboard::layout::has_flag(vk::keyboard::flag flag) const noexcept
3475{
3476 return (m_flags & flag) > 0;
3477}
3478#include "events/common_event.hpp"
3479
3480#include "simdjson.h"
3481
3482vk::event::common::~common() = default;
3483
3484vk::event::common::common(std::string_view ts, simdjson::dom::object&& event)
3485 : m_ts(ts)
3486 , m_update_type()
3487 , m_event(std::make_shared<simdjson::dom::object>(std::move(event)))
3488{
3489 m_update_type = (*m_event)["type"].get_string().take_value().data();
3490}
3491
3492simdjson::dom::object& vk::event::common::get_event() const noexcept
3493{
3494 return *m_event;
3495}
3496
3497bool vk::event::common::on_type(std::string_view type) const noexcept
3498{
3499 return m_update_type == type;
3500}
3501
3502vk::event::message_new vk::event::common::get_message_event() const
3503{
3504 return vk::event::message_new(std::move(get_event()["object"]["message"]));
3505}
3506
3507vk::event::wall_post_new vk::event::common::get_wall_post_event() const
3508{
3509 return vk::event::wall_post_new(std::move(get_event()["object"]));
3510}
3511
3512vk::event::wall_reply_new vk::event::common::get_wall_reply_event() const
3513{
3514 return vk::event::wall_reply_new(std::move(get_event()["object"]));
3515}
3516
3517std::string vk::event::common::type() const noexcept
3518{
3519 return m_update_type;
3520}
3521
3522std::string vk::event::common::ts() const noexcept
3523{
3524 return m_ts;
3525}
3526
3527std::string vk::event::common::dump() const noexcept
3528{
3529 return simdjson::to_string(get_event());
3530}
3531#include "events/handlers/attachment_handler.hpp"
3532
3533#include "simdjson.h"
3534
3535static std::shared_ptr<vk::attachment::photo> get_photo(const simdjson::dom::element& attachment)
3536{
3537 return std::make_shared<vk::attachment::photo>(attachment["photo"]["owner_id"].get_int64(), attachment["photo"]["id"].get_int64());
3538}
3539
3540static std::shared_ptr<vk::attachment::video> get_video(const simdjson::dom::element& attachment)
3541{
3542 return std::make_shared<vk::attachment::video>(attachment["video"]["owner_id"].get_int64(), attachment["video"]["id"].get_int64());
3543}
3544
3545static std::shared_ptr<vk::attachment::document> get_doc(const simdjson::dom::element& attachment)
3546{
3547 return std::make_shared<vk::attachment::document>(
3548 attachment["doc"]["owner_id"].get_int64(),
3549 attachment["doc"]["id"].get_int64(),
3550 attachment["doc"]["url"].get_string());
3551}
3552
3553static std::shared_ptr<vk::attachment::audio> get_audio(const simdjson::dom::element& attachment)
3554{
3555 return std::make_shared<vk::attachment::audio>(attachment["audio"]["owner_id"].get_int64(), attachment["audio"]["id"].get_int64());
3556}
3557
3558static std::shared_ptr<vk::attachment::audio_message> get_audio_message(const simdjson::dom::element& attachment)
3559{
3560 return std::make_shared<vk::attachment::audio_message>(
3561 attachment["audio_message"]["owner_id"].get_int64(),
3562 attachment["audio_message"]["id"].get_int64(),
3563 attachment["audio_message"]["link_ogg"].get_string(),
3564 attachment["audio_message"]["link_mp3"].get_string());
3565}
3566
3567static std::shared_ptr<vk::attachment::wall> get_wall(const simdjson::dom::element& attachment)
3568{
3569 return std::make_shared<vk::attachment::wall>(attachment["wall"]["from_id"].get_int64(), attachment["wall"]["id"].get_int64());
3570}
3571
3572vk::attachment::attachments_t vk::event::attachment_handler::try_get(const simdjson::dom::array& attachments) const
3573{
3574 attachment::attachments_t attachment_list;
3575
3576 for (const simdjson::dom::element& attachment : attachments) {
3577 std::string type = attachment["type"].get_string().take_value().data();
3578 if (type == "photo") { attachment_list.emplace_back(get_photo(attachment)); }
3579 if (type == "video") { attachment_list.emplace_back(get_video(attachment)); }
3580 if (type == "doc") { attachment_list.emplace_back(get_doc(attachment)); }
3581 if (type == "audio") { attachment_list.emplace_back(get_audio(attachment)); }
3582 if (type == "wall") { attachment_list.emplace_back(get_wall(attachment)); }
3583 if (type == "audio_message") { attachment_list.emplace_back(get_audio_message(attachment)); }
3584 }
3585
3586 return attachment_list;
3587}
3588#include "events/wall_repost.hpp"
3589
3590vk::event::wall_repost::wall_repost(int64_t id, int64_t from_id, int64_t owner_id, std::string text)
3591 : m_id(id)
3592 , m_from_id(from_id)
3593 , m_owner_id(owner_id)
3594 , m_text(text)
3595 , m_attachments() {}
3596
3597void vk::event::wall_repost::construct_attachments(attachment::attachments_t&& attachments)
3598{
3599 m_attachments = attachments;
3600}
3601
3602int64_t vk::event::wall_repost::id() const noexcept
3603{
3604 return m_id;
3605}
3606
3607int64_t vk::event::wall_repost::from_id() const noexcept
3608{
3609 return m_from_id;
3610}
3611
3612int64_t vk::event::wall_repost::owner_id() const noexcept
3613{
3614 return m_owner_id;
3615}
3616
3617const std::string& vk::event::wall_repost::text() const noexcept
3618{
3619 return m_text;
3620}
3621
3622const vk::attachment::attachments_t& vk::event::wall_repost::attachments() const noexcept
3623{
3624 return m_attachments;
3625}
3626#include "events/message_new.hpp"
3627#include "exception/error-inl.hpp"
3628
3629#include "simdjson.h"
3630
3631#include <iomanip>
3632
3633vk::event::message_new::~message_new() = default;
3634
3635vk::event::message_new::message_new(simdjson::dom::object&& event)
3636 : m_event_json(std::make_shared<simdjson::dom::object>(std::move(event)))
3637 , m_action()
3638 , m_attachment_handler()
3639{
3640 if (get_event()["reply_message"].is_object()) {
3641 m_has_reply = true;
3642 }
3643
3644 if (get_event()["attachments"].is_array()) {
3645 m_has_attachments = true;
3646 }
3647
3648 if (get_event()["fwd_messages"].is_array() && get_event()["fwd_messages"].get_array().size() != 0) {
3649 m_has_attachments = true;
3650 }
3651
3652 if (get_event()["action"].is_object()) {
3653 m_has_action = true;
3654 try_get_actions();
3655 }
3656}
3657
3658void vk::event::message_new::try_get_actions()
3659{
3660 simdjson::dom::object action = get_event()["action"].get_object();
3661 std::string action_name = action["type"].get_string().take_value().data();
3662
3663 if (action_name == "chat_invite_user") {
3664 m_action = action::chat_invite_user{action["member_id"].get_int64()};
3665 }
3666
3667 if (action_name == "chat_kick_user") {
3668 m_action = action::chat_kick_user{action["member_id"].get_int64()};
3669 }
3670
3671 if (action_name == "chat_pin_message") {
3672 m_action = action::chat_pin_message{
3673 action["member_id"].get_int64(),
3674 action["conversation_message_id"].get_int64(),
3675 action["message"].get_c_str().take_value()};
3676 }
3677
3678 if (action_name == "chat_unpin_message") {
3679 m_action = action::chat_unpin_message{action["member_id"].get_int64(), action["conversation_message_id"].get_int64()};
3680 }
3681
3682 if (action_name == "chat_photo_update") {
3683 m_action = action::chat_photo_update{
3684 // empty
3685 };
3686 }
3687
3688 if (action_name == "chat_title_update") {
3689 m_action = action::chat_title_update{action["text"].get_c_str().take_value()};
3690 }
3691}
3692
3693bool vk::event::message_new::on_action(std::string_view action_type) const noexcept
3694{
3695 if (action_type == "chat_invite_user") {
3696 return std::holds_alternative<action::chat_invite_user>(m_action);
3697 }
3698
3699 if (action_type == "chat_kick_user") {
3700 return std::holds_alternative<action::chat_kick_user>(m_action);
3701 }
3702
3703 if (action_type == "chat_pin_message") {
3704 return std::holds_alternative<action::chat_pin_message>(m_action);
3705 }
3706
3707 if (action_type == "chat_unpin_message") {
3708 return std::holds_alternative<action::chat_unpin_message>(m_action);
3709 }
3710
3711 if (action_type == "chat_photo_update") {
3712 return std::holds_alternative<action::chat_photo_update>(m_action);
3713 }
3714
3715 if (action_type == "chat_title_update") {
3716 return std::holds_alternative<action::chat_title_update>(m_action);
3717 }
3718
3719 return false;
3720}
3721
3722simdjson::dom::object& vk::event::message_new::get_event() const
3723{
3724 return *m_event_json;
3725}
3726
3727int64_t vk::event::message_new::conversation_message_id() const noexcept
3728{
3729 return get_event()["conversation_message_id"].get_int64();
3730}
3731
3732int64_t vk::event::message_new::peer_id() const noexcept
3733{
3734 return get_event()["peer_id"].get_int64();
3735}
3736
3737int64_t vk::event::message_new::from_id() const noexcept
3738{
3739 return get_event()["from_id"].get_int64();
3740}
3741
3742std::string vk::event::message_new::text() const noexcept
3743{
3744 return get_event()["text"].get_c_str().take_value();
3745}
3746
3747bool vk::event::message_new::has_action() const noexcept
3748{
3749 return m_has_action;
3750}
3751
3752bool vk::event::message_new::has_reply() const noexcept
3753{
3754 return m_has_reply;
3755}
3756
3757bool vk::event::message_new::has_fwd_messages() const noexcept
3758{
3759 return m_has_fwd_messages;
3760}
3761
3762vk::action::any_action_t vk::event::message_new::action() const
3763{
3764 if (m_has_action) {
3765 return m_action;
3766 } else {
3767 throw exception::access_error(-1, "Attempting accessing empty action");
3768 }
3769}
3770
3771vk::attachment::attachments_t vk::event::message_new::attachments() const
3772{
3773 if (m_has_attachments) {
3774 return m_attachment_handler.try_get(get_event()["attachments"].get_array());
3775 } else {
3776 throw exception::access_error (-1, "Attempting accessing empty attachment list");
3777 }
3778}
3779
3780std::vector<std::unique_ptr<vk::event::message_new>> vk::event::message_new::fwd_messages() const
3781{
3782 if (m_has_attachments) {
3783 std::vector<std::unique_ptr<message_new>> fwd_messages;
3784
3785 for (const simdjson::dom::element& fwd_message : get_event()["fwd_messages"].get_array()) {
3786 fwd_messages.emplace_back(std::make_unique<message_new>(fwd_message));
3787 }
3788
3789 return fwd_messages;
3790 } else {
3791 throw exception::access_error(-1, "Attempting accessing empty forward messages list");
3792 }
3793}
3794
3795std::shared_ptr<vk::event::message_new> vk::event::message_new::reply() const
3796{
3797 if (m_has_reply) {
3798 return std::make_unique<message_new>(get_event()["reply_message"].get_object());
3799 } else {
3800 throw exception::access_error(-1, "Attempting accessing empty reply");
3801 }
3802}
3803
3804void dispatch_events(std::ostream& ostream, const vk::event::message_new& event)
3805{
3806 if (event.has_action()) {
3807 if (event.on_action("chat_invite_user")) {
3808 ostream << std::setw(40) << "chat_invite_user action: ";
3809 ostream << std::get<vk::action::chat_invite_user>(event.action()).member_id;
3810 ostream << std::endl;
3811 }
3812
3813 if (event.on_action("chat_kick_user")) {
3814 ostream << std::setw(40) << "chat_kick_user action: ";
3815 ostream << std::get<vk::action::chat_kick_user>(event.action()).member_id;
3816 ostream << std::endl;
3817 }
3818
3819 if (event.on_action("chat_pin_message")) {
3820 ostream << std::setw(40) << "chat_pin_message action: ";
3821 ostream << std::get<vk::action::chat_pin_message>(event.action()).member_id;
3822 ostream << std::endl;
3823 }
3824
3825 if (event.on_action("chat_unpin_message")) {
3826 ostream << std::setw(40) << "chat_unpin_message action: ";
3827 ostream << std::get<vk::action::chat_unpin_message>(event.action()).member_id;
3828 ostream << std::endl;
3829 }
3830
3831 if (event.on_action("chat_photo_update")) {
3832 ostream << std::setw(40) << "chat_photo_update action: ";
3833 ostream << "<empty>";
3834 ostream << std::endl;
3835 }
3836
3837 if (event.on_action("chat_title_update")) {
3838 ostream << std::setw(30) << "chat_title_update action: ";
3839 ostream << std::get<vk::action::chat_title_update>(event.action()).text;
3840 ostream << std::endl;
3841 }
3842 }
3843}
3844
3845std::ostream& operator<<(std::ostream& ostream, const vk::event::message_new& event)
3846{
3847 ostream << "message_new:" << std::endl;
3848
3849 ostream << std::setw(30)
3850 << "conversation_message_id: " << event.conversation_message_id() << std::endl;
3851 ostream << std::setw(30)
3852 << "peer_id: " << event.peer_id() << std::endl;
3853 ostream << std::setw(30)
3854 << "from_id: " << event.from_id() << std::endl;
3855 ostream << std::setw(30)
3856 << "text: " << event.text() << std::endl;
3857 ostream << std::setw(30)
3858 << "has_action: " << event.has_action() << std::endl;
3859 ostream << std::setw(30)
3860 << "has_reply: " << event.has_reply() << std::endl;
3861 ostream << std::setw(30)
3862 << "has_fwd_messages: " << event.has_fwd_messages() << std::endl;
3863
3864 if (event.has_reply()) {
3865 ostream << std::setw(30)
3866 << "reply: " << event.reply() << std::endl;
3867 }
3868
3869 dispatch_events(ostream, event);
3870
3871 for (auto& attachment : event.attachments()) {
3872 ostream << std::setw(30)
3873 << "attachment: ";
3874 ostream << attachment->value();
3875 ostream << std::endl;
3876 }
3877
3878 if (event.has_fwd_messages()) {
3879 for (auto& message : event.fwd_messages()) {
3880 ostream << std::setw(30)
3881 << "fwd_message: ";
3882 ostream << message.get();
3883 ostream << std::endl;
3884 }
3885 }
3886
3887 return ostream;
3888}
3889#include "events/wall_post_new.hpp"
3890#include "exception/error-inl.hpp"
3891#include "misc/cppdefs.hpp"
3892
3893#include "simdjson.h"
3894
3895vk::event::wall_post_new::~wall_post_new() = default;
3896
3897vk::event::wall_post_new::wall_post_new(simdjson::dom::object&& event)
3898 : m_event_json(std::make_shared<simdjson::dom::object>(std::move(event)))
3899 , m_attachment_handler()
3900{
3901 if (get_event()["attachments"].is_array()) {
3902 m_has_attachments = true;
3903 }
3904
3905 if (get_event()["copy_history"].is_array()) {
3906 m_has_repost = true;
3907 }
3908}
3909
3910int64_t vk::event::wall_post_new::id() const noexcept
3911{
3912 return get_event()["id"].get_int64();
3913}
3914
3915int64_t vk::event::wall_post_new::from_id() const noexcept
3916{
3917 return get_event()["from_id"].get_int64();
3918}
3919
3920int64_t vk::event::wall_post_new::owner_id() const noexcept
3921{
3922 return get_event()["owner_id"].get_int64();
3923}
3924
3925int64_t vk::event::wall_post_new::created_by() const noexcept
3926{
3927 return get_event()["created_by"].get_int64();
3928}
3929
3930std::string vk::event::wall_post_new::text() const noexcept
3931{
3932 return get_event()["text"].get_string().take_value().data();
3933}
3934
3935bool vk::event::wall_post_new::can_edit() const noexcept
3936{
3937 return get_event()["can_edit"].get_int64();
3938}
3939
3940bool vk::event::wall_post_new::can_delete() const noexcept
3941{
3942 return get_event()["can_delete"].get_int64();
3943}
3944
3945bool vk::event::wall_post_new::marked_as_ads() const noexcept
3946{
3947 return get_event()["marked_as_ads"].get_int64();
3948}
3949
3950bool vk::event::wall_post_new::has_attachments() const noexcept
3951{
3952 return m_has_attachments;
3953}
3954
3955bool vk::event::wall_post_new::has_repost() const noexcept
3956{
3957 return m_has_repost;
3958}
3959
3960simdjson::dom::object& vk::event::wall_post_new::get_event() const
3961{
3962 return *m_event_json;
3963}
3964
3965vk::attachment::attachments_t vk::event::wall_post_new::attachments() const
3966{
3967 if (m_has_attachments) {
3968 return m_attachment_handler.try_get(get_event()["attachments"].get_array());
3969 } else {
3970 throw exception::access_error(-1 ,"Attempting accessing empty attachment list.");
3971 }
3972}
3973
3974std::shared_ptr<vk::event::wall_repost> vk::event::wall_post_new::repost() const
3975{
3976 simdjson::dom::object repost_json = get_event()["copy_history"].get_array().at(0).get_object();
3977
3978 if (m_has_repost) {
3979 std::shared_ptr<wall_repost> repost = std::make_shared<wall_repost>(
3980 repost_json["id"].get_int64(),
3981 repost_json["from_id"].get_int64(),
3982 repost_json["owner_id"].get_int64(),
3983 repost_json["text"].get_c_str().take_value());
3984
3985 if (repost_json["attachments"].is_array() && repost_json["attachments"].get_array().size() > 0) {
3986 repost->construct_attachments(m_attachment_handler.try_get(repost_json["attachments"].get_array()));
3987 }
3988
3989 return repost;
3990 } else {
3991 throw exception::access_error(-1 ,"Attempting accessing empty repost");
3992 }
3993}
3994
3995std::ostream& operator<<(std::ostream& ostream, const vk::event::wall_post_new& event)
3996{
3997 ostream << "wall_post_new:" << std::endl;
3998
3999 ostream << std::setw(30)
4000 << "id: " << event.id() << std::endl;
4001 ostream << std::setw(30)
4002 << "from_id: " << event.from_id() << std::endl;
4003 ostream << std::setw(30)
4004 << "owner_id: " << event.owner_id() << std::endl;
4005 ostream << std::setw(30)
4006 << "created_by: " << event.created_by() << std::endl;
4007 ostream << std::setw(30)
4008 << "text: " << event.text() << std::endl;
4009 ostream << std::setw(30)
4010 << "can_edit? " << std::boolalpha << event.can_edit() << std::endl;
4011 ostream << std::setw(30)
4012 << "can_delete? " << std::boolalpha << event.can_delete() << std::endl;
4013 ostream << std::setw(30)
4014 << "marked_as_ads? " << std::boolalpha << event.marked_as_ads() << std::endl;
4015
4016 auto append_attachments = [&ostream](const auto& attachments) {
4017 for (auto& attachment : attachments) {
4018 ostream << std::setw(40) << "attachment: ";
4019 ostream << attachment->value();
4020 ostream << std::endl;
4021 }
4022 };
4023
4024 if (event.has_attachments()) {
4025 append_attachments(event.attachments());
4026 }
4027
4028 if (event.has_repost()) {
4029 ostream << "repost:" << std::endl;
4030 ostream << std::setw(30)
4031 << "from_id: " << event.repost()->from_id() << std::endl;
4032 ostream << std::setw(30)
4033 << "id: " << event.repost()->id() << std::endl;
4034 ostream << std::setw(30)
4035 << "owner_id: " << event.repost()->owner_id() << std::endl;
4036 ostream << std::setw(30)
4037 << "text: " << event.repost()->text() << std::endl;
4038
4039 if (!event.repost()->attachments().empty()) {
4040 append_attachments(event.repost()->attachments());
4041 }
4042 }
4043
4044 return ostream;
4045}
4046#include "events/wall_reply_new.hpp"
4047
4048#include "exception/error-inl.hpp"
4049#include "simdjson.h"
4050
4051vk::event::wall_reply_new::~wall_reply_new() = default;
4052
4053vk::event::wall_reply_new::wall_reply_new(simdjson::dom::object&& event)
4054 : m_event_json(std::make_shared<simdjson::dom::object>(std::move(event)))
4055 , m_attachment_handler()
4056{
4057 if (get_event()["attachments"].is_array() && get_event()["attachments"].get_array().size() > 0) {
4058 m_has_attachments = true;
4059 }
4060}
4061
4062simdjson::dom::object& vk::event::wall_reply_new::get_event() const
4063{
4064 return *m_event_json;
4065}
4066
4067int64_t vk::event::wall_reply_new::id() const noexcept
4068{
4069 return get_event()["id"].get_int64();
4070}
4071
4072int64_t vk::event::wall_reply_new::from_id() const noexcept
4073{
4074 return get_event()["from_id"].get_int64();
4075}
4076
4077int64_t vk::event::wall_reply_new::post_id() const noexcept
4078{
4079 return get_event()["post_id"].get_int64();
4080}
4081
4082int64_t vk::event::wall_reply_new::owner_id() const noexcept
4083{
4084 return get_event()["owner_id"].get_int64();
4085}
4086
4087std::string vk::event::wall_reply_new::text() const noexcept
4088{
4089 return get_event()["text"].get_c_str().take_value();
4090}
4091
4092bool vk::event::wall_reply_new::has_attachments() const noexcept
4093{
4094 return m_has_attachments;
4095}
4096
4097vk::attachment::attachments_t vk::event::wall_reply_new::attachments() const
4098{
4099 if (m_has_attachments) {
4100 return m_attachment_handler.try_get(get_event()["attachments"].get_array());
4101 } else {
4102 throw exception::access_error(-1, "Attempting accessing empty attachment list");
4103 }
4104}
4105
4106std::ostream& operator<<(std::ostream& ostream, const vk::event::wall_reply_new& reply)
4107{
4108 ostream << "wall_reply_new:" << std::endl;
4109
4110 ostream << std::setw(30)
4111 << "id: " << reply.id() << std::endl;
4112 ostream << std::setw(30)
4113 << "from_id: " << reply.from_id() << std::endl;
4114 ostream << std::setw(30)
4115 << "post_id: " << reply.post_id() << std::endl;
4116 ostream << std::setw(30)
4117 << "owner_id: " << reply.owner_id() << std::endl;
4118 ostream << std::setw(30)
4119 << "text: " << reply.text() << std::endl;
4120 ostream << std::setw(30)
4121 << "has_attachments? " << reply.has_attachments() << std::endl;
4122
4123 if (reply.has_attachments()) {
4124 for (auto& attachment : reply.attachments()) {
4125 ostream << std::setw(40)
4126 << "attachment: ";
4127 ostream << attachment->value();
4128 ostream << std::endl;
4129 }
4130 }
4131
4132 return ostream;
4133}
4134#include "oauth/client.hpp"
4135
4136#include "exception/error-inl.hpp"
4137#include "methods/utility/constructor.hpp"
4138#include "simdjson.h"
4139
4140vk::oauth::client::client(std::string_view username, std::string_view password, vk::oauth::target_client client_type)
4141 : m_client_type(client_type)
4142 , m_username(username)
4143 , m_password(password)
4144 , m_target_client_secret()
4145 , m_target_client_id()
4146 , m_pulled_token()
4147 , m_pulled_user_id(0)
4148 , m_parser(std::make_shared<simdjson::dom::parser>())
4149{
4150 switch (m_client_type) {
4151 case target_client::android:
4152 m_target_client_id = m_android_app_client_id;
4153 m_target_client_secret = m_android_app_client_secret;
4154 break;
4155 case target_client::iphone:
4156 m_target_client_id = m_iphone_app_client_id;
4157 m_target_client_secret = m_iphone_app_client_secret;
4158 break;
4159 case target_client::windows:
4160 m_target_client_id = m_windows_app_client_id;
4161 m_target_client_secret = m_windows_app_client_secret;
4162 break;
4163 }
4164 pull();
4165}
4166
4167vk::oauth::client::~client() = default;
4168
4169static bool error_returned(const simdjson::dom::object& response, std::string_view error_desc)
4170{
4171 return response.begin().key() == "error" && response["error"].get_string().take_value() == error_desc;
4172}
4173
4174void vk::oauth::client::pull()
4175{
4176 method::raw_constructor constructor;
4177
4178 constructor
4179 .method(std::string(m_oauth_link) + "token?")
4180 .param("grant_type", "password")
4181 .param("client_id", std::to_string(m_target_client_id))
4182 .param("client_secret", m_target_client_secret)
4183 .param("username", m_username.data())
4184 .param("password", m_password.data());
4185
4186 const simdjson::dom::object response = m_parser->parse(constructor.perform_request());
4187
4188 if (error_returned(response, "invalid_client") || error_returned(response, "invalid_request") ||
4189 error_returned(response, "invalid_grant")) {
4190 throw exception::access_error(-1, response["error_description"].get_c_str().take_value());
4191 }
4192
4193 m_pulled_token = response["access_token"].get_c_str().take_value();
4194 m_pulled_user_id = response["user_id"].get_int64();
4195}
4196
4197std::string vk::oauth::client::token() const noexcept
4198{
4199 return m_pulled_token;
4200}
4201
4202int64_t vk::oauth::client::user_id() const noexcept
4203{
4204 return m_pulled_user_id;
4205}
4206int main() {}