summaryrefslogtreecommitdiff
path: root/discord/discord.cpp
blob: cd1e723c804de002aa427e571054f9cc3a4bc20e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include "discord.hpp"

DiscordClient::DiscordClient() {}

void DiscordClient::Start() {
    if (m_client_connected)
        throw std::runtime_error("attempt to start client twice consecutively");

    m_client_connected = true;
    m_websocket.StartConnection(DiscordGateway);
    m_websocket.SetJSONCallback(std::bind(&DiscordClient::HandleGatewayMessage, this, std::placeholders::_1));
}

void DiscordClient::Stop() {
    if (!m_client_connected) return;
    m_heartbeat_waiter.kill();
    m_heartbeat_thread.join();
    m_client_connected = false;
}

bool DiscordClient::IsStarted() const {
    return m_client_connected;
}

void DiscordClient::HandleGatewayMessage(nlohmann::json j) {
    GatewayMessage m;
    try {
        m = j;
    } catch (std::exception &e) {
        printf("Error decoding JSON. Discarding message: %s\n", e.what());
        return;
    }

    switch (m.Opcode) {
        case GatewayOp::Hello: {
            HelloMessageData d = m.Data;
            m_heartbeat_msec = d.HeartbeatInterval;
            m_heartbeat_thread = std::thread(std::bind(&DiscordClient::HeartbeatThread, this));
        } break;
        case GatewayOp::HeartbeatAck: {
            m_heartbeat_acked = true;
        } break;
        default:
            printf("Unknown opcode %d\n", m.Opcode);
            break;
    }
}

void DiscordClient::HeartbeatThread() {
    while (m_client_connected) {
        if (!m_heartbeat_acked) {
            printf("wow! a heartbeat wasn't acked! how could this happen?");
        }

        m_heartbeat_acked = false;

        HeartbeatMessage msg;
        msg.Sequence = m_last_sequence;
        nlohmann::json j = msg;
        m_websocket.Send(j.dump());

        if (!m_heartbeat_waiter.wait_for(std::chrono::milliseconds(m_heartbeat_msec)))
            break;
    }
}

void from_json(const nlohmann::json &j, GatewayMessage &m) {
    j.at("op").get_to(m.Opcode);
    m.Data = j.at("d");

    if (j.contains("t") && !j.at("t").is_null())
        j.at("t").get_to(m.Type);
}

void from_json(const nlohmann::json &j, HelloMessageData &m) {
    j.at("heartbeat_interval").get_to(m.HeartbeatInterval);
}

void to_json(nlohmann::json &j, const HeartbeatMessage &m) {
    j["op"] = GatewayOp::Heartbeat;
    if (m.Sequence == -1)
        j["d"] = nullptr;
    else
        j["d"] = m.Sequence;
}