gruubY 0.2.0

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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +6 -0
  4. data/Rakefile +51 -0
  5. data/config/config.rb +5 -0
  6. data/example/bot.rb +160 -0
  7. data/example/generate_user_session.rb +81 -0
  8. data/example/ntgcalls.rb +80 -0
  9. data/example/tdlib.rb +77 -0
  10. data/example/userbot.rb +133 -0
  11. data/lib/grubY/api.rb +363 -0
  12. data/lib/grubY/async.rb +13 -0
  13. data/lib/grubY/bot.rb +90 -0
  14. data/lib/grubY/bound.rb +14 -0
  15. data/lib/grubY/client.rb +296 -0
  16. data/lib/grubY/context.rb +242 -0
  17. data/lib/grubY/dispatcher.rb +60 -0
  18. data/lib/grubY/enums.rb +62 -0
  19. data/lib/grubY/exception.rb +5 -0
  20. data/lib/grubY/file_stream.rb +17 -0
  21. data/lib/grubY/filters.rb +292 -0
  22. data/lib/grubY/group_manager.rb +213 -0
  23. data/lib/grubY/handlers.rb +170 -0
  24. data/lib/grubY/keyboard.rb +49 -0
  25. data/lib/grubY/media.rb +38 -0
  26. data/lib/grubY/middleware.rb +18 -0
  27. data/lib/grubY/ntgcalls/native.rb +100 -0
  28. data/lib/grubY/ntgcalls.rb +478 -0
  29. data/lib/grubY/plugin.rb +14 -0
  30. data/lib/grubY/raw.rb +25 -0
  31. data/lib/grubY/raw_types.rb +57 -0
  32. data/lib/grubY/retry.rb +14 -0
  33. data/lib/grubY/server.rb +18 -0
  34. data/lib/grubY/session.rb +30 -0
  35. data/lib/grubY/tdlib/client.rb +565 -0
  36. data/lib/grubY/tdlib/client_manager.rb +37 -0
  37. data/lib/grubY/tdlib/decorators.rb +17 -0
  38. data/lib/grubY/tdlib/errors.rb +6 -0
  39. data/lib/grubY/tdlib/group_manager.rb +58 -0
  40. data/lib/grubY/tdlib/native.rb +72 -0
  41. data/lib/grubY/tdlib/schema_builder.rb +237 -0
  42. data/lib/grubY/tdlib/tdjson.rb +49 -0
  43. data/lib/grubY/tdlib/user_session.rb +69 -0
  44. data/lib/grubY/types/base_object.rb +72 -0
  45. data/lib/grubY/types/bound_entities.rb +245 -0
  46. data/lib/grubY/types/chat.rb +93 -0
  47. data/lib/grubY/types/chat_info.rb +31 -0
  48. data/lib/grubY/types/extra.rb +251 -0
  49. data/lib/grubY/types/message.rb +295 -0
  50. data/lib/grubY/types/message_entity.rb +14 -0
  51. data/lib/grubY/types/registry.rb +67 -0
  52. data/lib/grubY/types/user.rb +31 -0
  53. data/lib/grubY/webapp.rb +22 -0
  54. data/lib/grubY.rb +41 -0
  55. data/plugins/logger.rb +7 -0
  56. data/plugins/media_tools.rb +13 -0
  57. metadata +110 -0
