onyxcord 2.0.8 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -1
- data/lib/onyxcord/bot.rb +18 -4
- data/lib/onyxcord/data/server.rb +4 -2
- data/lib/onyxcord/data/voice_state.rb +5 -1
- data/lib/onyxcord/events/channels.rb +17 -1
- data/lib/onyxcord/events/raw.rb +1 -1
- data/lib/onyxcord/events/voice_state_update.rb +36 -5
- data/lib/onyxcord/gateway.rb +17 -5
- data/lib/onyxcord/http.rb +4 -4
- data/lib/onyxcord/version.rb +1 -1
- data/lib/onyxcord/webhooks/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '080907ed18fd9ca727ddeb4d6ee21935995b3027c53a864442b9ffb315a9fe1e'
|
|
4
|
+
data.tar.gz: 4ad0ff3358ae5bde79a83f8e019a007661a1225d1bc752b06c2c1b992a1eb0a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c9098f8a981433ab24e3567bbdbb057d12ac0cf6d80358e2c99dfce84a04e7d68ed3fd59b5b71479580c6997cd17aaac52250b33179754565df0e95493cfc9b4
|
|
7
|
+
data.tar.gz: 137922d8f76e11add880ec027167ce04bbfa3d18b8547a8ed5b726184a7448b4629e0875f49455193185ade34c119d87a49bbbff59070d56eb3cc21c80c39a6f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.13 - 2026-06-28
|
|
4
|
+
|
|
5
|
+
### Correcoes de gateway
|
|
6
|
+
|
|
7
|
+
- Evita chamadas REST em `Bot#update_voice_state` ao processar `VOICE_STATE_UPDATE`.
|
|
8
|
+
- Torna `GUILD_UPDATE` tolerante a guild ainda nao cacheada.
|
|
9
|
+
|
|
10
|
+
## 2.0.12 - 2026-06-28
|
|
11
|
+
|
|
12
|
+
### Correcoes de gateway
|
|
13
|
+
|
|
14
|
+
- Evita chamadas REST durante a criação de eventos do Gateway para `VOICE_STATE_UPDATE` e `CHANNEL_CREATE`.
|
|
15
|
+
- Trata `Async::Cancel` durante dispatch do Gateway como cancelamento esperado, sem registrar erro fatal.
|
|
16
|
+
|
|
17
|
+
## 2.0.11 - 2026-06-28
|
|
18
|
+
|
|
19
|
+
### Correcoes de estabilidade
|
|
20
|
+
|
|
21
|
+
- Isola a sessao HTTP persistente por thread para evitar que tarefas em segundo plano disputem a mesma conexao REST das interacoes.
|
|
22
|
+
|
|
23
|
+
## 2.0.10 - 2026-06-28
|
|
24
|
+
|
|
25
|
+
### Correcoes de voz
|
|
26
|
+
|
|
27
|
+
- Mantem `channel_id` no cache de `VoiceState` mesmo quando o objeto do canal ainda nao foi resolvido.
|
|
28
|
+
- Expoe `channel_id` e `old_channel_id` em `VoiceStateUpdateEvent`, corrigindo entradas em call que pareciam `nil -> nil`.
|
|
29
|
+
- Permite que raw dispatch handlers filtrem pacotes com chaves string ou symbol.
|
|
30
|
+
|
|
31
|
+
## 2.0.9 - 2026-06-28
|
|
32
|
+
|
|
33
|
+
### Correcoes de estabilidade
|
|
34
|
+
|
|
35
|
+
- Tolera um heartbeat sem ACK antes de reconectar, evitando queda por ACK atrasado isolado.
|
|
36
|
+
|
|
3
37
|
## 2.0.8 - 2026-06-28
|
|
4
38
|
|
|
5
39
|
### Correcoes de gateway
|
|
@@ -166,4 +200,3 @@ bot.sync_application_commands!(server_id: ENV.fetch('DISCORD_SERVER_ID'))
|
|
|
166
200
|
- `bundle exec rspec`: 456 exemplos, 0 falhas, 3 pendentes.
|
|
167
201
|
- RuboCop nos arquivos alterados: sem offenses.
|
|
168
202
|
- `gem build onyxcord.gemspec`: sucesso.
|
|
169
|
-
- `gem build onyxcord-webhooks.gemspec`: sucesso.
|
data/lib/onyxcord/bot.rb
CHANGED
|
@@ -1119,12 +1119,12 @@ module OnyxCord
|
|
|
1119
1119
|
@session_id = data['session_id']
|
|
1120
1120
|
|
|
1121
1121
|
server_id = data['guild_id'].to_i
|
|
1122
|
-
server =
|
|
1122
|
+
server = @servers&.[](server_id)
|
|
1123
1123
|
return unless server
|
|
1124
1124
|
|
|
1125
1125
|
user_id = data['user_id'].to_i
|
|
1126
1126
|
old_voice_state = server.voice_states[user_id]
|
|
1127
|
-
old_channel_id = old_voice_state
|
|
1127
|
+
old_channel_id = old_voice_state&.channel_id || old_voice_state&.voice_channel&.id
|
|
1128
1128
|
|
|
1129
1129
|
server.update_voice_state(data)
|
|
1130
1130
|
|
|
@@ -1132,7 +1132,8 @@ module OnyxCord
|
|
|
1132
1132
|
if user_id == @profile.id && existing_voice
|
|
1133
1133
|
new_channel_id = data['channel_id']
|
|
1134
1134
|
if new_channel_id
|
|
1135
|
-
|
|
1135
|
+
channel_id = new_channel_id.to_i
|
|
1136
|
+
new_channel = @channels&.[](channel_id) || server.channels.find { |channel| channel.id == channel_id }
|
|
1136
1137
|
existing_voice.channel = new_channel
|
|
1137
1138
|
else
|
|
1138
1139
|
voice_destroy(server_id)
|
|
@@ -1248,7 +1249,15 @@ module OnyxCord
|
|
|
1248
1249
|
|
|
1249
1250
|
# Internal handler for GUILD_UPDATE
|
|
1250
1251
|
def update_guild(data)
|
|
1251
|
-
|
|
1252
|
+
server_id = data['id'].to_i
|
|
1253
|
+
server = @servers&.[](server_id)
|
|
1254
|
+
|
|
1255
|
+
if server
|
|
1256
|
+
server.update_data(data)
|
|
1257
|
+
else
|
|
1258
|
+
LOGGER.warn("GUILD_UPDATE received for uncached server #{server_id}; caching from payload")
|
|
1259
|
+
ensure_server(data, true)
|
|
1260
|
+
end
|
|
1252
1261
|
end
|
|
1253
1262
|
|
|
1254
1263
|
# Internal handler for GUILD_DELETE
|
|
@@ -1868,6 +1877,11 @@ module OnyxCord
|
|
|
1868
1877
|
raise_event(event)
|
|
1869
1878
|
end
|
|
1870
1879
|
rescue Exception => e
|
|
1880
|
+
if defined?(Async::Cancel) && e.is_a?(Async::Cancel)
|
|
1881
|
+
LOGGER.debug('Gateway message handling was cancelled.')
|
|
1882
|
+
return
|
|
1883
|
+
end
|
|
1884
|
+
|
|
1871
1885
|
LOGGER.error('Gateway message error!')
|
|
1872
1886
|
log_exception(e)
|
|
1873
1887
|
end
|
data/lib/onyxcord/data/server.rb
CHANGED
|
@@ -697,13 +697,15 @@ module OnyxCord
|
|
|
697
697
|
end
|
|
698
698
|
|
|
699
699
|
# Update the existing voice state (or the one we just created)
|
|
700
|
-
|
|
700
|
+
channel_id = data['channel_id'].to_i
|
|
701
|
+
channel = @channels_by_id[channel_id]
|
|
701
702
|
@voice_states[user_id].update(
|
|
702
703
|
channel,
|
|
703
704
|
data['mute'],
|
|
704
705
|
data['deaf'],
|
|
705
706
|
data['self_mute'],
|
|
706
|
-
data['self_deaf']
|
|
707
|
+
data['self_deaf'],
|
|
708
|
+
channel_id
|
|
707
709
|
)
|
|
708
710
|
else
|
|
709
711
|
# The user is not in a voice channel anymore, so delete its voice state
|
|
@@ -22,6 +22,9 @@ module OnyxCord
|
|
|
22
22
|
# @return [Channel] the voice channel this voice state's member is in.
|
|
23
23
|
attr_reader :voice_channel
|
|
24
24
|
|
|
25
|
+
# @return [Integer, nil] the ID of the voice channel this member is in.
|
|
26
|
+
attr_reader :channel_id
|
|
27
|
+
|
|
25
28
|
# @!visibility private
|
|
26
29
|
def initialize(user_id)
|
|
27
30
|
@user_id = user_id
|
|
@@ -30,8 +33,9 @@ module OnyxCord
|
|
|
30
33
|
# Update this voice state with new data from Discord
|
|
31
34
|
# @note For internal use only.
|
|
32
35
|
# @!visibility private
|
|
33
|
-
def update(channel, mute, deaf, self_mute, self_deaf)
|
|
36
|
+
def update(channel, mute, deaf, self_mute, self_deaf, channel_id = nil)
|
|
34
37
|
@voice_channel = channel
|
|
38
|
+
@channel_id = channel_id&.to_i || channel&.id
|
|
35
39
|
@mute = mute
|
|
36
40
|
@deaf = deaf
|
|
37
41
|
@self_mute = self_mute
|
|
@@ -32,7 +32,23 @@ module OnyxCord::Events
|
|
|
32
32
|
# @!visibility private
|
|
33
33
|
def initialize(data, bot)
|
|
34
34
|
@bot = bot
|
|
35
|
-
@channel = data.is_a?(OnyxCord::Channel)
|
|
35
|
+
@channel = if data.is_a?(OnyxCord::Channel)
|
|
36
|
+
data
|
|
37
|
+
else
|
|
38
|
+
cached_channel(bot, data['id']) || OnyxCord::Channel.new(data, bot, cached_server(bot, data['guild_id']))
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def cached_channel(bot, channel_id)
|
|
43
|
+
channels = bot.instance_variable_get(:@channels)
|
|
44
|
+
channels&.[](channel_id.to_i)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def cached_server(bot, server_id)
|
|
48
|
+
return nil unless server_id
|
|
49
|
+
|
|
50
|
+
servers = bot.instance_variable_get(:@servers)
|
|
51
|
+
servers&.[](server_id.to_i)
|
|
36
52
|
end
|
|
37
53
|
end
|
|
38
54
|
|
data/lib/onyxcord/events/raw.rb
CHANGED
|
@@ -6,11 +6,17 @@ require 'onyxcord/data'
|
|
|
6
6
|
module OnyxCord::Events
|
|
7
7
|
# Event raised when a user's voice state updates
|
|
8
8
|
class VoiceStateUpdateEvent < Event
|
|
9
|
-
attr_reader :user, :token, :suppress, :session_id, :self_mute, :self_deaf, :mute, :deaf, :server, :channel
|
|
9
|
+
attr_reader :user, :user_id, :token, :suppress, :session_id, :self_mute, :self_deaf, :mute, :deaf, :server, :channel
|
|
10
10
|
|
|
11
11
|
# @return [Channel, nil] the old channel this user was on, or nil if the user is newly joining voice.
|
|
12
12
|
attr_reader :old_channel
|
|
13
13
|
|
|
14
|
+
# @return [Integer, nil] the current voice channel ID, or nil if the user left voice.
|
|
15
|
+
attr_reader :channel_id
|
|
16
|
+
|
|
17
|
+
# @return [Integer, nil] the previous voice channel ID, or nil if the user joined voice.
|
|
18
|
+
attr_reader :old_channel_id
|
|
19
|
+
|
|
14
20
|
# @!visibility private
|
|
15
21
|
def initialize(data, old_channel_id, bot)
|
|
16
22
|
@bot = bot
|
|
@@ -22,12 +28,35 @@ module OnyxCord::Events
|
|
|
22
28
|
@self_deaf = data['self_deaf']
|
|
23
29
|
@mute = data['mute']
|
|
24
30
|
@deaf = data['deaf']
|
|
25
|
-
@
|
|
31
|
+
@user_id = data['user_id']&.to_i
|
|
32
|
+
@channel_id = data['channel_id']&.to_i
|
|
33
|
+
@old_channel_id = old_channel_id&.to_i
|
|
34
|
+
@server = cached_server(bot, data['guild_id'])
|
|
26
35
|
return unless @server
|
|
27
36
|
|
|
28
|
-
@channel = bot
|
|
29
|
-
@old_channel = bot
|
|
30
|
-
@user = bot
|
|
37
|
+
@channel = cached_channel(bot, @channel_id, @server)
|
|
38
|
+
@old_channel = cached_channel(bot, @old_channel_id, @server)
|
|
39
|
+
@user = cached_user(bot, data)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def cached_server(bot, server_id)
|
|
43
|
+
servers = bot.instance_variable_get(:@servers)
|
|
44
|
+
servers&.[](server_id.to_i)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def cached_channel(bot, channel_id, server)
|
|
48
|
+
return nil unless channel_id
|
|
49
|
+
|
|
50
|
+
channels = bot.instance_variable_get(:@channels)
|
|
51
|
+
channels&.[](channel_id.to_i) || server.channels.find { |channel| channel.id == channel_id.to_i }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def cached_user(bot, data)
|
|
55
|
+
user_data = data.dig('member', 'user') || data['user']
|
|
56
|
+
return bot.ensure_user(user_data) if user_data
|
|
57
|
+
|
|
58
|
+
users = bot.instance_variable_get(:@users)
|
|
59
|
+
users&.[](data['user_id'].to_i)
|
|
31
60
|
end
|
|
32
61
|
end
|
|
33
62
|
|
|
@@ -39,6 +68,8 @@ module OnyxCord::Events
|
|
|
39
68
|
|
|
40
69
|
[
|
|
41
70
|
matches_all(@attributes[:from], event.user) do |a, e|
|
|
71
|
+
next unless e
|
|
72
|
+
|
|
42
73
|
a == case a
|
|
43
74
|
when String
|
|
44
75
|
e.name
|
data/lib/onyxcord/gateway.rb
CHANGED
|
@@ -84,6 +84,7 @@ module OnyxCord
|
|
|
84
84
|
@connection = nil
|
|
85
85
|
@closed = true
|
|
86
86
|
@pipe_broken = false
|
|
87
|
+
@missed_heartbeat_acks = 0
|
|
87
88
|
end
|
|
88
89
|
|
|
89
90
|
# Connect to the gateway server inside an Async reactor.
|
|
@@ -142,10 +143,17 @@ module OnyxCord
|
|
|
142
143
|
def heartbeat
|
|
143
144
|
if check_heartbeat_acks
|
|
144
145
|
unless @last_heartbeat_acked
|
|
145
|
-
|
|
146
|
-
@
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
@missed_heartbeat_acks += 1
|
|
147
|
+
if @missed_heartbeat_acks >= 2
|
|
148
|
+
LOGGER.warn('Last heartbeats were not acked — zombie connection! Reconnecting')
|
|
149
|
+
@pipe_broken = true
|
|
150
|
+
reconnect
|
|
151
|
+
return
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
LOGGER.warn('Last heartbeat was not acked — waiting one more interval before reconnecting')
|
|
155
|
+
else
|
|
156
|
+
@missed_heartbeat_acks = 0
|
|
149
157
|
end
|
|
150
158
|
@last_heartbeat_acked = false
|
|
151
159
|
end
|
|
@@ -219,6 +227,7 @@ module OnyxCord
|
|
|
219
227
|
|
|
220
228
|
def setup_heartbeats(interval)
|
|
221
229
|
@last_heartbeat_acked = true
|
|
230
|
+
@missed_heartbeat_acks = 0
|
|
222
231
|
return if @heartbeat_task
|
|
223
232
|
|
|
224
233
|
@heartbeat_interval = interval
|
|
@@ -432,7 +441,10 @@ module OnyxCord
|
|
|
432
441
|
# Op 11
|
|
433
442
|
def handle_heartbeat_ack(packet)
|
|
434
443
|
LOGGER.debug("Heartbeat ACK: #{packet.inspect}")
|
|
435
|
-
|
|
444
|
+
if @check_heartbeat_acks
|
|
445
|
+
@last_heartbeat_acked = true
|
|
446
|
+
@missed_heartbeat_acks = 0
|
|
447
|
+
end
|
|
436
448
|
end
|
|
437
449
|
|
|
438
450
|
def handle_internal_close(e)
|
data/lib/onyxcord/http.rb
CHANGED
|
@@ -45,15 +45,15 @@ module OnyxCord
|
|
|
45
45
|
|
|
46
46
|
module_function
|
|
47
47
|
|
|
48
|
-
# The shared HTTPX session with persistent connections.
|
|
48
|
+
# The shared HTTPX session with persistent connections for the current thread.
|
|
49
49
|
def session
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
Thread.current[:onyxcord_http_session] ||= HTTPX.plugin(:persistent)
|
|
51
|
+
.plugin(:follow_redirects)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# Reset the HTTP session (useful for tests).
|
|
55
55
|
def reset!
|
|
56
|
-
|
|
56
|
+
Thread.current[:onyxcord_http_session] = nil
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
# Perform a raw HTTP request and return a {Response}.
|
data/lib/onyxcord/version.rb
CHANGED