Module: MTProto::Updates::Parser
- Defined in:
- lib/mtproto/updates/parser.rb
Overview
Parses a raw Telegram update reply into normalized Events, shared by the live push path and the getDifference/getChannelDifference recovery paths so both yield one event shape. This module walks the ENVELOPE (which container, which vector, the trailing peers and state); EventBuilder turns each Update into an Event. Every vector entry is advanced via Schema#skip, so an update type we don't model never drops the ones around it.
Defined Under Namespace
Classes: Batch, Difference
Constant Summary collapse
- UPDATES =
0x74ae4240- UPDATES_COMBINED =
0x725b04c3- UPDATES_TOO_LONG =
0xe317af7e- UPDATE_SHORT =
0x78d4dec1- UPDATES_DIFFERENCE =
0x00f49ca0- UPDATES_DIFFERENCE_SLICE =
0xa8fb1981- UPDATES_DIFFERENCE_EMPTY =
0x5d75a138- UPDATES_DIFFERENCE_TOO_LONG =
0x4afe8f6d- CHANNEL_DIFFERENCE =
0x2064674e- CHANNEL_DIFFERENCE_EMPTY =
0x3e11affb- CHANNEL_DIFFERENCE_TOO_LONG =
0xa4bcc6fe- UPDATE_CHANNEL_TOO_LONG =
0x108d941f
Class Method Summary collapse
- .attach_peers(events, users, chats) ⇒ Object
-
.channel_difference_body(data) ⇒ Object
channelDifference#2064674e flags:# final:flags.0?true pts:int timeout:flags.1?int new_messages:Vector
other_updates:Vector chats users. -
.difference_body(data, constructor) ⇒ Object
difference / differenceSlice: new_messages new_encrypted_messages other_updates chats users state.
- .empty_difference(type: :empty, final: false, pts: nil, date: nil, seq: nil) ⇒ Object
-
.messages_vector(data, offset, channel:) ⇒ Object
A Vector
(a difference's new_messages) → ordered :message / :join / :leave events WITHOUT pts (the protocol omits per-message pts here). -
.parse_channel_difference(data) ⇒ Object
updates.channelDifference reply → a Difference carrying the channel's new pts and
final. -
.parse_container(constructor, data) ⇒ Object
A live
updates/updatesCombinedpush container (or a bodylessupdatesTooLonggap signal) → a Batch of ordered events plus the batch peers and gap signals. -
.parse_difference(data) ⇒ Object
updates.difference / differenceSlice reply → a Difference with the recovered events and the new account position (in the trailing updates.State).
-
.parse_short(data) ⇒ Object
updateShort#78d4dec1 update:Update date:int — a single update with no peers attached.
-
.parse_updates(data) ⇒ Object
updates: updates:Vector
users:Vector chats:Vector date:int (updatesCombined only inserts seq_start before the trailing seq, past date). -
.updates_vector(data, offset) ⇒ Object
A Vector
(a live container's updates, or a difference's other_updates) → [ordered events, updateChannelTooLong channel ids, offset past the vector].
Class Method Details
.attach_peers(events, users, chats) ⇒ Object
201 202 203 204 205 206 |
# File 'lib/mtproto/updates/parser.rb', line 201 def attach_peers(events, users, chats) events.each do |event| event.users = users event.chats = chats end end |
.channel_difference_body(data) ⇒ Object
channelDifference#2064674e flags:# final:flags.0?true pts:int timeout:flags.1?int
new_messages:Vector
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/mtproto/updates/parser.rb', line 146 def channel_difference_body(data) flags = data[4, 4].unpack1('L<') pts = data[8, 4].unpack1('l<') offset = 12 offset += 4 if flags.anybits?(1 << 1) # timeout msg_events, offset = (data, offset, channel: true) upd_events, channel_too_long, offset = updates_vector(data, offset) chats, offset = TL::Dialogs.chats_at(data, offset) users, = TL::Dialogs.users_at(data, offset) events = msg_events + upd_events attach_peers(events, users, chats) Difference.new(type: :difference, final: flags.anybits?(1 << 0), pts: pts, events: events, users: users, chats: chats, channel_too_long: channel_too_long) end |
.difference_body(data, constructor) ⇒ Object
difference / differenceSlice: new_messages new_encrypted_messages other_updates chats users state. Channel messages ride in other_updates (with pts); common new_messages ride in new_messages (no pts).
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/mtproto/updates/parser.rb', line 127 def difference_body(data, constructor) msg_events, offset = (data, 4, channel: false) offset = TL::Reader.schema.skip_vector(data, offset) # new_encrypted_messages upd_events, channel_too_long, offset = updates_vector(data, offset) chats, offset = TL::Dialogs.chats_at(data, offset) users, offset = TL::Dialogs.users_at(data, offset) state = TL::UpdatesState.deserialize(data[offset..]) events = msg_events + upd_events attach_peers(events, users, chats) Difference.new( type: constructor == UPDATES_DIFFERENCE ? :difference : :slice, final: false, events: events, users: users, chats: chats, channel_too_long: channel_too_long, pts: state.pts, qts: state.qts, date: state.date, seq: state.seq ) end |
.empty_difference(type: :empty, final: false, pts: nil, date: nil, seq: nil) ⇒ Object
119 120 121 122 |
# File 'lib/mtproto/updates/parser.rb', line 119 def empty_difference(type: :empty, final: false, pts: nil, date: nil, seq: nil) Difference.new(type: type, final: final, events: [], users: [], chats: [], channel_too_long: [], pts: pts, date: date, seq: seq) end |
.messages_vector(data, offset, channel:) ⇒ Object
A Vector
165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/mtproto/updates/parser.rb', line 165 def (data, offset, channel:) offset += 4 # vector constructor count = data[offset, 4].unpack1('L<') offset += 4 events = [] count.times do event = EventBuilder.(data, offset, data[offset, 4].unpack1('L<'), channel: channel) events << event if event offset = TL::Reader.schema.skip(data, offset) end [events, offset] end |
.parse_channel_difference(data) ⇒ Object
updates.channelDifference reply → a Difference carrying the channel's new pts
and final. channel messages ride in new_messages without a per-message pts;
the state machine advances the channel pts to this reply's pts.
105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/mtproto/updates/parser.rb', line 105 def parse_channel_difference(data) case data[0, 4].unpack1('L<') when CHANNEL_DIFFERENCE_EMPTY flags = data[4, 4].unpack1('L<') empty_difference(final: flags.anybits?(1 << 0), pts: data[8, 4].unpack1('l<')) when CHANNEL_DIFFERENCE channel_difference_body(data) when CHANNEL_DIFFERENCE_TOO_LONG empty_difference(type: :too_long, final: true) else empty_difference(final: true) end end |
.parse_container(constructor, data) ⇒ Object
A live updates / updatesCombined push container (or a bodyless
updatesTooLong gap signal) → a Batch of ordered events plus the batch peers
and gap signals.
45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/mtproto/updates/parser.rb', line 45 def parse_container(constructor, data) case constructor when UPDATES_TOO_LONG Batch.new(events: [], users: [], chats: [], channel_too_long: [], too_long: true, date: nil) when UPDATES, UPDATES_COMBINED parse_updates(data) when UPDATE_SHORT parse_short(data) else Batch.new(events: [], users: [], chats: [], channel_too_long: [], too_long: false, date: nil) end end |
.parse_difference(data) ⇒ Object
updates.difference / differenceSlice reply → a Difference with the recovered events and the new account position (in the trailing updates.State). Common new_messages arrive WITHOUT a per-message pts (the protocol omits it), so their events carry pts nil; the state machine advances to the batch-terminal state.pts.
89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/mtproto/updates/parser.rb', line 89 def parse_difference(data) case data[0, 4].unpack1('L<') when UPDATES_DIFFERENCE_EMPTY empty_difference(date: data[4, 4].unpack1('L<'), seq: data[8, 4].unpack1('L<')) when UPDATES_DIFFERENCE, UPDATES_DIFFERENCE_SLICE difference_body(data, data[0, 4].unpack1('L<')) when UPDATES_DIFFERENCE_TOO_LONG empty_difference(type: :too_long, pts: data[4, 4].unpack1('l<')) else empty_difference end end |
.parse_short(data) ⇒ Object
updateShort#78d4dec1 update:Update date:int — a single update with no peers attached. Channel messages never arrive this way, but edits/reactions do, so classify the one update (or surface an updateChannelTooLong gap signal).
61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/mtproto/updates/parser.rb', line 61 def parse_short(data) inner_ctor = data[4, 4].unpack1('L<') if inner_ctor == UPDATE_CHANNEL_TOO_LONG return Batch.new(events: [], users: [], chats: [], channel_too_long: [data[12, 8].unpack1('Q<')], too_long: false, date: nil) end event = EventBuilder.classify_update(data, 4, inner_ctor) Batch.new(events: [event].compact, users: [], chats: [], channel_too_long: [], too_long: false, date: data[TL::Reader.schema.skip(data, 4), 4].unpack1('L<')) end |
.parse_updates(data) ⇒ Object
updates: updates:Vector
75 76 77 78 79 80 81 82 83 |
# File 'lib/mtproto/updates/parser.rb', line 75 def parse_updates(data) events, channel_too_long, offset = updates_vector(data, 4) users, offset = TL::Dialogs.users_at(data, offset) chats, offset = TL::Dialogs.chats_at(data, offset) date = data[offset, 4].unpack1('L<') attach_peers(events, users, chats) Batch.new(events: events, users: users, chats: chats, channel_too_long: channel_too_long, too_long: false, date: date) end |
.updates_vector(data, offset) ⇒ Object
A Vector
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/mtproto/updates/parser.rb', line 181 def updates_vector(data, offset) offset += 4 # vector constructor count = data[offset, 4].unpack1('L<') offset += 4 events = [] channel_too_long = [] count.times do ctor = data[offset, 4].unpack1('L<') if ctor == UPDATE_CHANNEL_TOO_LONG channel_too_long << data[offset + 8, 8].unpack1('Q<') # constructor(4) + flags(4) else event = EventBuilder.classify_update(data, offset, ctor) events << event if event end offset = TL::Reader.schema.skip(data, offset) end [events, channel_too_long, offset] end |