@@ -0,0 +1,245 @@
1
+ require_relative "base_object"
2
+
3
+ module GrubY
4
+ class CallbackQuery < BaseObject
5
+ fields :id, :from, :message, :inline_message_id, :chat_instance, :data, :game_short_name
6
+
7
+ def answer(text = nil, show_alert: false, **opts)
8
+ call_api("answerCallbackQuery", { callback_query_id: id, text: text, show_alert: show_alert }.merge(opts))
9
+ end
10
+
11
+ def edit_message_text(text, **opts)
12
+ edit_payload("editMessageText", { text: text }.merge(opts))
13
+ end
14
+
15
+ def edit_message_caption(caption, **opts)
16
+ edit_payload("editMessageCaption", { caption: caption }.merge(opts))
17
+ end
18
+
19
+ def edit_message_media(media, **opts)
20
+ edit_payload("editMessageMedia", { media: media }.merge(opts))
21
+ end
22
+
23
+ def edit_message_reply_markup(reply_markup = nil, **opts)
24
+ edit_payload("editMessageReplyMarkup", { reply_markup: reply_markup }.merge(opts))
25
+ end
26
+
27
+ private
28
+
29
+ def edit_payload(method, payload)
30
+ if inline_message_id.to_s.empty?
31
+ msg = message || {}
32
+ call_api(method, payload.merge(chat_id: msg.dig("chat", "id"), message_id: msg["message_id"]))
33
+ else
34
+ call_api(method, payload.merge(inline_message_id: inline_message_id))
35
+ end
36
+ end
37
+ end
38
+
39
+ class InlineQuery < BaseObject
40
+ fields :id, :from, :query, :offset, :chat_type, :location
41
+
42
+ def answer(results, **opts)
43
+ call_api("answerInlineQuery", { inline_query_id: id, results: results }.merge(opts))
44
+ end
45
+ end
46
+
47
+ class PreCheckoutQuery < BaseObject
48
+ fields :id, :from, :currency, :total_amount, :invoice_payload, :shipping_option_id, :order_info
49
+
50
+ def answer(ok:, error_message: nil)
51
+ call_api("answerPreCheckoutQuery", { pre_checkout_query_id: id, ok: ok, error_message: error_message }.compact)
52
+ end
53
+ end
54
+
55
+ class ShippingQuery < BaseObject
56
+ fields :id, :from, :invoice_payload, :shipping_address
57
+
58
+ def answer(ok:, shipping_options: nil, error_message: nil)
59
+ call_api("answerShippingQuery", {
60
+ shipping_query_id: id,
61
+ ok: ok,
62
+ shipping_options: shipping_options,
63
+ error_message: error_message
64
+ }.compact)
65
+ end
66
+ end
67
+
68
+ class ChatJoinRequest < BaseObject
69
+ fields :chat, :from, :user_chat_id, :date, :bio, :invite_link
70
+
71
+ def approve
72
+ call_api("approveChatJoinRequest", { chat_id: chat_id!, user_id: user_id! })
73
+ end
74
+
75
+ def decline
76
+ call_api("declineChatJoinRequest", { chat_id: chat_id!, user_id: user_id! })
77
+ end
78
+
79
+ private
80
+
81
+ def chat_id!
82
+ chat.is_a?(Hash) ? chat["id"] : chat&.id
83
+ end
84
+
85
+ def user_id!
86
+ from.is_a?(Hash) ? from["id"] : from&.id
87
+ end
88
+ end
89
+
90
+ class Story < BaseObject
91
+ fields :id, :chat, :date
92
+
93
+ def reply_text(text, **opts)
94
+ chat_id = chat.is_a?(Hash) ? chat["id"] : chat&.id
95
+ call_api("sendMessage", { chat_id: chat_id, text: text.to_s }.merge(opts))
96
+ end
97
+
98
+ alias reply reply_text
99
+ alias answer reply_text
100
+
101
+ STORY_SENDERS = {
102
+ animation: "sendAnimation",
103
+ audio: "sendAudio",
104
+ cached_media: "sendDocument",
105
+ media_group: "sendMediaGroup",
106
+ photo: "sendPhoto",
107
+ sticker: "sendSticker",
108
+ video: "sendVideo",
109
+ video_note: "sendVideoNote",
110
+ voice: "sendVoice"
111
+ }.freeze
112
+
113
+ STORY_SENDERS.each do |name, method_name|
114
+ define_method("reply_#{name}") do |payload = nil, **opts|
115
+ cid = chat.is_a?(Hash) ? chat["id"] : chat&.id
116
+ params = { chat_id: cid }.merge(opts)
117
+ params = if name == :media_group
118
+ params.merge(media: payload)
119
+ elsif name == :cached_media
120
+ params.merge(document: payload)
121
+ else
122
+ params.merge(name => payload)
123
+ end
124
+ call_api(method_name, params)
125
+ end
126
+
127
+ define_method("answer_#{name}") do |payload = nil, **opts|
128
+ public_send("reply_#{name}", payload, **opts)
129
+ end
130
+ end
131
+
132
+ def delete
133
+ call_raw_api("deleteStories", { story_ids: [id] })
134
+ end
135
+
136
+ def edit_media(media, **opts)
137
+ call_raw_api("editStoryMedia", { story_id: id, media: media }.merge(opts))
138
+ end
139
+
140
+ def edit_caption(caption, **opts)
141
+ call_raw_api("editStoryCaption", { story_id: id, caption: caption.to_s }.merge(opts))
142
+ end
143
+
144
+ def edit_privacy(privacy, **opts)
145
+ call_raw_api("editStoryPrivacy", { story_id: id, privacy: privacy }.merge(opts))
146
+ end
147
+
148
+ def react(reaction)
149
+ call_raw_api("setStoryReaction", { story_id: id, reaction: reaction })
150
+ end
151
+
152
+ def forward(chat_id:)
153
+ call_raw_api("forwardStory", { story_id: id, chat_id: chat_id })
154
+ end
155
+
156
+ def download(file_name = "story.dat")
157
+ call_raw_api("downloadFile", { story_id: id, file_name: file_name })
158
+ end
159
+
160
+ def read
161
+ call_raw_api("readChatStories", { story_id: id })
162
+ end
163
+
164
+ def view
165
+ call_raw_api("viewStories", { story_ids: [id] })
166
+ end
167
+ end
168
+
169
+ class Folder < BaseObject
170
+ fields :id, :name
171
+
172
+ def delete
173
+ call_raw_api("deleteFolder", { folder_id: id })
174
+ end
175
+
176
+ def edit(**opts)
177
+ call_raw_api("editFolder", { folder_id: id }.merge(opts))
178
+ end
179
+ end
180
+
181
+ class ActiveSession < BaseObject
182
+ fields :hash
183
+
184
+ def reset
185
+ call_raw_api("resetSession", { session_hash: self["hash"] || hash })
186
+ end
187
+ end
188
+
189
+ class Gift < BaseObject
190
+ fields :id, :owned_gift_id
191
+
192
+ def show
193
+ call_raw_api("showGift", gift_payload)
194
+ end
195
+
196
+ def hide
197
+ call_raw_api("hideGift", gift_payload)
198
+ end
199
+
200
+ def convert
201
+ call_raw_api("convertGiftToStars", gift_payload)
202
+ end
203
+
204
+ def upgrade(**opts)
205
+ call_raw_api("upgradeGift", gift_payload.merge(opts))
206
+ end
207
+
208
+ def transfer(user_id:, **opts)
209
+ call_raw_api("transferGift", gift_payload.merge(user_id: user_id).merge(opts))
210
+ end
211
+
212
+ def wear
213
+ call_raw_api("setProfileGift", gift_payload)
214
+ end
215
+
216
+ def buy(**opts)
217
+ call_raw_api("sendGift", gift_payload.merge(opts))
218
+ end
219
+
220
+ def send(user_id:, **opts)
221
+ call_raw_api("sendGift", gift_payload.merge(user_id: user_id).merge(opts))
222
+ end
223
+
224
+ def get_auction_state
225
+ call_raw_api("getGiftAuctionState", gift_payload)
226
+ end
227
+
228
+ def send_purchase_offer(**opts)
229
+ call_raw_api("sendGiftPurchaseOffer", gift_payload.merge(opts))
230
+ end
231
+
232
+ private
233
+
234
+ def gift_payload
235
+ { gift_id: self["id"] || id, owned_gift_id: self["owned_gift_id"] || owned_gift_id }.compact
236
+ end
237
+ end
238
+
239
+ class Animation < BaseObject
240
+ def add_to_gifs
241
+ file_id = self["file_id"] || dig("file_id")
242
+ call_raw_api("addToGifs", { file_id: file_id })
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,93 @@
1
+ require_relative "base_object"
2
+
3
+ module GrubY
4
+ class Chat < BaseObject
5
+ fields :id, :type, :title, :username, :first_name, :last_name,
6
+ :is_forum, :is_direct_messages
7
+
8
+ def archive
9
+ call_raw_api("archiveChats", { chat_ids: [id] })
10
+ end
11
+
12
+ def unarchive
13
+ call_raw_api("unarchiveChats", { chat_ids: [id] })
14
+ end
15
+
16
+ def set_title(title)
17
+ call_api("setChatTitle", { chat_id: id, title: title.to_s })
18
+ end
19
+
20
+ def set_description(description)
21
+ call_api("setChatDescription", { chat_id: id, description: description.to_s })
22
+ end
23
+
24
+ def set_photo(photo, **opts)
25
+ call_api("setChatPhoto", { chat_id: id, photo: photo }.merge(opts))
26
+ end
27
+
28
+ def set_ttl(message_auto_delete_time)
29
+ call_api("setChatMessageAutoDeleteTime", { chat_id: id, message_auto_delete_time: message_auto_delete_time.to_i })
30
+ end
31
+
32
+ def ban_member(user_id, **opts)
33
+ call_api("banChatMember", { chat_id: id, user_id: user_id }.merge(opts))
34
+ end
35
+
36
+ def unban_member(user_id, **opts)
37
+ call_api("unbanChatMember", { chat_id: id, user_id: user_id }.merge(opts))
38
+ end
39
+
40
+ def restrict_member(user_id, permissions:, **opts)
41
+ call_api("restrictChatMember", { chat_id: id, user_id: user_id, permissions: permissions }.merge(opts))
42
+ end
43
+
44
+ def promote_member(user_id, **opts)
45
+ call_api("promoteChatMember", { chat_id: id, user_id: user_id }.merge(opts))
46
+ end
47
+
48
+ def join
49
+ call_api("joinChat", { chat_id: id })
50
+ end
51
+
52
+ def leave
53
+ call_api("leaveChat", { chat_id: id })
54
+ end
55
+
56
+ def export_invite_link
57
+ call_api("exportChatInviteLink", { chat_id: id })
58
+ end
59
+
60
+ def get_member(user_id)
61
+ call_api("getChatMember", { chat_id: id, user_id: user_id })
62
+ end
63
+
64
+ def get_members(**opts)
65
+ call_raw_api("getChatAdministrators", { chat_id: id }.merge(opts))
66
+ end
67
+
68
+ def add_members(user_ids)
69
+ ids = Array(user_ids)
70
+ call_api("addChatMembers", { chat_id: id, user_ids: ids })
71
+ end
72
+
73
+ def mark_unread
74
+ call_raw_api("markChatUnread", { chat_id: id, is_marked_as_unread: true })
75
+ end
76
+
77
+ def set_protected_content(enabled: true)
78
+ call_raw_api("setChatProtectedContent", { chat_id: id, has_protected_content: enabled })
79
+ end
80
+
81
+ def unpin_all_messages
82
+ call_api("unpinAllChatMessages", { chat_id: id })
83
+ end
84
+
85
+ def mute
86
+ call_raw_api("setChatNotificationSettings", { chat_id: id, mute_for: 2_147_483_647 })
87
+ end
88
+
89
+ def unmute
90
+ call_raw_api("setChatNotificationSettings", { chat_id: id, mute_for: 0 })
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,31 @@
1
+ require_relative "base_object"
2
+ require_relative "chat"
3
+
4
+ module GrubY
5
+ class ChatFullInfo < BaseObject
6
+ fields :id, :type, :title, :username, :first_name, :last_name, :is_forum,
7
+ :is_direct_messages, :accent_color_id, :max_reaction_count, :photo,
8
+ :active_usernames, :birthdate, :business_intro, :business_location,
9
+ :business_opening_hours, :personal_chat, :parent_chat,
10
+ :available_reactions, :background_custom_emoji_id,
11
+ :profile_accent_color_id, :profile_background_custom_emoji_id,
12
+ :emoji_status_custom_emoji_id, :emoji_status_expiration_date, :bio,
13
+ :has_private_forwards, :has_restricted_voice_and_video_messages,
14
+ :join_to_send_messages, :join_by_request, :description, :invite_link,
15
+ :pinned_message, :permissions, :accepted_gift_types,
16
+ :can_send_paid_media, :slow_mode_delay, :unrestrict_boost_count,
17
+ :message_auto_delete_time, :has_aggressive_anti_spam_enabled,
18
+ :has_hidden_members, :has_protected_content, :has_visible_history,
19
+ :sticker_set_name, :can_set_sticker_set,
20
+ :custom_emoji_sticker_set_name, :linked_chat_id, :location, :rating,
21
+ :first_profile_audio, :unique_gift_colors, :paid_message_star_count
22
+
23
+ def initialize(data)
24
+ super(data)
25
+ @personal_chat = Chat.new(@personal_chat) if @personal_chat.is_a?(Hash)
26
+ @parent_chat = Chat.new(@parent_chat) if @parent_chat.is_a?(Hash)
27
+ end
28
+ end
29
+
30
+ ChatInfo = ChatFullInfo
31
+ end
@@ -0,0 +1,251 @@
1
+ require_relative "base_object"
2
+ require_relative "chat"
3
+ require_relative "user"
4
+ require_relative "message_entity"
5
+
6
+ module GrubY
7
+ class MessageId < BaseObject
8
+ fields :message_id
9
+ end
10
+
11
+ class InaccessibleMessage < BaseObject
12
+ fields :chat, :message_id, :date
13
+
14
+ def initialize(data)
15
+ super(data)
16
+ @chat = Chat.new(@chat) if @chat.is_a?(Hash)
17
+ end
18
+ end
19
+
20
+ class MaybeInaccessibleMessage < BaseObject
21
+ fields :raw
22
+
23
+ def initialize(data)
24
+ super({ raw: data })
25
+ end
26
+
27
+ def inaccessible?
28
+ @raw.is_a?(Hash) && @raw["date"].to_i.zero?
29
+ end
30
+
31
+ def as_inaccessible
32
+ return nil unless inaccessible?
33
+
34
+ InaccessibleMessage.new(@raw)
35
+ end
36
+ end
37
+
38
+ class TextQuote < BaseObject
39
+ fields :text, :entities, :position, :is_manual
40
+
41
+ def initialize(data)
42
+ super(data)
43
+ @entities = Array(@entities).map { |e| MessageEntity.new(e) }
44
+ end
45
+ end
46
+
47
+ class ExternalReplyInfo < BaseObject
48
+ fields :origin, :chat, :message_id, :link_preview_options, :animation, :audio,
49
+ :document, :paid_media, :photo, :sticker, :story, :video, :video_note,
50
+ :voice, :has_media_spoiler, :checklist, :contact, :dice, :game,
51
+ :giveaway, :giveaway_winners, :invoice, :location, :poll, :venue
52
+
53
+ def initialize(data)
54
+ super(data)
55
+ @chat = Chat.new(@chat) if @chat.is_a?(Hash)
56
+ end
57
+ end
58
+
59
+ class MessageOrigin < BaseObject
60
+ fields :type, :date
61
+ end
62
+
63
+ class MessageOriginUser < MessageOrigin
64
+ fields :sender_user
65
+
66
+ def initialize(data)
67
+ super(data)
68
+ @sender_user = User.new(@sender_user) if @sender_user.is_a?(Hash)
69
+ end
70
+ end
71
+
72
+ class MessageOriginHiddenUser < MessageOrigin
73
+ fields :sender_user_name
74
+ end
75
+
76
+ class MessageOriginChat < MessageOrigin
77
+ fields :sender_chat, :author_signature
78
+
79
+ def initialize(data)
80
+ super(data)
81
+ @sender_chat = Chat.new(@sender_chat) if @sender_chat.is_a?(Hash)
82
+ end
83
+ end
84
+
85
+ class MessageOriginChannel < MessageOrigin
86
+ fields :chat, :message_id, :author_signature
87
+
88
+ def initialize(data)
89
+ super(data)
90
+ @chat = Chat.new(@chat) if @chat.is_a?(Hash)
91
+ end
92
+ end
93
+
94
+ class PhotoSize < BaseObject
95
+ fields :file_id, :file_unique_id, :width, :height, :file_size
96
+ end
97
+
98
+ class Animation < BaseObject
99
+ fields :file_id, :file_unique_id, :width, :height, :duration, :thumbnail,
100
+ :file_name, :mime_type, :file_size
101
+
102
+ def initialize(data)
103
+ super(data)
104
+ @thumbnail = PhotoSize.new(@thumbnail) if @thumbnail.is_a?(Hash)
105
+ end
106
+ end
107
+
108
+ class Audio < BaseObject
109
+ fields :file_id, :file_unique_id, :duration, :performer, :title, :file_name,
110
+ :mime_type, :file_size, :thumbnail
111
+
112
+ def initialize(data)
113
+ super(data)
114
+ @thumbnail = PhotoSize.new(@thumbnail) if @thumbnail.is_a?(Hash)
115
+ end
116
+ end
117
+
118
+ class Document < BaseObject
119
+ fields :file_id, :file_unique_id, :thumbnail, :file_name, :mime_type,
120
+ :file_size
121
+
122
+ def initialize(data)
123
+ super(data)
124
+ @thumbnail = PhotoSize.new(@thumbnail) if @thumbnail.is_a?(Hash)
125
+ end
126
+ end
127
+
128
+ class Story < BaseObject
129
+ fields :chat, :id
130
+
131
+ def initialize(data)
132
+ super(data)
133
+ @chat = Chat.new(@chat) if @chat.is_a?(Hash)
134
+ end
135
+ end
136
+
137
+ class VideoQuality < BaseObject
138
+ fields :file_id, :file_unique_id, :width, :height, :codec, :file_size
139
+ end
140
+
141
+ class Video < BaseObject
142
+ fields :file_id, :file_unique_id, :width, :height, :duration, :thumbnail,
143
+ :cover, :start_timestamp, :qualities, :file_name, :mime_type, :file_size
144
+
145
+ def initialize(data)
146
+ super(data)
147
+ @thumbnail = PhotoSize.new(@thumbnail) if @thumbnail.is_a?(Hash)
148
+ @cover = Array(@cover).map { |c| PhotoSize.new(c) }
149
+ @qualities = Array(@qualities).map { |q| VideoQuality.new(q) }
150
+ end
151
+ end
152
+
153
+ class VideoNote < BaseObject
154
+ fields :file_id, :file_unique_id, :length, :duration, :thumbnail, :file_size
155
+
156
+ def initialize(data)
157
+ super(data)
158
+ @thumbnail = PhotoSize.new(@thumbnail) if @thumbnail.is_a?(Hash)
159
+ end
160
+ end
161
+
162
+ class Voice < BaseObject
163
+ fields :file_id, :file_unique_id, :duration, :mime_type, :file_size
164
+ end
165
+
166
+ class PaidMediaInfo < BaseObject
167
+ fields :star_count, :paid_media
168
+ end
169
+
170
+ class PaidMedia < BaseObject
171
+ fields :type
172
+ end
173
+
174
+ class PaidMediaPreview < PaidMedia
175
+ fields :width, :height, :duration
176
+ end
177
+
178
+ class PaidMediaPhoto < PaidMedia
179
+ fields :photo
180
+
181
+ def initialize(data)
182
+ super(data)
183
+ @photo = Array(@photo).map { |p| PhotoSize.new(p) }
184
+ end
185
+ end
186
+
187
+ class PaidMediaVideo < PaidMedia
188
+ fields :video
189
+
190
+ def initialize(data)
191
+ super(data)
192
+ @video = Video.new(@video) if @video.is_a?(Hash)
193
+ end
194
+ end
195
+
196
+ class Contact < BaseObject
197
+ fields :phone_number, :first_name, :last_name, :user_id, :vcard
198
+ end
199
+
200
+ class Dice < BaseObject
201
+ fields :emoji, :value
202
+ end
203
+
204
+ class PollOption < BaseObject
205
+ fields :persistent_id, :text, :text_entities, :voter_count, :added_by_user,
206
+ :added_by_chat, :addition_date
207
+
208
+ def initialize(data)
209
+ super(data)
210
+ @text_entities = Array(@text_entities).map { |e| MessageEntity.new(e) }
211
+ @added_by_user = User.new(@added_by_user) if @added_by_user.is_a?(Hash)
212
+ @added_by_chat = Chat.new(@added_by_chat) if @added_by_chat.is_a?(Hash)
213
+ end
214
+ end
215
+
216
+ class InputPollOption < BaseObject
217
+ fields :text, :text_parse_mode, :text_entities
218
+ end
219
+
220
+ class PollAnswer < BaseObject
221
+ fields :poll_id, :voter_chat, :user, :option_ids, :option_persistent_ids
222
+
223
+ def initialize(data)
224
+ super(data)
225
+ @voter_chat = Chat.new(@voter_chat) if @voter_chat.is_a?(Hash)
226
+ @user = User.new(@user) if @user.is_a?(Hash)
227
+ end
228
+ end
229
+
230
+ class Poll < BaseObject
231
+ fields :id, :question, :question_entities, :options, :total_voter_count,
232
+ :is_closed, :is_anonymous, :type, :allows_multiple_answers,
233
+ :allows_revoting, :correct_option_ids, :explanation,
234
+ :explanation_entities, :open_period, :close_date, :description,
235
+ :description_entities
236
+
237
+ def initialize(data)
238
+ super(data)
239
+ @question_entities = Array(@question_entities).map { |e| MessageEntity.new(e) }
240
+ @options = Array(@options).map { |o| PollOption.new(o) }
241
+ @explanation_entities = Array(@explanation_entities).map { |e| MessageEntity.new(e) }
242
+ @description_entities = Array(@description_entities).map { |e| MessageEntity.new(e) }
243
+ end
244
+ end
245
+
246
+ class ReplyParameters < BaseObject
247
+ fields :message_id, :chat_id, :allow_sending_without_reply, :quote,
248
+ :quote_parse_mode, :quote_entities, :quote_position, :checklist_task_id,
249
+ :poll_option_id
250
+ end
251
+ end