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