1 module telega.botapi;
2 
3 import vibe.core.log;
4 import asdf;
5 import std.conv;
6 import std.typecons;
7 import std.exception;
8 import std.traits;
9 import telega.http;
10 import telega.serialization;
11 
12 enum HTTPMethod
13 {
14 	GET,
15 	POST
16 }
17 
18 @serializedAs!ChatIdProxy
19 struct ChatId
20 {
21     import std.conv;
22 
23     string id;
24 
25     private bool _isString = true;
26 
27     alias id this;
28 
29     this(long id)
30     {
31         this.id = id.to!string;
32         _isString = false;
33     }
34 
35     this(string id)
36     {
37         this.id = id;
38     }
39 
40     void opAssign(long id)
41     {
42         this.id = id.to!string;
43         _isString = false;
44     }
45 
46     void opAssign(string id)
47     {
48         this.id = id;
49         _isString = true;
50     }
51 
52     @property
53     bool isString()
54     {
55         return _isString;
56     }
57 
58     long opCast(T)()
59         if (is(T == long))
60     {
61         if (_isString) {
62             return 0;
63         }
64 
65         return id.to!long;
66     }
67 }
68 
69 struct ChatIdProxy
70 {
71     ChatId id;
72 
73     this(ChatId id)
74     {
75         this.id = id;
76     }
77 
78     ChatId opCast(T : ChatId)()
79     {
80         return id;
81     }
82 
83     static ChatIdProxy deserialize(Asdf v)
84     {
85         return ChatIdProxy(ChatId(cast(string)v));
86     }
87 }
88 
89 unittest
90 {
91     ChatId chatId;
92 
93     chatId = 45;
94     assert(chatId.isString() == false);
95 
96     chatId = "@chat";
97     assert(chatId.isString() == true);
98 
99     string chatIdString = chatId;
100     assert(chatIdString == "@chat");
101 
102     long chatIdNum = cast(long)chatId;
103     assert(chatIdNum == 0);
104 
105     chatId = 42;
106 
107     chatIdNum = cast(long)chatId;
108     assert(chatIdNum == 42);
109 
110     string chatIdFunc(ChatId id)
111     {
112         return id;
113     }
114 
115     assert(chatIdFunc(cast(ChatId)"abc") == "abc");
116     assert(chatIdFunc(cast(ChatId)45) == "45");
117 }
118 
119 
120 class TelegramBotApiException : Exception
121 {
122     ushort code;
123 
124     this(ushort code, string description, string file = __FILE__, size_t line = __LINE__,
125          Throwable next = null) @nogc @safe pure nothrow
126     {
127         this.code = code;
128         super(description, file, line, next);
129     }
130 }
131 
132 enum isTelegramId(T) = isSomeString!T || isIntegral!T || is(T == ChatId);
133 
134 /******************************************************************/
135 /*                    Telegram types and enums                    */
136 /******************************************************************/
137 
138 struct User
139 {
140     int    id;
141     bool   is_bot;
142     string first_name;
143 
144     Nullable!string last_name;
145     Nullable!string username;
146     Nullable!string language_code;
147 }
148 
149 unittest
150 {
151     string json = `{
152         "id": 42,
153         "is_bot": false,
154         "first_name": "FirstName"
155     }`;
156 
157     User u = deserialize!User(json);
158 
159     assert(u.last_name.isNull);
160 }
161 
162 unittest
163 {
164     string json = `{
165         "id": 42,
166         "is_bot": false,
167         "first_name": "FirstName",
168         "last_name": "LastName"
169     }`;
170 
171     User u = deserialize!User(json);
172 
173     assert(false == u.last_name.isNull);
174 }
175 
176 
177 
178 @serializedAs!ChatTypeProxy
179 enum ChatType : string
180 {
181     Private    = "private",
182     Group      = "group",
183     Supergroup = "supergroup",
184     Channel    = "channel"
185 }
186 
187 struct ChatTypeProxy
188 {
189     ChatType t;
190 
191     this(ChatType type)
192     {
193         t = type;
194     }
195 
196     ChatType opCast(T : ChatType)()
197     {
198         return t;
199     }
200 
201     static ChatTypeProxy deserialize(Asdf v)
202     {
203         return ChatTypeProxy(cast(ChatType)cast(string)v);
204     }
205 }
206 
207 struct Chat
208 {
209     long id;
210     ChatType type;
211     Nullable!string title;
212     Nullable!string first_name;
213     Nullable!string last_name;
214     Nullable!string username;
215     Nullable!bool all_members_are_administrators;
216     Nullable!ChatPhoto photo;
217     Nullable!string description;
218     Nullable!string invite_link;
219     // TODO Nullable!Message pinned_message;
220     Nullable!string sticker_set_name;
221     Nullable!bool can_set_sticker_set;
222 }
223 
224 unittest
225 {
226     string json = `{
227         "id": 42,
228         "type": "group",
229         "title": "chat title"
230     }`;
231 
232     Chat c = deserialize!Chat(json);
233 
234     assert(c.id == 42);
235     assert(c.type == ChatType.Group);
236 }
237 
238 struct Message
239 {
240     uint                 message_id;
241     uint                 date;
242     Chat                 chat;
243     Nullable!User        from;
244     Nullable!User        forward_from;
245     Nullable!Chat        forward_from_chat;
246     Nullable!uint        forward_from_message_id;
247     Nullable!string      forward_signature;
248     Nullable!uint        forward_date;
249     Nullable!uint        edit_date;
250     Nullable!string      media_group_id;
251     Nullable!string      author_signature;
252     Nullable!string      text;
253     Nullable!MessageEntity[] entities;
254     Nullable!MessageEntity[] caption_entities;
255     Nullable!Audio           audio;
256     Nullable!Document        document;
257     Nullable!Animation       animation;
258     Nullable!Game            game;
259     Nullable!PhotoSize[]     photo;
260     Nullable!Sticker         sticker;
261     Nullable!Video           video;
262     Nullable!Voice           voice;
263     Nullable!VideoNote       video_note;
264     // TODO Nullable!Message reply_to_message;
265     // TODO Nullable!Message pinned_message;
266     Nullable!string          caption;
267     Nullable!Contact         contact;
268     Nullable!Location        location;
269     Nullable!Venue           venue;
270     Nullable!User[]          new_chat_members;
271     Nullable!User            left_chat_member;
272     Nullable!string          new_chat_title;
273     Nullable!PhotoSize[]     new_chat_photo;
274     Nullable!bool            delete_chat_photo;
275     Nullable!bool            group_chat_created;
276     Nullable!bool            supergroup_chat_created;
277     Nullable!bool            channel_chat_created;
278     Nullable!long            migrate_to_chat_id;
279     Nullable!long            migrate_from_chat_id;
280     Nullable!Invoice         invoice;
281     Nullable!SuccessfulPayment successful_payment;
282     Nullable!string              connected_website;
283 
284     @property
285     uint id()
286     {
287         return message_id;
288     }
289 }
290 
291 struct Update
292 {
293     uint             update_id;
294     Nullable!Message message;
295 
296     Nullable!Message edited_message;
297     Nullable!Message channel_post;
298     Nullable!Message edited_channel_post;
299     Nullable!InlineQuery        inline_query;
300     Nullable!ChosenInlineResult chosen_inline_result;
301     Nullable!CallbackQuery      callback_query;
302     Nullable!ShippingQuery      shipping_query;
303     Nullable!PreCheckoutQuery   pre_checkout_query;
304 
305     @property @safe @nogc nothrow pure
306     uint id()
307     {
308         return update_id;
309     }
310 }
311 
312 @safe @nogc nothrow pure
313 bool isMessageType(in Update update)
314 {
315     return !update.message.isNull;
316 }
317 
318 unittest
319 {
320     string json = `{
321         "update_id": 143,
322         "message": {
323             "message_id": 243,
324             "text": "message text"
325         }
326     }`;
327 
328     Update u = deserialize!Update(json);
329 
330     assert(u.id == 143);
331     assert(u.message.message_id == 243);
332     assert(u.message.text == "message text");
333 }
334 
335 struct WebhookInfo
336 {
337     string   url;
338     bool     has_custom_certificate;
339     uint     pending_update_count;
340     Nullable!uint     last_error_date;
341     Nullable!string   last_error_message;
342     Nullable!uint     max_connections;
343     Nullable!string[] allowed_updates;
344 }
345 
346 enum ParseMode
347 {
348     Markdown = "Markdown",
349     HTML     = "HTML",
350     None     = "",
351 }
352 
353 struct MessageEntity
354 {
355     string        type;
356     uint          offset;
357     uint          length;
358     Nullable!string  url;
359     Nullable!User    user;
360 }
361 
362 struct PhotoSize
363 {
364     string        file_id;
365     int           width;
366     int           height;
367 
368     Nullable!uint file_size;
369 }
370 
371 struct Audio
372 {
373     string file_id;
374     uint   duration;
375     Nullable!string performer;
376     Nullable!string title;
377     Nullable!string mime_type;
378     Nullable!uint   file_size;
379     Nullable!PhotoSize thumb;
380 }
381 
382 // TODO Add Nullable fields
383 struct Document
384 {
385     string    file_id;
386     PhotoSize thumb;
387     string    file_name;
388     string    mime_type;
389     uint      file_size;
390 }
391 
392 // TODO Add Nullable fields
393 struct Video
394 {
395     string file_id;
396     uint width;
397     uint height;
398     uint duration;
399     PhotoSize thumb;
400     string mime_type;
401     uint file_size;
402 }
403 
404 // TODO Add Nullable fields
405 struct Voice
406 {
407     string file_id;
408     uint   duration;
409     string mime_type;
410     uint   file_size;
411 }
412 
413 // TODO Add Nullable fields
414 struct VideoNote
415 {
416     string    file_id;
417     uint      length;
418     uint      duration;
419     PhotoSize thumb;
420     uint      file_size;
421 }
422 
423 // TODO Add Nullable fields
424 struct Contact
425 {
426     string phone_number;
427     string first_name;
428     string last_name;
429     string user_id;
430 }
431 
432 // TODO Add Nullable fields
433 struct Location
434 {
435     float longitude;
436     float latitude;
437 }
438 
439 // TODO Add Nullable fields
440 struct Venue
441 {
442     Location location;
443     string   title;
444     string   address;
445     string   foursquare_id;
446 }
447 
448 // TODO Add Nullable fields
449 struct UserProfilePhotos
450 {
451     uint          total_count;
452     PhotoSize[][] photos;
453 }
454 
455 // TODO Add Nullable fields
456 struct File
457 {
458     string file_id;
459     uint   file_size;
460     string file_path;
461 }
462 
463 import std.meta : AliasSeq, staticIndexOf;
464 import std.variant : Algebraic;
465 
466 alias ReplyMarkupStructs = AliasSeq!(ReplyKeyboardMarkup, ReplyKeyboardRemove, InlineKeyboardMarkup, ForceReply);
467 
468 /**
469  Abstract structure for unioining ReplyKeyboardMarkup, ReplyKeyboardRemove,
470  InlineKeyboardMarkup and ForceReply
471 */
472 
473 alias ReplyMarkup = JsonableAlgebraicProxy!ReplyMarkupStructs;
474 enum isReplyMarkup(T) =
475     is(T == ReplyMarkup) || staticIndexOf!(T, ReplyMarkupStructs) >= 0;
476 
477 import std.algorithm.iteration;
478 import std.array;
479 
480 static bool falseIfNull(Nullable!bool value)
481 {
482     if (value.isNull) {
483         return false;
484     }
485 
486     return cast(bool)value;
487 }
488 
489 static bool trueIfNull(Nullable!bool value)
490 {
491     if (value.isNull) {
492         return true;
493     }
494 
495     return cast(bool)value;
496 }
497 
498 struct ReplyKeyboardMarkup
499 {
500     KeyboardButton[][] keyboard;
501 
502  // TODO   @serializationTransformOut!falseIfNull
503     Nullable!bool      resize_keyboard = false;
504 
505 // TODO     @serializationTransformOut!falseIfNull
506     Nullable!bool      one_time_keyboard = false;
507 
508 // TODO    @serializationTransformOut!falseIfNull
509     Nullable!bool      selective = false;
510 
511     this (string[][] keyboard)
512     {
513         this.keyboard = keyboard.map!toKeyboardButtonRow.array;
514     }
515 
516     void opOpAssign(string op : "~")(KeyboardButton[] buttons)
517     {
518         keyboard ~= buttons;
519     }
520 }
521 
522 struct KeyboardButton
523 {
524     string text;
525 
526     Nullable!bool   request_contact;
527     Nullable!bool   request_location;
528 
529     this(string text, bool requestContact = false, bool requestLocation = false)
530     {
531         this.text = text;
532         this.request_contact = requestContact;
533         this.request_location = requestLocation;
534     }
535 }
536 
537 KeyboardButton[] toKeyboardButtonRow(string[] row)
538 {
539     return row.map!(b => KeyboardButton(b)).array;
540 }
541 
542 struct ReplyKeyboardRemove
543 {
544     bool remove_keyboard = true;
545     Nullable!bool           selective = false;
546 }
547 
548 struct InlineKeyboardMarkup
549 {
550     InlineKeyboardButton[][] inline_keyboard;
551 }
552 
553 struct InlineKeyboardButton
554 {
555     string       text;
556     Nullable!string       url;
557     Nullable!string       callback_data;
558     Nullable!string       switch_inline_query;
559     Nullable!string       switch_inline_query_current_chat;
560     Nullable!CallbackGame callback_game;
561     Nullable!bool         pay;
562 }
563 
564 // TODO Add Nullable fields
565 struct CallbackQuery
566 {
567     string           id;
568     User             from;
569     Nullable!Message message;
570     string           inline_message_id;
571     string           chat_instance;
572     string           data;
573     string           game_short_name;
574 }
575 
576 struct ForceReply
577 {
578     bool     force_reply = true;
579     Nullable!bool     selective;
580 }
581 
582 struct ChatPhoto
583 {
584     string small_file_id;
585     string big_file_id;
586 }
587 
588 // TODO Add Nullable fields
589 struct ChatMember
590 {
591     User   user;
592     string status;
593     uint   until_date;
594     bool   can_be_edited;
595     bool   can_change_info;
596     bool   can_post_messages;
597     bool   can_edit_messages;
598     bool   can_delete_messages;
599     bool   can_invite_users;
600     bool   can_restrict_members;
601     bool   can_pin_messages;
602     bool   can_promote_members;
603     bool   can_send_messages;
604     bool   can_send_media_messages;
605     bool   can_send_other_messages;
606     bool   can_add_web_page_previews;
607 }
608 
609 // TODO Add Nullable fields
610 struct ResponseParameters
611 {
612     long migrate_to_chat_id;
613     uint retry_after;
614 }
615 
616 alias InputMediaStructs = AliasSeq!(InputMediaPhoto, InputMediaVideo);
617 
618 alias InputMedia = JsonableAlgebraicProxy!InputMediaStructs;
619 
620 struct InputMediaPhoto
621 {
622     string type;
623     string media;
624     Nullable!string caption;
625     Nullable!ParseMode parse_mode;
626 }
627 
628 struct InputMediaVideo
629 {
630     string type;
631     string media;
632     Nullable!string    caption;
633     Nullable!ParseMode parse_mode;
634     Nullable!uint   width;
635     Nullable!uint   height;
636     Nullable!uint   duration;
637     Nullable!bool   supports_streaming;
638 }
639 
640 // TODO InputMediaAnimation
641 // TODO InputMediaAudio
642 // TODO InputMediaDocument
643 
644 struct InputFile
645 {
646     // no fields
647 }
648 
649 struct Sticker
650 {
651     string       file_id;
652     uint         width;
653     uint         height;
654     PhotoSize    thumb;
655     string       emoji;
656     string       set_name;
657     MaskPosition mask_position;
658     uint         file_size;
659 }
660 
661 struct StickerSet
662 {
663     string    name;
664     string    title;
665     bool      contains_masks;
666     Sticker[] stickers;
667 }
668 
669 struct MaskPosition
670 {
671     string point;
672     float  x_shift;
673     float  y_shift;
674     float  scale;
675 }
676 
677 
678 /*** Inline mode types ***/
679 
680 struct InlineQuery
681 {
682     string id;
683     User from;
684     Nullable!Location location;
685     string query;
686     string offset;
687 }
688 
689 alias InlineQueryResultStructs = AliasSeq!(
690     InlineQueryResultArticle, InlineQueryResultPhoto, InlineQueryResultGif, InlineQueryResultMpeg4Gif,
691     InlineQueryResultVideo, InlineQueryResultAudio, InlineQueryResultVoice, InlineQueryResultDocument,
692     InlineQueryResultLocation, InlineQueryResultVenue, InlineQueryResultContact, InlineQueryResultGame,
693     InlineQueryResultCachedPhoto, InlineQueryResultCachedGif, InlineQueryResultCachedMpeg4Gif,
694     InlineQueryResultCachedSticker, InlineQueryResultCachedDocument, InlineQueryResultCachedVideo,
695     InlineQueryResultCachedVoice, InlineQueryResultCachedAudio
696 );
697 
698 alias InlineQueryResult = JsonableAlgebraicProxy!InlineQueryResultStructs;
699 
700 mixin template InlineQueryFields()
701 {
702     Nullable!InlineKeyboardMarkup reply_markup;
703     Nullable!InputMessageContent  input_message_content;
704 }
705 
706 struct InlineQueryResultArticle
707 {
708     string type = "article";
709     string id;
710     string title;
711     Nullable!string url;
712     Nullable!bool hide_url;
713     Nullable!string description;
714     Nullable!string thumb_url;
715     Nullable!uint thumb_width;
716     Nullable!uint thumb_height;
717 
718     Nullable!InlineKeyboardMarkup reply_markup;
719     InputMessageContent  input_message_content; // can't be nullable
720 }
721 
722 struct InlineQueryResultPhoto
723 {
724     string type = "photo";
725     string id;
726     string photo_url;
727     string thumb_url;
728     Nullable!uint photo_width;
729     Nullable!uint photo_height;
730     Nullable!string title;
731     Nullable!string description;
732     Nullable!string caption;
733     Nullable!ParseMode parse_mode;
734 
735     mixin InlineQueryFields;
736 }
737 
738 struct InlineQueryResultGif
739 {
740     string type = "gif";
741     string id;
742     string gif_url;
743     Nullable!uint gif_width;
744     Nullable!uint gif_height;
745     Nullable!uint gif_duration;
746     Nullable!string thumb_url;
747     Nullable!string title;
748     Nullable!string caption;
749     Nullable!ParseMode parse_mode;
750 
751     mixin InlineQueryFields;
752 }
753 
754 struct InlineQueryResultMpeg4Gif
755 {
756     string type ="mpeg4_gif";
757     string id;
758     string mpeg4_url;
759     Nullable!uint mpeg4_width;
760     Nullable!uint mpeg4_height;
761     Nullable!uint mpeg4_duration;
762     Nullable!string thumb_url;
763     Nullable!string title;
764     Nullable!string caption;
765     Nullable!ParseMode parse_mode;
766 
767     mixin InlineQueryFields;
768 }
769 
770 struct InlineQueryResultVideo
771 {
772     string type ="video";
773     string id;
774     string video_url;
775     string mime_type;
776     string thumb_url;
777     string title;
778     Nullable!string caption;
779     Nullable!ParseMode parse_mode;
780     Nullable!uint video_width;
781     Nullable!uint video_height;
782     Nullable!uint video_duration;
783     Nullable!string description;
784 
785     mixin InlineQueryFields;
786 }
787 
788 struct InlineQueryResultAudio
789 {
790     string    type = "audio";
791     string    id;
792     string    audio_url;
793     string    title;
794     Nullable!string    caption;
795     Nullable!ParseMode parse_mode;
796     Nullable!string    performer;
797     Nullable!uint      audio_duration;
798 
799     mixin InlineQueryFields;
800 }
801 
802 struct InlineQueryResultVoice
803 {
804     string    type = "voice";
805     string    id;
806     string    voice_url;
807     string    title;
808     Nullable!string    caption;
809     Nullable!ParseMode parse_mode;
810     Nullable!uint      voice_duration;
811 
812     mixin InlineQueryFields;
813 }
814 
815 struct InlineQueryResultDocument
816 {
817     string    type = "document";
818     string    id;
819     string    title;
820     Nullable!string    caption;
821     Nullable!ParseMode parse_mode;
822     Nullable!string    document_url;
823     Nullable!string    mime_type;
824     Nullable!string    description;
825     Nullable!string    thumb_url;
826     Nullable!uint      thumb_width;
827     Nullable!uint      thumb_height;
828 
829     mixin InlineQueryFields;
830 }
831 
832 struct InlineQueryResultLocation
833 {
834     string type = "location";
835     string id;
836     float latitude;
837     float longitude;
838     string title;
839     Nullable!uint live_period;
840     Nullable!string thumb_url;
841     Nullable!uint thumb_width;
842     Nullable!uint thumb_height;
843 
844     mixin InlineQueryFields;
845 }
846 
847 struct InlineQueryResultVenue
848 {
849     string type = "venue";
850     string id;
851     float latitude;
852     float longitude;
853     string title;
854     string address;
855     Nullable!string foursquare_id;
856     Nullable!string thumb_url;
857     Nullable!uint thumb_width;
858     Nullable!uint thumb_height;
859 
860     mixin InlineQueryFields;
861 }
862 
863 struct InlineQueryResultContact
864 {
865     string type = "contact";
866     string id;
867     string phone_number;
868     string first_name;
869     Nullable!string last_name;
870     Nullable!string thumb_url;
871     Nullable!uint thumb_width;
872     Nullable!uint thumb_height;
873 
874     mixin InlineQueryFields;
875 }
876 
877 struct InlineQueryResultGame
878 {
879     string type = "game";
880     string id;
881     string game_short_name;
882     Nullable!InlineKeyboardMarkup reply_markup;
883 }
884 
885 
886 struct InlineQueryResultCachedPhoto
887 {
888     string type = "photo";
889     string id;
890     string photo_file_id;
891     Nullable!string title;
892     Nullable!string description;
893     Nullable!string caption;
894     Nullable!ParseMode parse_mode;
895 
896     mixin InlineQueryFields;
897 }
898 
899 struct InlineQueryResultCachedGif
900 {
901     string type = "gif";
902     string id;
903     string gif_file_id;
904     Nullable!string title;
905     Nullable!string caption;
906     Nullable!ParseMode parse_mode;
907 
908     mixin InlineQueryFields;
909 }
910 
911 struct InlineQueryResultCachedMpeg4Gif
912 {
913     string type = "mpeg4_gif";
914     string id;
915     string mpeg4_file_id;
916     Nullable!string title;
917     Nullable!string caption;
918     Nullable!ParseMode parse_mode;
919 
920     mixin InlineQueryFields;
921 }
922 
923 struct InlineQueryResultCachedSticker
924 {
925     string type = "sticker";
926     string id;
927     string sticker_file_id;
928 
929     mixin InlineQueryFields;
930 }
931 
932 struct InlineQueryResultCachedDocument
933 {
934     string type = "document";
935     string    id;
936     string    title;
937     string    document_file_id;
938     Nullable!string    description;
939     Nullable!string    caption;
940     Nullable!ParseMode parse_mode;
941 
942     mixin InlineQueryFields;
943 }
944 
945 struct InlineQueryResultCachedVideo
946 {
947     string type = "video";
948     string    id;
949     string    video_file_id;
950     string    title;
951     Nullable!string    description;
952     Nullable!string    caption;
953     Nullable!ParseMode parse_mode;
954 
955     mixin InlineQueryFields;
956 }
957 
958 struct InlineQueryResultCachedVoice
959 {
960     string type = "voice";
961     string    id;
962     string    voice_file_id;
963     string    title;
964     Nullable!string    caption;
965     Nullable!ParseMode parse_mode;
966 
967     mixin InlineQueryFields;
968 }
969 
970 
971 struct InlineQueryResultCachedAudio
972 {
973     string type = "audio";
974     string    id;
975     string    audio_file_id;
976     Nullable!string    caption;
977     Nullable!ParseMode parse_mode;
978 
979     mixin InlineQueryFields;
980 }
981 
982 alias InputMessageContentStructs = AliasSeq!(
983     InputTextMessageContent, InputLocationMessageContent, InputVenueMessageContent, InputContactMessageContent
984 );
985 
986 alias InputMessageContent = JsonableAlgebraicProxy!InputMessageContentStructs;
987 
988 struct InputTextMessageContent
989 {
990     string message_text;
991     Nullable!ParseMode parse_mode;
992     Nullable!bool   disable_web_page_preview;
993 }
994 
995 struct InputLocationMessageContent
996 {
997     float latitude;
998     float longitude;
999     Nullable!uint  live_period;
1000 }
1001 
1002 struct InputVenueMessageContent
1003 {
1004     float  latitude;
1005     float  longitude;
1006     string title;
1007     string address;
1008     Nullable!string foursquare_id;
1009     // TODO new field Nullable!string foursquare_type;
1010 }
1011 
1012 struct InputContactMessageContent
1013 {
1014     string phone_number;
1015     string first_name;
1016     Nullable!string last_name;
1017     // TODO new field Nullable!string vcard;
1018 }
1019 
1020 struct ChosenInlineResult
1021 {
1022     string   result_id;
1023     User     from;
1024     Nullable!Location location;
1025     Nullable!string   inline_message_id;
1026     string   query;
1027 }
1028 
1029 /*** Payments types ***/
1030 struct LabeledPrice
1031 {
1032     string label;
1033     uint   amount;
1034 }
1035 
1036 struct Invoice
1037 {
1038     string title;
1039     string description;
1040     string start_parameter;
1041     string currency;
1042     uint   total_amount;
1043 }
1044 
1045 struct ShippingAddress
1046 {
1047     string country_code;
1048     string state;
1049     string city;
1050     string street_line1;
1051     string street_line2;
1052     string post_code;
1053 }
1054 
1055 // TODO add nullable fields
1056 struct OrderInfo
1057 {
1058     string name;
1059     string phone_number;
1060     string email;
1061     Nullable!ShippingAddress shipping_address;
1062 }
1063 
1064 struct ShippingOption
1065 {
1066     string id;
1067     string title;
1068     LabeledPrice[] prices;
1069 }
1070 
1071 // TODO add nullable fields
1072 struct SuccessfulPayment
1073 {
1074     string currency;
1075     uint   total_amount;
1076     string invoice_payload;
1077     string shipping_option_id;
1078     Nullable!OrderInfo order_info;
1079     string telegram_payment_charge_id;
1080     string provider_payment_charge_id;
1081 }
1082 
1083 struct ShippingQuery
1084 {
1085     string id;
1086     User   from;
1087     string invoice_payload;
1088     ShippingAddress shipping_address;
1089 }
1090 
1091 // TODO add nullable fields
1092 struct PreCheckoutQuery
1093 {
1094     string             id;
1095     User               from;
1096     string             currency;
1097     uint               total_amount;
1098     string             invoice_payload;
1099     string             shipping_option_id;
1100     Nullable!OrderInfo order_info;
1101 }
1102 
1103 /*** Telegram Passport ***/
1104 // TODO
1105 
1106 /*** Games types ***/
1107 
1108 // TODO add nullable fields
1109 struct Game
1110 {
1111     string        title;
1112     string        description;
1113     PhotoSize[]   photo;
1114     string        text;
1115     MessageEntity text_entities;
1116     Animation     animation;
1117 }
1118 
1119 // TODO add nullable fields and a new fields
1120 struct Animation
1121 {
1122     string    file_id;
1123     PhotoSize thumb;
1124     string    file_name;
1125     string    mime_type;
1126     uint      file_size;
1127 }
1128 
1129 struct CallbackGame
1130 {
1131     // no fields
1132 }
1133 
1134 struct GameHighScore
1135 {
1136     uint  position;
1137     User  user;
1138     uint  score;
1139 }
1140 
1141 /******************************************************************/
1142 /*                        Telegram methods                        */
1143 /******************************************************************/
1144 
1145 mixin template TelegramMethod(string path, HTTPMethod method = HTTPMethod.POST)
1146 {
1147     package:
1148         immutable string _path       = path;
1149         HTTPMethod       _httpMethod = method;
1150 }
1151 
1152 /// UDA for telegram methods
1153 struct Method
1154 {
1155     string path;
1156 }
1157 
1158 struct GetUpdatesMethod
1159 {
1160     mixin TelegramMethod!"/getUpdates";
1161 
1162     int   offset;
1163     ubyte limit;
1164     uint  timeout;
1165     string[] allowed_updates;
1166 }
1167 
1168 struct SetWebhookMethod
1169 {
1170     mixin TelegramMethod!"/setWebhook";
1171 
1172     string             url;
1173     Nullable!InputFile certificate;
1174     uint               max_connections;
1175     string[]           allowed_updates;
1176 }
1177 
1178 struct DeleteWebhookMethod
1179 {
1180     mixin TelegramMethod!"/deleteWebhook";
1181 }
1182 
1183 struct GetWebhookInfoMethod
1184 {
1185     mixin TelegramMethod!("/getWebhookInfo", HTTPMethod.GET);
1186 }
1187 
1188 struct GetMeMethod
1189 {
1190     mixin TelegramMethod!("/getMe", HTTPMethod.GET);
1191 }
1192 
1193 struct SendMessageMethod
1194 {
1195     mixin TelegramMethod!"/sendMessage";
1196 
1197     ChatId    chat_id;
1198     string    text;
1199     ParseMode parse_mode;
1200     bool      disable_web_page_preview;
1201     bool      disable_notification;
1202     uint      reply_to_message_id;
1203 
1204     ReplyMarkup reply_markup;
1205 }
1206 
1207 struct ForwardMessageMethod
1208 {
1209     mixin TelegramMethod!"/forwardMessage";
1210 
1211     ChatId chat_id;
1212     string from_chat_id;
1213     bool   disable_notification;
1214     uint   message_id;
1215 }
1216 
1217 struct SendPhotoMethod
1218 {
1219     mixin TelegramMethod!"/sendPhoto";
1220 
1221     string      chat_id;
1222     string      photo;
1223     string      caption;
1224     ParseMode   parse_mode;
1225     bool        disable_notification;
1226     uint        reply_to_message_id;
1227     ReplyMarkup reply_markup;
1228 }
1229 
1230 struct SendAudioMethod
1231 {
1232     mixin TelegramMethod!"/sendAudio";
1233 
1234     string      chat_id;
1235     string      audio;
1236     string      caption;
1237     ParseMode   parse_mode;
1238     uint        duration;
1239     string      performer;
1240     string      title;
1241     bool        disable_notification;
1242     uint        reply_to_message_id;
1243     ReplyMarkup reply_markup;
1244 
1245 }
1246 
1247 struct SendDocumentMethod
1248 {
1249     mixin TelegramMethod!"/sendDocument";
1250 
1251     string      chat_id;
1252     string      document;
1253     string      caption;
1254     ParseMode   parse_mode;
1255     bool        disable_notification;
1256     uint        reply_to_message_id;
1257     ReplyMarkup reply_markup;
1258 }
1259 
1260 struct SendVideoMethod
1261 {
1262     mixin TelegramMethod!"/sendVideo";
1263 
1264     string      chat_id;
1265     string      video;
1266     uint        duration;
1267     uint        width;
1268     uint        height;
1269     string      caption;
1270     ParseMode   parse_mode;
1271     bool        supports_streaming;
1272     bool        disable_notification;
1273     uint        reply_to_message_id;
1274     ReplyMarkup reply_markup;
1275 }
1276 
1277 struct SendVoiceMethod
1278 {
1279     mixin TelegramMethod!"/sendVoice";
1280 
1281     string      chat_id;
1282     string      voice;
1283     string      caption;
1284     ParseMode   parse_mode;
1285     uint        duration;
1286     bool        disable_notification;
1287     uint        reply_to_message_id;
1288     ReplyMarkup reply_markup;
1289 }
1290 
1291 struct SendVideoNoteMethod
1292 {
1293     mixin TelegramMethod!"/sendVideoNote";
1294 
1295     string      chat_id;
1296     string      video_note;
1297     uint        duration;
1298     uint        length;
1299     bool        disable_notification;
1300     uint        reply_to_message_id;
1301     ReplyMarkup reply_markup;
1302 
1303 }
1304 
1305 struct SendMediaGroupMethod
1306 {
1307     mixin TelegramMethod!"/sendMediaGroup";
1308 
1309     string       chat_id;
1310     InputMedia[] media;
1311     bool         disable_notification;
1312     uint         reply_to_message_id;
1313 }
1314 
1315 struct SendLocationMethod
1316 {
1317     mixin TelegramMethod!"/sendLocation";
1318 
1319     string      chat_id;
1320     float       latitude;
1321     float       longitude;
1322     uint        live_period;
1323     bool        disable_notification;
1324     uint        reply_to_message_id;
1325     ReplyMarkup reply_markup;
1326 }
1327 
1328 struct EditMessageLiveLocationMethod
1329 {
1330     mixin TelegramMethod!"/editMessageLiveLocation";
1331 
1332     string      chat_id;
1333     uint        message_id;
1334     string      inline_message_id;
1335     float       latitude;
1336     float       longitude;
1337     ReplyMarkup reply_markup;
1338 }
1339 
1340 struct StopMessageLiveLocationMethod
1341 {
1342     mixin TelegramMethod!"/stopMessageLiveLocation";
1343 
1344     string      chat_id;
1345     uint        message_id;
1346     string      inline_message_id;
1347     ReplyMarkup reply_markup;
1348 }
1349 
1350 struct SendVenueMethod
1351 {
1352     mixin TelegramMethod!"/sendVenue";
1353 
1354     string      chat_id;
1355     float       latitude;
1356     float       longitude;
1357     string      title;
1358     string      address;
1359     string      foursquare_id;
1360     bool        disable_notification;
1361     uint        reply_to_message_id;
1362     ReplyMarkup reply_markup;
1363 }
1364 
1365 struct SendContactMethod
1366 {
1367     mixin TelegramMethod!"/sendContact";
1368 
1369     string      chat_id;
1370     string      phone_number;
1371     string      first_name;
1372     string      last_name;
1373     bool        disable_notification;
1374     uint        reply_to_message_id;
1375     ReplyMarkup reply_markup;
1376 }
1377 
1378 struct SendChatActionMethod
1379 {
1380     mixin TelegramMethod!"/sendChatAction";
1381 
1382     ChatId chat_id;
1383     string action; // TODO enum
1384 }
1385 
1386 struct GetUserProfilePhotosMethod
1387 {
1388     mixin TelegramMethod!("/getUserProfilePhotos", HTTPMethod.GET);
1389 
1390     int  user_id;
1391     uint offset;
1392     uint limit;
1393 }
1394 
1395 struct GetFileMethod
1396 {
1397     mixin TelegramMethod!("/getFile", HTTPMethod.GET);
1398 
1399     string file_id;
1400 }
1401 
1402 struct KickChatMemberMethod
1403 {
1404     mixin TelegramMethod!"/kickChatMember";
1405 
1406     ChatId chat_id;
1407     uint   user_id;
1408     uint   until_date;
1409 }
1410 
1411 struct UnbanChatMemberMethod
1412 {
1413     mixin TelegramMethod!"/unbanChatMember";
1414 
1415     ChatId chat_id;
1416     uint   user_id;
1417 }
1418 
1419 struct RestrictChatMemberMethod
1420 {
1421     mixin TelegramMethod!"/restrictChatMember";
1422 
1423     ChatId chat_id;
1424     uint   user_id;
1425     uint   until_date;
1426     bool   can_send_messages;
1427     bool   can_send_media_messages;
1428     bool   can_send_other_messages;
1429     bool   can_add_web_page_previews;
1430 }
1431 
1432 struct PromoteChatMemberMethod
1433 {
1434     mixin TelegramMethod!"/promoteChatMember";
1435 
1436     ChatId chat_id;
1437     uint   user_id;
1438     bool   can_change_info;
1439     bool   can_post_messages;
1440     bool   can_edit_messages;
1441     bool   can_delete_messages;
1442     bool   can_invite_users;
1443     bool   can_restrict_members;
1444     bool   can_pin_messages;
1445     bool   can_promote_members;
1446 }
1447 
1448 struct ExportChatInviteLinkMethod
1449 {
1450     mixin TelegramMethod!"/exportChatInviteLink";
1451 
1452     ChatId chat_id;
1453 }
1454 
1455 struct SetChatPhotoMethod
1456 {
1457     mixin TelegramMethod!"/setChatPhoto";
1458 
1459     string    chat_id;
1460     InputFile photo;
1461 
1462 }
1463 
1464 struct DeleteChatPhotoMethod
1465 {
1466     mixin TelegramMethod!"/deleteChatPhoto";
1467 
1468     ChatId chat_id;
1469 }
1470 
1471 struct SetChatTitleMethod
1472 {
1473     mixin TelegramMethod!"/setChatTitle";
1474 
1475     ChatId chat_id;
1476     string title;
1477 }
1478 
1479 struct SetChatDescriptionMethod
1480 {
1481     mixin TelegramMethod!"/setChatDescription";
1482 
1483     ChatId chat_id;
1484     string description;
1485 }
1486 
1487 struct PinChatMessageMethod
1488 {
1489     mixin TelegramMethod!"/pinChatMessage";
1490 
1491     ChatId chat_id;
1492     uint   message_id;
1493     bool   disable_notification;
1494 }
1495 
1496 struct UnpinChatMessageMethod
1497 {
1498     mixin TelegramMethod!"/unpinChatMessage";
1499 
1500     ChatId chat_id;
1501 }
1502 
1503 struct LeaveChatMethod
1504 {
1505     mixin TelegramMethod!"/leaveChat";
1506 
1507     ChatId chat_id;
1508 }
1509 
1510 struct GetChatMethod
1511 {
1512     mixin TelegramMethod!("/getChat", HTTPMethod.GET);
1513 
1514     ChatId chat_id;
1515 }
1516 
1517 struct GetChatAdministratorsMethod
1518 {
1519     mixin TelegramMethod!("/getChatAdministrators", HTTPMethod.GET);
1520 
1521     ChatId chat_id;
1522 }
1523 
1524 struct GetChatMembersCountMethod
1525 {
1526     mixin TelegramMethod!("/getChatMembersCount", HTTPMethod.GET);
1527 
1528     ChatId chat_id;
1529 }
1530 
1531 struct GetChatMemberMethod
1532 {
1533     mixin TelegramMethod!("/getChatMember", HTTPMethod.GET);
1534 
1535     ChatId chat_id;
1536     uint   user_id;
1537 }
1538 
1539 struct SetChatStickerSetMethod
1540 {
1541     mixin TelegramMethod!"/setChatStickerSet";
1542 
1543     ChatId chat_id;
1544     string sticker_set_name;
1545 }
1546 
1547 struct DeleteChatStickerSetMethod
1548 {
1549     mixin TelegramMethod!"/deleteChatStickerSet";
1550 
1551     ChatId chat_id;
1552 }
1553 
1554 struct AnswerCallbackQueryMethod
1555 {
1556     mixin TelegramMethod!"/answerCallbackQuery";
1557 
1558     string callback_query_id;
1559     string text;
1560     bool   show_alert;
1561     string url;
1562     uint   cache_time;
1563 }
1564 
1565 struct EditMessageTextMethod
1566 {
1567     mixin TelegramMethod!"/editMessageTextMethod";
1568 
1569     string      chat_id;
1570     uint        message_id;
1571     string      inline_message_id;
1572     string      text;
1573     ParseMode   parse_mode;
1574     bool        disable_web_page_preview;
1575     ReplyMarkup reply_markup;
1576 }
1577 
1578 struct EditMessageCaptionMethod
1579 {
1580     mixin TelegramMethod!"/editMessageCaptionMethod";
1581 
1582     string      chat_id;
1583     uint        message_id;
1584     string      inline_message_id;
1585     string      caption;
1586     ParseMode   parse_mode;
1587     ReplyMarkup reply_markup;
1588 }
1589 
1590 struct EditMessageReplyMarkupMethod
1591 {
1592     mixin TelegramMethod!"/editMessageReplyMarkupMethod";
1593 
1594     string      chat_id;
1595     uint        message_id;
1596     string      inline_message_id;
1597     ReplyMarkup reply_markup;
1598 }
1599 
1600 struct DeleteMessageMethod
1601 {
1602     mixin TelegramMethod!"/deleteMessageMethod";
1603 
1604     ChatId chat_id;
1605     uint   message_id;
1606 }
1607 
1608 struct SendStickerMethod
1609 {
1610     mixin TelegramMethod!"/sendStickerMethod";
1611 
1612     string      chat_id;
1613     string      sticker; // TODO InputFile|string
1614     bool        disable_notification;
1615     uint        reply_to_message_id;
1616     ReplyMarkup reply_markup;
1617 }
1618 
1619 struct GetStickerSetMethod
1620 {
1621     mixin TelegramMethod!("/getStickerSetMethod", HTTPMethod.GET);
1622 
1623     string name;
1624 }
1625 
1626 struct UploadStickerFileMethod
1627 {
1628     mixin TelegramMethod!"/uploadStickerFileMethod";
1629 
1630     int       user_id;
1631     InputFile png_sticker;
1632 }
1633 
1634 struct CreateNewStickerSetMethod
1635 {
1636     mixin TelegramMethod!"/createNewStickerSetMethod";
1637 
1638     int          user_id;
1639     string       name;
1640     string       title;
1641     string       png_sticker; // TODO InputFile|string
1642     string       emojis;
1643     bool         contains_masks;
1644     MaskPosition mask_position;
1645 }
1646 
1647 struct AddStickerToSetMethod
1648 {
1649     mixin TelegramMethod!"/addStickerToSetMethod";
1650 
1651     int          user_id;
1652     string       name;
1653     string       png_sticker; // TODO InputFile|string
1654     string       emojis;
1655     MaskPosition mask_position;
1656 }
1657 
1658 struct SetStickerPositionInSetMethod
1659 {
1660     mixin TelegramMethod!"/setStickerPositionInSetMethod";
1661 
1662     string sticker;
1663     int    position;
1664 }
1665 
1666 struct DeleteStickerFromSetMethod
1667 {
1668     mixin TelegramMethod!"/deleteStickerFromSetMethod";
1669 
1670     string sticker;
1671 }
1672 
1673 struct AnswerInlineQueryMethod
1674 {
1675     mixin TelegramMethod!"/answerInlineQuery";
1676 
1677     string              inline_query_id;
1678     InlineQueryResult[] results;
1679     uint                cache_time;
1680     bool                is_personal;
1681     string              next_offset;
1682     string              switch_pm_text;
1683     string              switch_pm_parameter;
1684 }
1685 
1686 /******************************************************************/
1687 /*                          Telegram API                          */
1688 /******************************************************************/
1689 
1690 enum BaseApiUrl = "https://api.telegram.org/bot";
1691 
1692 class BotApi
1693 {
1694     private:
1695         string baseUrl;
1696         string apiUrl;
1697 
1698         ulong requestCounter = 1;
1699 
1700         struct MethodResult(T)
1701         {
1702             bool   ok;
1703             T      result;
1704             ushort error_code;
1705             string description;
1706         }
1707 
1708     protected:
1709         HttpClient httpClient;
1710 
1711     public:
1712         this(string token, string baseUrl = BaseApiUrl, HttpClient httpClient = null)
1713         {
1714             this.baseUrl = baseUrl;
1715             this.apiUrl = baseUrl ~ token;
1716 
1717             if (httpClient is null) {
1718                 version(TelegaVibedDriver) {
1719                     import telega.drivers.vibe;
1720 
1721                     httpClient = new VibedHttpClient();
1722                 } else version(TelegaRequestsDriver) {
1723                     import telega.drivers.requests;
1724 
1725                     httpClient = new RequestsHttpClient();
1726                 } else {
1727                     assert(false, `No HTTP client is set, maybe you should enable "default" configuration?`);
1728                 }
1729             }
1730             this.httpClient = httpClient;
1731         }
1732 
1733         T callMethod(T, M)(M method)
1734         {
1735             T result;
1736 
1737             logDiagnostic("[%d] Requesting %s", requestCounter, method._path);
1738 
1739             version(unittest)
1740             {
1741                 import std.stdio;
1742                 serializeToJsonString(method).writeln();
1743             } else {
1744                 string answer;
1745 
1746                 if (method._httpMethod == HTTPMethod.POST) {
1747                     string bodyJson = serializeToJsonString(method);
1748                     logDebugV("[%d] Sending body:\n  %s", requestCounter, bodyJson);
1749 
1750                     answer = httpClient.sendPostRequestJson(apiUrl ~ method._path, bodyJson);
1751                 } else {
1752                     answer = httpClient.sendGetRequest(apiUrl ~ method._path);
1753                 }
1754 
1755                 logDebugV("[%d] Data received:\n %s", requestCounter, answer);
1756 
1757                 auto json = answer.deserialize!(MethodResult!T);
1758 
1759                 enforce(json.ok == true, new TelegramBotApiException(json.error_code, json.description));
1760 
1761                 result = json.result;
1762                 requestCounter++;
1763             }
1764 
1765             return result;
1766         }
1767 
1768         Update[] getUpdates(int offset, ubyte limit = 5, uint timeout = 30, string[] allowedUpdates = [])
1769         {
1770             GetUpdatesMethod m = {
1771                 offset:  offset,
1772                 limit:   limit,
1773                 timeout: timeout,
1774                 allowed_updates: allowedUpdates
1775             };
1776 
1777             return callMethod!(Update[], GetUpdatesMethod)(m);
1778         }
1779 
1780         bool setWebhook(string url)
1781         {
1782             SetWebhookMethod m = {
1783                 url : url
1784             };
1785 
1786             return setWebhook(m);
1787         }
1788 
1789         bool setWebhook(ref SetWebhookMethod m)
1790         {
1791             return callMethod!(bool, SetWebhookMethod)(m);
1792         }
1793 
1794         bool deleteWebhook()
1795         {
1796             DeleteWebhookMethod m = DeleteWebhookMethod();
1797 
1798             return callMethod!(bool, DeleteWebhookMethod)(m);
1799         }
1800 
1801         WebhookInfo getWebhookInfo()
1802         {
1803             GetWebhookInfoMethod m = GetWebhookInfoMethod();
1804 
1805             return callMethod!(WebhookInfo, GetWebhookInfoMethod)(m);
1806         }
1807 
1808         User getMe()
1809         {
1810             GetMeMethod m;
1811 
1812             return callMethod!(User, GetMeMethod)(m);
1813         }
1814 
1815         Message sendMessage(T)(T chatId, string text)
1816             if (isTelegramId!T)
1817         {
1818             SendMessageMethod m = {
1819                 chat_id    : chatId,
1820                 text       : text,
1821             };
1822 
1823             return sendMessage(m);
1824         }
1825 
1826         Message sendMessage(ref SendMessageMethod m)
1827         {
1828             return callMethod!(Message, SendMessageMethod)(m);
1829         }
1830 
1831         Message forwardMessage(T1, T2)(T1 chatId, T2 fromChatId, uint messageId)
1832             if (isTelegramId!T1 && isTelegramId!T2)
1833         {
1834             ForwardMessageMethod m = {
1835                 message_id : messageId,
1836                 chat_id : chatId,
1837                 from_chat_id: fromChatId,
1838             };
1839 
1840             return callMethod!(Message, ForwardMessageMethod)(m);
1841         }
1842 
1843         Message forwardMessage(ref ForwardMessageMethod m)
1844         {
1845             return callMethod!(Message, ForwardMessageMethod)(m);
1846         }
1847 
1848         Message sendPhoto(ref SendPhotoMethod m)
1849         {
1850             return callMethod!(Message, SendPhotoMethod)(m);
1851         }
1852 
1853         Message sendPhoto(T1)(T1 chatId, string photo)
1854             if (isTelegramId!T1)
1855         {
1856             SendPhotoMethod m = {
1857                 chat_id : chatId,
1858                 photo : photo,
1859             };
1860 
1861             return sendPhoto(m);
1862         }
1863 
1864         Message sendAudio(ref SendAudioMethod m)
1865         {
1866             return callMethod!(Message, SendAudioMethod)(m);
1867         }
1868 
1869         Message sendAudio(T1)(T1 chatId, string audio)
1870             if (isTelegramId!T1)
1871         {
1872             SendAudioMethod m = {
1873                 chat_id : chatId,
1874                 audio : audio
1875             };
1876 
1877             return sendAudio(m);
1878         }
1879 
1880         Message sendDocument(ref SendDocumentMethod m)
1881         {
1882             return callMethod!(Message, SendDocumentMethod)(m);
1883         }
1884 
1885         Message sendDocument(T1)(T1 chatId, string document)
1886             if (isTelegramId!T1)
1887         {
1888             SendDocumentMethod m = {
1889                 chat_id : chatId,
1890                 document : document
1891             };
1892 
1893             return sendDocument(m);
1894         }
1895 
1896         Message sendVideo(ref SendVideoMethod m)
1897         {
1898             return callMethod!(Message, SendVideoMethod)(m);
1899         }
1900 
1901         Message sendVideo(T1)(T1 chatId, string video)
1902             if (isTelegramId!T1)
1903         {
1904             SendVideoMethod m = {
1905                 chat_id : chatId,
1906                 video : video
1907             };
1908 
1909             return sendVideo(m);
1910         }
1911 
1912         Message sendVoice(ref SendVoiceMethod m)
1913         {
1914             return callMethod!(Message, SendVoiceMethod)(m);
1915         }
1916 
1917         Message sendVoice(T1)(T1 chatId, string voice)
1918             if (isTelegramId!T1)
1919         {
1920             SendVoiceMethod m = {
1921                 chat_id : chatId,
1922                 voice : voice
1923             };
1924 
1925             return sendVoice(m);
1926         }
1927 
1928         Message sendVideoNote(ref SendVideoNoteMethod m)
1929         {
1930             return callMethod!(Message, SendVideoNoteMethod)(m);
1931         }
1932 
1933         Message sendVideoNote(T1)(T1 chatId, string videoNote)
1934             if (isTelegramId!T1)
1935         {
1936             SendVideoNoteMethod m = {
1937                 chat_id : chatId,
1938                 video_note : videoNote
1939             };
1940 
1941             return sendVideoNote(m);
1942         }
1943 
1944         Message sendMediaGroup(ref SendMediaGroupMethod m)
1945         {
1946             return callMethod!(Message, SendMediaGroupMethod)(m);
1947         }
1948 
1949         Message sendMediaGroup(T1)(T1 chatId, InputMedia[] media)
1950             if (isTelegramId!T1)
1951         {
1952             SendMediaGroupMethod m = {
1953                 chat_id : chatId,
1954                 media : media
1955             };
1956 
1957             return sendMediaGroup(m);
1958         }
1959 
1960         Message sendLocation(ref SendLocationMethod m)
1961         {
1962             return callMethod!(Message, SendLocationMethod)(m);
1963         }
1964 
1965         Message sendLocation(T1)(T1 chatId, float latitude, float longitude)
1966             if (isTelegramId!T1)
1967         {
1968             SendLocationMethod m = {
1969                 chat_id : chatId,
1970                 latitude : latitude,
1971                 longitude : longitude,
1972             };
1973 
1974             return sendLocation(m);
1975         }
1976 
1977         Nullable!Message editMessageLiveLocation(ref EditMessageLiveLocationMethod m)
1978         {
1979             return callMethod!(Nullable!Message, EditMessageLiveLocationMethod)(m);
1980         }
1981 
1982         Nullable!Message editMessageLiveLocation(string inlineMessageId, float latitude, float longitude)
1983         {
1984             EditMessageLiveLocationMethod m = {
1985                 inline_message_id : inlineMessageId,
1986                 latitude : latitude,
1987                 longitude : longitude
1988             };
1989 
1990             return editMessageLiveLocation(m);
1991         }
1992 
1993         Nullable!Message editMessageLiveLocation(T1)(T1 chatId, uint messageId, float latitude, float longitude)
1994             if (isTelegramId!T1)
1995         {
1996             EditMessageLiveLocationMethod m = {
1997                 chat_id : chatId,
1998                 message_id : messageId,
1999                 latitude : latitude,
2000                 longitude : longitude
2001             };
2002 
2003             return editMessageLiveLocation(m);
2004         }
2005 
2006         Nullable!Message stopMessageLiveLocation(ref StopMessageLiveLocationMethod m)
2007         {
2008             return callMethod!(Nullable!Message, StopMessageLiveLocationMethod)(m);
2009         }
2010 
2011         Nullable!Message stopMessageLiveLocation(string inlineMessageId)
2012         {
2013             StopMessageLiveLocationMethod m = {
2014                 inline_message_id : inlineMessageId
2015             };
2016 
2017             return stopMessageLiveLocation(m);
2018         }
2019 
2020         Nullable!Message stopMessageLiveLocation(T1)(T1 chatId, uint messageId)
2021             if (isTelegramId!T1)
2022         {
2023             StopMessageLiveLocationMethod m = {
2024                 chat_id : chatId,
2025                 message_id : messageId
2026             };
2027 
2028             return stopMessageLiveLocation(m);
2029         }
2030 
2031         Message sendVenue(ref SendVenueMethod m)
2032         {
2033             return callMethod!(Message, SendVenueMethod)(m);
2034         }
2035 
2036         Message sendVenue(T1)(T1 chatId, float latitude, float longitude,
2037             string title, string address)
2038             if (isTelegramId!T1)
2039         {
2040             SendVenueMethod m = {
2041                 chat_id : chatId,
2042                 latitude : latitude,
2043                 longitude : longitude,
2044                 title : title,
2045                 address : address
2046             };
2047 
2048             return sendVenue(m);
2049         }
2050 
2051         Message sendContact(ref SendContactMethod m)
2052         {
2053             return callMethod!(Message, SendContactMethod)(m);
2054         }
2055 
2056         Message sendContact(T1)(T1 chatId, string phone_number, string first_name)
2057             if (isTelegramId!T1)
2058         {
2059             SendContactMethod m = {
2060                 chat_id : chatId,
2061                 phone_number : phone_number,
2062                 first_name : first_name
2063             };
2064 
2065             return sendContact(m);
2066         }
2067 
2068         bool sendChatAction(ref SendChatActionMethod m)
2069         {
2070             return callMethod!(bool, SendChatActionMethod)(m);
2071         }
2072 
2073         bool sendChatAction(T1)(T1 chatId, string action)
2074             if (isTelegramId!T1)
2075         {
2076             SendChatActionMethod m = {
2077                 chat_id : chatId,
2078                 action : action
2079             };
2080 
2081             return sendChatAction(m);
2082         }
2083 
2084         UserProfilePhotos getUserProfilePhotos(ref GetUserProfilePhotosMethod m)
2085         {
2086             return callMethod!(UserProfilePhotos, GetUserProfilePhotosMethod)(m);
2087         }
2088 
2089         UserProfilePhotos getUserProfilePhotos(int userId)
2090         {
2091             GetUserProfilePhotosMethod m = {
2092                 user_id : userId
2093             };
2094 
2095             return getUserProfilePhotos(m);
2096         }
2097 
2098         File getFile(ref GetFileMethod m)
2099         {
2100             return callMethod!(File, GetFileMethod)(m);
2101         }
2102 
2103         File getFile(string fileId)
2104         {
2105             GetFileMethod m = {
2106                 file_id : fileId
2107             };
2108 
2109             return getFile(m);
2110         }
2111 
2112         bool kickChatMember(ref KickChatMemberMethod m)
2113         {
2114             return callMethod!(bool, KickChatMemberMethod)(m);
2115         }
2116 
2117         bool kickChatMember(T1)(T1 chatId, int userId)
2118             if(isTelegramId!T1)
2119         {
2120             KickChatMemberMethod m = {
2121                 chat_id : chatId,
2122                 user_id : userId
2123             };
2124 
2125             return kickChatMember(m);
2126         }
2127 
2128         bool unbanChatMember(ref UnbanChatMemberMethod m)
2129         {
2130             return callMethod!(bool, UnbanChatMemberMethod)(m);
2131         }
2132 
2133         bool unbanChatMember(T1)(T1 chatId, int userId)
2134             if(isTelegramId!T1)
2135         {
2136             UnbanChatMemberMethod m = {
2137                 chat_id : chatId,
2138                 user_id : userId
2139             };
2140 
2141             return unbanChatMember(m);
2142         }
2143 
2144         bool restrictChatMember(ref RestrictChatMemberMethod m)
2145         {
2146             return callMethod!bool(m);
2147         }
2148 
2149         bool restrictChatMember(T1)(T1 chatId, int userId)
2150             if(isTelegramId!T1)
2151         {
2152             RestrictChatMemberMethod m = {
2153                 chat_id : chatId,
2154                 user_id : userId
2155             };
2156 
2157             return restrictChatMember(m);
2158         }
2159 
2160         bool promoteChatMember(ref PromoteChatMemberMethod m)
2161         {
2162             return callMethod!bool(m);
2163         }
2164 
2165         bool promoteChatMember(T1)(T1 chatId, int userId)
2166             if(isTelegramId!T1)
2167         {
2168             PromoteChatMemberMethod m = {
2169                 chat_id : chatId,
2170                 user_id : userId
2171             };
2172 
2173             return promoteChatMember(m);
2174         }
2175 
2176         string exportChatInviteLink(ref ExportChatInviteLinkMethod m)
2177         {
2178             return callMethod!string(m);
2179         }
2180 
2181         string exportChatInviteLink(T1)(T1 chatId)
2182             if(isTelegramId!T1)
2183         {
2184             ExportChatInviteLinkMethod m = {
2185                 chat_id : chatId,
2186             };
2187 
2188             return exportChatInviteLink(m);
2189         }
2190 
2191         bool setChatPhoto(ref SetChatPhotoMethod m)
2192         {
2193             return callMethod!bool(m);
2194         }
2195 
2196         bool setChatPhoto(T1)(T1 chatId, InputFile photo)
2197             if (isTelegramId!T1)
2198         {
2199             SetChatPhotoMethod m = {
2200                 chat_id : chatId,
2201                 photo : photo
2202             };
2203 
2204             return setChatPhoto(m);
2205         }
2206 
2207         bool deleteChatPhoto(ref DeleteChatPhotoMethod m)
2208         {
2209             return callMethod!bool(m);
2210         }
2211 
2212         bool deleteChatPhoto(T1)(T1 chatId)
2213             if (isTelegramId!T1)
2214         {
2215             DeleteChatPhotoMethod m = {
2216                 chat_id : chatId,
2217             };
2218 
2219             return deleteChatPhoto(m);
2220         }
2221 
2222         bool setChatTitle(ref SetChatTitleMethod m)
2223         {
2224             return callMethod!bool(m);
2225         }
2226 
2227         bool setChatTitle(T1)(T1 chatId, string title)
2228             if (isTelegramId!T1)
2229         {
2230             SetChatTitleMethod m = {
2231                 chat_id : chatId,
2232                 title : title
2233             };
2234 
2235             return setChatTitle(m);
2236         }
2237 
2238         bool setChatDescription(ref SetChatDescriptionMethod m)
2239         {
2240             return callMethod!bool(m);
2241         }
2242 
2243         bool setChatDescription(T1)(T1 chatId, string description)
2244             if (isTelegramId!T1)
2245         {
2246             SetChatDescriptionMethod m = {
2247                 chat_id : chatId,
2248                 description : description
2249             };
2250 
2251             return setChatDescription(m);
2252         }
2253 
2254         bool pinChatMessage(ref PinChatMessageMethod m)
2255         {
2256             return callMethod!bool(m);
2257         }
2258 
2259         bool pinChatMessage(T1)(T1 chatId, uint messageId)
2260             if (isTelegramId!T1)
2261         {
2262             PinChatMessageMethod m = {
2263                 chat_id : chatId,
2264                 message_id : messageId
2265             };
2266 
2267             return pinChatMessage(m);
2268         }
2269 
2270         bool unpinChatMessage(ref UnpinChatMessageMethod m)
2271         {
2272             return callMethod!bool(m);
2273         }
2274 
2275         bool unpinChatMessage(T1)(T1 chatId)
2276             if (isTelegramId!T1)
2277         {
2278             UnpinChatMessageMethod m = {
2279                 chat_id : chatId,
2280             };
2281 
2282             return unpinChatMessage(m);
2283         }
2284 
2285         bool leaveChat(ref LeaveChatMethod m)
2286         {
2287             return callMethod!bool(m);
2288         }
2289 
2290         bool leaveChat(T1)(T1 chatId)
2291             if (isTelegramId!T1)
2292         {
2293             LeaveChatMethod m = {
2294                 chat_id : chatId,
2295             };
2296 
2297             return leaveChat(m);
2298         }
2299 
2300         Chat getChat(ref GetChatMethod m)
2301         {
2302             return callMethod!Chat(m);
2303         }
2304 
2305         Chat getChat(T1)(T1 chatId)
2306             if (isTelegramId!T1)
2307         {
2308             GetChatMethod m = {
2309                 chat_id : chatId,
2310             };
2311 
2312             return getChat(m);
2313         }
2314 
2315         ChatMember getChatAdministrators(ref GetChatAdministratorsMethod m)
2316         {
2317             return callMethod!ChatMember(m);
2318         }
2319 
2320         ChatMember getChatAdministrators(T1)(T1 chatId)
2321             if (isTelegramId!T1)
2322         {
2323             GetChatAdministratorsMethod m = {
2324                 chat_id : chatId,
2325             };
2326 
2327             return getChatAdministrators(m);
2328         }
2329 
2330         uint getChatMembersCount(ref GetChatMembersCountMethod m)
2331         {
2332             return callMethod!uint(m);
2333         }
2334 
2335         uint getChatMembersCount(T1)(T1 chatId)
2336             if (isTelegramId!T1)
2337         {
2338             GetChatMembersCountMethod m = {
2339                 chat_id : chatId,
2340             };
2341 
2342             return getChatMembersCount(m);
2343         }
2344 
2345         ChatMember getChatMember(ref GetChatMemberMethod m)
2346         {
2347             return callMethod!ChatMember(m);
2348         }
2349 
2350         ChatMember getChatMember(T1)(T1 chatId, int userId)
2351             if (isTelegramId!T1)
2352         {
2353             GetChatMemberMethod m = {
2354                 chat_id : chatId,
2355                 user_id : userId
2356             };
2357 
2358             return getChatMember(m);
2359         }
2360 
2361         bool setChatStickerSet(ref SetChatStickerSetMethod m)
2362         {
2363             return callMethod!bool(m);
2364         }
2365 
2366         bool setChatStickerSet(T1)(T1 chatId, string stickerSetName)
2367             if (isTelegramId!T1)
2368         {
2369             SetChatStickerSetMethod m = {
2370                 chat_id : chatId,
2371                 sticker_set_name : stickerSetName
2372             };
2373 
2374             return setChatStickerSet(m);
2375         }
2376 
2377         bool deleteChatStickerSet(ref DeleteChatStickerSetMethod m)
2378         {
2379             return callMethod!bool(m);
2380         }
2381 
2382         bool deleteChatStickerSet(T1)(T1 chatId)
2383             if (isTelegramId!T1)
2384         {
2385             DeleteChatStickerSetMethod m = {
2386                 chat_id : chatId,
2387             };
2388 
2389             return deleteChatStickerSet(m);
2390         }
2391 
2392         bool answerCallbackQuery(ref AnswerCallbackQueryMethod m)
2393         {
2394             return callMethod!bool(m);
2395         }
2396 
2397         bool answerCallbackQuery(string callbackQueryId)
2398         {
2399             AnswerCallbackQueryMethod m = {
2400                 callback_query_id : callbackQueryId
2401             };
2402 
2403             return answerCallbackQuery(m);
2404         }
2405 
2406         bool editMessageText(ref EditMessageTextMethod m)
2407         {
2408             return callMethod!bool(m);
2409         }
2410 
2411         bool editMessageText(T1)(T1 chatId, uint messageId, string text)
2412             if (isTelegramId!T1)
2413         {
2414             EditMessageTextMethod m = {
2415                 chat_id : chatId,
2416                 message_id : messageId,
2417                 text : text
2418             };
2419 
2420             return editMessageText(m);
2421         }
2422 
2423         bool editMessageText(string inlineMessageId, string text)
2424         {
2425             EditMessageTextMethod m = {
2426                 inline_message_id : inlineMessageId,
2427                 text : text
2428             };
2429 
2430             return editMessageText(m);
2431         }
2432 
2433         bool editMessageCaption(ref EditMessageCaptionMethod m)
2434         {
2435             return callMethod!bool(m);
2436         }
2437 
2438         bool editMessageCaption(T1)(T1 chatId, uint messageId, string caption = null)
2439             if (isTelegramId!T1)
2440         {
2441             EditMessageCaptionMethod m = {
2442                 chat_id : chatId,
2443                 message_id : messageId,
2444                 caption : caption
2445             };
2446 
2447             return editMessageCaption(m);
2448         }
2449 
2450         bool editMessageCaption(string inlineMessageId, string caption = null)
2451         {
2452             EditMessageCaptionMethod m = {
2453                 inline_message_id : inlineMessageId,
2454                 caption : caption
2455             };
2456 
2457             return editMessageCaption(m);
2458         }
2459 
2460         bool editMessageReplyMarkup(ref EditMessageReplyMarkupMethod m)
2461         {
2462             return callMethod!bool(m);
2463         }
2464 
2465         bool editMessageReplyMarkup(T1, T2)(T1 chatId, uint messageId, T2 replyMarkup)
2466             if (isTelegramId!T1 && isReplyMarkup!T2)
2467         {
2468             EditMessageReplyMarkupMethod m = {
2469                 chat_id : chatId,
2470                 message_id : messageId
2471             };
2472 
2473             m.reply_markup = replyMarkup;
2474 
2475             return editMessageReplyMarkup(m);
2476         }
2477 
2478         bool editMessageReplyMarkup(string inlineMessageId, Nullable!ReplyMarkup replyMarkup)
2479         {
2480             EditMessageReplyMarkupMethod m = {
2481                 inline_message_id : inlineMessageId,
2482                 reply_markup : replyMarkup
2483             };
2484 
2485             return editMessageReplyMarkup(m);
2486         }
2487 
2488         bool deleteMessage(ref DeleteMessageMethod m)
2489         {
2490             return callMethod!bool(m);
2491         }
2492 
2493         bool deleteMessage(T1)(T1 chatId, uint messageId)
2494             if (isTelegramId!T1)
2495         {
2496             DeleteMessageMethod m = {
2497                 chat_id : chatId,
2498                 message_id : messageId
2499             };
2500 
2501             return deleteMessage(m);
2502         }
2503 
2504         Message sendSticker(ref SendStickerMethod m)
2505         {
2506             return callMethod!Message(m);
2507         }
2508 
2509         // TODO sticker is InputFile|string
2510         Message sendSticker(T1)(T1 chatId, string sticker)
2511             if (isTelegramId!T1)
2512         {
2513             SendStickerMethod m = {
2514                 chat_id : chatId,
2515                 sticker : sticker
2516             };
2517 
2518             return sendSticker(m);
2519         }
2520 
2521         StickerSet getStickerSet(ref GetStickerSetMethod m)
2522         {
2523             return callMethod!StickerSet(m);
2524         }
2525 
2526         StickerSet getStickerSet(string name)
2527         {
2528             GetStickerSetMethod m = {
2529                 name : name
2530             };
2531 
2532             return getStickerSet(m);
2533         }
2534 
2535         File uploadStickerFile(ref UploadStickerFileMethod m)
2536         {
2537             return callMethod!File(m);
2538         }
2539 
2540         File uploadStickerFile(int userId, InputFile pngSticker)
2541         {
2542             UploadStickerFileMethod m = {
2543                 user_id : userId,
2544                 png_sticker : pngSticker
2545             };
2546 
2547             return uploadStickerFile(m);
2548         }
2549 
2550         bool createNewStickerSet(ref CreateNewStickerSetMethod m)
2551         {
2552             return callMethod!bool(m);
2553         }
2554 
2555         // TODO pngSticker is InputFile|string
2556         bool createNewStickerSet(int userId, string name, string title, string pngSticker, string emojis)
2557         {
2558             CreateNewStickerSetMethod m = {
2559                 user_id : userId,
2560                 name : name,
2561                 title : title,
2562                 png_sticker : pngSticker,
2563                 emojis : emojis
2564             };
2565 
2566             return createNewStickerSet(m);
2567         }
2568 
2569         bool addStickerToSet(ref AddStickerToSetMethod m)
2570         {
2571             return callMethod!bool(m);
2572         }
2573 
2574         bool addStickerToSet(int userId, string name, string pngSticker, string emojis)
2575         {
2576             AddStickerToSetMethod m = {
2577                 user_id : userId,
2578                 name : name,
2579                 png_sticker : pngSticker,
2580                 emojis : emojis
2581             };
2582 
2583             return addStickerToSet(m);
2584         }
2585 
2586         bool setStickerPositionInSet(ref SetStickerPositionInSetMethod m)
2587         {
2588             return callMethod!bool(m);
2589         }
2590 
2591         bool setStickerPositionInSet(string sticker, uint position)
2592         {
2593             SetStickerPositionInSetMethod m = {
2594                 sticker : sticker,
2595                 position : position
2596             };
2597 
2598             return setStickerPositionInSet(m);
2599         }
2600 
2601         bool deleteStickerFromSet(ref DeleteStickerFromSetMethod m)
2602         {
2603             return callMethod!bool(m);
2604         }
2605 
2606         bool deleteStickerFromSet(string sticker)
2607         {
2608             SetStickerPositionInSetMethod m = {
2609                 sticker : sticker
2610             };
2611 
2612             return setStickerPositionInSet(m);
2613         }
2614 
2615         bool answerInlineQuery(ref AnswerInlineQueryMethod m)
2616         {
2617             return callMethod!bool(m);
2618         }
2619 
2620         bool answerInlineQuery(string inlineQueryId, InlineQueryResult[] results)
2621         {
2622             AnswerInlineQueryMethod m = {
2623                 inline_query_id : inlineQueryId,
2624                 results : results
2625             };
2626 
2627             return answerInlineQuery(m);
2628         }
2629 
2630         unittest
2631         {
2632             class BotApiMock : BotApi
2633             {
2634                 this(string token)
2635                 {
2636                     super(token);
2637                 }
2638 
2639                 T callMethod(T, M)(M method)
2640                 {
2641                     T result;
2642 
2643                     logDiagnostic("[%d] Requesting %s", requestCounter, method.name);
2644 
2645                     return result;
2646                 }
2647             }
2648 
2649             auto api = new BotApiMock(null);
2650 
2651             api.getUpdates(5,30);
2652             api.setWebhook("https://webhook.url");
2653             api.deleteWebhook();
2654             api.getWebhookInfo();
2655             api.getMe();
2656             api.sendMessage("chat-id", "hello");
2657             api.forwardMessage("chat-id", "from-chat-id", 123);
2658             api.sendPhoto("chat-id", "photo-url");
2659             api.sendAudio("chat-id", "audio-url");
2660             api.sendDocument("chat-id", "document-url");
2661             api.sendVideo("chat-id", "video-url");
2662             api.sendVoice("chat-id", "voice-url");
2663             api.sendVideoNote("chat-id", "video-note-url");
2664             api.sendMediaGroup("chat-id", []);
2665             api.sendLocation("chat-id", 123, 123);
2666             api.editMessageLiveLocation("chat-id", 1, 1.23, 4.56);
2667             api.editMessageLiveLocation("inline-message-id", 1.23, 4.56);
2668             api.stopMessageLiveLocation("chat-id", 1);
2669             api.stopMessageLiveLocation("inline-message-id");
2670             api.sendVenue("chat-id", 123, 123, "title", "address");
2671             api.sendContact("chat-id", "+123", "First Name");
2672             api.sendChatAction("chat-id", "typing");
2673             api.getUserProfilePhotos(1);
2674             api.getFile("file-id");
2675             api.kickChatMember("chat-id", 1);
2676             api.unbanChatMember("chat-id", 1);
2677             api.restrictChatMember("chat-id", 1);
2678             api.promoteChatMember("chat-id", 1);
2679             api.exportChatInviteLink("chat-id");
2680             api.setChatPhoto("chat-id", InputFile());
2681             api.deleteChatPhoto("chat-id");
2682             api.setChatTitle("chat-id", "chat-title");
2683             api.setChatDescription("chat-id", "chat-description");
2684             api.pinChatMessage("chat-id", 1);
2685             api.unpinChatMessage("chat-id");
2686             api.leaveChat("chat-id");
2687             api.getChat("chat-id");
2688             api.getChatAdministrators("chat-id");
2689             api.getChatMembersCount("chat-id");
2690             api.getChatMember("chat-id", 1);
2691             api.setChatStickerSet("chat-id", "sticker-set");
2692             api.deleteChatStickerSet("chat-id");
2693             api.answerCallbackQuery("callback-query-id");
2694             api.editMessageText("chat-id", 123, "new text");
2695             api.editMessageText("inline-message-id", "new text");
2696             api.editMessageCaption("chat-id", 123, "new caption");
2697             api.editMessageCaption("chat-id", 123, null);
2698             api.editMessageCaption("inline-message-id", "new caption");
2699             api.editMessageCaption("inline-message-id", null);
2700 
2701             api.editMessageReplyMarkup("chat-id", 123, ForceReply());
2702             api.editMessageReplyMarkup("chat-id", 123, ReplyKeyboardMarkup());
2703             api.editMessageReplyMarkup("chat-id", 123, ReplyKeyboardRemove());
2704             api.editMessageReplyMarkup("chat-id", 123, InlineKeyboardMarkup());
2705             api.editMessageReplyMarkup("chat-id", 123, ReplyMarkup());
2706 
2707             api.deleteMessage("chat-id", 123);
2708             api.sendSticker("chat-id", "sticker");
2709             api.getStickerSet("sticker-set");
2710             api.uploadStickerFile(1, InputFile());
2711             api.createNewStickerSet(1, "name", "title", "png-sticker", "emojis");
2712             api.addStickerToSet(1, "name", "png-sticker", "emojis");
2713             api.setStickerPositionInSet("sticker", 42);
2714             api.deleteStickerFromSet("sticker");
2715 
2716             InlineQueryResult[] iqr = new InlineQueryResult[20];
2717 
2718             iqr[0] = InlineQueryResultArticle();
2719             iqr[1] = InlineQueryResultPhoto();
2720             iqr[2] = InlineQueryResultGif();
2721             iqr[3] = InlineQueryResultMpeg4Gif();
2722             iqr[4] = InlineQueryResultVideo();
2723             iqr[5] = InlineQueryResultAudio();
2724             iqr[6] = InlineQueryResultVoice();
2725             iqr[7] = InlineQueryResultDocument();
2726             iqr[8] = InlineQueryResultLocation();
2727             iqr[9] = InlineQueryResultVenue();
2728             iqr[10] = InlineQueryResultContact();
2729             iqr[11] = InlineQueryResultGame();
2730             iqr[12] = InlineQueryResultCachedPhoto();
2731             iqr[13] = InlineQueryResultCachedGif();
2732             iqr[14] = InlineQueryResultCachedMpeg4Gif();
2733             iqr[15] = InlineQueryResultCachedSticker();
2734             iqr[16] = InlineQueryResultCachedDocument();
2735             iqr[17] = InlineQueryResultCachedVideo();
2736             iqr[18] = InlineQueryResultCachedVoice();
2737             iqr[19] = InlineQueryResultCachedAudio();
2738 
2739             api.answerInlineQuery("answer-inline-query", iqr);
2740         }
2741 }
2742 
2743 class UpdatesRange
2744 {
2745     import std.algorithm.comparison : max;
2746 
2747     enum bool empty = false;
2748 
2749     protected:
2750         BotApi _api;
2751         uint _maxUpdateId;
2752 
2753         string[] _allowedUpdates = [];
2754         ubyte _updatesLimit = 5;
2755         uint _timeout = 30;
2756 
2757     private:
2758         bool _isEmpty;
2759         Update[] _updates;
2760         ushort _index;
2761 
2762     public: @safe:
2763         this(BotApi api, uint maxUpdateId = 0, ubyte limit = 5, uint timeout = 30)
2764         {
2765             _api = api;
2766             _maxUpdateId = maxUpdateId;
2767             _updatesLimit = limit;
2768             _timeout = timeout;
2769 
2770             _updates.reserve(_updatesLimit);
2771         }
2772 
2773         @property
2774         uint maxUpdateId()
2775         {
2776             return _maxUpdateId;
2777         }
2778 
2779         auto front()
2780         {
2781             if (_updates.length == 0) {
2782                 getUpdates();
2783                 _maxUpdateId = max(_maxUpdateId, _updates[_index].id);
2784             }
2785 
2786             return _updates[_index];
2787         }
2788 
2789         void popFront()
2790         {
2791             _maxUpdateId = max(_maxUpdateId, _updates[_index].id);
2792 
2793             if (++_index >= _updates.length) {
2794                 getUpdates();
2795             }
2796         }
2797 
2798     protected:
2799         @trusted
2800         void getUpdates()
2801         {
2802             do {
2803                 _updates = _api.getUpdates(
2804                     _maxUpdateId+1,
2805                     _updatesLimit,
2806                     _timeout,
2807                     _allowedUpdates
2808                 );
2809             } while (_updates.length == 0);
2810             _index = 0;
2811         }
2812 }