Module: MTProto::Updates::EventBuilder
- Defined in:
- lib/mtproto/updates/event_builder.rb
Overview
Turns ONE Telegram Update (a single entry of a Vector
Constant Summary collapse
- UPDATE_NEW_MESSAGE =
0x1f2b0afd- UPDATE_NEW_CHANNEL_MESSAGE =
0x62ba04d9- UPDATE_EDIT_MESSAGE =
0xe40370a3- UPDATE_EDIT_CHANNEL_MESSAGE =
0x1b3f4df7- UPDATE_MESSAGE_REACTIONS =
0x1e297bfa- UPDATE_BOT_MESSAGE_REACTION =
0xac21d3ce- UPDATE_DELETE_CHANNEL_MESSAGES =
0xc32d5b12- UPDATE_BOT_CALLBACK_QUERY =
0xb9cfc48d- UPDATE_CHANNEL_PARTICIPANT =
0x985d3abb- UPDATE_BOT_GUEST_CHAT_QUERY =
0xcdd4093d- UPDATE_MANAGED_BOT =
0x4880ed9a- ACTION_CHAT_ADD_USER =
0x15cefd00- ACTION_JOINED_BY_LINK =
0x031224c3- ACTION_CHAT_DELETE_USER =
0xa43f30cc- CHANNEL_PARTICIPANT =
0x1bd54456- MEMBER_PARTICIPANTS =
channelParticipant / Self / Admin / Creator — a still-present member.
[0x1bd54456, 0xa9478a1a, 0x34c3bb53, 0x2fe601d3].freeze
- REACTION_EMOJI =
0x1b2286b8- REACTION_EMPTY =
0x79f5d419- REACTION_CUSTOM_EMOJI =
0x8935fc73- REACTION_PAID =
0x523da4eb- VECTOR =
0x1cb5c415
Class Method Summary collapse
-
.bot_reaction_event(data, offset) ⇒ Object
updateBotMessageReaction#ac21d3ce peer:Peer msg_id:int date:int actor:Peer old_reactions:Vector
new_reactions:Vector qts:int — the bot-facing per-user reaction, the one that names the reactor. - .callback_event(data, offset) ⇒ Object
-
.classify_update(data, offset, ctor) ⇒ Object
One Vector
entry at offset→ its Event, or nil for an update we don't surface (the caller's Schema#skip still advances past it). -
.delete_event(data, offset) ⇒ Object
updateDeleteChannelMessages#c32d5b12 channel_id:long messages:Vector
pts:int pts_count:int. - .edit_message(data, offset, channel:) ⇒ Object
- .guest_query_event(data, offset) ⇒ Object
-
.managed_bot_event(data, offset) ⇒ Object
updateManagedBot#4880ed9a user_id:long bot_id:long — a bot the user just minted through this bot acting as its manager.
- .membership_event(data, header) ⇒ Object
- .message_event(data, offset, constructor, kind:, channel:, pts:, pts_count:) ⇒ Object
-
.message_or_service(data, offset, ctor, channel:, pts:, pts_count:) ⇒ Object
One message — an entry of a Vector
, or the one inside an updateNew(Channel)Message — → a :message / :join / :leave Event, or nil. -
.new_message(data, offset, channel:) ⇒ Object
updateNew(Channel)Message: the update tag, an inner Message (plain message, or a messageService that may be a join/leave), then pts:int pts_count:int.
-
.participant_event(data, offset) ⇒ Object
updateChannelParticipant#985d3abb: a member joining a supergroup arrives here (not as a service message).
-
.pts_after(data, msg_off) ⇒ Object
pts:int pts_count:int sit right after the inner Message; skip it to read them.
-
.reaction_event(data, offset) ⇒ Object
updateMessageReactions# → { peer, msg_id } (the message whose reactions changed; the recent reactors are read from the message tail elsewhere).
-
.read_int_vector(data, offset) ⇒ Object
Vector
at offset→ [[int, …], offset_past]. -
.read_reaction(data, offset) ⇒ Object
One Reaction at
offset→ [key, emoji_or_nil, offset_past]. -
.read_reactions_vector(data, offset) ⇒ Object
Vector
at offset→ [ [{ key:, emoji: }, …], offset_past ]. -
.service_event(data, offset, ctor, channel:, pts:, pts_count:) ⇒ Object
messageService carrying a membership action → a :join / :leave Event, else nil.
Class Method Details
.bot_reaction_event(data, offset) ⇒ Object
updateBotMessageReaction#ac21d3ce peer:Peer msg_id:int date:int actor:Peer
old_reactions:Vector
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/mtproto/updates/event_builder.rb', line 218 def bot_reaction_event(data, offset) io = offset + 4 peer_type, peer_id, io = TL::Reader.parse_peer(data, io) msg_id = data[io, 4].unpack1('l<') io += 4 date = data[io, 4].unpack1('L<') io += 4 actor_type, actor_id, io = TL::Reader.parse_peer(data, io) old_reactions, io = read_reactions_vector(data, io) new_reactions, io = read_reactions_vector(data, io) qts = data[io, 4].unpack1('l<') added = new_reactions.reject { |r| old_reactions.any? { |o| o[:key] == r[:key] } } removed = old_reactions.reject { |r| new_reactions.any? { |o| o[:key] == r[:key] } } kind, diff = if added.any? then [:reaction, added] elsif removed.any? then [:unreaction, removed] end return unless kind reactor = { peer_type: actor_type, peer_id: actor_id, date: date, emoji: diff.first[:emoji] } Event.new(kind: kind, msg_id: msg_id, qts: qts, peer_type: peer_type, peer_id: peer_id, user_id: actor_id, reactors: [reactor]) rescue StandardError nil end |
.callback_event(data, offset) ⇒ Object
192 193 194 195 196 |
# File 'lib/mtproto/updates/event_builder.rb', line 192 def callback_event(data, offset) query = TL::Updates.extract_callback_query(data, offset) Event.new(kind: :callback, query_id: query[:query_id], user_id: query[:user_id], peer_type: query[:peer_type], peer_id: query[:peer_id], msg_id: query[:msg_id], data: query[:data]) end |
.classify_update(data, offset, ctor) ⇒ Object
One Vectoroffset → its Event, or nil for an update we
don't surface (the caller's Schema#skip still advances past it).
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/mtproto/updates/event_builder.rb', line 47 def classify_update(data, offset, ctor) case ctor when UPDATE_NEW_MESSAGE, UPDATE_NEW_CHANNEL_MESSAGE (data, offset, channel: ctor == UPDATE_NEW_CHANNEL_MESSAGE) when UPDATE_EDIT_MESSAGE, UPDATE_EDIT_CHANNEL_MESSAGE (data, offset, channel: ctor == UPDATE_EDIT_CHANNEL_MESSAGE) when UPDATE_MESSAGE_REACTIONS reaction_event(data, offset) when UPDATE_BOT_MESSAGE_REACTION bot_reaction_event(data, offset) when UPDATE_DELETE_CHANNEL_MESSAGES delete_event(data, offset) when UPDATE_BOT_CALLBACK_QUERY callback_event(data, offset) when UPDATE_CHANNEL_PARTICIPANT participant_event(data, offset) when UPDATE_BOT_GUEST_CHAT_QUERY guest_query_event(data, offset) when UPDATE_MANAGED_BOT managed_bot_event(data, offset) end end |
.delete_event(data, offset) ⇒ Object
updateDeleteChannelMessages#c32d5b12 channel_id:long messages:Vector
183 184 185 186 187 188 189 190 |
# File 'lib/mtproto/updates/event_builder.rb', line 183 def delete_event(data, offset) io = offset + 4 channel_id = data[io, 8].unpack1('Q<') io += 8 deleted_ids, io = read_int_vector(data, io) Event.new(kind: :delete, channel: true, peer_type: :channel, peer_id: channel_id, deleted_ids: deleted_ids, pts: data[io, 4].unpack1('l<'), pts_count: data[io + 4, 4].unpack1('l<')) end |
.edit_message(data, offset, channel:) ⇒ Object
98 99 100 101 102 103 104 105 |
# File 'lib/mtproto/updates/event_builder.rb', line 98 def (data, offset, channel:) inner_off = offset + 4 inner_ctor = data[inner_off, 4].unpack1('L<') return unless inner_ctor == TL::Constructors::MESSAGE pts, pts_count = pts_after(data, inner_off) (data, inner_off, inner_ctor, kind: :edit, channel: channel, pts: pts, pts_count: pts_count) end |
.guest_query_event(data, offset) ⇒ Object
198 199 200 201 202 203 204 |
# File 'lib/mtproto/updates/event_builder.rb', line 198 def guest_query_event(data, offset) query = TL::Updates.extract_guest_query(data, offset) = query[:message] Event.new(kind: :guest_query, query_id: query[:query_id], qts: query[:qts], message: , peer_type: && [:peer_type], peer_id: && [:peer_id], user_id: && [:from_id]) end |
.managed_bot_event(data, offset) ⇒ Object
updateManagedBot#4880ed9a user_id:long bot_id:long — a bot the user just minted through this bot acting as its manager. Carries the owner (user_id) and the new bot (bot_id); the bot's access_hash rides in the batch users the parser attaches.
73 74 75 76 77 |
# File 'lib/mtproto/updates/event_builder.rb', line 73 def managed_bot_event(data, offset) Event.new(kind: :managed_bot, user_id: data[offset + 4, 8].unpack1('Q<'), bot_id: data[offset + 12, 8].unpack1('Q<')) end |
.membership_event(data, header) ⇒ Object
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/mtproto/updates/event_builder.rb', line 138 def membership_event(data, header) io = header.action_offset case data[io, 4].unpack1('L<') when ACTION_CHAT_ADD_USER joined = header.from_id if data[io + 4, 4].unpack1('L<') == VECTOR && data[io + 8, 4].unpack1('L<').positive? joined = data[io + 12, 8].unpack1('Q<') end Event.new(kind: :join, joined_user_id: joined, peer_type: header.peer_type, peer_id: header.peer_id, msg_id: header.id) when ACTION_JOINED_BY_LINK Event.new(kind: :join, joined_user_id: header.from_id, peer_type: header.peer_type, peer_id: header.peer_id, msg_id: header.id) when ACTION_CHAT_DELETE_USER Event.new(kind: :leave, left_user_id: data[io + 4, 8].unpack1('Q<'), peer_type: header.peer_type, peer_id: header.peer_id, msg_id: header.id) end end |
.message_event(data, offset, constructor, kind:, channel:, pts:, pts_count:) ⇒ Object
107 108 109 110 111 112 113 |
# File 'lib/mtproto/updates/event_builder.rb', line 107 def (data, offset, constructor, kind:, channel:, pts:, pts_count:) msg = TL::Message.deserialize(data, offset, constructor) return unless msg Event.new(kind: kind, channel: channel, pts: pts, pts_count: pts_count, peer_type: msg.peer_type, peer_id: msg.peer_id, msg_id: msg.id, message: msg.to_h) end |
.message_or_service(data, offset, ctor, channel:, pts:, pts_count:) ⇒ Object
One message — an entry of a Vectorchannel is the envelope's kind (true for a channelDifference), since the
message itself doesn't say.
83 84 85 86 |
# File 'lib/mtproto/updates/event_builder.rb', line 83 def (data, offset, ctor, channel:, pts:, pts_count:) (data, offset, ctor, kind: :message, channel: channel, pts: pts, pts_count: pts_count) || service_event(data, offset, ctor, channel: channel, pts: pts, pts_count: pts_count) end |
.new_message(data, offset, channel:) ⇒ Object
updateNew(Channel)Message: the update tag, an inner Message (plain message, or a messageService that may be a join/leave), then pts:int pts_count:int.
90 91 92 93 94 95 96 |
# File 'lib/mtproto/updates/event_builder.rb', line 90 def (data, offset, channel:) inner_off = offset + 4 inner_ctor = data[inner_off, 4].unpack1('L<') pts, pts_count = pts_after(data, inner_off) (data, inner_off, inner_ctor, channel: channel, pts: pts, pts_count: pts_count) end |
.participant_event(data, offset) ⇒ Object
updateChannelParticipant#985d3abb: a member joining a supergroup arrives here (not as a service message). Surface only a real join — a new_participant that is present AND was not present before.
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/mtproto/updates/event_builder.rb', line 160 def participant_event(data, offset) io = offset + 4 flags = data[io, 4].unpack1('L<') channel_id = data[io + 4, 8].unpack1('Q<') user_id = data[io + 24, 8].unpack1('Q<') io += 32 return unless flags.anybits?(1 << 1) # no new_participant → not a join if flags.anybits?(1 << 0) # prev_participant return if MEMBER_PARTICIPANTS.include?(data[io, 4].unpack1('L<')) io = TL::Reader.schema.skip(data, io) end return unless data[io, 4].unpack1('L<') == CHANNEL_PARTICIPANT Event.new(kind: :join, channel: true, joined_user_id: user_id, peer_type: :channel, peer_id: channel_id) rescue StandardError nil end |
.pts_after(data, msg_off) ⇒ Object
pts:int pts_count:int sit right after the inner Message; skip it to read them.
116 117 118 119 |
# File 'lib/mtproto/updates/event_builder.rb', line 116 def pts_after(data, msg_off) after = TL::Reader.schema.skip(data, msg_off) [data[after, 4].unpack1('l<'), data[after + 4, 4].unpack1('l<')] end |
.reaction_event(data, offset) ⇒ Object
updateMessageReactions# → { peer, msg_id } (the message whose reactions changed; the recent reactors are read from the message tail elsewhere).
208 209 210 211 212 |
# File 'lib/mtproto/updates/event_builder.rb', line 208 def reaction_event(data, offset) io = offset + 8 # constructor + flags peer_type, peer_id, io = TL::Reader.parse_peer(data, io) Event.new(kind: :reaction, peer_type: peer_type, peer_id: peer_id, msg_id: data[io, 4].unpack1('l<')) end |
.read_int_vector(data, offset) ⇒ Object
Vectoroffset → [[int, …], offset_past].
245 246 247 248 249 250 251 |
# File 'lib/mtproto/updates/event_builder.rb', line 245 def read_int_vector(data, offset) offset += 4 # vector constructor count = data[offset, 4].unpack1('L<') offset += 4 ids = Array.new(count) { |i| data[offset + (i * 4), 4].unpack1('l<') } [ids, offset + (count * 4)] end |
.read_reaction(data, offset) ⇒ Object
One Reaction at offset → [key, emoji_or_nil, offset_past]. An unknown reaction
constructor is stepped over via the schema so the vector stays aligned.
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
# File 'lib/mtproto/updates/event_builder.rb', line 269 def read_reaction(data, offset) case data[offset, 4].unpack1('L<') when REACTION_EMOJI emoji, io = TL::Reader.read_tl_string(data, offset + 4) [emoji, emoji, io] when REACTION_CUSTOM_EMOJI ["custom:#{data[offset + 4, 8].unpack1('Q<')}", nil, offset + 12] when REACTION_PAID ['paid', nil, offset + 4] when REACTION_EMPTY ['empty', nil, offset + 4] else [nil, nil, TL::Reader.schema.skip(data, offset)] end end |
.read_reactions_vector(data, offset) ⇒ Object
Vectoroffset → [ [{ key:, emoji: }, …], offset_past ]. key is a
stable identity for the add/remove diff; emoji is set for plain reactions.
255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/mtproto/updates/event_builder.rb', line 255 def read_reactions_vector(data, offset) offset += 4 # vector constructor count = data[offset, 4].unpack1('L<') offset += 4 items = [] count.times do key, emoji, offset = read_reaction(data, offset) items << { key: key, emoji: emoji } end [items, offset] end |
.service_event(data, offset, ctor, channel:, pts:, pts_count:) ⇒ Object
messageService carrying a membership action → a :join / :leave Event, else nil. peer_id is the service message's own chat (where the event happened).
123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/mtproto/updates/event_builder.rb', line 123 def service_event(data, offset, ctor, channel:, pts:, pts_count:) header = TL::MessageService.deserialize(data, offset, ctor) return unless header event = membership_event(data, header) return unless event event.channel = channel event.pts = pts event.pts_count = pts_count event rescue StandardError nil end |