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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
#include "ratelimitindicator.hpp"
#include "abaddon.hpp"
#include <filesystem>
RateLimitIndicator::RateLimitIndicator()
: Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) {
m_label.set_text("");
m_label.set_ellipsize(Pango::ELLIPSIZE_START);
m_label.set_valign(Gtk::ALIGN_END);
get_style_context()->add_class("ratelimit-indicator");
m_img.set_margin_start(7);
add(m_label);
add(m_img);
m_label.show();
const static auto clock_path = Abaddon::GetResPath("/clock.png");
if (std::filesystem::exists(clock_path)) {
try {
const auto pixbuf = Gdk::Pixbuf::create_from_file(clock_path);
int w, h;
GetImageDimensions(pixbuf->get_width(), pixbuf->get_height(), w, h, 20, 10);
m_img.property_pixbuf() = pixbuf->scale_simple(w, h, Gdk::INTERP_BILINEAR);
} catch (...) {}
}
Abaddon::Get().GetDiscordClient().signal_message_create().connect(sigc::mem_fun(*this, &RateLimitIndicator::OnMessageCreate));
Abaddon::Get().GetDiscordClient().signal_message_send_fail().connect(sigc::mem_fun(*this, &RateLimitIndicator::OnMessageSendFail));
Abaddon::Get().GetDiscordClient().signal_channel_update().connect(sigc::mem_fun(*this, &RateLimitIndicator::OnChannelUpdate));
}
void RateLimitIndicator::SetActiveChannel(Snowflake id) {
m_active_channel = id;
const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(m_active_channel);
if (channel.has_value() && channel->RateLimitPerUser.has_value())
m_rate_limit = *channel->RateLimitPerUser;
else
m_rate_limit = 0;
UpdateIndicator();
}
bool RateLimitIndicator::CanSpeak() const {
const auto rate_limit = GetRateLimit();
if (rate_limit == 0) return true;
const auto it = m_times.find(m_active_channel);
if (it == m_times.end())
return true;
const auto now = std::chrono::steady_clock::now();
const auto sec_diff = std::chrono::duration_cast<std::chrono::seconds>(it->second - now).count();
return sec_diff <= 0;
}
int RateLimitIndicator::GetTimeLeft() const {
if (CanSpeak()) return 0;
auto it = m_times.find(m_active_channel);
if (it == m_times.end()) return 0;
const auto now = std::chrono::steady_clock::now();
const auto sec_diff = std::chrono::duration_cast<std::chrono::seconds>(it->second - now).count();
if (sec_diff <= 0)
return 0;
else
return sec_diff;
}
int RateLimitIndicator::GetRateLimit() const {
return m_rate_limit;
}
bool RateLimitIndicator::UpdateIndicator() {
if (const auto rate_limit = GetRateLimit(); rate_limit != 0) {
m_img.show();
auto &discord = Abaddon::Get().GetDiscordClient();
if (discord.HasAnyChannelPermission(discord.GetUserData().ID, m_active_channel, Permission::MANAGE_MESSAGES | Permission::MANAGE_CHANNELS)) {
m_label.set_text("You may bypass slowmode.");
set_has_tooltip(false);
} else {
const auto time_left = GetTimeLeft();
if (time_left > 0)
m_label.set_text(std::to_string(time_left) + "s");
else
m_label.set_text("");
set_tooltip_text("Slowmode is enabled. Members can send one message every " + std::to_string(rate_limit) + " seconds.");
}
} else {
m_img.hide();
m_label.set_text("");
set_has_tooltip(false);
}
if (m_connection)
m_connection.disconnect();
m_connection = Glib::signal_timeout().connect_seconds(sigc::mem_fun(*this, &RateLimitIndicator::UpdateIndicator), 1);
return false;
}
void RateLimitIndicator::OnMessageCreate(const Message &message) {
auto &discord = Abaddon::Get().GetDiscordClient();
if (message.Author.ID != discord.GetUserData().ID) return;
if (!message.GuildID.has_value()) return;
const bool can_bypass = discord.HasAnyChannelPermission(discord.GetUserData().ID, m_active_channel, Permission::MANAGE_MESSAGES | Permission::MANAGE_CHANNELS);
const auto rate_limit = GetRateLimit();
if (rate_limit > 0 && !can_bypass) {
m_times[message.ChannelID] = std::chrono::steady_clock::now() + std::chrono::duration<int>(rate_limit + 1);
UpdateIndicator();
}
}
void RateLimitIndicator::OnMessageSendFail(const std::string &nonce, float retry_after) {
if (retry_after != 0) { // failed to rate limit
const auto msg = Abaddon::Get().GetDiscordClient().GetMessage(nonce);
const auto channel_id = msg->ChannelID;
m_times[channel_id] = std::chrono::steady_clock::now() + std::chrono::duration<int>(std::lroundf(retry_after + 0.5f) + 1); // + 0.5 will ceil it
UpdateIndicator();
}
}
void RateLimitIndicator::OnChannelUpdate(Snowflake channel_id) {
if (channel_id != m_active_channel) return;
const auto chan = Abaddon::Get().GetDiscordClient().GetChannel(m_active_channel);
if (!chan.has_value()) return;
const auto r = chan->RateLimitPerUser;
if (r.has_value())
m_rate_limit = *r;
else
m_rate_limit = 0;
UpdateIndicator();
}
|