1+ #pragma once
2+ #include " client.h"
3+ #include " message_receiver.h"
4+ #include " asio_schedule.h"
5+ #include " asio_websocketconnection.h"
6+
7+ namespace SleepyDiscord {
8+
9+ class ASIOWebSocketDiscordClient : public BaseDiscordClient {
10+ public:
11+ ASIOWebSocketDiscordClient () = default ;
12+ ASIOWebSocketDiscordClient (const std::string token, const char _n = 0 )
13+ : ASIOWebSocketDiscordClient(std::make_shared<asio::io_context>(), token) {}
14+ ASIOWebSocketDiscordClient (std::shared_ptr<asio::io_context> _ioContext, const std::string token) :
15+ ioContext (_ioContext)
16+ {
17+ setup (_ioContext, token);
18+ }
19+ ~ASIOWebSocketDiscordClient () {}
20+
21+ std::shared_ptr<asio::io_context> ioContext;
22+ void setup (std::shared_ptr<asio::io_context> _ioContext, const std::string token) {
23+ if (workGuard && workGuard->owns_work ()) {
24+ onError (GENERAL_ERROR, " can't set io context while client has work" );
25+ return ;
26+ }
27+
28+ ioContext = _ioContext;
29+ setScheduleHandler<ASIOScheduleHandler>(ioContext);
30+
31+ start (token);
32+ }
33+
34+ void run () override {
35+ if (!ioContext) { // shouldn't happen but can if default constructor is used without setup
36+ onError (SleepyDiscord::GENERAL_ERROR, " Can't run Discord Client without async IO or Discord token." );
37+ return ;
38+ }
39+
40+ BaseDiscordClient::connect ();
41+ workGuard = std::make_shared<asio::executor_work_guard<asio::io_context::executor_type>>(asio::make_work_guard (*ioContext));
42+ ioContext->run ();
43+ }
44+
45+ Timer schedule (TimedTask code, const time_t milliseconds) override {
46+ return ASIOScheduleHandler::schedule (*ioContext, std::move (code), milliseconds);
47+ }
48+
49+ void postTask (PostableTask code) override {
50+ asio::post (code);
51+ }
52+ protected:
53+ #include " standard_config_header.h"
54+ private:
55+
56+ std::shared_ptr<asio::executor_work_guard<asio::io_context::executor_type>> workGuard;
57+
58+ bool connect (const std::string& uri,
59+ GenericMessageReceiver& messageProcessor,
60+ WebsocketConnection& connection
61+ ) override {
62+ // set up websocket events
63+ // The connection object will own the eventListener
64+ // we can capture messageProcessor because the messageProcessor outlives the connection object
65+ ASIOWebSocketConnection::EventReceiver eventListener;
66+ eventListener.setOnMessage ([&messageProcessor](std::vector<uint8_t > data) { // copy stream to vector
67+ auto message = std::make_shared<std::string>(data.begin (), data.end ()); // copy again to string, dumb but it's simple and memory safe. I'll fix it later, just a temp solution
68+ messageProcessor.processMessage (WebSocketMessage{ WebSocketMessage::text, *message, message });
69+ });
70+ eventListener.setOnClose ([&messageProcessor](uint16_t code, std::string _) {
71+ messageProcessor.processCloseCode (static_cast <WebSocketMessage::OPCode>(code));
72+ });
73+ eventListener.setOnOpen ([&messageProcessor]() {
74+ messageProcessor.initialize ();
75+ });
76+
77+ auto sharedConnection = std::make_shared<ASIOWebSocketConnection>(std::move (eventListener));
78+ connection = sharedConnection;
79+ sharedConnection->connect (uri, ioContext);
80+ return true ;
81+ }
82+
83+ void disconnect (unsigned int code, const std::string reason, WebsocketConnection& _connection) override {
84+ if (auto websocketConnection = _connection.lock ()) {
85+ websocketConnection->disconnect (code, reason);
86+ }
87+ std::cout << " disconnected\n " ;
88+ }
89+
90+ void send (std::string message, WebsocketConnection& _connection) override {
91+ if (auto websocketConnection = _connection.lock ()) {
92+ websocketConnection->send (message, WebSocketMessage::text, nullptr );
93+ }
94+ }
95+
96+ void stopClient () override {
97+ workGuard.reset ();
98+ ioContext->stop ();
99+ }
100+ };
101+
102+ typedef ASIOWebSocketDiscordClient DiscordClient;
103+ }
0 commit comments