1 module telega.telegram.basic; 2 3 import std.typecons : Nullable; 4 import asdf : Asdf, serializedAs, deserialize; 5 import telega.serialization : JsonableAlgebraicProxy, serializeToJsonString; 6 import telega.botapi : BotApi, TelegramMethod, HTTPMethod, ChatId, isTelegramId; 7 import telega.telegram.stickers : Sticker; 8 import telega.telegram.games : Game, Animation, CallbackGame; 9 import telega.telegram.payments : Invoice, SuccessfulPayment, ShippingQuery, PreCheckoutQuery; 10 import telega.telegram.inline : InlineQuery; 11 12 /******************************************************************/ 13 /* Telegram types and enums */ 14 /******************************************************************/ 15 16 struct User 17 { 18 int id; 19 bool is_bot; 20 string first_name; 21 22 Nullable!string last_name; 23 Nullable!string username; 24 Nullable!string language_code; 25 } 26 27 unittest 28 { 29 string json = `{ 30 "id": 42, 31 "is_bot": false, 32 "first_name": "FirstName" 33 }`; 34 35 User u = deserialize!User(json); 36 37 assert(u.last_name.isNull); 38 } 39 40 unittest 41 { 42 string json = `{ 43 "id": 42, 44 "is_bot": false, 45 "first_name": "FirstName", 46 "last_name": "LastName" 47 }`; 48 49 User u = deserialize!User(json); 50 51 assert(false == u.last_name.isNull); 52 } 53 54 55 56 @serializedAs!ChatTypeProxy 57 enum ChatType : string 58 { 59 Private = "private", 60 Group = "group", 61 Supergroup = "supergroup", 62 Channel = "channel" 63 } 64 65 struct ChatTypeProxy 66 { 67 ChatType t; 68 69 this(ChatType type) 70 { 71 t = type; 72 } 73 74 ChatType opCast(T : ChatType)() 75 { 76 return t; 77 } 78 79 static ChatTypeProxy deserialize(Asdf v) 80 { 81 return ChatTypeProxy(cast(ChatType)cast(string)v); 82 } 83 } 84 85 struct Chat 86 { 87 long id; 88 ChatType type; 89 Nullable!string title; 90 Nullable!string first_name; 91 Nullable!string last_name; 92 Nullable!string username; 93 Nullable!bool all_members_are_administrators; 94 Nullable!ChatPhoto photo; 95 Nullable!string description; 96 Nullable!string invite_link; 97 // TODO Nullable!Message pinned_message; 98 Nullable!string sticker_set_name; 99 Nullable!bool can_set_sticker_set; 100 } 101 102 unittest 103 { 104 string json = `{ 105 "id": 42, 106 "type": "group", 107 "title": "chat title", 108 "all_members_are_administrators": false 109 }`; 110 111 Chat c = deserialize!Chat(json); 112 113 assert(c.id == 42); 114 assert(c.type == ChatType.Group); 115 } 116 117 struct Message 118 { 119 uint message_id; 120 uint date; 121 Chat chat; 122 Nullable!User from; 123 Nullable!User forward_from; 124 Nullable!Chat forward_from_chat; 125 Nullable!uint forward_from_message_id; 126 Nullable!string forward_signature; 127 Nullable!uint forward_date; 128 Nullable!uint edit_date; 129 Nullable!string media_group_id; 130 Nullable!string author_signature; 131 Nullable!string text; 132 Nullable!MessageEntity[] entities; 133 Nullable!MessageEntity[] caption_entities; 134 Nullable!Audio audio; 135 Nullable!Document document; 136 Nullable!Animation animation; 137 Nullable!Game game; 138 Nullable!PhotoSize[] photo; 139 Nullable!Sticker sticker; 140 Nullable!Video video; 141 Nullable!Voice voice; 142 Nullable!VideoNote video_note; 143 // TODO Nullable!Message reply_to_message; 144 // TODO Nullable!Message pinned_message; 145 Nullable!string caption; 146 Nullable!Contact contact; 147 Nullable!Location location; 148 Nullable!Venue venue; 149 Nullable!User[] new_chat_members; 150 Nullable!User left_chat_member; 151 Nullable!string new_chat_title; 152 Nullable!PhotoSize[] new_chat_photo; 153 Nullable!bool delete_chat_photo; 154 Nullable!bool group_chat_created; 155 Nullable!bool supergroup_chat_created; 156 Nullable!bool channel_chat_created; 157 Nullable!long migrate_to_chat_id; 158 Nullable!long migrate_from_chat_id; 159 Nullable!Invoice invoice; 160 Nullable!SuccessfulPayment successful_payment; 161 Nullable!string connected_website; 162 163 @property 164 uint id() 165 { 166 return message_id; 167 } 168 } 169 170 struct Update 171 { 172 uint update_id; 173 Nullable!Message message; 174 175 Nullable!Message edited_message; 176 Nullable!Message channel_post; 177 Nullable!Message edited_channel_post; 178 Nullable!InlineQuery inline_query; 179 Nullable!ChosenInlineResult chosen_inline_result; 180 Nullable!CallbackQuery callback_query; 181 Nullable!ShippingQuery shipping_query; 182 Nullable!PreCheckoutQuery pre_checkout_query; 183 184 @property @safe @nogc nothrow pure 185 uint id() 186 { 187 return update_id; 188 } 189 } 190 191 192 193 unittest 194 { 195 string json = `{ 196 "update_id": 143, 197 "message": { 198 "message_id": 243, 199 "text": "message text" 200 } 201 }`; 202 203 Update u = deserialize!Update(json); 204 205 assert(u.id == 143); 206 assert(u.message.message_id == 243); 207 assert(u.message.text.get == "message text"); 208 } 209 210 enum ParseMode : string 211 { 212 Markdown = "Markdown", 213 HTML = "HTML", 214 None = "", 215 } 216 217 struct MessageEntity 218 { 219 string type; 220 uint offset; 221 uint length; 222 Nullable!string url; 223 Nullable!User user; 224 } 225 226 struct PhotoSize 227 { 228 string file_id; 229 int width; 230 int height; 231 232 Nullable!uint file_size; 233 } 234 235 struct Audio 236 { 237 string file_id; 238 uint duration; 239 Nullable!string performer; 240 Nullable!string title; 241 Nullable!string mime_type; 242 Nullable!uint file_size; 243 Nullable!PhotoSize thumb; 244 } 245 246 // TODO Add Nullable fields 247 struct Document 248 { 249 string file_id; 250 PhotoSize thumb; 251 string file_name; 252 string mime_type; 253 uint file_size; 254 } 255 256 // TODO Add Nullable fields 257 struct Video 258 { 259 string file_id; 260 uint width; 261 uint height; 262 uint duration; 263 PhotoSize thumb; 264 string mime_type; 265 uint file_size; 266 } 267 268 // TODO Add Nullable fields 269 struct Voice 270 { 271 string file_id; 272 uint duration; 273 string mime_type; 274 uint file_size; 275 } 276 277 // TODO Add Nullable fields 278 struct VideoNote 279 { 280 string file_id; 281 uint length; 282 uint duration; 283 PhotoSize thumb; 284 uint file_size; 285 } 286 287 // TODO Add Nullable fields 288 struct Contact 289 { 290 string phone_number; 291 string first_name; 292 string last_name; 293 string user_id; 294 } 295 296 // TODO Add Nullable fields 297 struct Location 298 { 299 float longitude; 300 float latitude; 301 } 302 303 // TODO Add Nullable fields 304 struct Venue 305 { 306 Location location; 307 string title; 308 string address; 309 string foursquare_id; 310 } 311 312 // TODO Add Nullable fields 313 struct UserProfilePhotos 314 { 315 uint total_count; 316 PhotoSize[][] photos; 317 } 318 319 // TODO Add Nullable fields 320 struct File 321 { 322 string file_id; 323 uint file_size; 324 string file_path; 325 } 326 327 328 import std.meta : AliasSeq, staticIndexOf; 329 import std.variant : Algebraic; 330 331 alias ReplyMarkupStructs = AliasSeq!( 332 ReplyKeyboardMarkup, 333 ReplyKeyboardRemove, 334 InlineKeyboardMarkup, 335 ForceReply 336 ); 337 338 /** 339 Abstract structure for unioining ReplyKeyboardMarkup, ReplyKeyboardRemove, 340 InlineKeyboardMarkup and ForceReply 341 */ 342 343 alias ReplyMarkup = JsonableAlgebraicProxy!ReplyMarkupStructs; 344 enum isReplyMarkup(T) = 345 is(T == ReplyMarkup) || staticIndexOf!(T, ReplyMarkupStructs) >= 0; 346 347 import std.algorithm.iteration; 348 import std.array; 349 350 struct ReplyKeyboardMarkup 351 { 352 KeyboardButton[][] keyboard; 353 354 Nullable!bool resize_keyboard; 355 Nullable!bool one_time_keyboard; 356 Nullable!bool selective; 357 358 this (string[][] keyboard) 359 { 360 this.keyboard = keyboard.map!toKeyboardButtonRow.array; 361 } 362 363 void opOpAssign(string op : "~")(KeyboardButton[] buttons) 364 { 365 keyboard ~= buttons; 366 } 367 } 368 369 struct KeyboardButton 370 { 371 string text; 372 373 Nullable!bool request_contact; 374 Nullable!bool request_location; 375 376 this(string text) 377 { 378 this.text = text; 379 } 380 381 this(string text, bool requestContact) 382 { 383 this(text); 384 this.request_contact = requestContact; 385 } 386 387 this(string text, bool requestContact, bool requestLocation) 388 { 389 this(text, requestContact); 390 this.request_location = requestLocation; 391 } 392 393 typeof(this) requestContact(bool value = true) 394 { 395 request_contact = value; 396 397 return this; 398 } 399 400 typeof(this) requestLocation(bool value = true) 401 { 402 request_location = value; 403 404 return this; 405 } 406 } 407 408 KeyboardButton[] toKeyboardButtonRow(string[] row) 409 { 410 return row.map!(b => KeyboardButton(b)).array; 411 } 412 413 struct ReplyKeyboardRemove 414 { 415 bool remove_keyboard = true; 416 Nullable!bool selective; 417 } 418 419 struct InlineKeyboardMarkup 420 { 421 InlineKeyboardButton[][] inline_keyboard; 422 } 423 424 struct InlineKeyboardButton 425 { 426 string text; 427 Nullable!string url; 428 Nullable!string callback_data; 429 Nullable!string switch_inline_query; 430 Nullable!string switch_inline_query_current_chat; 431 Nullable!CallbackGame callback_game; 432 Nullable!bool pay; 433 } 434 435 // TODO Add Nullable fields 436 struct CallbackQuery 437 { 438 string id; 439 User from; 440 Nullable!Message message; 441 string inline_message_id; 442 string chat_instance; 443 string data; 444 string game_short_name; 445 } 446 447 struct ForceReply 448 { 449 bool force_reply = true; 450 Nullable!bool selective; 451 } 452 453 struct ChatPhoto 454 { 455 string small_file_id; 456 string big_file_id; 457 } 458 459 // TODO Add Nullable fields 460 struct ResponseParameters 461 { 462 long migrate_to_chat_id; 463 uint retry_after; 464 } 465 466 alias InputMediaStructs = AliasSeq!(InputMediaPhoto, InputMediaVideo); 467 468 alias InputMedia = JsonableAlgebraicProxy!InputMediaStructs; 469 470 struct InputMediaPhoto 471 { 472 string type; 473 string media; 474 Nullable!string caption; 475 Nullable!ParseMode parse_mode; 476 } 477 478 struct InputMediaVideo 479 { 480 string type; 481 string media; 482 Nullable!string caption; 483 Nullable!ParseMode parse_mode; 484 Nullable!uint width; 485 Nullable!uint height; 486 Nullable!uint duration; 487 Nullable!bool supports_streaming; 488 } 489 490 // TODO InputMediaAnimation 491 // TODO InputMediaAudio 492 // TODO InputMediaDocument 493 494 struct InputFile 495 { 496 // no fields 497 } 498 499 alias InputMessageContentStructs = AliasSeq!( 500 InputTextMessageContent, InputLocationMessageContent, InputVenueMessageContent, InputContactMessageContent 501 ); 502 503 alias InputMessageContent = JsonableAlgebraicProxy!InputMessageContentStructs; 504 505 struct InputTextMessageContent 506 { 507 string message_text; 508 Nullable!ParseMode parse_mode; 509 Nullable!bool disable_web_page_preview; 510 } 511 512 struct InputLocationMessageContent 513 { 514 float latitude; 515 float longitude; 516 Nullable!uint live_period; 517 } 518 519 struct InputVenueMessageContent 520 { 521 float latitude; 522 float longitude; 523 string title; 524 string address; 525 Nullable!string foursquare_id; 526 // TODO new field Nullable!string foursquare_type; 527 } 528 529 struct InputContactMessageContent 530 { 531 string phone_number; 532 string first_name; 533 Nullable!string last_name; 534 // TODO new field Nullable!string vcard; 535 } 536 537 struct ChosenInlineResult 538 { 539 string result_id; 540 User from; 541 Nullable!Location location; 542 Nullable!string inline_message_id; 543 string query; 544 } 545 546 /******************************************************************/ 547 /* Telegram methods */ 548 /******************************************************************/ 549 550 struct GetUpdatesMethod 551 { 552 mixin TelegramMethod!"/getUpdates"; 553 554 int offset; 555 ubyte limit; 556 uint timeout; 557 string[] allowed_updates; 558 } 559 560 struct GetMeMethod 561 { 562 mixin TelegramMethod!("/getMe", HTTPMethod.GET); 563 } 564 565 struct SendMessageMethod 566 { 567 mixin TelegramMethod!"/sendMessage"; 568 569 ChatId chat_id; 570 string text; 571 Nullable!ParseMode parse_mode; 572 Nullable!bool disable_web_page_preview; 573 Nullable!bool disable_notification; 574 Nullable!uint reply_to_message_id; 575 576 ReplyMarkup reply_markup; 577 } 578 579 unittest 580 { 581 SendMessageMethod m = { 582 chat_id: 111, 583 text: "Message text" 584 }; 585 586 assert(m.serializeToJsonString() == 587 `{"chat_id":"111","text":"Message text"}`); 588 } 589 590 struct ForwardMessageMethod 591 { 592 mixin TelegramMethod!"/forwardMessage"; 593 594 ChatId chat_id; 595 string from_chat_id; 596 Nullable!bool disable_notification; 597 uint message_id; 598 } 599 600 struct SendPhotoMethod 601 { 602 mixin TelegramMethod!"/sendPhoto"; 603 604 ChatId chat_id; 605 string photo; 606 Nullable!string caption; 607 Nullable!ParseMode parse_mode; 608 Nullable!bool disable_notification; 609 Nullable!uint reply_to_message_id; 610 Nullable!ReplyMarkup reply_markup; 611 } 612 613 unittest 614 { 615 SendPhotoMethod m = { 616 chat_id: "111", 617 photo: "Photo url" 618 }; 619 620 import std.stdio; 621 622 m.serializeToJsonString().writeln; 623 624 assert(m.serializeToJsonString() == 625 `{"chat_id":"111","photo":"Photo url"}`); 626 } 627 unittest 628 { 629 SendPhotoMethod m = { 630 chat_id: "111", 631 photo: "Photo url", 632 disable_notification: false, 633 reply_to_message_id: 0 634 }; 635 636 assert(m.serializeToJsonString() == 637 `{"chat_id":"111","photo":"Photo url","disable_notification":false,"reply_to_message_id":0}`); 638 } 639 640 struct SendAudioMethod 641 { 642 mixin TelegramMethod!"/sendAudio"; 643 644 ChatId chat_id; 645 string audio; 646 Nullable!string caption; 647 Nullable!ParseMode parse_mode; 648 Nullable!uint duration; 649 Nullable!string performer; 650 Nullable!string title; 651 Nullable!bool disable_notification; 652 Nullable!uint reply_to_message_id; 653 Nullable!ReplyMarkup reply_markup; 654 655 } 656 657 unittest 658 { 659 SendAudioMethod m = { 660 chat_id: "111", 661 audio: "data" 662 }; 663 664 assert(m.serializeToJsonString() == 665 `{"chat_id":"111","audio":"data"}`); 666 } 667 668 unittest 669 { 670 SendAudioMethod m = { 671 chat_id: "111", 672 audio: "data", 673 duration: 0, 674 disable_notification:false, 675 reply_to_message_id: 0, 676 }; 677 678 assert(m.serializeToJsonString() == 679 `{"chat_id":"111","audio":"data","duration":0,"disable_notification":false,"reply_to_message_id":0}`); 680 } 681 682 struct SendDocumentMethod 683 { 684 mixin TelegramMethod!"/sendDocument"; 685 686 ChatId chat_id; 687 string document; 688 Nullable!string caption; 689 Nullable!ParseMode parse_mode; 690 Nullable!bool disable_notification; 691 Nullable!uint reply_to_message_id; 692 Nullable!ReplyMarkup reply_markup; 693 } 694 695 unittest 696 { 697 SendDocumentMethod m = { 698 chat_id: "111", 699 document: "data" 700 }; 701 702 assert(m.serializeToJsonString() == 703 `{"chat_id":"111","document":"data"}`); 704 } 705 706 unittest 707 { 708 SendDocumentMethod m = { 709 chat_id: "111", 710 document: "data", 711 disable_notification: false, 712 reply_to_message_id: 0, 713 }; 714 715 assert(m.serializeToJsonString() == 716 `{"chat_id":"111","document":"data","disable_notification":false,"reply_to_message_id":0}`); 717 } 718 719 struct SendVideoMethod 720 { 721 mixin TelegramMethod!"/sendVideo"; 722 723 string chat_id; 724 string video; 725 Nullable!uint duration; 726 Nullable!uint width; 727 Nullable!uint height; 728 Nullable!string caption; 729 Nullable!ParseMode parse_mode; 730 Nullable!bool supports_streaming; 731 Nullable!bool disable_notification; 732 Nullable!uint reply_to_message_id; 733 Nullable!ReplyMarkup reply_markup; 734 } 735 736 unittest 737 { 738 SendVideoMethod m = { 739 chat_id: "111", 740 video: "data" 741 }; 742 743 assert(m.serializeToJsonString() == 744 `{"chat_id":"111","video":"data"}` 745 ); 746 } 747 748 unittest 749 { 750 SendVideoMethod m = { 751 chat_id: "111", 752 video: "data", 753 duration: 0, 754 width: 0, 755 height: 0, 756 supports_streaming: false, 757 disable_notification: false, 758 reply_to_message_id: 0 759 }; 760 761 assert(m.serializeToJsonString() == 762 `{"chat_id":"111","video":"data","duration":0,"width":0,"height":0,"supports_streaming":false,"disable_notification":false,"reply_to_message_id":0}` 763 ); 764 } 765 766 struct SendVoiceMethod 767 { 768 mixin TelegramMethod!"/sendVoice"; 769 770 ChatId chat_id; 771 string voice; 772 Nullable!string caption; 773 Nullable!ParseMode parse_mode; 774 Nullable!uint duration; 775 Nullable!bool disable_notification; 776 Nullable!uint reply_to_message_id; 777 Nullable!ReplyMarkup reply_markup; 778 } 779 780 unittest 781 { 782 SendVoiceMethod m = { 783 chat_id: "111", 784 voice: "data" 785 }; 786 787 assert(m.serializeToJsonString() == 788 `{"chat_id":"111","voice":"data"}`); 789 } 790 791 unittest 792 { 793 SendVoiceMethod m = { 794 chat_id: "111", 795 voice: "data", 796 duration: 0, 797 disable_notification: false, 798 reply_to_message_id: 0, 799 }; 800 801 assert(m.serializeToJsonString() == 802 `{"chat_id":"111","voice":"data","duration":0,"disable_notification":false,"reply_to_message_id":0}`); 803 } 804 805 struct SendVideoNoteMethod 806 { 807 mixin TelegramMethod!"/sendVideoNote"; 808 809 ChatId chat_id; 810 string video_note; 811 Nullable!uint duration; 812 Nullable!uint length; 813 Nullable!bool disable_notification; 814 Nullable!uint reply_to_message_id; 815 Nullable!ReplyMarkup reply_markup; 816 } 817 818 unittest 819 { 820 SendVideoNoteMethod m = { 821 chat_id: "111", 822 video_note: "data" 823 }; 824 825 assert(m.serializeToJsonString() == 826 `{"chat_id":"111","video_note":"data"}` 827 ); 828 } 829 830 struct SendMediaGroupMethod 831 { 832 mixin TelegramMethod!"/sendMediaGroup"; 833 834 ChatId chat_id; 835 InputMedia[] media; 836 Nullable!bool disable_notification; 837 Nullable!uint reply_to_message_id; 838 } 839 840 unittest 841 { 842 InputMedia im; 843 844 InputMediaPhoto imp = { 845 type: "t", 846 media: "m" 847 }; 848 849 im = imp; 850 851 SendMediaGroupMethod m = { 852 chat_id: "111", 853 media: [im], 854 }; 855 856 assert(m.serializeToJsonString() == 857 `{"chat_id":"111","media":[{"type":"t","media":"m"}]}` 858 ); 859 } 860 861 struct SendLocationMethod 862 { 863 mixin TelegramMethod!"/sendLocation"; 864 865 ChatId chat_id; 866 float latitude; 867 float longitude; 868 Nullable!uint live_period; 869 Nullable!bool disable_notification; 870 Nullable!uint reply_to_message_id; 871 Nullable!ReplyMarkup reply_markup; 872 } 873 874 unittest 875 { 876 SendLocationMethod m = { 877 chat_id: "111", 878 latitude: 0.01, 879 longitude: 0.02 880 }; 881 882 assert(m.serializeToJsonString() == 883 `{"chat_id":"111","latitude":0.01,"longitude":0.02}` 884 ); 885 } 886 887 struct EditMessageLiveLocationMethod 888 { 889 mixin TelegramMethod!"/editMessageLiveLocation"; 890 891 ChatId chat_id; 892 uint message_id; 893 string inline_message_id; 894 float latitude; 895 float longitude; 896 ReplyMarkup reply_markup; 897 } 898 899 struct StopMessageLiveLocationMethod 900 { 901 mixin TelegramMethod!"/stopMessageLiveLocation"; 902 903 ChatId chat_id; 904 uint message_id; 905 string inline_message_id; 906 ReplyMarkup reply_markup; 907 } 908 909 struct SendVenueMethod 910 { 911 mixin TelegramMethod!"/sendVenue"; 912 913 ChatId chat_id; 914 float latitude; 915 float longitude; 916 string title; 917 string address; 918 Nullable!string foursquare_id; 919 Nullable!bool disable_notification; 920 Nullable!uint reply_to_message_id; 921 Nullable!ReplyMarkup reply_markup; 922 } 923 924 unittest 925 { 926 SendVenueMethod m = { 927 chat_id: "111", 928 latitude: 0.01, 929 longitude: 0.02, 930 title: "t", 931 address: "a" 932 }; 933 934 assert(m.serializeToJsonString() == 935 `{"chat_id":"111","latitude":0.01,"longitude":0.02,"title":"t","address":"a"}` 936 ); 937 } 938 939 struct SendContactMethod 940 { 941 mixin TelegramMethod!"/sendContact"; 942 943 ChatId chat_id; 944 string phone_number; 945 string first_name; 946 Nullable!string last_name; 947 Nullable!bool disable_notification; 948 Nullable!uint reply_to_message_id; 949 Nullable!ReplyMarkup reply_markup; 950 } 951 952 unittest 953 { 954 SendContactMethod m = { 955 chat_id: "111", 956 phone_number: "+7123", 957 first_name: "fn" 958 }; 959 960 assert(m.serializeToJsonString() == 961 `{"chat_id":"111","phone_number":"+7123","first_name":"fn"}` 962 ); 963 } 964 965 enum ChatAction : string 966 { 967 Typing = "typing", 968 UploadPhoto = "upload_photo", 969 RecordVideo = "record_video", 970 UploadVideo = "upload_video", 971 RecordAudio = "record_audio", 972 UploadAudio = "upload_audio", 973 UploadDocument = "upload_document", 974 FindLocation = "find_location", 975 RecordVideoNote = "record_video_note", 976 UploadVideoNote = "upload_video_note" 977 } 978 979 struct SendChatActionMethod 980 { 981 mixin TelegramMethod!"/sendChatAction"; 982 983 ChatId chat_id; 984 string action; 985 } 986 987 struct GetUserProfilePhotosMethod 988 { 989 mixin TelegramMethod!("/getUserProfilePhotos", HTTPMethod.GET); 990 991 int user_id; 992 uint offset; 993 uint limit; 994 } 995 996 struct GetFileMethod 997 { 998 mixin TelegramMethod!("/getFile", HTTPMethod.GET); 999 1000 string file_id; 1001 } 1002 1003 struct GetChatMethod 1004 { 1005 mixin TelegramMethod!("/getChat", HTTPMethod.GET); 1006 1007 ChatId chat_id; 1008 } 1009 1010 struct AnswerCallbackQueryMethod 1011 { 1012 mixin TelegramMethod!"/answerCallbackQuery"; 1013 1014 string callback_query_id; 1015 string text; 1016 bool show_alert; 1017 string url; 1018 uint cache_time; 1019 } 1020 1021 1022 // API methods 1023 1024 Update[] getUpdates(BotApi api, int offset, ubyte limit = 5, uint timeout = 30, string[] allowedUpdates = []) 1025 { 1026 GetUpdatesMethod m = { 1027 offset: offset, 1028 limit: limit, 1029 timeout: timeout, 1030 allowed_updates: allowedUpdates 1031 }; 1032 1033 return api.callMethod!(Update[], GetUpdatesMethod)(m); 1034 } 1035 1036 User getMe(BotApi api) 1037 { 1038 GetMeMethod m; 1039 1040 return api.callMethod!(User, GetMeMethod)(m); 1041 } 1042 1043 Message sendMessage(BotApi api, ref SendMessageMethod m) 1044 { 1045 return api.callMethod!(Message, SendMessageMethod)(m); 1046 } 1047 1048 Message sendMessage(T)(BotApi api, T chatId, string text) 1049 if (isTelegramId!T) 1050 { 1051 SendMessageMethod m = { 1052 chat_id : chatId, 1053 text : text, 1054 }; 1055 1056 return sendMessage(api, m); 1057 } 1058 1059 Message forwardMessage(T1, T2)(BotApi api, T1 chatId, T2 fromChatId, uint messageId) 1060 if (isTelegramId!T1 && isTelegramId!T2) 1061 { 1062 ForwardMessageMethod m = { 1063 message_id : messageId, 1064 chat_id : chatId, 1065 from_chat_id: fromChatId, 1066 }; 1067 1068 return api.callMethod!(Message, ForwardMessageMethod)(m); 1069 } 1070 1071 Message forwardMessage(BotApi api, ref ForwardMessageMethod m) 1072 { 1073 return api.callMethod!(Message, ForwardMessageMethod)(m); 1074 } 1075 1076 Message sendPhoto(BotApi api, ref SendPhotoMethod m) 1077 { 1078 return api.callMethod!(Message, SendPhotoMethod)(m); 1079 } 1080 1081 Message sendPhoto(T1)(BotApi api, T1 chatId, string photo) 1082 if (isTelegramId!T1) 1083 { 1084 SendPhotoMethod m = { 1085 chat_id : chatId, 1086 photo : photo, 1087 }; 1088 1089 return sendPhoto(api, m); 1090 } 1091 1092 Message sendAudio(BotApi api, ref SendAudioMethod m) 1093 { 1094 return api.callMethod!(Message, SendAudioMethod)(m); 1095 } 1096 1097 Message sendAudio(T1)(BotApi api, T1 chatId, string audio) 1098 if (isTelegramId!T1) 1099 { 1100 SendAudioMethod m = { 1101 chat_id : chatId, 1102 audio : audio 1103 }; 1104 1105 return sendAudio(api, m); 1106 } 1107 1108 Message sendDocument(BotApi api, ref SendDocumentMethod m) 1109 { 1110 return api.callMethod!(Message, SendDocumentMethod)(m); 1111 } 1112 1113 Message sendDocument(T1)(BotApi api, T1 chatId, string document) 1114 if (isTelegramId!T1) 1115 { 1116 SendDocumentMethod m = { 1117 chat_id : chatId, 1118 document : document 1119 }; 1120 1121 return sendDocument(api, m); 1122 } 1123 1124 Message sendVideo(BotApi api, ref SendVideoMethod m) 1125 { 1126 return api.callMethod!(Message, SendVideoMethod)(m); 1127 } 1128 1129 Message sendVideo(T1)(BotApi api, T1 chatId, string video) 1130 if (isTelegramId!T1) 1131 { 1132 SendVideoMethod m = { 1133 chat_id : chatId, 1134 video : video 1135 }; 1136 1137 return sendVideo(api, m); 1138 } 1139 1140 Message sendVoice(BotApi api, ref SendVoiceMethod m) 1141 { 1142 return api.callMethod!(Message, SendVoiceMethod)(m); 1143 } 1144 1145 Message sendVoice(T1)(BotApi api, T1 chatId, string voice) 1146 if (isTelegramId!T1) 1147 { 1148 SendVoiceMethod m = { 1149 chat_id : chatId, 1150 voice : voice 1151 }; 1152 1153 return sendVoice(api, m); 1154 } 1155 1156 Message sendVideoNote(BotApi api, ref SendVideoNoteMethod m) 1157 { 1158 return api.callMethod!(Message, SendVideoNoteMethod)(m); 1159 } 1160 1161 Message sendVideoNote(T1)(BotApi api, T1 chatId, string videoNote) 1162 if (isTelegramId!T1) 1163 { 1164 SendVideoNoteMethod m = { 1165 chat_id : chatId, 1166 video_note : videoNote 1167 }; 1168 1169 return sendVideoNote(api, m); 1170 } 1171 1172 Message sendMediaGroup(BotApi api, ref SendMediaGroupMethod m) 1173 { 1174 return api.callMethod!(Message, SendMediaGroupMethod)(m); 1175 } 1176 1177 Message sendMediaGroup(T1)(BotApi api, T1 chatId, InputMedia[] media) 1178 if (isTelegramId!T1) 1179 { 1180 SendMediaGroupMethod m = { 1181 chat_id : chatId, 1182 media : media 1183 }; 1184 1185 return sendMediaGroup(api, m); 1186 } 1187 1188 Message sendLocation(BotApi api, ref SendLocationMethod m) 1189 { 1190 return api.callMethod!(Message, SendLocationMethod)(m); 1191 } 1192 1193 Message sendLocation(T1)(BotApi api, T1 chatId, float latitude, float longitude) 1194 if (isTelegramId!T1) 1195 { 1196 SendLocationMethod m = { 1197 chat_id : chatId, 1198 latitude : latitude, 1199 longitude : longitude, 1200 }; 1201 1202 return sendLocation(api, m); 1203 } 1204 1205 Nullable!Message editMessageLiveLocation(BotApi api, ref EditMessageLiveLocationMethod m) 1206 { 1207 return api.callMethod!(Nullable!Message, EditMessageLiveLocationMethod)(m); 1208 } 1209 1210 Nullable!Message editMessageLiveLocation(BotApi api, string inlineMessageId, float latitude, float longitude) 1211 { 1212 EditMessageLiveLocationMethod m = { 1213 inline_message_id : inlineMessageId, 1214 latitude : latitude, 1215 longitude : longitude 1216 }; 1217 1218 return editMessageLiveLocation(api, m); 1219 } 1220 1221 Nullable!Message editMessageLiveLocation(T1)(BotApi api, T1 chatId, uint messageId, float latitude, float longitude) 1222 if (isTelegramId!T1) 1223 { 1224 EditMessageLiveLocationMethod m = { 1225 chat_id : chatId, 1226 message_id : messageId, 1227 latitude : latitude, 1228 longitude : longitude 1229 }; 1230 1231 return editMessageLiveLocation(api, m); 1232 } 1233 1234 Nullable!Message stopMessageLiveLocation(BotApi api, ref StopMessageLiveLocationMethod m) 1235 { 1236 return api.callMethod!(Nullable!Message, StopMessageLiveLocationMethod)(m); 1237 } 1238 1239 Nullable!Message stopMessageLiveLocation(BotApi api, string inlineMessageId) 1240 { 1241 StopMessageLiveLocationMethod m = { 1242 inline_message_id : inlineMessageId 1243 }; 1244 1245 return stopMessageLiveLocation(api, m); 1246 } 1247 1248 Nullable!Message stopMessageLiveLocation(T1)(BotApi api, T1 chatId, uint messageId) 1249 if (isTelegramId!T1) 1250 { 1251 StopMessageLiveLocationMethod m = { 1252 chat_id : chatId, 1253 message_id : messageId 1254 }; 1255 1256 return stopMessageLiveLocation(api, m); 1257 } 1258 1259 Message sendVenue(BotApi api, ref SendVenueMethod m) 1260 { 1261 return api.callMethod!(Message, SendVenueMethod)(m); 1262 } 1263 1264 Message sendVenue(T1)(BotApi api, T1 chatId, float latitude, float longitude, 1265 string title, string address) 1266 if (isTelegramId!T1) 1267 { 1268 SendVenueMethod m = { 1269 chat_id : chatId, 1270 latitude : latitude, 1271 longitude : longitude, 1272 title : title, 1273 address : address 1274 }; 1275 1276 return sendVenue(api, m); 1277 } 1278 1279 Message sendContact(BotApi api, ref SendContactMethod m) 1280 { 1281 return api.callMethod!(Message, SendContactMethod)(m); 1282 } 1283 1284 Message sendContact(T1)(BotApi api, T1 chatId, string phone_number, string first_name) 1285 if (isTelegramId!T1) 1286 { 1287 SendContactMethod m = { 1288 chat_id : chatId, 1289 phone_number : phone_number, 1290 first_name : first_name 1291 }; 1292 1293 return sendContact(api, m); 1294 } 1295 1296 bool sendChatAction(BotApi api, ref SendChatActionMethod m) 1297 { 1298 return api.callMethod!(bool, SendChatActionMethod)(m); 1299 } 1300 1301 bool sendChatAction(T1)(BotApi api, T1 chatId, string action) 1302 if (isTelegramId!T1) 1303 { 1304 SendChatActionMethod m = { 1305 chat_id : chatId, 1306 action : action 1307 }; 1308 1309 return sendChatAction(api, m); 1310 } 1311 1312 UserProfilePhotos getUserProfilePhotos(BotApi api, ref GetUserProfilePhotosMethod m) 1313 { 1314 return api.callMethod!(UserProfilePhotos, GetUserProfilePhotosMethod)(m); 1315 } 1316 1317 UserProfilePhotos getUserProfilePhotos(BotApi api, int userId) 1318 { 1319 GetUserProfilePhotosMethod m = { 1320 user_id : userId 1321 }; 1322 1323 return getUserProfilePhotos(api, m); 1324 } 1325 1326 File getFile(BotApi api, ref GetFileMethod m) 1327 { 1328 return api.callMethod!(File, GetFileMethod)(m); 1329 } 1330 1331 File getFile(BotApi api, string fileId) 1332 { 1333 GetFileMethod m = { 1334 file_id : fileId 1335 }; 1336 1337 return getFile(api, m); 1338 } 1339 1340 1341 1342 Chat getChat(BotApi api, ref GetChatMethod m) 1343 { 1344 return api.callMethod!Chat(m); 1345 } 1346 1347 Chat getChat(T1)(BotApi api, T1 chatId) 1348 if (isTelegramId!T1) 1349 { 1350 GetChatMethod m = { 1351 chat_id : chatId, 1352 }; 1353 1354 return getChat(api, m); 1355 } 1356 1357 bool answerCallbackQuery(BotApi api, ref AnswerCallbackQueryMethod m) 1358 { 1359 return api.callMethod!bool(m); 1360 } 1361 1362 bool answerCallbackQuery(BotApi api, string callbackQueryId) 1363 { 1364 AnswerCallbackQueryMethod m = { 1365 callback_query_id : callbackQueryId 1366 }; 1367 1368 return answerCallbackQuery(api, m); 1369 } 1370 1371 unittest 1372 { 1373 class BotApiMock : BotApi 1374 { 1375 this(string token) 1376 { 1377 super(token); 1378 } 1379 1380 T callMethod(T, M)(M method) 1381 { 1382 T result; 1383 1384 logDiagnostic("[%d] Requesting %s", requestCounter, method.name); 1385 1386 return result; 1387 } 1388 } 1389 1390 auto api = new BotApiMock(null); 1391 1392 api.getUpdates(5,30); 1393 api.getMe(); 1394 api.sendMessage("chat-id", "hello"); 1395 api.forwardMessage("chat-id", "from-chat-id", 123); 1396 api.sendPhoto("chat-id", "photo-url"); 1397 api.sendAudio("chat-id", "audio-url"); 1398 api.sendDocument("chat-id", "document-url"); 1399 api.sendVideo("chat-id", "video-url"); 1400 api.sendVoice("chat-id", "voice-url"); 1401 api.sendVideoNote("chat-id", "video-note-url"); 1402 api.sendMediaGroup("chat-id", []); 1403 api.sendLocation("chat-id", 123, 123); 1404 api.editMessageLiveLocation("chat-id", 1, 1.23, 4.56); 1405 api.editMessageLiveLocation("inline-message-id", 1.23, 4.56); 1406 api.stopMessageLiveLocation("chat-id", 1); 1407 api.stopMessageLiveLocation("inline-message-id"); 1408 api.sendVenue("chat-id", 123, 123, "title", "address"); 1409 api.sendContact("chat-id", "+123", "First Name"); 1410 api.sendChatAction("chat-id", "typing"); 1411 api.getUserProfilePhotos(1); 1412 api.getFile("file-id"); 1413 api.getChat("chat-id"); 1414 api.answerCallbackQuery("callback-query-id"); 1415 }