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 }