1 /**
2  * Tg.d is a D client library for Telegram Bot API
3  *
4  * Take a look at `TelegramBot` which is the main structure representing a bot.
5  *
6  * ---
7  * import tg.d;
8  *
9  * int main() {
10  * 	auto Bot = TelegramBot("token");
11  * 	while(true) {
12  * 		foreach(update; Bot.pollUpdates) {
13  * 			Bot.sendMessage(update.message.chat.id, "Hello, world!");
14  * 		}
15  * 	 }
16  * }
17  * ---
18  *
19  * Examples: See $(LINK2 https://gitlab.com/ohboi/tg.d/tree/master/examples, "examples") directory
20  * Authors: Anton Fediushin, Pavel Chebotarev
21  * Licence: MIT, see LICENCE
22  * Copyright: Copyright for portions of project tg.d are held by Pavel Chebotarev, 2018 as part of project telega (https://github.com/nexor/telega). All other copyright for project tg.d are held by Anton Fediushin, 2018.
23  * See_Also: $(LINK https://gitlab.com/ohboi/tg.d), $(LINK https://core.telegram.org/bots/api)
24  */
25 module tg.d;
26 
27 import core.time;
28 import std.math : isNaN;
29 import std.datetime.systime : SysTime;
30 
31 import vibe.data.json : Json;
32 
33 version(unittest) import fluent.asserts;
34 
35 version(TgD_Verbose)
36 	pragma(msg, "tg.d | Warning! tg.d is compiled in verbose mode, user data can end up in logs. DISABLE THIS IN PRODUCTION BUILDS");
37 
38 
39 /**
40  * An exception thrown by tg.d on errors
41  */
42 class TelegramBotException : Exception {
43 	/**
44 	 * Telegram Bot API error code
45 	 *
46 	 * Doesn't mean anything useful, meaning may change in the future
47 	 * See_Also: $(LINK https://core.telegram.org/bots/api#making-requests)
48 	 */
49 	ushort code;
50 
51 	/// Constructor
52 	this(ushort code, string description, string file = __FILE__,
53 			size_t line = __LINE__, Throwable next = null) @nogc @safe pure nothrow {
54 		this.code = code;
55 		super(description, file, line, next);
56 	}
57 }
58 
59 /**
60  * Main structure representing one bot
61  *
62  * Every method has an overload which takes `<MethodName>Method` structure which can be used to
63  * pass arguments that were recently added or that are rarely used
64  * and thus doesn't have an appropriate overload.
65  */
66 struct TelegramBot {
67 @trusted:
68 	private {
69 		string m_url;
70 
71 		struct MethodResult(T) {
72 			bool ok;
73 
74 		@optional:
75 			T result;
76 			ushort error_code;
77 			string description;
78 		}
79 
80 		version(unittest) Json delegate(string, Json) @safe m_fakecall;
81 	}
82 	/**
83 	 * Create a new bot using token
84 	 *
85 	 * To obtain a new token ask $(LINK2 https://core.telegram.org/bots#botfather, BotFather).
86 	 * Params:
87 	 *     token = Telegram bot token
88 	 */
89 	this(string token) {
90 		this.m_url = "https://api.telegram.org/bot" ~ token;
91 	}
92 
93 	version(unittest) {
94 		this(string token, Json delegate(string, Json) @safe fakecall) {
95 			this.m_url = "https://api.telegram.org/bot" ~ token;
96 			m_fakecall = fakecall;
97 		}
98 
99 		private T callMethod(T, M)(M method) {
100 			auto json = m_fakecall(m_url ~ method.m_path, method.serializeToJson).deserializeJson!(MethodResult!T);
101 
102 			if(!json.ok)
103 				throw new TelegramBotException(json.error_code, json.description);
104 
105 			return json.result;
106 		}
107 	} else {
108 		private T callMethod(T, M)(M method) {
109 			import vibe.core.log : logDebugV;
110 			import vibe.http.client : requestHTTP, HTTPMethod;
111 
112 			T result;
113 
114 			debug "tg.d | Requesting %s".logDebugV(method.m_path);
115 
116 			requestHTTP(m_url ~ method.m_path,
117 				(scope req) {
118 					req.method = HTTPMethod.POST;
119 
120 					Json j = method.serializeToJson;
121 
122 					debug version(TgD_Verbose) "tg.d | Sending body: %s".logDebugV(j);
123 					req.writeJsonBody(j);
124 				},
125 				(scope res) {
126 					auto answer = res.readJson;
127 					debug version(TgD_Verbose) "tg.d | Response data: %s".logDebugV(answer);
128 
129 					auto json = answer.deserializeJson!(MethodResult!T);
130 
131 					if(!json.ok)
132 						throw new TelegramBotException(json.error_code, json.description);
133 
134 					result = json.result;
135 				}
136 			);
137 
138 			return result;
139 		}
140 	}
141 
142 	/**
143 	 * Receive incoming updates using long polling
144 	 *
145 	 * Params:
146 	 *     offset          = Identifier of the first update to be returned
147 	 *     limit           = Limits the number of updates to be retrieved
148 	 *     timeout         = Timeout for long polling
149 	 *                       Should be positive, short polling (timeout == 0) should be used for testing purposes only.
150 	 *     allowed_updates = List the types of updates you want your bot to receive
151 	 * Returns: An array of updates
152 	 * Throws: `TelegramBotException` on errors
153 	 * See_Also: `GetUpdatesMethod`, $(LINK https://core.telegram.org/bots/api#getupdates)
154 	 */
155 	Update[] getUpdates(int offset = 0, int limit = 100, Duration timeout = 3.seconds, string[] allowed_updates = []) {
156 		GetUpdatesMethod m = {
157 			offset: offset,
158 			limit: limit,
159 			timeout: timeout,
160 			allowed_updates: allowed_updates,
161 		};
162 
163 		return getUpdates(m);
164 	}
165 
166 	/// ditto
167 	Update[] getUpdates(GetUpdatesMethod m) {
168 		return callMethod!(Update[])(m);
169 	}
170 
171 	@("getUpdates()")
172 	unittest {
173 		TelegramBot(
174 			"TOKEN",
175 			(string url, Json data) @trusted {
176 				url.should.be.equal("https://api.telegram.org/botTOKEN/getUpdates");
177 				data.should.be.equal(
178 					Json([
179 						"limit": Json(100),
180 						"timeout": Json(3),
181 					])
182 				);
183 
184 				return Json([
185 					"ok": Json(true),
186 					"result": Json.emptyArray,
187 				]);
188 			}
189 		).getUpdates.length.should.be.equal(0);
190 	}
191 
192 	/**
193 	 * Range-based interface for `getUpdates`
194 	 *
195 	 * This is a preferred way to receive updates because it lazily adjusts `offset` and calls `getUpdates`
196 	 * when necessary, allowing you to get more than a 100 updates in a single call.
197 	 * Params:
198 	 *     timeout         = Timeout in seconds for long polling
199 	 *     allowed_updates = List the types of updates you want your bot to receive
200 	 * Returns: An InputRange of `Update`
201 	 * Throws: `TelegramBotException` on errors
202 	 * See_Also: `getUpdates`
203 	 */
204 	auto pollUpdates(Duration timeout = 3.seconds, string[] allowed_updates = []) {
205 		struct pollUpdatesImpl {
206 		@safe:
207 			private {
208 				TelegramBot m_bot;
209 				Update[] m_buffer;
210 				size_t m_index;
211 				bool m_empty;
212 
213 				Duration m_timeout;
214 				string[] m_allowed_updates;
215 			}
216 
217 			this(TelegramBot bot, Duration timeout, string[] allowed_updates) {
218 				m_bot = bot;
219 				m_buffer.reserve = 100;
220 
221 				m_timeout = timeout;
222 				m_allowed_updates = allowed_updates;
223 				this.popFront;
224 			}
225 
226 			auto front() { return m_buffer[m_index]; }
227 			bool empty() { return m_empty; }
228 			void popFront() {
229 				if(m_buffer.length > ++m_index) {
230 					return;
231 				} else {
232 					m_buffer = m_bot.getUpdates(
233 						m_buffer.length ? m_buffer[$-1].update_id+1 : 0,
234 						100, // Limit
235 						m_timeout,
236 						m_allowed_updates,
237 					);
238 					m_index = 0;
239 
240 					if(!m_buffer.length)
241 						m_empty = true;
242 				}
243 			}
244 		}
245 
246 
247 		return pollUpdatesImpl(this, timeout, allowed_updates);
248 	}
249 
250 	@("pollUpdates() returns valid input range")
251 	unittest {
252 		import std.range : ElementType, isInputRange;
253 		import std.traits: ReturnType;
254 		isInputRange!(ReturnType!(TelegramBot.pollUpdates)).should.be.equal(true);
255 		is(ElementType!(ReturnType!(TelegramBot.pollUpdates)) == Update).should.be.equal(true);
256 	}
257 
258 	@("pollUpdates()")
259 	unittest {
260 		import std.range : generate, take, drop;
261 		import std.array : array;
262 		import std.algorithm : map;
263 		int requestNo;
264 
265 		int updateNo;
266 		auto updates = generate!(() => Update(++updateNo)).take(400).array;
267 
268 		auto fake = (string url, Json data) @trusted {
269 			url.should.be.equal("https://api.telegram.org/botTOKEN/getUpdates");
270 
271 			auto j = Json([
272 					"limit": Json(100),
273 					"timeout": Json(3),
274 			]);
275 
276 			if(requestNo)
277 				j["offset"] = Json((requestNo * 100) + 1);
278 
279 			data.should.be.equal(j);
280 
281 			if(requestNo++ > 3)
282 				return Json([
283 					"ok": Json(true),
284 					"result": Json.emptyArray,
285 				]);
286 
287 			return Json([
288 				"ok": Json(true),
289 				"result": updates.drop((requestNo - 1) * 100).take(100).serializeToJson
290 			]);
291 		};
292 
293 		TelegramBot("TOKEN", fake).pollUpdates()
294 			.map!(a => a.update_id).array.should.be.equal(updates.map!(a => a.update_id).array);
295 	}
296 
297 	/**
298 	 * Set webhook to be used to receive incoming updates
299 	 *
300 	 * Params:
301 	 *     url          = HTTPS url to send updates to. Use an empty string to remove webhook integration
302 	 *     allowed_updates = List the types of updates you want your bot to receive
303 	 *     max_connections = Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100
304 	 * Returns: `true` on success
305 	 * Throws: `TelegramBotException` on errors
306 	 * Deprecated: Webhooks aren't fully implemented,
307 	 * see an $(LINK2 https://gitlab.com/ohboi/tg.d/issues/4, issue) for more info
308 	 * See_Also: `SetWebhookMethod`, $(LINK https://core.telegram.org/bots/api#setwebhook)
309 	 */
310 	deprecated("Webhooks aren't fully implemented yet")
311 	bool setWebhook(string url, string[] allowed_updates = [], int max_connections = 40) {
312 		SetWebhookMethod m = {
313 			url: url,
314 			allowed_updates: allowed_updates,
315 			max_connections: max_connections,
316 		};
317 
318 		return callMethod!bool(m);
319 	}
320 
321 	/**
322 	 * Delete webhook integration
323 	 *
324 	 * Returns: `true` on success
325 	 * Throws: `TelegramBotException` on errors
326 	 * Deprecated: Webhooks aren't fully implemented,
327 	 * see an $(LINK2 https://gitlab.com/ohboi/tg.d/issues/4, issue) for more info
328 	 * See_Also: $(LINK https://core.telegram.org/bots/api#deletewebhook)
329 	 */
330 	deprecated("Webhooks aren't fully implemented yet")
331 	bool deleteWebhook() {
332 		return callMethod!bool(DeleteWebhookMethod());
333 	}
334 
335 	/**
336 	 * Get current webhook status
337 	 *
338 	 * Throws: `TelegramBotException` on errors
339 	 * Deprecated: Webhooks aren't fully implemented,
340 	 * see an $(LINK2 https://gitlab.com/ohboi/tg.d/issues/4, issue) for more info
341 	 * See_Also: $(LINK https://core.telegram.org/bots/api#getwebhookinfo)
342 	 */
343 	deprecated("Webhooks aren't fully implemented yet")
344 	WebhookInfo getWebhookInfo() {
345 		return callMethod!WebhookInfo(GetWebhookInfoMethod());
346 	}
347 
348 	/**
349 	 * Get current bot info
350 	 *
351 	 * Returns: Basic information about the bot in a `User` structure
352 	 * Throws: `TelegramBotException` on errors
353 	 * See_Also: $(LINK https://core.telegram.org/bots/api#getme)
354 	 */
355 	User getMe() {
356 		return callMethod!User(GetMeMethod());
357 	}
358 
359 	@("getMe()")
360 	unittest {
361 		TelegramBot(
362 			"TOKEN",
363 			(string url, Json data) @trusted {
364 				url.should.be.equal("https://api.telegram.org/botTOKEN/getMe");
365 				data.should.be.equal(Json.emptyObject);
366 
367 				return Json([
368 					"ok": Json(true),
369 					"result": Json([
370 						"id": Json(42),
371 						"is_bot": Json(true),
372 						"first_name": Json("John"),
373 					])
374 				]);
375 			}
376 		).getMe.should.be.equal(
377 			User(42, true, "John")
378 		);
379 
380 		TelegramBot(
381 			"TOKEN",
382 			(string url, Json data) @trusted {
383 				url.should.be.equal("https://api.telegram.org/botTOKEN/getMe");
384 				data.should.be.equal(Json.emptyObject);
385 
386 				return Json([
387 					"ok": Json(true),
388 					"result": Json([
389 						"id": Json(42),
390 						"is_bot": Json(false),
391 						"first_name": Json("John"),
392 						"last_name": Json("Smith"),
393 						"username": Json("js"),
394 						"language_code": Json("en-GB")
395 					])
396 				]);
397 			}
398 		).getMe.should.be.equal(
399 			User(42, false, "John", "Smith", "js", "en-GB")
400 		);
401 	}
402 
403 	/**
404 	 * Send text message
405 	 *
406 	 * Params:
407 	 *     chat_id  = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
408 	 *     reply_to = If the message is a reply, ID of the original message
409 	 *     text     = Text to be sent
410 	 * Returns: Sent `Message`
411 	 * Throws: `TelegramBotException` on errors
412 	 * See_Also: `SendMessageMethod`, $(LINK https://core.telegram.org/bots/api#sendmessage)
413 	 */
414 	Message sendMessage(T)(T chat_id, string text) if(isTelegramID!T) {
415 		SendMessageMethod m = {
416 			text: text,
417 			chat_id: chat_id,
418 		};
419 
420 		return sendMessage(m);
421 	}
422 	/// ditto
423 	Message sendMessage(T)(T chat_id, int reply_to, string text) if(isTelegramID!T) {
424 		SendMessageMethod m = {
425 			text: text,
426 			chat_id: chat_id,
427 			reply_to_message_id: reply_to,
428 		};
429 
430 		return sendMessage(m);
431 	}
432 	/// ditto
433 	Message sendMessage(SendMessageMethod m) {
434 		return callMethod!Message(m);
435 	}
436 
437 	@("sendMessage()")
438 	unittest {
439 		TelegramBot(
440 			"TOKEN",
441 			(string url, Json data) @trusted {
442 				url.should.be.equal("https://api.telegram.org/botTOKEN/sendMessage");
443 				data.should.be.equal(
444 					Json([
445 						"chat_id": Json(42),
446 						"text": Json("text"),
447 					])
448 				);
449 
450 				return Json([
451 					"ok": Json(true),
452 					"result": Message().serializeToJson,
453 				]);
454 			}
455 		).sendMessage(42L, "text").isNull.should.be.equal(true);
456 
457 		TelegramBot(
458 			"TOKEN",
459 			(string url, Json data) @trusted {
460 				url.should.be.equal("https://api.telegram.org/botTOKEN/sendMessage");
461 				data.should.be.equal(
462 					Json([
463 						"chat_id": Json("@superchat"),
464 						"text": Json("text"),
465 						"reply_to_message_id": Json(123),
466 					])
467 				);
468 
469 				return Json([
470 					"ok": Json(true),
471 					"result": Message().serializeToJson,
472 				]);
473 			}
474 		).sendMessage("@superchat", 123, "text").isNull.should.be.equal(true);
475 	}
476 
477 	/**
478 	 * Forward message from `from_chat_id` to `chat_id`
479 	 *
480 	 * Params:
481 	 *     chat_id      = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
482 	 *     from_chat_id = Unique identifier for the chat where the original message was sent (or channel username in the format `@channelusername`)
483 	 *     message_id   = ID of the original message
484 	 * Returns: Sent `Message`
485 	 * Throws: `TelegramBotException` on errors
486 	 * See_Also: `ForwardMessageMethod`, $(LINK https://core.telegram.org/bots/api#forwardmessage)
487 	 */
488 	Message forwardMessage(T1, T2)(T1 chat_id, T2 from_chat_id, int message_id)
489 	if(isTelegramID!T1 && isTelegramID!T2){
490 		ForwardMessageMethod m = {
491 			message_id: message_id,
492 			chat_id: chat_id,
493 			from_chat_id: from_chat_id,
494 		};
495 
496 		return callMethod!Message(m);
497 	}
498 	/// ditto
499 	Message forwardMessage(ForwardMessageMethod m) {
500 		return callMethod!Message(m);
501 	}
502 
503 	@("forwardMessage()")
504 	unittest {
505 		TelegramBot(
506 			"TOKEN",
507 			(string url, Json data) @trusted {
508 				url.should.be.equal("https://api.telegram.org/botTOKEN/forwardMessage");
509 				data.should.be.equal(
510 					Json([
511 						"chat_id": Json(42),
512 						"from_chat_id": Json(43),
513 						"message_id": Json(1337),
514 					])
515 				);
516 
517 				return Json([
518 					"ok": Json(true),
519 					"result": Message().serializeToJson,
520 				]);
521 			}
522 		).forwardMessage(42L, 43L, 1337).isNull.should.be.equal(true);
523 	}
524 
525 	/**
526 	 * Send photo
527 	 *
528 	 * Params:
529 	 *     chat_id = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
530 	 *     photo   = HTTP URL to get photo from the internet or `file_id` of the file on Telegram
531 	 * Returns: Sent `Message`
532 	 * Throws: `TelegramBotException` on errors
533 	 * See_Also: `SendPhotoMethod`, $(LINK https://core.telegram.org/bots/api#sendphoto)
534 	 */
535 	Message sendPhoto(T)(T chat_id, string photo) if(isTelegramID!T) {
536 		SendPhotoMethod m = {
537 			photo: photo,
538 			chat_id: chat_id,
539 		};
540 
541 		return sendPhoto(m);
542 	}
543 	/// ditto
544 	Message sendPhoto(SendPhotoMethod m) {
545 		return callMethod!Message(m);
546 	}
547 
548 	@("sendPhoto()")
549 	unittest {
550 		TelegramBot(
551 			"TOKEN",
552 			(string url, Json data) @trusted {
553 				url.should.be.equal("https://api.telegram.org/botTOKEN/sendPhoto");
554 				data.should.be.equal(
555 					Json([
556 						"chat_id": Json(42),
557 						"photo": Json("https://example.com/dogs.jpg"),
558 					]),
559 				);
560 
561 				return Json([
562 					"ok": Json(true),
563 					"result": Message().serializeToJson,
564 				]);
565 			}
566 		).sendPhoto(42L, "https://example.com/dogs.jpg").isNull.should.be.equal(true);
567 	}
568 
569 	/**
570 	 * Send audio
571 	 *
572 	 * Audio must be in mp3 format
573 	 *
574 	 * Params:
575 	 *     chat_id = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
576 	 *     audio   = HTTP URL to get audio from the internet or `file_id` of the file on Telegram
577 	 * Returns: Sent `Message`
578 	 * Throws: `TelegramBotException` on errors
579 	 * See_Also: `SendAudioMethod`, $(LINK https://core.telegram.org/bots/api#sendaudio)
580 	 */
581 	Message sendAudio(T)(T chat_id, string audio) if(isTelegramID!T) {
582 		SendAudioMethod m = {
583 			audio: audio,
584 			chat_id: chat_id,
585 		};
586 
587 		return sendAudio(m);
588 	}
589 	/// ditto
590 	Message sendAudio(SendAudioMethod m) {
591 		return callMethod!Message(m);
592 	}
593 
594 	@("sendAudio()")
595 	unittest {
596 		TelegramBot(
597 			"TOKEN",
598 			(string url, Json data) @trusted {
599 				url.should.be.equal("https://api.telegram.org/botTOKEN/sendAudio");
600 				data.should.be.equal(
601 					Json([
602 						"chat_id": Json(42),
603 						"audio": Json("https://example.com/woof.mp3"),
604 					]),
605 				);
606 
607 				return Json([
608 					"ok": Json(true),
609 					"result": Message().serializeToJson,
610 				]);
611 			}
612 		).sendAudio(42L, "https://example.com/woof.mp3").isNull.should.be.equal(true);
613 	}
614 
615 	/**
616 	 * Send file
617 	 *
618 	 * Params:
619 	 *     chat_id  = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
620 	 *     document = HTTP URL to get document from the internet or `file_id` of the file on Telegram
621 	 * Returns: Sent `Message`
622 	 * Throws: `TelegramBotException` on errors
623 	 * See_Also: `SendDocumentMethod`, $(LINK https://core.telegram.org/bots/api#senddocument)
624 	 */
625 	Message sendDocument(T)(T chat_id, string document) if(isTelegramID!T) {
626 		SendDocumentMethod m = {
627 			document: document,
628 			chat_id: chat_id,
629 		};
630 
631 		return sendDocument(m);
632 	}
633 	/// ditto
634 	Message sendDocument(SendDocumentMethod m) {
635 		return callMethod!Message(m);
636 	}
637 
638 	@("sendDocument()")
639 	unittest {
640 		TelegramBot(
641 			"TOKEN",
642 			(string url, Json data) @trusted {
643 				url.should.be.equal("https://api.telegram.org/botTOKEN/sendDocument");
644 				data.should.be.equal(
645 					Json([
646 						"chat_id": Json(42),
647 						"document": Json("https://example.com/document.pdf"),
648 					]),
649 				);
650 
651 				return Json([
652 					"ok": Json(true),
653 					"result": Message().serializeToJson,
654 				]);
655 			}
656 		).sendDocument(42L, "https://example.com/document.pdf").isNull.should.be.equal(true);
657 	}
658 
659 	/**
660 	 * Send video
661 	 *
662 	 * Video must be in mp4 format
663 	 *
664 	 * Params:
665 	 *     chat_id = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
666 	 *     video   = HTTP URL to get video from the internet or `file_id` of the file on Telegram
667 	 * Returns: Sent `Message`
668 	 * Throws: `TelegramBotException` on errors
669 	 * See_Also: `SendVideoMethod`, $(LINK https://core.telegram.org/bots/api#sendvideo)
670 	 */
671 	Message sendVideo(T)(T chat_id, string video) if(isTelegramID!T) {
672 		SendVideoMethod m = {
673 			video: video,
674 			chat_id: chat_id,
675 		};
676 
677 		return sendVideo(m);
678 	}
679 	/// ditto
680 	Message sendVideo(SendVideoMethod m) {
681 		return callMethod!Message(m);
682 	}
683 
684 	@("sendVideo()")
685 	unittest {
686 		TelegramBot(
687 			"TOKEN",
688 			(string url, Json data) @trusted {
689 				url.should.be.equal("https://api.telegram.org/botTOKEN/sendVideo");
690 				data.should.be.equal(
691 					Json([
692 						"chat_id": Json(42),
693 						"video": Json("https://example.com/video.mp4"),
694 					]),
695 				);
696 
697 				return Json([
698 					"ok": Json(true),
699 					"result": Message().serializeToJson,
700 				]);
701 			}
702 		).sendVideo(42L, "https://example.com/video.mp4").isNull.should.be.equal(true);
703 	}
704 
705 	/**
706 	 * Send animation
707 	 *
708 	 * Animation must be in GIF format or H.264/MPEG-4 AVC video without sound 
709 	 *
710 	 * Params:
711 	 *     chat_id   = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
712 	 *     animation = HTTP URL to get animation from the internet or `file_id` of the file on Telegram
713 	 * Returns: Sent `Message`
714 	 * Throws: `TelegramBotException` on errors
715 	 * See_Also: `SendAnimationMethod`, $(LINK https://core.telegram.org/bots/api#sendanimation)
716 	 */
717 	Message sendAnimation(T)(T chat_id, string animation) if(isTelegramID!T) {
718 		SendAnimationMethod m = {
719 			animation: animation,
720 			chat_id: chat_id,
721 		};
722 
723 		return sendAnimation(m);
724 	}
725 	/// ditto
726 	Message sendAnimation(SendAnimationMethod m) {
727 		return callMethod!Message(m);
728 	}
729 
730 	@("sendAnimation()")
731 	unittest {
732 		TelegramBot(
733 			"TOKEN",
734 			(string url, Json data) @trusted {
735 				url.should.be.equal("https://api.telegram.org/botTOKEN/sendAnimation");
736 				data.should.be.equal(
737 					Json([
738 						"chat_id": Json(42),
739 						"animation": Json("https://example.com/me.gif"),
740 					]),
741 				);
742 
743 				return Json([
744 					"ok": Json(true),
745 					"result": Message().serializeToJson,
746 				]);
747 			}
748 		).sendAnimation(42L, "https://example.com/me.gif").isNull.should.be.equal(true);
749 	}
750 
751 	/**
752 	 * Send voice message
753 	 *
754 	 * Voice message must be in ogg format encoded with OPUS
755 	 *
756 	 * Params:
757 	 *     chat_id = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
758 	 *     voice   = HTTP URL to get audio from the internet or `file_id` of the file on Telegram
759 	 * Returns: Sent `Message`
760 	 * Throws: `TelegramBotException` on errors
761 	 * See_Also: `SendVoiceMethod`, $(LINK https://core.telegram.org/bots/api#sendvoice)
762 	 */
763 	Message sendVoice(T)(T chat_id, string voice) if(isTelegramID!T) {
764 		SendVoiceMethod m = {
765 			voice: voice,
766 			chat_id: chat_id,
767 		};
768 
769 		return sendVoice(m);
770 	}
771 	/// ditto
772 	Message sendVoice(SendVoiceMethod m) {
773 		return callMethod!Message(m);
774 	}
775 
776 	@("sendVoice()")
777 	unittest {
778 		TelegramBot(
779 			"TOKEN",
780 			(string url, Json data) @trusted {
781 				url.should.be.equal("https://api.telegram.org/botTOKEN/sendVoice");
782 				data.should.be.equal(
783 					Json([
784 						"chat_id": Json(42),
785 						"voice": Json("https://example.com/voice.ogg"),
786 					]),
787 				);
788 
789 				return Json([
790 					"ok": Json(true),
791 					"result": Message().serializeToJson,
792 				]);
793 			}
794 		).sendVoice(42L, "https://example.com/voice.ogg").isNull.should.be.equal(true);
795 	}
796 
797 	/**
798 	 * Send video message
799 	 *
800 	 * Video must be square and shoudln't be longer than 1 minute
801 	 *
802 	 * Params:
803 	 *     chat_id    = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
804 	 *     video_note = HTTP URL to get video from the internet or `file_id` of the file on Telegram
805 	 * Returns: Sent `Message`
806 	 * Throws: `TelegramBotException` on errors
807 	 * See_Also: `SendVideoNoteMethod`, $(LINK https://core.telegram.org/bots/api#sendvideonote)
808 	 */
809 	Message sendVideoNote(T)(T chat_id, string video_note) if(isTelegramID!T) {
810 		SendVideoNoteMethod m = {
811 			video_note: video_note,
812 			chat_id: chat_id,
813 		};
814 
815 		return sendVideoNote(m);
816 	}
817 	/// ditto
818 	Message sendVideoNote(SendVideoNoteMethod m) {
819 		return callMethod!Message(m);
820 	}
821 
822 	/**
823 	 * Send group of photos or videos as an album
824 	 *
825 	 * Params:
826 	 *     chat_id = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
827 	 *     media   = Photos and videos to be sent
828 	 * Returns: Sent `Message`
829 	 * Throws: `TelegramBotException` on errors
830 	 * See_Also: `SendMediaGroupMethod`, $(LINK https://core.telegram.org/bots/api#sendmediagroup)
831 	 */
832 	Message sendMediaGroup(T)(T chat_id, Algebraic!(InputMediaPhoto, InputMediaVideo)[] media)
833 	if(isTelegramID!T)
834 	in(2 <= media.length && media.length <= 10) {
835 		SendMediaGroupMethod m = {
836 			media: media,
837 			chat_id: chat_id,
838 		};
839 
840 		return sendMediaGroup(m);
841 	}
842 	/// ditto
843 	Message sendMediaGroup(SendMediaGroupMethod m) {
844 		return callMethod!Message(m);
845 	}
846 
847 	/**
848 	 * Send point on the map
849 	 *
850 	 * Params:
851 	 *     chat_id   = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
852 	 *     latitude  = Latitude of the location
853 	 *     longitude = Longitude of the location
854 	 * Returns: Sent `Message`
855 	 * Throws: `TelegramBotException` on errors
856 	 * See_Also: `SendLocationMethod`, $(LINK https://core.telegram.org/bots/api#sendlocation)
857 	 */
858 	Message sendLocation(T)(T chat_id, float latitude, float longitude) if(isTelegramID!T) {
859 		SendLocationMethod m = {
860 			latitude: latitude,
861 			longitude: longitude,
862 			chat_id: chat_id,
863 		};
864 
865 		return sendLocation(m);
866 	}
867 	/// ditto
868 	Message sendLocation(SendLocationMethod m) {
869 		return callMethod!Message(m);
870 	}
871 
872 	/**
873 	 * Edit live location message
874 	 *
875 	 * Overloads take either `chat_id` and `message_id` or `inline_message_id`
876 	 *
877 	 * Params:
878 	 *     chat_id           = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
879 	 *     message_id        = ID of the message to edit
880 	 *     inline_message_id = ID of the inline message
881 	 *     latitude          = Latitude of new location
882 	 *     longitude         = longitude of new location
883 	 * Returns: Edited `Message`
884 	 * Throws: `TelegramBotException` on errors
885 	 * See_Also: `EditMessageLiveLocationMethod`, $(LINK https://core.telegram.org/bots/api#editmessagelivelocation)
886 	 */
887 	 Message editMessageLiveLocation(T)(T chat_id, int message_id, float latitude, float longitude)
888 	if(isTelegramID!T) {
889 		EditMessageLiveLocationMethod m = {
890 			message_id: message_id,
891 			latitude: latitude,
892 			longitude: longitude,
893 			chat_id: chat_id,
894 		};
895 
896 		return editMessageLiveLocation(m);
897 	}
898 	/// ditto
899 	Message editMessageLiveLocation(string inline_message_id, float latitude, float longitude) {
900 		EditMessageLiveLocationMethod m = {
901 			inline_message_id: inline_message_id,
902 			latitude : latitude,
903 			longitude : longitude,
904 		};
905 
906 		return editMessageLiveLocation(m);
907 	}
908 	/// ditto
909 	Message editMessageLiveLocation(EditMessageLiveLocationMethod m) {
910 		return callMethod!Message(m);
911 	}
912 
913 	/**
914 	 * Stop updating a live location message
915 	 *
916 	 * Overloads take either `chat_id` and `message_id` or `inline_message_id`
917 	 *
918 	 * Params:
919 	 *     chat_id           = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
920 	 *     message_id        = ID of the message to edit
921 	 *     inline_message_id = ID of the inline message
922 	 * Returns: Edited `Message`
923 	 * Throws: `TelegramBotException` on errors
924 	 * See_Also: `StopMessageLiveLocation`, $(LINK https://core.telegram.org/bots/api#stopmessagelivelocation)
925 	 */
926 	Message stopMessageLiveLocation(T)(T chat_id, int message_id) if(isTelegramID!T) {
927 		StopMessageLiveLocationMethod m = {
928 			message_id: message_id,
929 			chat_id: chat_id,
930 		};
931 
932 		return stopMessageLiveLocation(m);
933 	}
934 	/// ditto
935 	Message stopMessageLiveLocation(string inline_message_id) {
936 		StopMessageLiveLocationMethod m = {
937 			inline_message_id: inline_message_id,
938 		};
939 
940 		return stopMessageLiveLocation(m);
941 	}
942 	/// ditto
943 	Message stopMessageLiveLocation(StopMessageLiveLocationMethod m) {
944 		return callMethod!Message(m);
945 	}
946 
947 	/**
948 	 * Send information about a venue
949 	 *
950 	 * Params:
951 	 *     chat_id   = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
952 	 *     latitude  = Latitude of the venue
953 	 *     longitude = Longitude of the venue
954 	 *     title     = Name of the venue
955 	 *     address   = Address of the venue
956 	 * Returns: Sent `Message`
957 	 * Throws: `TelegramBotException` on errors
958 	 * See_Also: `SendVenueMethod`, $(LINK https://core.telegram.org/bots/api#sendvenue)
959 	 */
960 	Message sendVenue(T)(T chat_id, float latitude, float longitude, string title, string address)
961 	if(isTelegramID!T) {
962 		SendVenueMethod m = {
963 			latitude: latitude,
964 			longitude : longitude,
965 			title : title,
966 			address : address,
967 			chat_id: chat_id,
968 		};
969 
970 		return sendVenue(m);
971 	}
972 	/// ditto
973 	Message sendVenue(SendVenueMethod m) {
974 		return callMethod!Message(m);
975 	}
976 
977 	/**
978 	 * Send phone contact
979 	 *
980 	 * Params:
981 	 *     chat_id      = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
982 	 *     phone_number = Contact's phone number
983 	 *     first_name   = Contact's first name
984 	 *     last_name    = Contact's last name
985 	 * Returns: Sent `Message`
986 	 * Throws: `TelegramBotException` on errors
987 	 * See_Also: `SendContactMethod`, $(LINK https://core.telegram.org/bots/api#sendcontact)
988 	 */
989 	Message sendContact(T)(T chat_id, string phone_number, string first_name, string last_name = "")
990 	if(isTelegramID!T) {
991 		SendContactMethod m = {
992 			phone_number: phone_number,
993 			first_name : first_name,
994 			last_name: last_name,
995 			chat_id: chat_id,
996 		};
997 
998 		return sendContact(m);
999 	}
1000 	/// ditto
1001 	Message sendContact(SendContactMethod m) {
1002 		return callMethod!Message(m);
1003 	}
1004 
1005 	/**
1006 	 * Send chat action
1007 	 *
1008 	 * Params:
1009 	 *     chat_id = Unique identifier for the target chat or username of the target channel  (in the format `@channelusername`)
1010 	 *     action  = Type of action, (typing, upload_photo, record_video, etc)
1011 	 * Returns: `true` on success
1012 	 * Throws: `TelegramBotException` on errors
1013 	 * See_Also: `SendChatActionMethod`, $(LINK https://core.telegram.org/bots/api#sendchataction)
1014 	 */
1015 	bool sendChatAction(T)(T chat_id, ChatAction action) if(isTelegramID!T) {
1016 		SendChatActionMethod m = {
1017 			action: action,
1018 			chat_id: chat_id,
1019 		};
1020 
1021 		return sendChatAction(m);
1022 	}
1023 	/// ditto
1024 	bool sendChatAction(SendChatActionMethod m) {
1025 		return callMethod!bool(m);
1026 	}
1027 
1028 	/**
1029 	 * Get a list of profile pictures for specified user
1030 	 *
1031 	 * Params:
1032 	 *     user_id = Unique identifier of the target user
1033 	 * Returns: `UserProfilePhotos` struct
1034 	 * Throws: `TelegramBotException` on errors
1035 	 * See_Also: `GetUserProfilePhotosMethod`, $(LINK https://core.telegram.org/bots/api#getuserprofilephotos)
1036 	 */
1037 	UserProfilePhotos getUserProfilePhotos(int user_id) {
1038 		GetUserProfilePhotosMethod m = {
1039 			user_id: user_id,
1040 		};
1041 
1042 		return getUserProfilePhotos(m);
1043 	}
1044 	/// ditto
1045 	UserProfilePhotos getUserProfilePhotos(GetUserProfilePhotosMethod m) {
1046 		return callMethod!UserProfilePhotos(m);
1047 	}
1048 
1049 	/**
1050 	 * Get info about a file and prepare it for downloading
1051 	 *
1052 	 * Params:
1053 	 *     file_id      = File identifier to get info about
1054 	 * Returns: `File` on success
1055 	 * Throws: `TelegramBotException` on errors
1056 	 * See_Also: `GetFileMethod`, $(LINK https://core.telegram.org/bots/api#getfile)
1057 	 */
1058 	File getFile(string file_id) {
1059 		GetFileMethod m = {
1060 			file_id: file_id,
1061 		};
1062 
1063 		return getFile(m);
1064 	}
1065 	/// ditto
1066 	File getFile(GetFileMethod m) {
1067 		return callMethod!File(m);
1068 	}
1069 
1070 	/**
1071 	 * Kick a user from a group, a supergroup or a channel
1072 	 *
1073 	 * Params:
1074 	 *     chat_id = Unique identifier for the target group or username of the target supergroup or channel (in the format `@channelusername`)
1075 	 *     user_id = Unique identifier of the target user
1076 	 * Returns: `true` on success
1077 	 * Throws: `TelegramBotException` on errors
1078 	 * See_Also: `KickChatMemberMethod`, $(LINK https://core.telegram.org/bots/api#kickchatmember)
1079 	 */
1080 	bool kickChatMember(T)(T chat_id, int user_id) if(isTelegramID!T) {
1081 		KickChatMemberMethod m = {
1082 			user_id: user_id,
1083 			chat_id: chat_id,
1084 		};
1085 
1086 		return kickChatMember(m);
1087 	}
1088 	/// ditto
1089 	bool kickChatMember(KickChatMemberMethod m) {
1090 		return callMethod!bool(m);
1091 	}
1092 
1093 	/**
1094 	 * Unban a previously kicked user in a group, a supergroup or a channel
1095 	 *
1096 	 * Params:
1097 	 *     chat_id = Unique identifier for the target group or username of the target supergroup or channel (in the format `@username`)
1098 	 *     user_id = Unique identifier of the target user
1099 	 * Returns: `true` on success
1100 	 * Throws: `TelegramBotException` on errors
1101 	 * See_Also: `UnbanChatMemberMethod`, $(LINK https://core.telegram.org/bots/api#unbanchatmember)
1102 	 */
1103 	bool unbanChatMember(T)(T chat_id, int user_id) if(isTelegramID!T) {
1104 		UnbanChatMemberMethod m = {
1105 			user_id: user_id,
1106 			chat_id: chat_id,
1107 		};
1108 
1109 		return unbanChatMember(m);
1110 	}
1111 	/// ditto
1112 	bool unbanChatMember(UnbanChatMemberMethod m) {
1113 		return callMethod!bool(m);
1114 	}
1115 
1116 	/**
1117 	 * Restrict a user in a supergroup
1118 	 *
1119 	 * Params:
1120 	 *     chat_id = Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`)
1121 	 *     user_id = Unique identifier of the target user
1122 	 * Returns: Sent `Message`
1123 	 * Throws: `TelegramBotException` on errors
1124 	 * See_Also: `RestrictChatMemberMethod`, $(LINK https://core.telegram.org/bots/api#restrictchatmember)
1125 	 */
1126 	bool restrictChatMember(T)(T chat_id, int user_id) if(isTelegramID!T) {
1127 		RestrictChatMemberMethod m = {
1128 			user_id: user_id,
1129 			chat_id: chat_id,
1130 		};
1131 
1132 		return restrictChatMember(m);
1133 	}
1134 	/// ditto
1135 	bool restrictChatMember(RestrictChatMemberMethod m) {
1136 		return callMethod!bool(m);
1137 	}
1138 
1139 	/**
1140 	 * Promote or demote a user in a supergroup or a channel
1141 	 *
1142 	 * Params:
1143 	 *     chat_id = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1144 	 *     user_id = Unique identifier of the target user
1145 	 * Returns: `true` on success
1146 	 * Throws: `TelegramBotException` on errors
1147 	 * See_Also: `PromoteChatMemberMethod`, $(LINKhttps://core.telegram.org/bots/api#promotechatmember)
1148 	 */
1149 	bool promoteChatMember(T)(T chat_id, int user_id) if(isTelegramID!T) {
1150 		PromoteChatMemberMethod m = {
1151 			user_id: user_id,
1152 			chat_id: chat_id,
1153 		};
1154 
1155 		return promoteChatMember(m);
1156 	}
1157 	/// ditto
1158 	bool promoteChatMember(PromoteChatMemberMethod m) {
1159 		return callMethod!bool(m);
1160 	}
1161 
1162 	/**
1163 	 * Generate a new invite link for a chat
1164 	 *
1165 	 * Any previously generated link is revoked
1166 	 *
1167 	 * Params:
1168 	 *     chat_id      = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1169 	 * Returns: invite link
1170 	 * Throws: `TelegramBotException` on errors
1171 	 * See_Also: `ExportChatInviteLinkMethod`, $(LINK https://core.telegram.org/bots/api#exportchatinvitelink)
1172 	 */
1173 	string exportChatInviteLink(T)(T chat_id) if(isTelegramID!T) {
1174 		ExportChatInviteLinkMethod m = {
1175 			chat_id: chat_id,
1176 		};
1177 
1178 		return exportChatInviteLink(m);
1179 	}
1180 	/// ditto
1181 	string exportChatInviteLink(ExportChatInviteLinkMethod m) {
1182 		return callMethod!string(m);
1183 	}
1184 
1185 	/**
1186 	 * Set a new profile photo for the chat
1187 	 *
1188 	 * Params:
1189 	 *     chat_id = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1190 	 *     photo   = New chat photo
1191 	 * Returns: `true` on success
1192 	 * Throws: `TelegramBotException` on errors
1193 	 * Deprecated: `InputFile` isn't supported yet
1194 	 * See_Also: `SetChatPhotoMethod`, $(LINK https://core.telegram.org/bots/api#setchatphoto)
1195 	 */
1196 	deprecated("InputFile and every method that uses it aren't supported yet")
1197 	bool setChatPhoto(T)(T chat_id, InputFile photo) if(isTelegramID!T) {
1198 		SetChatPhotoMethod m = {
1199 			photo: photo,
1200 			chat_id: chat_id,
1201 		};
1202 
1203 		return setChatPhoto(m);
1204 	}
1205 	/// ditto
1206 	bool setChatPhoto(SetChatPhotoMethod m) {
1207 		return callMethod!bool(m);
1208 	}
1209 
1210 	/**
1211 	 * Delete a chat photo
1212 	 *
1213 	 * Params:
1214 	 *     chat_id = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1215 	 * Returns: `true` on success
1216 	 * Throws: `TelegramBotException` on errors
1217 	 * See_Also: `deleteChatPhoto`, $(LINK https://core.telegram.org/bots/api#deletechatphoto)
1218 	 */
1219 	bool deleteChatPhoto(T)(T chat_id) if(isTelegramID!T) {
1220 		DeleteChatPhotoMethod m = {
1221 			chat_id: chat_id,
1222 		};
1223 
1224 		return deleteChatPhoto(m);
1225 	}
1226 	/// ditto
1227 	bool deleteChatPhoto(DeleteChatPhotoMethod m) {
1228 		return callMethod!bool(m);
1229 	}
1230 
1231 	/**
1232 	 * Change the title of a chat
1233 	 *
1234 	 * Params:
1235 	 *     chat_id = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1236 	 *     title   = New chat title
1237 	 * Returns: `true` on success
1238 	 * Throws: `TelegramBotException` on errors
1239 	 * See_Also: `SetChatTitleMethod`, $(LINK https://core.telegram.org/bots/api#setchattitle)
1240 	 */
1241 	bool setChatTitle(T)(T chat_id, string title)
1242 	if(isTelegramID!T)
1243 	in(1 <= title.length && title.length <= 255) {
1244 		SetChatTitleMethod m = {
1245 			title: title,
1246 			chat_id: chat_id,
1247 		};
1248 
1249 		return setChatTitle(m);
1250 	}
1251 	/// ditto
1252 	bool setChatTitle(SetChatTitleMethod m) {
1253 		return callMethod!bool(m);
1254 	}
1255 
1256 	/**
1257 	 * Change the description of a supergroup or a channel
1258 	 *
1259 	 * Params:
1260 	 *     chat_id     = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1261 	 *     description = New chat description
1262 	 * Returns: Sent `Message`
1263 	 * Throws: `TelegramBotException` on errors
1264 	 * See_Also: `SetChatDescriptionMethod`, $(LINK https://core.telegram.org/bots/api#setchatdescription)
1265 	 */
1266 	bool setChatDescription(T)(T chat_id, string description = "")
1267 	if(isTelegramID!T)
1268 	in(title.length <= 255) {
1269 		SetChatDescriptionMethod m = {
1270 			description: description,
1271 			chat_id: chat_id,
1272 		};
1273 
1274 		return setChatDescription(m);
1275 	}
1276 	/// ditto
1277 	bool setChatDescription(SetChatDescriptionMethod m) {
1278 		return callMethod!bool(m);
1279 	}
1280 
1281 	/**
1282 	 * Pin a message in a supergroup or a channel
1283 	 *
1284 	 * Params:
1285 	 *     chat_id    = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1286 	 *     message_id = Identifier of a message to pin
1287 	 * Returns: `true` on success
1288 	 * Throws: `TelegramBotException` on errors
1289 	 * See_Also: `PinChatMessageMethod`, $(LINK https://core.telegram.org/bots/api#pinchatmessage)
1290 	 */
1291 	bool pinChatMessage(T)(T chat_id, int message_id) if(isTelegramID!T) {
1292 		PinChatMessageMethod m = {
1293 			message_id: message_id,
1294 			chat_id: chat_id,
1295 		};
1296 
1297 		return pinChatMessage(m);
1298 	}
1299 	/// ditto
1300 	bool pinChatMessage(PinChatMessageMethod m) {
1301 		return callMethod!bool(m);
1302 	}
1303 
1304 	/**
1305 	 * Unpin a message in a supergroup or a channel
1306 	 *
1307 	 * Params:
1308 	 *     chat_id = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1309 	 * Returns: `true` on success
1310 	 * Throws: `TelegramBotException` on errors
1311 	 * See_Also: `UnpinChatMessageMethod`, $(LINK https://core.telegram.org/bots/api#unpinchatmessage)
1312 	 */
1313 	bool unpinChatMessage(T)(T chat_id) if(isTelegramID!T) {
1314 		UnpinChatMessageMethod m = {
1315 			chat_id: chat_id,
1316 		};
1317 
1318 		return unpinChatMessage(m);
1319 	}
1320 	/// ditto
1321 	bool unpinChatMessage(UnpinChatMessageMethod m) {
1322 		return callMethod!bool(m);
1323 	}
1324 
1325 	/**
1326 	 * Leave a group, supergroup or channel
1327 	 *
1328 	 * Params:
1329 	 *     chat_id = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1330 	 * Returns: `true` on success
1331 	 * Throws: `TelegramBotException` on errors
1332 	 * See_Also: `LeaveChatMethod`, $(LINK https://core.telegram.org/bots/api#leavechat)
1333 	 */
1334 	bool leaveChat(T)(T chat_id) if(isTelegramID!T) {
1335 		LeaveChatMethod m = {
1336 			chat_id: chat_id,
1337 		};
1338 
1339 		return leaveChat(m);
1340 	}
1341 	/// ditto
1342 	bool leaveChat(LeaveChatMethod m) {
1343 		return callMethod!bool(m);
1344 	}
1345 
1346 	/**
1347 	 * Get up-to-date information about the chat
1348 	 *
1349 	 * Params:
1350 	 *     chat_id = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1351 	 * Returns: `Chat` on success
1352 	 * Throws: `TelegramBotException` on errors
1353 	 * See_Also: `GetChatMethod`, $(LINK https://core.telegram.org/bots/api#getchat)
1354 	 */
1355 	Chat getChat(T)(T chat_id) if(isTelegramID!T) {
1356 		GetChatMethod m = {
1357 			chat_id: chat_id,
1358 		};
1359 
1360 		return getChat(m);
1361 	}
1362 	/// ditto
1363 	Chat getChat(GetChatMethod m) {
1364 		return callMethod!Chat(m);
1365 	}
1366 
1367 	/**
1368 	 * Get a list of administrators in a chat
1369 	 *
1370 	 * Params:
1371 	 *     chat_id = Unique identifier for the target chat or username of the target supergroup or channel (in the format `@channelusername`)
1372 	 * Returns: An array of `ChatMember` on success
1373 	 * Throws: `TelegramBotException` on errors
1374 	 * See_Also: `GetChatAdministatorsMethod`, $(LINK https://core.telegram.org/bots/api#getchatadministrators)
1375 	 */
1376 	ChatMember[] getChatAdministrators(T)(T chat_id) if(isTelegramID!T) {
1377 		GetChatAdministratorsMethod m = {
1378 			chat_id: chat_id,
1379 		};
1380 
1381 		return getChatAdministrators(m);
1382 	}
1383 	/// ditto
1384 	ChatMember[] getChatAdministrators(GetChatAdministratorsMethod m) {
1385 		return callMethod!(ChatMember[])(m);
1386 	}
1387 
1388 	/**
1389 	 * Get the number of members in a chat
1390 	 *
1391 	 * Params:
1392 	 *     chat_id = Unique identifier for the target chat or username of the target supergroup or channel (in the format `@channelusername`)
1393 	 * Returns: number of members
1394 	 * Throws: `TelegramBotException` on errors
1395 	 * See_Also: `GetChatMembersCountMethod`, $(LINK https://core.telegram.org/bots/api#getchatmemberscount)
1396 	 */
1397 	int getChatMembersCount(T)(T chat_id) if(isTelegramID!T) {
1398 		GetChatMembersCountMethod m = {
1399 			chat_id: chat_id,
1400 		};
1401 
1402 		return getChatMembersCount(m);
1403 	}
1404 	/// ditto
1405 	int getChatMembersCount(GetChatMembersCountMethod m) {
1406 		return callMethod!int(m);
1407 	}
1408 
1409 	/**
1410 	 * Get information about a member of a chat
1411 	 *
1412 	 * Params:
1413 	 *     chat_id = Unique identifier for the target chat or username of the target supergroup or channel (in the format `@channelusername`)
1414 	 *     user_id = Unique identifier of the target user
1415 	 * Returns: `ChatMember` on success
1416 	 * Throws: `TelegramBotException` on errors
1417 	 * See_Also: `GetChatMemberMethod`, $(LINK https://core.telegram.org/bots/api#getchatmember)
1418 	 */
1419 	ChatMember getChatMember(T)(T chat_id, int user_id) if(isTelegramID!T) {
1420 		GetChatMemberMethod m = {
1421 			user_id: user_id,
1422 			chat_id: chat_id,
1423 		};
1424 
1425 		return getChatMember(m);
1426 	}
1427 	/// ditto
1428 	ChatMember getChatMember(GetChatMemberMethod m) {
1429 		return callMethod!ChatMember(m);
1430 	}
1431 
1432 	/**
1433 	 * Set a new group sticker set for a supergroup
1434 	 *
1435 	 * Params:
1436 	 *     chat_id = Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`)
1437 	 * Returns: `true` on success
1438 	 * Throws: `TelegramBotException` on errors
1439 	 * See_Also: `SetCharStickerMethod`, $(LINK https://core.telegram.org/bots/api#setchatstickerset)
1440 	 */
1441 	bool setChatStickerSet(T)(T chat_id, string sticker_set_name) if(isTelegramID!T) {
1442 		SetChatStickerSetMethod m = {
1443 			sticker_set_name: sticker_set_name,
1444 			chat_id: chat_id,
1445 		};
1446 
1447 		return setChatStickerSet(m);
1448 	}
1449 	/// ditto
1450 	bool setChatStickerSet(SetChatStickerSetMethod m) {
1451 		return callMethod!bool(m);
1452 	}
1453 
1454 	/**
1455 	 * Delete a group sticker set from a supergroup
1456 	 *
1457 	 * Params:
1458 	 *     chat_id = Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`)
1459 	 * Returns: `true` on success
1460 	 * Throws: `TelegramBotException` on errors
1461 	 * See_Also: `DeleteChatStickerSet`, $(LINK https://core.telegram.org/bots/api#deletechatstickerset)
1462 	 */
1463 	bool deleteChatStickerSet(T)(T chat_id) if(isTelegramID!T) {
1464 		DeleteChatStickerSetMethod m = {
1465 			chat_id: chat_id,
1466 		};
1467 
1468 		return deleteChatStickerSet(m);
1469 	}
1470 	/// ditto
1471 	bool deleteChatStickerSet(DeleteChatStickerSetMethod m) {
1472 		return callMethod!bool(m);
1473 	}
1474 
1475 	/**
1476 	 * Answer to a callback query sent from inline keyboard
1477 	 *
1478 	 * Params:
1479 	 *     callback_query_id = Unique identifier for the query to be answered
1480 	 * Returns: `true` on success
1481 	 * Throws: `TelegramBotException` on errors
1482 	 * See_Also: `AnswerCallbackQueryMethod`, $(LINK https://core.telegram.org/bots/api#answercallbackquery)
1483 	 */
1484 	bool answerCallbackQuery(string callback_query_id) {
1485 		AnswerCallbackQueryMethod m = {
1486 			callback_query_id: callback_query_id,
1487 		};
1488 
1489 		return answerCallbackQuery(m);
1490 	}
1491 	/// ditto
1492 	bool answerCallbackQuery(AnswerCallbackQueryMethod m) {
1493 		return callMethod!bool(m);
1494 	}
1495 
1496 	/**
1497 	 * Edit text of a message
1498 	 *
1499 	 * Overloads take either `chat_id` and `message_id` or `inline_message_id`
1500 	 *
1501 	 * Params:
1502 	 *     chat_id           = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1503 	 *     message_id        = Identifier of the sent message
1504 	 *     inline_message_id = Identifier of the inline message
1505 	 *     text              = New text of the message
1506 	 * Returns: Edited `Message`
1507 	 * Throws: `TelegramBotException` on errors
1508 	 * See_Also: `EditMessageTextMethod`, $(LINK https://core.telegram.org/bots/api#editmessagetext)
1509 	 */
1510 	Message editMessageText(T)(T chat_id, int message_id, string text)
1511 	if(isTelegramID!T) {
1512 		EditMessageTextMethod m = {
1513 			message_id: message_id,
1514 			text: text,
1515 			chat_id: chat_id,
1516 		};
1517 
1518 		return editMessageText(m);
1519 	}
1520 	/// ditto
1521 	Message editMessageText(string inline_message_id, string text) {
1522 		EditMessageTextMethod m = {
1523 			inline_message_id: inline_message_id,
1524 			text: text,
1525 		};
1526 
1527 		return editMessageText(m);
1528 	}
1529 	/// ditto
1530 	Message editMessageText(EditMessageTextMethod m) {
1531 		return callMethod!Message(m);
1532 	}
1533 
1534 	/**
1535 	 * Edit caption of a message
1536 	 *
1537 	 * Overloads take either `chat_id` and `message_id` or `inline_message_id`
1538 	 *
1539 	 * Params:
1540 	 *     chat_id           = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1541 	 *     message_id        = Identifier of the sent message
1542 	 *     inline_message_id = Identifier of the inline message
1543 	 *     caption           = New caption of the message
1544 	 * Returns: Edited `Message`
1545 	 * Throws: `TelegramBotException` on errors
1546 	 * See_Also: `EditMessageCaptionMethod`, $(LINK https://core.telegram.org/bots/api#editmessagecaption)
1547 	 */
1548 	Message editMessageCaption(T)(T chat_id, int message_id, string caption)
1549 	if(isTelegramID!T) {
1550 		EditMessageCaptionMethod m = {
1551 			message_id: message_id,
1552 			caption: caption,
1553 			chat_id: chat_id,
1554 		};
1555 
1556 		return editMessageCaption(m);
1557 	}
1558 	/// ditto
1559 	Message editMessageCaption(string inline_message_id, string caption) {
1560 		EditMessageCaptionMethod m = {
1561 			inline_message_id: inline_message_id,
1562 			caption: caption,
1563 		};
1564 
1565 		return editMessageCaption(m);
1566 	}
1567 	/// ditto
1568 	Message editMessageCaption(EditMessageCaptionMethod m) {
1569 		return callMethod!Message(m);
1570 	}
1571 
1572 	/**
1573 	 * Edit audio, document, photo or video message
1574 	 *
1575 	 * Overloads take either `chat_id` and `message_id` or `inline_message_id`
1576 	 *
1577 	 * Params:
1578 	 *     chat_id           = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1579 	 *     message_id        = Identifier of the sent message
1580 	 *     inline_message_id = Identifier of the inline message
1581 	 *     media             = New media content of the message
1582 	 * Returns: Edited `Message`
1583 	 * Throws: `TelegramBotException` on errors
1584 	 * See_Also: `EditMessageMediaMethod`, $(LINK https://core.telegram.org/bots/api#editmessagemedia)
1585 	 */
1586 	Message editMessageMedia(T)(T chat_id, int message_id, InputMedia media) {
1587 		EditMessageMediaMethod m = {
1588 			chat_id: chat_id,
1589 			message_id: message_id,
1590 			media: media,
1591 		};
1592 		return editMessageMedia(m);
1593 	}
1594 	/// ditto
1595 	Message editMessageMedia(string inline_message_id, InputMedia media) {
1596 		EditMessageMediaMethod m = {
1597 			inline_message_id: inline_message_id,
1598 			media: media,
1599 		};
1600 		return editMessageMedia(m);
1601 	}
1602 	/// ditto
1603 	Message editMessageMedia(EditMessageMediaMethod m) {
1604 		return callMethod!Message(m);
1605 	}
1606 
1607 	/**
1608 	 * Edit reply markup of a message
1609 	 *
1610 	 * Overloads take either `chat_id` and `message_id` or `inline_message_id`
1611 	 *
1612 	 * Params:
1613 	 *     chat_id           = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1614 	 *     message_id        = Identifier of the sent message
1615 	 *     inline_message_id = Identifier of the inline message
1616 	 *     reply_markup      = Object for a new inline keyboard
1617 	 * Returns: Edited `Message`
1618 	 * Throws: `TelegramBotException` on errors
1619 	 * See_Also: `EditMessageReplyMarkup`, $(LINK https://core.telegram.org/bots/api#editmessagereplymarkup)
1620 	 */
1621 	Message editMessageReplyMarkup(T)(T chat_id, int message_id, InlineKeyboardMarkup reply_markup)
1622 	if(isTelegramID!T) {
1623 		EditMessageReplyMarkupMethod m = {
1624 			message_id: message_id,
1625 			chat_id: chat_id,
1626 			reply_markup: reply_markup,
1627 		};
1628 
1629 		return editMessageReplyMarkup(m);
1630 	}
1631 	/// ditto
1632 	Message editMessageReplyMarkup(string inline_message_id, InlineKeyboardMarkup reply_markup) {
1633 		EditMessageReplyMarkupMethod m = {
1634 			inline_message_id: inline_message_id,
1635 			reply_markup: reply_markup,
1636 		};
1637 
1638 		return editMessageReplyMarkup(m);
1639 	}
1640 	/// ditto
1641 	Message editMessageReplyMarkup(EditMessageReplyMarkupMethod m) {
1642 		return callMethod!Message(m);
1643 	}
1644 
1645 	/**
1646 	 * Delete a message
1647 	 *
1648 	 * Params:
1649 	 *     chat_id    = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1650 	 *     message_id = Identifier of the message to delete
1651 	 * Returns: `true` on success
1652 	 * Throws: `TelegramBotException` on errors
1653 	 * See_Also: `DeleteMessageMethod`, $(LINK https://core.telegram.org/bots/api#deletemessage)
1654 	 */
1655 	bool deleteMessage(T)(T chat_id, int message_id) if(isTelegramID!T) {
1656 		DeleteMessageMethod m = {
1657 			message_id: message_id,
1658 			chat_id: chat_id,
1659 		};
1660 
1661 		return deleteMessage(m);
1662 	}
1663 	/// ditto
1664 	bool deleteMessage(DeleteMessageMethod m) {
1665 		return callMethod!bool(m);
1666 	}
1667 
1668 	/**
1669 	 * Send webp sticker
1670 	 *
1671 	 * Params:
1672 	 *     chat_id = Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
1673 	 *     sticker = HTTP URL to get sticker from the internet or `file_id` of the file on Telegram
1674 	 * Returns: Sent `Message`
1675 	 * Throws: `TelegramBotException` on errors
1676 	 * See_Also: `SendStickerMethod`, $(LINK https://core.telegram.org/bots/api#sendsticker)
1677 	 */
1678 	Message sendSticker(T)(T chat_id, string sticker) if(isTelegramID!T) {
1679 		SendStickerMethod m = {
1680 			sticker: sticker,
1681 			chat_id: chat_id,
1682 		};
1683 
1684 		return sendSticker(m);
1685 	}
1686 	/// ditto
1687 	Message sendSticker(SendStickerMethod m) {
1688 		return callMethod!Message(m);
1689 	}
1690 
1691 	/**
1692 	 * Get a sticker set
1693 	 *
1694 	 * Params:
1695 	 *     name = Name of the sticker set
1696 	 * Returns: `StickerSet` on success
1697 	 * Throws: `TelegramBotException` on errors
1698 	 * See_Also: `GetStickerMethod`, $(LINK https://core.telegram.org/bots/api#getstickerset)
1699 	 */
1700 	StickerSet getStickerSet(string name) {
1701 		GetStickerSetMethod m = {
1702 			name: name,
1703 		};
1704 
1705 		return getStickerSet(m);
1706 	}
1707 	/// ditto
1708 	StickerSet getStickerSet(GetStickerSetMethod m) {
1709 		return callMethod!StickerSet(m);
1710 	}
1711 
1712 	/**
1713 	 * Upload a .png file to create a new sticker set or add to an existing one
1714 	 *
1715 	 * Params:
1716 	 *     user_id     = User identifier of sticker file owner
1717 	 *     png_sticker = Png image with the sticker
1718 	 * Returns: Uploaded `File` on success
1719 	 * Throws: `TelegramBotException` on errors
1720 	 * See_Also: `UploadStickerFileMethod`, $(LINK https://core.telegram.org/bots/api#uploadstickerfile)
1721 	 * Deprecated: `InputFile` isn't supported yet
1722 	 */
1723 	deprecated("InputFile and every method that uses it aren't supported yet")
1724 	File uploadStickerFile(int user_id, InputFile png_sticker) {
1725 		UploadStickerFileMethod m = {
1726 			user_id: user_id,
1727 			png_sticker: png_sticker,
1728 		};
1729 
1730 		return uploadStickerFile(m);
1731 	}
1732 	/// ditto
1733 	File uploadStickerFile(UploadStickerFileMethod m) {
1734 		return callMethod!File(m);
1735 	}
1736 
1737 	/**
1738 	 * Create new sticker set owned by a user
1739 	 *
1740 	 * Params:
1741 	 *     user_id     = User identifier of created sticker set owner
1742 	 *     name        = Short name of sticker set, to be used in `t.me/addstickers/` URLs
1743 	 *     title       = Sticker set title
1744 	 *     png_sticker = Png image with a sticker, Pass `file_id` or an HTTP URL to get a file from the Internet
1745 	 *     emojis      = One or more emoji corresponding to the sticker
1746 	 * Returns: `true` on success
1747 	 * Throws: `TelegramBotException` on errors
1748 	 * See_Also: `CreateNewStickerSetMethod`, $(LINK https://core.telegram.org/bots/api#createnewstickerset)
1749 	 */
1750 	bool createNewStickerSet(int user_id,
1751 		string name,
1752 		string title,
1753 		string png_sticker,
1754 		string emojis) {
1755 			CreateNewStickerSetMethod m = {
1756 				user_id: user_id,
1757 				name: name,
1758 				title: title,
1759 				png_sticker: png_sticker,
1760 				emojis: emojis,
1761 			};
1762 
1763 			return createNewStickerSet(m);
1764 	}
1765 	/// ditto
1766 	bool createNewStickerSet(CreateNewStickerSetMethod m) {
1767 		return callMethod!bool(m);
1768 	}
1769 
1770 	/**
1771 	 * Add a new sticker to a set
1772 	 *
1773 	 * Params:
1774 	 *     user_id     = User identifier of created sticker set owner
1775 	 *     name        = Sticker set name
1776 	 *     png_sticker = Png image with a sticker, Pass `file_id` or an HTTP URL to get a file from the Internet
1777 	 *     emojis      = One or more emoji corresponding to the sticker
1778 	 * Returns: `true` on success
1779 	 * Throws: `TelegramBotException` on errors
1780 	 * See_Also: `AddStickerToSetMethod`, $(LINK https://core.telegram.org/bots/api#addstickertoset)
1781 	 */
1782 	bool addStickerToSet(int user_id, string name, string png_sticker, string emojis) {
1783 		AddStickerToSetMethod m = {
1784 			user_id: user_id,
1785 			name : name,
1786 			png_sticker: png_sticker,
1787 			emojis: emojis,
1788 		};
1789 
1790 		return addStickerToSet(m);
1791 	}
1792 	/// ditto
1793 	bool addStickerToSet(AddStickerToSetMethod m) {
1794 		return callMethod!bool(m);
1795 	}
1796 
1797 	/**
1798 	 * Move a sticker in a set to a specific position
1799 	 *
1800 	 * Params:
1801 	 *     sticker  = File identifier of the sticker
1802 	 *     position = New sticker position in the set, zero-based
1803 	 * Returns: `true` on success
1804 	 * Throws: `TelegramBotException` on errors
1805 	 * See_Also: `SetStickerPositionInSetMethod`, $(LINK https://core.telegram.org/bots/api#setstickerpositioninset)
1806 	 */
1807 	bool setStickerPositionInSet(string sticker, int position) {
1808 		SetStickerPositionInSetMethod m = {
1809 			sticker: sticker,
1810 			position: position,
1811 		};
1812 
1813 		return setStickerPositionInSet(m);
1814 	}
1815 	/// ditto
1816 	bool setStickerPositionInSet(SetStickerPositionInSetMethod m) {
1817 		return callMethod!bool(m);
1818 	}
1819 
1820 	/**
1821 	 * Delete a sticker from a set
1822 	 *
1823 	 * Params:
1824 	 *     sticker = File identifier of the sticker
1825 	 * Returns: `true` on success
1826 	 * Throws: `TelegramBotException` on errors
1827 	 * See_Also: `DeleteStickerFromSetMethod`, $(LINK https://core.telegram.org/bots/api#deletestickerfromset)
1828 	 */
1829 	bool deleteStickerFromSet(string sticker) {
1830 		DeleteStickerFromSetMethod m = {
1831 			sticker: sticker,
1832 		};
1833 
1834 		return deleteStickerFromSet(m);
1835 	}
1836 	/// ditto
1837 	bool deleteStickerFromSet(DeleteStickerFromSetMethod m) {
1838 		return callMethod!bool(m);
1839 	}
1840 
1841 	/**
1842 	 * Send answers to an inline query 
1843 	 *
1844 	 * Params:
1845 	 *     inline_query_id = Unique identifier for the answered query
1846 	 *     results         = Results for the inline query
1847 	 * Returns: `true` on success
1848 	 * Throws: `TelegramBotException` on errors
1849 	 * See_Also: `AnswerInlineQueryMethod`, $(LINK https://core.telegram.org/bots/api#answerinlinequery)
1850 	 */
1851 	bool answerInlineQuery(string inline_query_id, InlineQueryResult[] results)
1852 	in(results.length <= 50) {
1853 		AnswerInlineQueryMethod m = {
1854 			inline_query_id: inline_query_id,
1855 			results: results,
1856 		};
1857 
1858 		return answerInlineQuery(m);
1859 	}
1860 	/// ditto
1861 	bool answerInlineQuery(AnswerInlineQueryMethod m) {
1862 		return callMethod!bool(m);
1863 	}
1864 }
1865 
1866 /*                    Telegram types and enums                    */
1867 
1868 /// Type of chat
1869 enum ChatType : string {
1870 	private_   = "private",    /// Private chats
1871 	group      = "group",      /// Group chats
1872 	supergroup = "supergroup", /// SuperGroup. Just like a group, but *super*
1873 	channel    = "channel"     /// Channel
1874 }
1875 
1876 /// Formatting options
1877 enum ParseMode : string {
1878 	/**
1879 	 * No formatting
1880 	 */
1881 	none     = "",
1882 
1883 	/**
1884 	 * Markdown formatting
1885 	 * See_Also: $(LINK https://core.telegram.org/bots/api#markdown-style)
1886 	 */
1887 	markdown = "Markdown",
1888 
1889 	/**
1890 	 * HTML formatting
1891 	 * See_Also: $(LINK https://core.telegram.org/bots/api#html-style)
1892 	 */
1893 	html     = "HTML",
1894 }
1895 
1896 /// Type of the `MessageEntity`
1897 enum EntityType : string {
1898 	mention      = "mention",      /// Mention
1899 	hashtag      = "hashtag",      /// Hashtag
1900 	cashtag      = "cashtag",      /// Cashtag
1901 	bot_command  = "bot_command",  /// Bot command
1902 	url          = "url",          /// URL
1903 	email        = "email",        /// E-mail
1904 	phone_number = "phone_number", /// Phone number
1905 	bold         = "bold",         /// Bold text
1906 	italic       = "italic",       /// Italic text
1907 	code         = "code",         /// Code, monowidth string
1908 	pre          = "pre",          /// Pre, monowidth block
1909 	text_link    = "text_link",    /// For clickable text URLs
1910 	text_mention = "text_mention", /// For users without usernames
1911 }
1912 
1913 /// Member's status in the chat
1914 enum UserStatus : string {
1915 	creator       = "creator",       /// Creator
1916 	administrator = "administrator", /// Administrator
1917 	member        = "member",        /// Member
1918 	restricted    = "restricted",    /// Restricted
1919 	left          = "left",          /// Left
1920 	kicked        = "kicked",        /// Kicked
1921 }
1922 
1923 /// Represents parts of the face
1924 enum FacePart : string {
1925 	forehead = "forehead", /// Forehead
1926 	eyes     = "eyes",     /// Eyes
1927 	mouth    = "mouth",    /// Mouth
1928 	chin     = "chin",     /// Chin
1929 }
1930 
1931 /**
1932  * Chat action to be broadcast
1933  * See_Also: `TelegramBot.sendChatAction`, $(LINK https://core.telegram.org/bots/api#sendchataction)
1934  */
1935 enum ChatAction : string {
1936 	typing            = "typing",            /// "... typing"
1937 	upload_photo      = "upload_photo",      /// "... sending photo"
1938 	record_video      = "record_video",      /// "... recording video"
1939 	upload_video      = "upload_video",      /// "... sending video"
1940 	record_audio      = "record_audio",      /// "... recording voice"
1941 	upload_audio      = "upload_audio",      /// "... sending voice"
1942 	upload_document   = "upload_document",   /// "... sending file"
1943 	find_location     = "find_location",     /// "... choosing location"
1944 	record_video_note = "record_video_note", /// "... recording video"
1945 	upload_video_note = "upload_video_note", /// "... sending video message"
1946 }
1947 
1948 /**
1949  * An incoming update
1950  * See_Also: $(LINK https://core.telegram.org/bots/api#update)
1951  */
1952 struct Update {
1953 	/// Unique identifier of the update
1954 	int update_id;
1955 
1956 	/// Shorthand for `update_id`;
1957 	alias id = update_id;
1958 
1959 @optional:
1960 	/// New incoming message
1961 	Message message;
1962 
1963 	/// New version of an old message
1964 	Message edited_message;
1965 
1966 	/// New channel post
1967 	Message channel_post;
1968 
1969 	/// New version of a channel post
1970 	Message edited_channel_post;
1971 
1972 	/// New incoming inline query
1973 	InlineQuery inline_query;
1974 
1975 	/// Result of an inline query
1976 	ChosenInlineResult chosen_inline_result;
1977 
1978 	/// New incoming callback query
1979 	CallbackQuery callback_query;
1980 
1981 	/// New incoming shipping query
1982 	ShippingQuery shipping_query;
1983 
1984 	/// New incoming pre-checkout query
1985 	PreCheckoutQuery pre_checkout_query;
1986 
1987 	@safe pure nothrow @nogc bool isNull() { return update_id == typeof(update_id).init; }
1988 }
1989 
1990 /**
1991  * Information about the current status of a webhook
1992  * See_Also: $(LINK https://core.telegram.org/bots/api#webhookinfo)
1993  */
1994 struct WebhookInfo {
1995 	/// Webhook URL, may be empty if webhook is not set up
1996 	string url;
1997 
1998 	/// `true`, if a custom certificate was provided for webhook certificate checks
1999 	bool has_custom_certificate;
2000 
2001 	/// Number of updates awaiting delivery
2002 	int pending_update_count;
2003 
2004 @optional:
2005 	/// Date of the most recent error that happened when trying to deliver an update via webhook
2006 	SysTime last_error_date;
2007 
2008 	/// Error message in human-readable format for the most recent error that happened when trying to deliver an update via webhook
2009 	string last_error_message;
2010 
2011 	/// Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery
2012 	int max_connections;
2013 
2014 	/// A list of update types the bot is subscribed to. Defaults to all update types
2015 	string[] allowed_updates;
2016 
2017 	@safe pure nothrow @nogc bool isNull() { return url == typeof(url).init; }
2018 }
2019 
2020 /**
2021  * Telegram user or a bot
2022  * See_Also: $(LINK https://core.telegram.org/bots/api#user)
2023  */
2024 struct User {
2025 	/// Unique identifier
2026 	int id;
2027 
2028 	/// `true`, if user is a bot
2029 	bool is_bot;
2030 
2031 	/// User's first name
2032 	string first_name;
2033 
2034 @optional:
2035 	/// User's last name
2036 	string last_name;
2037 
2038 	/// User's username
2039 	string username;
2040 
2041 	/// IETF language tag of the user's language
2042 	string language_code;
2043 
2044 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
2045 }
2046 
2047 /**
2048  * Chat
2049  * See_Also: $(LINK https://core.telegram.org/bots/api#chat)
2050  */
2051 struct Chat {
2052 @safe:
2053 	/// Unique identifier
2054 	long id;
2055 
2056 	/// Type
2057 	ChatType type;
2058 
2059 @optional:
2060 	/// Title, for supergroups, channels and group chats
2061 	string title;
2062 
2063 	/// Username, for private chats, supergroups and channels if available
2064 	string username;
2065 
2066 	/// First name of the other party in a private chat
2067 	string first_name;
2068 
2069 	/// Last name of the other party in a private chat
2070 	string last_name;
2071 
2072 	/// True if a group has ‘All Members Are Admins’ enabled
2073 	bool all_members_are_administrators;
2074 
2075 	/// Chat photo. Returned only in `getChat`
2076 	ChatPhoto photo;
2077 
2078 	/// Description, for supergroups and channel chats. Returned only in `getChat`
2079 	string description;
2080 
2081 	/// Chat invite link, for supergroups and channel chats. Returned only in `getChat`
2082 	string invite_link;
2083 
2084 	private @name("pinned_message") Json m_pinned_message;
2085 
2086 	/// Pinned message, for supergroups and channel chats. Returned only in `getChat`
2087 	@property @ignore Message pinned_message() {
2088 		return m_pinned_message.type == Json.Type.null_ 
2089 				? Message.init
2090 				: m_pinned_message.deserializeJson!Message;
2091 	}
2092 	/// ditto
2093 	@property @ignore void pinned_message(Message m) {
2094 		m_pinned_message = m.serializeToJson;
2095 	}
2096 
2097 	/// For supergroups, name of group sticker set. Returned only in `getChat`
2098 	string sticker_set_name;
2099 
2100 	/// True, if the bot can change the group sticker set. Returned only in `getChat`
2101 	bool can_set_sticker_set;
2102 
2103 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
2104 }
2105 
2106 /**
2107  * Message
2108  * See_Also: $(LINK https://core.telegram.org/bots/api#message)
2109  */
2110 struct Message {
2111 @safe:
2112 	/// Unique message identifier inside this chat
2113 	int message_id;
2114 
2115 	/// Shorthand for `message_id`;
2116 	alias id = message_id;
2117 
2118 	/// Date the message was sent
2119 	SysTime date;
2120 
2121 	/// Conversation the message belongs to
2122 	Chat chat;
2123 
2124 @optional:
2125 	/// Sender, empty for messages sent to channels
2126 	User from;
2127 
2128 	/// For forwarded messages, sender of the original message
2129 	User forward_from;
2130 
2131 	/// For messages forwarded from channels, information about the original channel
2132 	Chat forward_from_chat;
2133 
2134 	/// For messages forwarded from channels, identifier of the original message in the channel
2135 	int forward_from_message_id;
2136 
2137 	/// For messages forwarded from channels, signature of the post author if present
2138 	string forward_signature;
2139 
2140 	/// For forwarded messages, date the original message was sent
2141 	SysTime forward_date;
2142 
2143 	private @name("reply_to_message") Json m_reply_to_message;
2144 	
2145 	/// For replies, the original message
2146 	@property @ignore Message reply_to_message() {
2147 		return m_reply_to_message.type == Json.Type.null_ 
2148 				? Message.init
2149 				: m_reply_to_message.deserializeJson!Message;
2150 	}
2151 	/// ditto
2152 	@property @ignore void reply_to_message(Message m) {
2153 		m_reply_to_message = m.serializeToJson;
2154 	}
2155 
2156 	/// Date the message was last edited
2157 	SysTime edit_date;
2158 
2159 	/// The unique identifier of a media message group this message belongs to
2160 	string media_group_id;
2161 
2162 	/// Signature of the post author for messages in channels
2163 	string author_signature;
2164 
2165 	/// For text messages, the actual UTF-8 text of the message
2166 	string text;
2167 
2168 	/// For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text
2169 	MessageEntity[] entities;
2170 
2171 	// For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption
2172 	MessageEntity[] caption_entities;
2173 
2174 	/// Message is an audio file, information about the file
2175 	Audio audio;
2176 
2177 	/// Message is a general file, information about the file
2178 	Document document;
2179 
2180 	/// Message is an animation, information about the animation
2181 	Animation animation; 
2182 
2183 	/// Message is a game, information about the game
2184 	Game game;
2185 
2186 	/// Message is a photo, available sizes of the photo
2187 	PhotoSize[] photo;
2188 
2189 	/// Message is a sticker, information about the sticker
2190 	Sticker sticker;
2191 
2192 	/// Message is a video, information about the video
2193 	Video video;
2194 
2195 	/// Message is a voice message, information about the file
2196 	Voice voice;
2197 
2198 	/// Message is a video note, information about the video message
2199 	VideoNote video_note;
2200 
2201 	/// Caption for the audio, document, photo, video or voice
2202 	string caption;
2203 
2204 	/// Message is a shared contact, information about the contact
2205 	Contact contact;
2206 
2207 	/// Message is a shared location, information about the location
2208 	Location location;
2209 
2210 	/// Message is a venue, information about the venue
2211 	Venue venue;
2212 
2213 	/// New members that were added to the group or supergroup and information about them
2214 	User[] new_chat_members;
2215 
2216 	/// A member was removed from the group, information about them
2217 	User left_chat_member;
2218 
2219 	/// A chat title was changed to this value
2220 	string new_chat_title;
2221 
2222 	/// A chat photo was change to this value
2223 	PhotoSize[] new_chat_photo;
2224 
2225 	/// Service message: the chat photo was deleted
2226 	bool delete_chat_photo;
2227 
2228 	/// Service message: the group has been created
2229 	bool group_chat_created;
2230 
2231 	/// Service message: the supergroup has been created
2232 	bool supergroup_chat_created;
2233 
2234 	/// Service message: the channel has been created
2235 	bool channel_chat_created;
2236 
2237 	/// The group has been migrated to a supergroup with the specified identifier
2238 	long migrate_to_chat_id;
2239 
2240 	/// The supergroup has been migrated from a group with the specified identifier
2241 	long migrate_from_chat_id;
2242 
2243 	private @name("pinned_message") Json m_pinned_message;
2244 
2245 	/// Specified message was pinned
2246 	@property @ignore Message pinned_message() {
2247 		return m_pinned_message.type == Json.Type.null_ 
2248 				? Message.init
2249 				: m_pinned_message.deserializeJson!Message;
2250 	}
2251 	/// ditto
2252 	@property @ignore void pinned_message(Message m) {
2253 		m_pinned_message = m.serializeToJson;
2254 	}
2255 
2256 	/// Message is an invoice for a payment, information about the invoice
2257 	Invoice invoice;
2258 
2259 	/// Message is a service message about a successful payment, information about the payment
2260 	SuccessfulPayment successful_payment;
2261 
2262 	/// The domain name of the website on which the user has logged in
2263 	string connected_website;
2264 
2265 	// TODO: Telegram Passport #10
2266 
2267 	@safe pure nothrow @nogc bool isNull() { return message_id == typeof(message_id).init; }
2268 }
2269 
2270 /**
2271  * One special entity in a text message
2272  * See_Also: $(LINK https://core.telegram.org/bots/api#messageentity)
2273  */
2274 struct MessageEntity {
2275 	/// Type of the entity
2276 	EntityType type;
2277 
2278 	/// Offset in UTF-16 code units to the start of the entity
2279 	int offset;
2280 
2281 	/// Length of the entity in UTF-16 code units
2282 	int length;
2283 
2284 @optional:
2285 	/// For “text_link” only, url that will be opened after user taps on the text
2286 	string url;
2287 
2288 	/// For “text_mention” only, the mentioned user
2289 	User user;
2290 
2291 	@safe pure nothrow @nogc bool isNull() { return length == typeof(length).init; }
2292 }
2293 
2294 /**
2295  * One size of a photo or a file/sticker thumbnail
2296  * See_Also: $(LINK https://core.telegram.org/bots/api#photosize)
2297  */
2298 struct PhotoSize {
2299 	/// Unique identifier for this file
2300 	string file_id;
2301 
2302 	/// Photo width
2303 	int width;
2304 
2305 	/// Photo height
2306 	int height;
2307 
2308 @optional:
2309 	/// File size
2310 	int file_size;
2311 
2312 	@safe pure nothrow @nogc bool isNull() { return file_id == typeof(file_id).init; }
2313 }
2314 
2315 /**
2316  * Audio file to be treated as music by the Telegram clients
2317  * See_Also: $(LINK https://core.telegram.org/bots/api#audio)
2318  */
2319 struct Audio {
2320 	/// Unique identifier for this file
2321 	string file_id;
2322 
2323 	/// Duration of the audio as defined by sender
2324 	Duration duration;
2325 
2326 @optional:
2327 	/// Performer of the audio as defined by sender or by audio tags
2328 	string performer;
2329 
2330 	/// Title of the audio as defined by sender or by audio tags
2331 	string title;
2332 
2333 	/// MIME type of the file as defined by sender
2334 	string mime_type;
2335 
2336 	/// File size
2337 	int file_size;
2338 
2339 	/// Thumbnail of the album cover to which the music file belongs
2340 	PhotoSize thumb;
2341 
2342 	@safe pure nothrow @nogc bool isNull() { return file_id == typeof(file_id).init; }
2343 }
2344 
2345 /**
2346  * General file (as opposed to photos, voice messages and audio files).
2347  * See_Also: $(LINK https://core.telegram.org/bots/api#document)
2348  */
2349 struct Document {
2350 	/// Unique file identifier
2351 	string file_id;
2352 
2353 @optional:
2354 	/// Document thumbnail as defined by sender
2355 	PhotoSize thumb;
2356 
2357 	/// Original filename as defined by sender
2358 	string file_name;
2359 
2360 	/// MIME type of the file as defined by sender
2361 	string mime_type;
2362 
2363 	/// File size
2364 	int file_size;
2365 
2366 	@safe pure nothrow @nogc bool isNull() { return file_id == typeof(file_id).init; }
2367 }
2368 
2369 /**
2370  * Video file
2371  * See_Also: $(LINK https://core.telegram.org/bots/api#video)
2372  */
2373 struct Video {
2374 	/// Unique identifier for this file
2375 	string file_id;
2376 
2377 	/// Video width as defined by sender
2378 	int width;
2379 
2380 	/// Video height as defined by sender
2381 	int height;
2382 
2383 	/// Duration of the video as defined by sender
2384 	Duration duration;
2385 
2386 @optional:
2387 	/// Video thumbnail
2388 	PhotoSize thumb;
2389 
2390 	/// Mime type of a file as defined by sender
2391 	string mime_type;
2392 
2393 	/// File size
2394 	int file_size;
2395 
2396 	@safe pure nothrow @nogc bool isNull() { return file_id == typeof(file_id).init; }
2397 }
2398 
2399 /**
2400  * Animation file (GIF or H.264/MPEG-4 AVC video without sound)
2401  * See_Also: $(LINK https://core.telegram.org/bots/api#animation)
2402  */
2403 struct Animation {
2404 	/// Unique file identifier
2405 	string file_id;
2406 
2407 	/// Video width as defined by sender
2408 	int width;
2409 
2410 	/// Video height as defined by sender
2411 	int height;
2412 
2413 	/// Duration of the video as defined by sender
2414 	Duration duration;
2415 
2416 @optional:
2417 	/// Animation thumbnail as defined by sender
2418 	PhotoSize thumb;
2419 
2420 	/// Original animation filename as defined by sender
2421 	string file_name;
2422 
2423 	/// MIME type of the file as defined by sender
2424 	string mime_type;
2425 
2426 	/// File size
2427 	int file_size;
2428 
2429 	@safe pure nothrow @nogc bool isNull() { return file_id == typeof(file_id).init; }
2430 }
2431 
2432 /**
2433  * Voice note
2434  * See_Also: $(LINK https://core.telegram.org/bots/api#voice)
2435  */
2436 struct Voice {
2437 	/// Unique identifier for this file
2438 	string file_id;
2439 
2440 	/// Duration of the audio as defined by sender
2441 	Duration duration;
2442 
2443 @optional:
2444 	/// MIME type of the file as defined by sender
2445 	string mime_type;
2446 
2447 	/// File size
2448 	int file_size;
2449 
2450 	@safe pure nothrow @nogc bool isNull() { return file_id == typeof(file_id).init; }
2451 }
2452 
2453 /**
2454  * Video message
2455  * See_Also: $(LINK https://core.telegram.org/bots/api#videonote)
2456  */
2457 struct VideoNote {
2458 	/// Unique identifier for this file
2459 	string file_id;
2460 
2461 	/// Video width and height as defined by sender
2462 	int length;
2463 
2464 	/// Duration of the video as defined by sender
2465 	Duration duration;
2466 
2467 @optional:
2468 	/// Video thumbnail
2469 	PhotoSize thumb;
2470 
2471 	/// File size
2472 	int file_size;
2473 
2474 	@safe pure nothrow @nogc bool isNull() { return file_id == typeof(file_id).init; }
2475 }
2476 
2477 /**
2478  * Phone contact
2479  * See_Also: $(LINK https://core.telegram.org/bots/api#contact)
2480  */
2481 struct Contact {
2482 	/// Contact's phone number
2483 	string phone_number;
2484 
2485 	/// Contact's first name
2486 	string first_name;
2487 
2488 @optional:
2489 	/// Contact's last name
2490 	string last_name;
2491 
2492 	/// Contact's user identifier in Telegram
2493 	int user_id;
2494 
2495 	/// Additional data about the contact in the form of a vCard
2496 	string vcard;
2497 
2498 	@safe pure nothrow @nogc bool isNull() { return phone_number == typeof(phone_number).init; }
2499 }
2500 
2501 /**
2502  * Point on the map
2503  * See_Also: $(LINK https://core.telegram.org/bots/api#location)
2504  */
2505 struct Location {
2506 	/// Longitude as defined by sender
2507 	float longitude;
2508 
2509 	/// Latitude as defined by sender
2510 	float latitude;
2511 
2512 	@safe pure nothrow @nogc bool isNull() { return longitude.isNaN; }
2513 }
2514 
2515 /**
2516  * Venue
2517  * See_Also: $(LINK https://core.telegram.org/bots/api#venue)
2518  */
2519 struct Venue {
2520 	/// Venue location
2521 	Location location;
2522 
2523 	/// Name of the venue
2524 	string title;
2525 
2526 	/// Address of the venue
2527 	string address;
2528 
2529 @optional:
2530 	/// Foursquare identifier of the venue
2531 	string foursquare_id;
2532 
2533 	/// Foursquare type of the venue
2534 	string foursquare_type;
2535 
2536 	@safe pure nothrow @nogc bool isNull() { return location.isNull; }
2537 }
2538 
2539 /**
2540  * User's profile pictures
2541  * See_Also: $(LINK https://core.telegram.org/bots/api#userprofilephotos)
2542  */
2543 struct UserProfilePhotos {
2544 	/// Total number of profile pictures the target user has
2545 	int total_count;
2546 
2547 	/// Requested profile pictures (in up to 4 sizes each)
2548 	PhotoSize[][] photos;
2549 
2550 	@safe pure nothrow @nogc bool isNull() { return total_count == typeof(total_count).init; }
2551 }
2552 
2553 /**
2554  * File ready to be downloaded
2555  * See_Also: $(LINK https://core.telegram.org/bots/api#file)
2556  */
2557 struct File {
2558 	/// Unique identifier for this file
2559 	string file_id;
2560 
2561 @optional:
2562 	/// File size, if known
2563 	int file_size;
2564 
2565 	/// File path
2566 	string file_path;
2567 
2568 	@safe pure nothrow @nogc bool isNull() { return file_id == typeof(file_id).init; }
2569 }
2570 
2571 /**
2572  * Inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user
2573  * See_Also: `InlineKeyboardMarkup`, `ReplyKeyboardMarkup`, `ReplyKeyboardRemove`, `ForceReply`
2574  */
2575 alias ReplyMarkup = Algebraic!(InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply);
2576 
2577 /// Checks if `T` is one of the `ReplyMarkup` types
2578 enum isReplyMarkup(T) = is(T == ReplyMarkup) || ReplyMarkup.allowed!T;
2579 
2580 ///
2581 @("isReplyMarkup")
2582 unittest {
2583 	isReplyMarkup!ReplyMarkup.should.be.equal(true);
2584 	isReplyMarkup!InlineKeyboardMarkup.should.be.equal(true);
2585 	isReplyMarkup!ReplyKeyboardMarkup.should.be.equal(true);
2586 	isReplyMarkup!ReplyKeyboardRemove.should.be.equal(true);
2587 	isReplyMarkup!ForceReply.should.be.equal(true);
2588 	isReplyMarkup!string.should.be.equal(false);
2589 	isReplyMarkup!int.should.be.equal(false);
2590 	isReplyMarkup!bool.should.be.equal(false);
2591 	isReplyMarkup!(Algebraic!(InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove)).should.be.equal(false);
2592 }
2593 
2594 /**
2595  * Custom keyboard with reply options
2596  * See_Also: $(LINK https://core.telegram.org/bots/api#replykeyboardmarkup)
2597  */
2598 struct ReplyKeyboardMarkup {
2599 	/// Keyboard layout
2600 	KeyboardButton[][] keyboard;
2601 
2602 @optional:
2603 	/// Request clients to resize the keyboard vertically for optimal fit
2604 	bool resize_keyboard;
2605 
2606 	/// Request clients to hide the keyboard as soon as it's been used
2607 	bool one_time_keyboard;
2608 
2609 	/// Show the keyboard to specific users only
2610 	bool selective;
2611 
2612 	@safe pure nothrow @nogc bool isNull() { return !keyboard.length; }
2613 }
2614 
2615 /**
2616  * One button of the reply keyboard
2617  * See_Also: $(LINK https://core.telegram.org/bots/api#keyboardbutton)
2618  */
2619 struct KeyboardButton {
2620 	/// Text of the button
2621 	string text;
2622 
2623 @optional:
2624 	/// If `true`, the user's phone number will be sent as a contact when the button is pressed
2625 	bool request_contact;
2626 
2627 	/// If `true`, the user's current location will be sent when the button is pressed
2628 	bool request_location;
2629 
2630 	@safe pure nothrow @nogc bool isNull() { return text == typeof(text).init; }
2631 }
2632 
2633 /**
2634  * Remove current custom keyboard
2635  * See_Also: $(LINK https://core.telegram.org/bots/api#replykeyboardremove)
2636  */
2637 struct ReplyKeyboardRemove {
2638 	/// `true` to remove the keyboard
2639 	bool remove_keyboard;
2640 
2641 @optional:
2642 	/// Remove for specific users only
2643 	bool selective;
2644 
2645 	@safe pure nothrow @nogc bool isNull() { return remove_keyboard == typeof(remove_keyboard).init; }
2646 }
2647 
2648 /**
2649  * Inline keyboard that appears right next to the message
2650  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinekeyboardmarkup)
2651  */
2652 struct InlineKeyboardMarkup {
2653 	/// Keyboard layout
2654 	InlineKeyboardButton[][] inline_keyboard;
2655 
2656 	@safe pure nothrow @nogc bool isNull() { return !inline_keyboard.length; }
2657 }
2658 
2659 /**
2660  * One button of an inline keyboard
2661  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinekeyboardbutton)
2662  */
2663 struct InlineKeyboardButton {
2664 	/// Label text on the button
2665 	string text;
2666 
2667 @optional:
2668 	/// HTTP or `tg://` url to be opened when button is pressed
2669 	string url;
2670 
2671 	/// Data to be sent in a callback query to the bot when button is pressed
2672 	string callback_data;
2673 
2674 	/// Pressing the button will prompt the user to select one of their chats, open that chat and insert the bot‘s username and the specified inline query in the input field
2675 	string switch_inline_query;
2676 
2677 	/// Pressing the button will insert the bot‘s username and the specified inline query in the current chat's input field
2678 	string switch_inline_query_current_chat;
2679 
2680 	/// Description of the game that will be launched when the user presses the button
2681 	CallbackGame callback_game;
2682 
2683 	/// `true` to send a pay button
2684 	bool pay;
2685 
2686 	@safe pure nothrow @nogc bool isNull() { return text == typeof(text).init; }
2687 }
2688 
2689 /**
2690  * Incoming callback query
2691  * See_Also: $(LINK https://core.telegram.org/bots/api#callbackquery)
2692  */
2693 struct CallbackQuery {
2694 	/// Unique identifier for this query
2695 	string id;
2696 
2697 	/// Sender
2698 	User from;
2699 
2700 	/// Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent
2701 	string chat_instance;
2702 
2703 @optional:
2704 	/// Message with the callback button that originated the query
2705 	Message message;
2706 
2707 	/// Identifier of the message sent via the bot in inline mode, that originated the query
2708 	string inline_message_id;
2709 
2710 	/// Data associated with the callback button
2711 	string data;
2712 
2713 	/// Short name of a `Game` to be returned
2714 	string game_short_name;
2715 
2716 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
2717 }
2718 
2719 /**
2720  * Force user reply
2721  * See_Also: $(LINK https://core.telegram.org/bots/api#forcereply)
2722  */
2723 struct ForceReply {
2724 	/// Show reply iterface to a user
2725 	bool force_reply;
2726 
2727 @optional:
2728 	/// Only for specific users
2729 	bool selective;
2730 
2731 	@safe pure nothrow @nogc bool isNull() { return force_reply == typeof(force_reply).init; }
2732 }
2733 
2734 /**
2735  * Chat photo
2736  * See_Also: $(LINK https://core.telegram.org/bots/api#chatphoto)
2737  */
2738 struct ChatPhoto {
2739 	/// Unique file identifier of small (160x160) chat photo
2740 	string small_file_id;
2741 
2742 	/// Unique file identifier of big (640x640) chat photo
2743 	string big_file_id;
2744 
2745 	@safe pure nothrow @nogc bool isNull() { return small_file_id == typeof(small_file_id).init; }
2746 }
2747 
2748 /**
2749  * Information about one member of a chat
2750  * See_Also: $(LINK https://core.telegram.org/bots/api#chatmember)
2751  */
2752 struct ChatMember {
2753 	/// Information about the user
2754 	User user;
2755 
2756 	/// Member's status in the chat
2757 	UserStatus status;
2758 
2759 @optional:
2760 	/// Restricted and kicked only. Date when restrictions will be lifted for this user
2761 	SysTime until_date;
2762 
2763 	/// Administrators only. `true`, if the bot is allowed to edit administrator privileges of that user
2764 	bool can_be_edited;
2765 
2766 	/// Administrators only. `true`, if the administrator can change the chat title, photo and other settings
2767 	bool can_change_info;
2768 
2769 	/// Administrators only. `true`, if the administrator can post in the channel, channels only
2770 	bool can_post_messages;
2771 
2772 	/// Administrators only. `true`, if the administrator can edit messages of other users and can pin messages, channels only
2773 	bool can_edit_messages;
2774 
2775 	/// Administrators only. `true`, if the administrator can delete messages of other users
2776 	bool can_delete_messages;
2777 
2778 	/// Administrators only. `true`, if the administrator can invite new users to the chat
2779 	bool can_invite_users;
2780 
2781 	/// Administrators only. `true`, if the administrator can restrict, ban or unban chat members
2782 	bool can_restrict_members;
2783 
2784 	/// Administrators only. `true`, if the administrator can pin messages, supergroups only
2785 	bool can_pin_messages;
2786 
2787 	/// Administrators only. `true`, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly
2788 	bool can_promote_members;
2789 
2790 	/// Restricted only. `true`, if the user can send text messages, contacts, locations and venues
2791 	bool can_send_messages;
2792 
2793 	/// Restricted only. `true`, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages
2794 	bool can_send_media_messages;
2795 
2796 	/// Restricted only. `true`, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages
2797 	bool can_send_other_messages;
2798 
2799 	/// Restricted only. `true`, if user may add web page previews to his messages, implies can_send_media_messages
2800 	bool can_add_web_page_previews;
2801 
2802 	@safe pure nothrow @nogc bool isNull() { return user.isNull; }
2803 }
2804 
2805 /**
2806  * Information about why a request was unsuccessful
2807  * See_Also: $(LINK https://core.telegram.org/bots/api#responseparameters)
2808  */
2809 struct ResponseParameters {
2810 @optional:
2811 	/// The group has been migrated to a supergroup with the specified identifier
2812 	long migrate_to_chat_id;
2813 
2814 	/// In case of exceeding flood control, the number of seconds left to wait before the request can be repeated
2815 	Duration retry_after;
2816 
2817 	@safe pure nothrow @nogc bool isNull() { return !migrate_to_chat_id && !retry_after; }
2818 }
2819 
2820 /**
2821  * Content of a media message to be sent
2822  * See_Also: `InputMediaAnimation`, `InputMediaDocument`, `InputMediaAudio`, `InputMediaPhoto`, `InputMediaVideo`
2823  */
2824 alias InputMedia = Algebraic!(InputMediaAnimation, InputMediaDocument, InputMediaAudio, InputMediaPhoto, InputMediaVideo);
2825 
2826 /// Checks if `T` is one of the `InputMedia` types
2827 enum isInputMedia(T) = is(T == InputMedia) || InputMedia.allowed!T;
2828 
2829 ///
2830 @("isInputMedia")
2831 unittest {
2832 	isInputMedia!InputMedia.should.be.equal(true);
2833 	isInputMedia!InputMediaAnimation.should.be.equal(true);
2834 	isInputMedia!InputMediaDocument.should.be.equal(true);
2835 	isInputMedia!InputMediaAudio.should.be.equal(true);
2836 	isInputMedia!InputMediaPhoto.should.be.equal(true);
2837 	isInputMedia!InputMediaVideo.should.be.equal(true);
2838 	isInputMedia!string.should.be.equal(false);
2839 	isInputMedia!bool.should.be.equal(false);
2840 	isInputMedia!int.should.be.equal(false);
2841 }
2842 
2843 /**
2844  * Photo to be sent
2845  * See_Also: $(LINK https://core.telegram.org/bots/api#inputmediaphoto)
2846  */
2847 struct InputMediaPhoto {
2848 	/// Type of the result, must be `"photo"`
2849 	string type = "photo";
2850 
2851 	/// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet
2852 	string media;
2853 
2854 @optional:
2855 	/// Caption of the photo to be sent
2856 	string caption;
2857 
2858 	/// Parse mode for the caption
2859 	ParseMode parse_mode;
2860 
2861 	@safe pure nothrow @nogc bool isNull() { return media == typeof(media).init; }
2862 }
2863 
2864 /**
2865  * Video file to be sent
2866  * See_Also: $(LINK https://core.telegram.org/bots/api#inputmediavideo)
2867  */
2868 struct InputMediaVideo {
2869 	/// Type of the result, must be `"video"`
2870 	string type = "video";
2871 
2872 	/// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet
2873 	string media;
2874 
2875 @optional:
2876 	/// Thumbnail of the file
2877 	string thumb;
2878 
2879 	/// Caption of the video to be sent
2880 	string caption;
2881 
2882 	/// Parse mode for the caption
2883 	ParseMode parse_mode;
2884 
2885 	/// Video width
2886 	int width;
2887 	
2888 	/// Video height
2889 	int height;
2890 
2891 	/// Video duration
2892 	Duration duration;
2893 
2894 	/// Pass `true`, if the uploaded video is suitable for streaming
2895 	bool supports_streaming;
2896 
2897 	@safe pure nothrow @nogc bool isNull() { return media == typeof(media).init; }
2898 }
2899 
2900 /**
2901  * Animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent
2902  * See_Also: $(LINK https://core.telegram.org/bots/api#inputmediaanimation)
2903  */
2904 struct InputMediaAnimation {
2905 	/// Type of the result, must be `"animation"`
2906 	string type = "animation";
2907 
2908 	/// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet
2909 	string media;
2910 
2911 @optional:
2912 	/// Thumbnail of the file
2913 	string thumb;
2914 
2915 	/// Caption of the animation to be sent
2916 	string caption;
2917 
2918 	/// Parse mode for the caption
2919 	ParseMode parse_mode;
2920 
2921 	/// Animation width
2922 	int width;
2923 
2924 	/// Animation height
2925 	int height;
2926 
2927 	/// Animation duration
2928 	Duration duration;
2929 
2930 	@safe pure nothrow @nogc bool isNull() { return media == typeof(media).init; }
2931 }
2932 
2933 /**
2934  * Audio file to be sent
2935  * See_Also: $(LINK https://core.telegram.org/bots/api#inputmediaaudio)
2936  */
2937 struct InputMediaAudio {
2938 	/// Type of the result, must be `"audio"`
2939 	string type = "audio";
2940 
2941 	/// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet
2942 	string media;
2943 
2944 @optional:
2945 	/// Thumbnail of the file
2946 	string thumb;
2947 
2948 	/// Caption of the audio to be sent
2949 	string caption;
2950 
2951 	/// Parse mode for the caption
2952 	ParseMode parse_mode;
2953 
2954 	/// Duration of the audio
2955 	Duration duration;
2956 
2957 	/// Performer of the audio
2958 	string performer;
2959 
2960 	/// Title of the audio
2961 	string title;
2962 
2963 	@safe pure nothrow @nogc bool isNull() { return media == typeof(media).init; }
2964 }
2965 
2966 /**
2967  * General file to be sent
2968  * See_Also: $(LINK https://core.telegram.org/bots/api#inputmediadocument)
2969  */
2970 struct InputMediaDocument {
2971 	/// Type of the result, must be `"document"`
2972 	string type = "document";
2973 
2974 	/// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet
2975 	string media;
2976 
2977 @optional:
2978 	/// Thumbnail of the file
2979 	string thumb;
2980 	/// Caption of the document to be sent
2981 	string caption;
2982 	/// Parse mode for the caption
2983 	ParseMode parse_mode;
2984 
2985 	@safe pure nothrow @nogc bool isNull() { return media == typeof(media).init; }
2986 }
2987 
2988 /**
2989  * Represents the contents of a file to be uploaded
2990  * 
2991  * Not yet implemented.
2992  * See_Also: $(LINK https://core.telegram.org/bots/api#inputfile)
2993  */
2994 struct InputFile {}
2995 
2996 /**
2997  * Sticker
2998  * See_Also: $(LINK https://core.telegram.org/bots/api#sticker)
2999  */
3000 struct Sticker {
3001 	/// Unique identifier for this file
3002 	string file_id;
3003 
3004 	/// Sticker width
3005 	int width;
3006 
3007 	/// Sticker height
3008 	int height;
3009 
3010 @optional:
3011 	/// Sticker thumbnail in the .webp or .jpg format
3012 	PhotoSize thumb;
3013 
3014 	/// Emoji associated with the sticker
3015 	string emoji;
3016 
3017 	/// Name of the sticker set to which the sticker belongs
3018 	string set_name;
3019 
3020 	/// For mask stickers, the position where the mask should be placed
3021 	MaskPosition mask_position;
3022 
3023 	/// File size
3024 	int file_size;
3025 
3026 	@safe pure nothrow @nogc bool isNull() { return file_id == typeof(file_id).init; }
3027 }
3028 
3029 /**
3030  * Sticker set
3031  * See_Also: $(LINK https://core.telegram.org/bots/api#stickerset)
3032  */
3033 struct StickerSet {
3034 	/// Sticker set name
3035 	string name;
3036 
3037 	/// Sticker set title
3038 	string title;
3039 
3040 	/// `true`, if the sticker set contains masks
3041 	bool contains_masks;
3042 
3043 	/// List of all set stickers
3044 	Sticker[] stickers;
3045 
3046 	@safe pure nothrow @nogc bool isNull() { return name == typeof(name).init; }
3047 }
3048 
3049 /**
3050  * Describes position on faces where a mask should be placed by default
3051  * See_Also: $(LINK https://core.telegram.org/bots/api#maskposition)
3052  */
3053 struct MaskPosition {
3054 	/// The part of the face relative to which the mask should be placed
3055 	FacePart point;
3056 
3057 	/// Shift by X-axis measured in widths of the mask scaled to the face size, from left to right
3058 	float x_shift;
3059 
3060 	/// Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom
3061 	float y_shift;
3062 
3063 	/// Mask scaling coefficient
3064 	float scale;
3065 
3066 	@safe pure nothrow @nogc bool isNull() { return point == typeof(point).init; }
3067 }
3068 
3069 /**
3070  * Incoming inline query 
3071  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequery)
3072  */
3073 struct InlineQuery {
3074 	/// Unique identifier for this query
3075 	string id;
3076 
3077 	/// Sender
3078 	User from;
3079 
3080 	/// Text of the query
3081 	string query;
3082 
3083 	/// Offset of the results to be returned
3084 	string offset;
3085 
3086 @optional:
3087 	/// Sender location
3088 	Location location;
3089 
3090 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3091 }
3092 
3093 /**
3094  * One result of an inline query
3095  * See_Also: `InlineQueryResultArticle`, `InlineQueryResultPhoto`, `InlineQueryResultGif`, `InlineQueryResultMpeg4Gif`, `InlineQueryResultVideo`, `InlineQueryResultAudio`, `InlineQueryResultVoice`, `InlineQueryResultDocument`, `InlineQueryResultLocation`, `InlineQueryResultVenue`, `InlineQueryResultContact`, `InlineQueryResultGame`, `InlineQueryResultCachedPhoto`, `InlineQueryResultCachedGif`, `InlineQueryResultCachedMpeg4Gif`, `InlineQueryResultCachedSticker`, `InlineQueryResultCachedDocument`, `InlineQueryResultCachedVideo`, `InlineQueryResultCachedVoice`, `InlineQueryResultCachedAudio`
3096  */ 
3097 alias InlineQueryResult = Algebraic!(
3098 	InlineQueryResultArticle,
3099 	InlineQueryResultPhoto,
3100 	InlineQueryResultGif,
3101 	InlineQueryResultMpeg4Gif,
3102 	InlineQueryResultVideo,
3103 	InlineQueryResultAudio,
3104 	InlineQueryResultVoice,
3105 	InlineQueryResultDocument,
3106 	InlineQueryResultLocation,
3107 	InlineQueryResultVenue,
3108 	InlineQueryResultContact,
3109 	InlineQueryResultGame,
3110 	InlineQueryResultCachedPhoto,
3111 	InlineQueryResultCachedGif,
3112 	InlineQueryResultCachedMpeg4Gif,
3113 	InlineQueryResultCachedSticker,
3114 	InlineQueryResultCachedDocument,
3115 	InlineQueryResultCachedVideo,
3116 	InlineQueryResultCachedVoice,
3117 	InlineQueryResultCachedAudio
3118 );
3119 
3120 /// Checks if `T` is one of the `InlineQueryResult` types
3121 enum isInlineQueryResult(T) = is(T == InlineQueryResult) || InlineQueryResult.allowed!T;
3122 
3123 ///
3124 @("isInlineQueryResult")
3125 unittest {
3126 	isInlineQueryResult!InlineQueryResult.should.be.equal(true);
3127 	isInlineQueryResult!InlineQueryResultArticle.should.be.equal(true);
3128 	isInlineQueryResult!InlineQueryResultPhoto.should.be.equal(true);
3129 	isInlineQueryResult!InlineQueryResultGif.should.be.equal(true);
3130 	isInlineQueryResult!InlineQueryResultMpeg4Gif.should.be.equal(true);
3131 	isInlineQueryResult!InlineQueryResultVideo.should.be.equal(true);
3132 	isInlineQueryResult!InlineQueryResultAudio.should.be.equal(true);
3133 	isInlineQueryResult!InlineQueryResultVoice.should.be.equal(true);
3134 	isInlineQueryResult!InlineQueryResultDocument.should.be.equal(true);
3135 	isInlineQueryResult!InlineQueryResultLocation.should.be.equal(true);
3136 	isInlineQueryResult!InlineQueryResultVenue.should.be.equal(true);
3137 	isInlineQueryResult!InlineQueryResultContact.should.be.equal(true);
3138 	isInlineQueryResult!InlineQueryResultGame.should.be.equal(true);
3139 	isInlineQueryResult!InlineQueryResultCachedPhoto.should.be.equal(true);
3140 	isInlineQueryResult!InlineQueryResultCachedGif.should.be.equal(true);
3141 	isInlineQueryResult!InlineQueryResultCachedMpeg4Gif.should.be.equal(true);
3142 	isInlineQueryResult!InlineQueryResultCachedSticker.should.be.equal(true);
3143 	isInlineQueryResult!InlineQueryResultCachedDocument.should.be.equal(true);
3144 	isInlineQueryResult!InlineQueryResultCachedVideo.should.be.equal(true);
3145 	isInlineQueryResult!InlineQueryResultCachedVoice.should.be.equal(true);
3146 	isInlineQueryResult!InlineQueryResultCachedAudio.should.be.equal(true);
3147 
3148 	isInlineQueryResult!int.should.be.equal(false);
3149 	isInlineQueryResult!string.should.be.equal(false);
3150 	isInlineQueryResult!bool.should.be.equal(false);
3151 }
3152 
3153 /**
3154  * Link to an article or web page
3155  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultarticle)
3156  */
3157 struct InlineQueryResultArticle {
3158 	/// Type of the result, must be `"article"`
3159 	string type = "article";
3160 
3161 	/// Unique identifier for this result
3162 	string id;
3163 
3164 	/// Title of the result
3165 	string title;
3166 
3167 	/// Content of the message to be sent
3168 	InputMessageContent input_message_content;
3169 
3170 @optional:
3171 	/// Inline keyboard attached to the message
3172 	InlineKeyboardMarkup reply_markup;
3173 
3174 	/// URL of the result
3175 	string url;
3176 
3177 	/// Pass `true`, if you don't want the URL to be shown in the message
3178 	bool hide_url;
3179 
3180 	/// Short description of the result
3181 	string description;
3182 
3183 	/// Url of the thumbnail for the result
3184 	string thumb_url;
3185 
3186 	/// Thumbnail width
3187 	int thumb_width;
3188 
3189 	/// Thumbnail height
3190 	int thumb_height;
3191 
3192 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3193 }
3194 
3195 /**
3196  * Link to a photo
3197  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultphoto)
3198  */
3199 struct InlineQueryResultPhoto {
3200 	/// Type of the result, must be `"photo"`
3201 	string type = "photo";
3202 
3203 	/// Unique identifier for this result
3204 	string id;
3205 
3206 	/// A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB
3207 	string photo_url;
3208 
3209 	/// URL of the thumbnail for the photo
3210 	string thumb_url;
3211 
3212 @optional:
3213 	/// Width of the photo
3214 	int photo_width;
3215 
3216 	/// Height of the photo
3217 	int photo_height;
3218 
3219 	/// Title for the result
3220 	string title;
3221 
3222 	/// Short description of the result
3223 	string description;
3224 
3225 	/// Caption of the photo to be sent
3226 	string caption;
3227 
3228 	/// Parse mode of the caption
3229 	ParseMode parse_mode;
3230 
3231 	/// Inline keyboard attached to the message
3232 	InlineKeyboardMarkup reply_markup;
3233 
3234 	/// Content of the message to be sent instead of the photo
3235 	InputMessageContent input_message_content;
3236 
3237 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3238 }
3239 
3240 /**
3241  * Link to an animated GIF file
3242  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultgif)
3243  */
3244 struct InlineQueryResultGif {
3245 	/// Type of the result, must be `"gif"`
3246 	string type = "gif";
3247 
3248 	/// Unique identifier for this result
3249 	string id;
3250 
3251 	/// A valid URL for the GIF file. File size must not exceed 1MB
3252 	string gif_url;
3253 
3254 	/// URL of the static thumbnail for the result (jpeg or gif)
3255 	string thumb_url;
3256 
3257 @optional:
3258 	/// Width of the GIF
3259 	int gif_width;
3260 
3261 	/// Height of the GIF
3262 	int gif_height;
3263 
3264 	/// Duration of the GIF
3265 	Duration gif_duration;
3266 
3267 	/// Title for the result
3268 	string title;
3269 
3270 	///  Caption of the GIF file to be sent
3271 	string caption;
3272 
3273 	/// Parse mode of the caption
3274 	ParseMode parse_mode;
3275 
3276 	/// Inline keyboard attached to the message
3277 	InlineKeyboardMarkup reply_markup;
3278 
3279 	/// Content of the message to be sent instead of the GIF animation
3280 	InputMessageContent input_message_content;
3281 
3282 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3283 }
3284 
3285 /**
3286  * Link to a vide animation (H.264/MPEG-4 AVC video without sound)
3287  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif)
3288  */
3289 struct InlineQueryResultMpeg4Gif {
3290 	/// Type of the result, must be `"mpeg4_gif"`
3291 	string type = "mpeg4_gif";
3292 
3293 	/// Unique identifier for this result
3294 	string id;
3295 
3296 	/// A valid URL for the MP4 file. File size must not exceed 1MB
3297 	string mpeg4_url;
3298 
3299 	/// Video width
3300 	int mpeg4_width;
3301 
3302 	/// Video height
3303 	int mpeg4_height;
3304 
3305 	/// Video duration
3306 	Duration mpeg4_duration;
3307 
3308 	/// URL of the static thumbnail (jpeg or gif) for the result
3309 	string thumb_url;
3310 
3311 @optional:
3312 	/// Title for the result
3313 	string title;
3314 
3315 	/// Caption of the MPEG-4 file to be sent
3316 	string caption;
3317 
3318 	/// Parse mode of the caption
3319 	ParseMode parse_mode;
3320 
3321 	/// Inline keyboard attached to the message
3322 	InlineKeyboardMarkup reply_markup;
3323 
3324 	/// Content of the message to be sent instead of the video animation
3325 	InputMessageContent input_message_content;
3326 
3327 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3328 }
3329 
3330 /**
3331  * Link to a page containing an embedded video player or a video file
3332  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultvideo)
3333  */
3334 struct InlineQueryResultVideo {
3335 	/// Type of the result, must be `"video"`
3336 	string type = "video";
3337 
3338 	/// Unique identifier for this result
3339 	string id;
3340 
3341 	/// A valid URL for the embedded video player or video file
3342 	string video_url;
3343 
3344 	/// Mime type of the content of video url, “text/html” or “video/mp4”
3345 	string mime_type;
3346 
3347 	/// URL of the thumbnail (jpeg only) for the video
3348 	string thumb_url;
3349 
3350 	/// Title for the result
3351 	string title;
3352 
3353 @optional:
3354 	/// Caption of the video to be sent
3355 	string caption;
3356 
3357 	/// Parse mode of the caption
3358 	ParseMode parse_mode;
3359 
3360 	/// Video width
3361 	int video_width;
3362 
3363 	/// Video height
3364 	int video_height;
3365 
3366 	/// Video duration
3367 	Duration video_duration;
3368 
3369 	/// Short description of the result
3370 	string description;
3371 
3372 	/// Inline keyboard attached to the message
3373 	InlineKeyboardMarkup reply_markup;
3374 
3375 	/// Content of the message to be sent instead of the video
3376 	InputMessageContent input_message_content;
3377 
3378 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3379 }
3380 
3381 /**
3382  * Link to an mp3 audio file
3383  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultaudio)
3384  */
3385 struct InlineQueryResultAudio {
3386 	/// Type of the result, must be `"audio"`
3387 	string type = "audio";
3388 
3389 	/// Unique identifier for this result
3390 	string id;
3391 
3392 	/// A valid URL for the audio file
3393 	string audio_url;
3394 
3395 	/// Title
3396 	string title;
3397 
3398 @optional:
3399 	/// Caption of the audio to be sent
3400 	string caption;
3401 
3402 	/// Parse mode of the caption
3403 	ParseMode parse_mode;
3404 
3405 	/// Performer
3406 	string performer;
3407 
3408 	/// Audio duration
3409 	Duration audio_duration;
3410 
3411 	/// Inline keyboard attached to the message
3412 	InlineKeyboardMarkup reply_markup;
3413 
3414 	/// Content of the message to be sent instead of the audio
3415 	InputMessageContent input_message_content;
3416 
3417 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3418 }
3419 
3420 /**
3421  * Link to a voice recording in an .ogg container encoded with OPUS
3422  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultvoice)
3423  */
3424 struct InlineQueryResultVoice {
3425 	/// Type of the result, must be `"voice"`
3426 	string type = "voice";
3427 
3428 	/// Unique identifier for this result
3429 	string id;
3430 
3431 	/// A valid URL for the voice recording
3432 	string voice_url;
3433 
3434 	/// Recording title
3435 	string title;
3436 
3437 @optional:
3438 	/// Caption of the recording to be sent
3439 	string caption;
3440 
3441 	/// Parse mode of the caption
3442 	ParseMode parse_mode;
3443 
3444 	/// Recording duration
3445 	Duration voice_duration;
3446 
3447 	/// Inline keyboard attached to the message
3448 	InlineKeyboardMarkup reply_markup;
3449 
3450 	/// Content of the message to be sent instead of the voice recording
3451 	InputMessageContent input_message_content;
3452 
3453 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3454 }
3455 
3456 /**
3457  * Link to a file
3458  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultdocument)
3459  */
3460 struct InlineQueryResultDocument {
3461 	/// Type of the result, must be `"document"`
3462 	string type = "document";
3463 
3464 	/// Unique identifier for this result
3465 	string id;
3466 
3467 	/// Title for the result
3468 	string title;
3469 
3470 	/// A valid URL for the file
3471 	string document_url;
3472 
3473 	/// Mime type of the content of the file, either `"application/pdf"` or `"application/zip"`
3474 	string mime_type;
3475 
3476 @optional:
3477 	/// Caption of the document to be sent
3478 	string caption;
3479 
3480 	/// Parse mode of the caption
3481 	ParseMode parse_mode;
3482 
3483 	///  Short description of the result
3484 	string description;
3485 
3486 	/// Inline keyboard attached to the message
3487 	InlineKeyboardMarkup reply_markup; 
3488 
3489 	/// Content of the message to be sent instead of the file
3490 	InputMessageContent input_message_content;
3491 
3492 	/// URL of the thumbnail (jpeg only) for the file
3493 	string thumb_url;
3494 
3495 	/// Thumbnail width
3496 	int thumb_width;
3497 
3498 	/// Thumbnail height
3499 	int thumb_height;
3500 
3501 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3502 }
3503 
3504 /**
3505  * Location on a map
3506  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultlocation)
3507  */
3508 struct InlineQueryResultLocation {
3509 	/// Type of the result, must be `"location"`
3510 	string type = "location";
3511 
3512 	/// Unique identifier for this result
3513 	string id;
3514 
3515 	/// Location latitude in degrees
3516 	float latitude;
3517 
3518 	/// Location longitude in degrees
3519 	float longitude;
3520 
3521 	/// Location title
3522 	string title;
3523 
3524 @optional:
3525 	/// Period for which the location can be updated
3526 	Duration live_period;
3527 
3528 	/// Inline keyboard attached to the message
3529 	InlineKeyboardMarkup reply_markup;
3530 
3531 	/// Content of the message to be sent instead of the location
3532 	InputMessageContent input_message_content;
3533 
3534 	/// Url of the thumbnail for the result
3535 	string thumb_url;
3536 
3537 	/// Thumbnail width
3538 	int thumb_width;
3539 
3540 	/// Thumbnail height
3541 	int thumb_height;
3542 
3543 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3544 }
3545 
3546 /**
3547  * Venue
3548  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultvenue)
3549  */
3550 struct InlineQueryResultVenue {
3551 	/// Type of the result, must be `"venue"`
3552 	string type = "venue";
3553 
3554 	/// Unique identifier for this result
3555 	string id;
3556 
3557 	/// Latitude of the venue location in degrees
3558 	float latitude;
3559 
3560 	/// Longitude of the venue location in degrees
3561 	float longitude;
3562 
3563 	/// Title of the venue
3564 	string title;
3565 
3566 	/// Address of the venue
3567 	string address;
3568 
3569 @optional:
3570 	/// Foursquare identifier of the venue if known
3571 	string foursquare_id;
3572 
3573 	/// Foursquare type of the venue, if known
3574 	string foursquare_type;
3575 
3576 	/// Inline keyboard attached to the message
3577 	InlineKeyboardMarkup reply_markup;
3578 
3579 	/// Content of the message to be sent instead of the venue
3580 	InputMessageContent input_message_content;
3581 
3582 	/// Url of the thumbnail for the result
3583 	string thumb_url;
3584 
3585 	/// Thumbnail width
3586 	int thumb_width;
3587 
3588 	/// Thumbnail height
3589 	int thumb_height;
3590 
3591 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3592 }
3593 
3594 /**
3595  * Contact with a phone number
3596  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultcontact)
3597  */
3598 struct InlineQueryResultContact {
3599 	/// Type of the result, must be `"contact"`
3600 	string type = "contact";
3601 
3602 	/// Unique identifier for this result
3603 	string id;
3604 
3605 	/// Contact's phone number
3606 	string phone_number;
3607 
3608 	/// Contact's first name
3609 	string first_name;
3610 
3611 @optional:
3612 	/// Contact's last name
3613 	string last_name;
3614 
3615 	/// Additional data about the contact in the form of a vCard
3616 	string vcard;
3617 
3618 	/// Inline keyboard attached to the message
3619 	InlineKeyboardMarkup reply_markup;
3620 
3621 	/// Content of the message to be sent instead of the contact
3622 	InputMessageContent input_message_content;
3623 
3624 	/// Url of the thumbnail for the result
3625 	string thumb_url;
3626 
3627 	/// Thumbnail width
3628 	int thumb_width;
3629 
3630 	/// Thumbnail height
3631 	int thumb_height;
3632 
3633 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3634 }
3635 
3636 /**
3637  * Game
3638  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultgame)
3639  */
3640 struct InlineQueryResultGame {
3641 	/// Type of the result, must be `"game"`
3642 	string type = "game";
3643 
3644 	/// Unique identifier for this result
3645 	string id;
3646 
3647 	/// Short name of the game
3648 	string game_short_name;
3649 
3650 @optional:
3651 	/// Inline keyboard attached to the message
3652 	InlineKeyboardMarkup reply_markup;
3653 
3654 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3655 }
3656 
3657 /**
3658  * Link to a photo stored on the Telegram servers
3659  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultcachedphoto)
3660  */
3661 struct InlineQueryResultCachedPhoto {
3662 	/// Type of the result, must be `"photo"`
3663 	string type = "photo";
3664 
3665 	/// Unique identifier for this result
3666 	string id;
3667 
3668 	/// A valid file identifier of the photo
3669 	string photo_file_id;
3670 
3671 @optional:
3672 	/// Title for the result
3673 	string title;
3674 
3675 	/// Short description of the result
3676 	string description;
3677 
3678 	/// Caption of the photo to be sent
3679 	string caption;
3680 
3681 	/// Parse mode of the caption
3682 	ParseMode parse_mode;
3683 
3684 	/// Inline keyboard attached to the message
3685 	InlineKeyboardMarkup reply_markup;
3686 
3687 	/// Content of the message to be sent instead of the photo
3688 	InputMessageContent input_message_content;
3689 
3690 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3691 }
3692 
3693 /**
3694  * Link to an animated GIF file stored on the Telegram servers
3695  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultcachedgif)
3696  */
3697 struct InlineQueryResultCachedGif {
3698 	/// Type of the result, must be `"gif"`
3699 	string type = "gif";
3700 
3701 	/// Unique identifier for this result
3702 	string id;
3703 
3704 	/// A valid file identifier for the GIF file
3705 	string gif_file_id;
3706 
3707 @optional:
3708 	/// Title for the result
3709 	string title;
3710 
3711 	/// Caption of the GIF file to be sent
3712 	string caption;
3713 
3714 	/// Parse mode of the caption
3715 	ParseMode parse_mode;
3716 
3717 	/// Inline keyboard attached to the message
3718 	InlineKeyboardMarkup reply_markup;
3719 
3720 	/// Content of the message to be sent instead of the GIF animation
3721 	InputMessageContent input_message_content;
3722 
3723 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3724 }
3725 
3726 /**
3727  * Link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the Telegram servers
3728  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultcachedmpeg4gif)
3729  */
3730 struct InlineQueryResultCachedMpeg4Gif {
3731 	/// Type of the result, must be `"mpeg4_gif"`
3732 	string type = "mpeg4_gif";
3733 
3734 	/// Unique identifier for this result
3735 	string id;
3736 
3737 	/// A valid file identifier for the MP4 file
3738 	string mpeg4_file_id;
3739 
3740 @optional:
3741 	/// Title for the result
3742 	string title;
3743 
3744 	/// Caption of the MPEG-4 file to be sent
3745 	string caption;
3746 
3747 	/// Parse mode of the caption
3748 	ParseMode parse_mode;
3749 
3750 	/// Inline keyboard attached to the message
3751 	InlineKeyboardMarkup reply_markup;
3752 
3753 	/// Content of the message to be sent instead of the video animation
3754 	InputMessageContent input_message_content;
3755 
3756 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3757 }
3758 
3759 /**
3760  * Link to a sticker stored on the Telegram servers
3761  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultcachedsticker)
3762  */
3763 struct InlineQueryResultCachedSticker {
3764 	/// Type of the result, must be `"sticker"`
3765 	string type = "sticker";
3766 
3767 	/// Unique identifier for this result
3768 	string id;
3769 
3770 	/// A valid file identifier of the sticker
3771 	string sticker_file_id;
3772 
3773 @optional:
3774 	/// Inline keyboard attached to the message
3775 	InlineKeyboardMarkup reply_markup;
3776 
3777 	/// Content of the message to be sent instead of the sticker
3778 	InputMessageContent input_message_content;
3779 
3780 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3781 }
3782 
3783 /**
3784  * Link to a file stored on the Telegram servers
3785  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultcacheddocument)
3786  */
3787 struct InlineQueryResultCachedDocument {
3788 	/// Type of the result, must be `"document"`
3789 	string type = "document";
3790 
3791 	/// Unique identifier for this result
3792 	string id;
3793 
3794 	/// Title for the result
3795 	string title;
3796 
3797 	/// A valid file identifier for the file
3798 	string document_file_id;
3799 
3800 @optional:
3801 	/// Short description of the result
3802 	string description;
3803 
3804 	/// Caption of the document to be sent
3805 	string caption;
3806 
3807 	/// Parse mode of the document
3808 	ParseMode parse_mode;
3809 
3810 	/// Inline keyboard attached to the message
3811 	InlineKeyboardMarkup reply_markup;
3812 
3813 	/// Content of the message to be sent instead of the file
3814 	InputMessageContent input_message_content;
3815 
3816 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3817 }
3818 
3819 /**
3820  * Link to a video file stored on the Telegram servers
3821  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultcachedvideo)
3822  */
3823 struct InlineQueryResultCachedVideo {
3824 	/// Type of the result, must be `"video"`
3825 	string type = "video";
3826 
3827 	/// Unique identifier for this result
3828 	string id;
3829 
3830 	/// A valid file identifier for the video file
3831 	string video_file_id;
3832 
3833 	/// Title for the result
3834 	string title;
3835 
3836 @optional:
3837 	/// Short description of the result
3838 	string description;
3839 
3840 	/// Caption of the video to be sent
3841 	string caption;
3842 
3843 	/// Parse mode of the caption
3844 	ParseMode parse_mode;
3845 
3846 	/// Inline keyboard attached to the message
3847 	InlineKeyboardMarkup reply_markup;
3848 
3849 	/// Content of the message to be sent instead of the video
3850 	InputMessageContent input_message_content;
3851 
3852 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3853 }
3854 
3855 /**
3856  * Link to a voice message stored on the Telegram servers
3857  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultcachedvoice)
3858  */
3859 struct InlineQueryResultCachedVoice {
3860 	/// Type of the result, must be `"voice"`
3861 	string type = "voice";
3862 
3863 	/// Unique identifier for this result
3864 	string id;
3865 
3866 	/// A valid file identifier for the voice message
3867 	string voice_file_id;
3868 
3869 	/// Voice message title
3870 	string title;
3871 
3872 @optional:
3873 	/// Caption of the voice message to be sent
3874 	string caption;
3875 
3876 	/// Parse mode of the caption
3877 	ParseMode parse_mode;
3878 
3879 	/// Inline keyboard attached to the message
3880 	InlineKeyboardMarkup reply_markup;
3881 
3882 	/// Content of the message to be sent instead of the voice message
3883 	InputMessageContent input_message_content;
3884 
3885 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3886 }
3887 
3888 /**
3889  * Link to an mp3 audio file stored on the Telegram servers
3890  * See_Also: $(LINK https://core.telegram.org/bots/api#inlinequeryresultcachedaudio)
3891  */
3892 struct InlineQueryResultCachedAudio {
3893 	/// Type of the result, must be `"audio"`
3894 	string type = "audio";
3895 
3896 	/// Unique identifier for this result
3897 	string id;
3898 
3899 	/// A valid file identifier for the audio file
3900 	string audio_file_id;
3901 
3902 @optional:
3903 	/// Caption of the audio to be sent
3904 	string caption;
3905 
3906 	/// Parse mode of the caption
3907 	ParseMode parse_mode;
3908 
3909 	/// Inline keyboard attached to the message
3910 	InlineKeyboardMarkup reply_markup;
3911 
3912 	/// Content of the message to be sent instead of the audio
3913 	InputMessageContent input_message_content;
3914 
3915 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
3916 }
3917 
3918 /**
3919  * Content of a message to be sent as a result of an inline query
3920  * See_Also: `InputTextMessageContent`, `InputLocationMessageContent`, `InputVenueMessageContent`, `InputContactMessageContent`
3921  */
3922 alias InputMessageContent = Algebraic!(InputTextMessageContent, InputLocationMessageContent, InputVenueMessageContent, InputContactMessageContent);
3923 
3924 /// Checks if `T` is one of the `InputMessageContent` types
3925 enum isInputMessageContent(T) = is(T == InputMessageContent) || InputMessageContent.allowed!T;
3926 
3927 ///
3928 @("isInputMessageContent")
3929 unittest {
3930 	isInputMessageContent!InputMessageContent.should.be.equal(true);
3931 	isInputMessageContent!InputTextMessageContent.should.be.equal(true);
3932 	isInputMessageContent!InputVenueMessageContent.should.be.equal(true);
3933 	isInputMessageContent!InputContactMessageContent.should.be.equal(true);
3934 	isInputMessageContent!string.should.be.equal(false);
3935 	isInputMessageContent!int.should.be.equal(false);
3936 	isInputMessageContent!bool.should.be.equal(false);
3937 }
3938 
3939 /**
3940  * Content of a text message to be sent as the result of an inline query
3941  * See_Also: $(LINK https://core.telegram.org/bots/api#inputtextmessagecontent)
3942  */
3943 struct InputTextMessageContent {
3944 	/// Text of the message to be sent
3945 	string message_text;
3946 
3947 @optional:
3948 	/// Parse mode of the text
3949 	ParseMode parse_mode;
3950 
3951 	/// Disable link previews for links in the sent message
3952 	bool disable_web_page_preview;
3953 
3954 	@safe pure nothrow @nogc bool isNull() { return message_text == typeof(message_text).init; }
3955 }
3956 
3957 /**
3958  * Content of a location message to be sent as the result of an inline query
3959  * See_Also: $(LINK https://core.telegram.org/bots/api#inputlocationmessagecontent)
3960  */
3961 struct InputLocationMessageContent {
3962 	/// Latitude of the location in degrees
3963 	float latitude;
3964 
3965 	/// Longitude of the location in degrees
3966 	float longitude;
3967 
3968 @optional:
3969 	/// Period for which the location can be updated
3970 	Duration live_period;
3971 
3972 	@safe pure nothrow @nogc bool isNull() { return latitude.isNaN; }
3973 }
3974 
3975 /**
3976  * Content of a venue message to be sent as the result of an inline query
3977  * See_Also: $(LINK https://core.telegram.org/bots/api#inputvenuemessagecontent)
3978  */
3979 struct InputVenueMessageContent {
3980 	/// Latitude of the venue in degrees
3981 	float latitude;
3982 
3983 	/// Longitude of the venue in degrees
3984 	float longitude;
3985 
3986 	/// Name of the venue
3987 	string title;
3988 
3989 	/// Address of the venue
3990 	string address;
3991 
3992 @optional:
3993 	/// Foursquare identifier of the venue, if known
3994 	string foursquare_id;
3995 
3996 	/// Foursquare type of the venue, if known
3997 	string foursquare_type;
3998 
3999 	@safe pure nothrow @nogc bool isNull() { return latitude.isNaN; }
4000 }
4001 
4002 /**
4003  * Content of a contact message to be sent as the result of an inline query
4004  * See_Also: $(LINK https://core.telegram.org/bots/api#inputcontactmessagecontent)
4005  */
4006 struct InputContactMessageContent {
4007 	/// Contact's phone number
4008 	string phone_number;
4009 
4010 	/// Contact's first name
4011 	string first_name;
4012 
4013 @optional:
4014 	/// Contact's last name
4015 	string last_name;
4016 
4017 	/// Additional data about the contact in the form of a vCard
4018 	string vcard;
4019 
4020 	@safe pure nothrow @nogc bool isNull() { return phone_number == typeof(phone_number).init; }
4021 }
4022 
4023 /**
4024  * Result of an inline query that was chosen by the user and sent to their chat partner
4025  * See_Also: $(LINK https://core.telegram.org/bots/api#choseninlineresult)
4026  */
4027 struct ChosenInlineResult {
4028 	/// The unique identifier for the result that was chosen
4029 	string result_id;
4030 
4031 	/// The user that chose the result
4032 	User from;
4033 
4034 	/// The query that was used to obtain the result
4035 	string query;
4036 
4037 @optional:
4038 	/// Sender location, only for bots that require user location
4039 	Location location;
4040 
4041 	/// Identifier of the sent inline message
4042 	string inline_message_id;
4043 
4044 	@safe pure nothrow @nogc bool isNull() { return result_id == typeof(result_id).init; }
4045 }
4046 
4047 /**
4048  * Portion of the price for goods or services
4049  * See_Also: $(LINK https://core.telegram.org/bots/api#labeledprice)
4050  */
4051 struct LabeledPrice {
4052 	/// Portion label
4053 	string label;
4054 
4055 	/// Price of the product in the smallest units of the currency
4056 	int amount;
4057 
4058 	@safe pure nothrow @nogc bool isNull() { return label == typeof(label).init; }
4059 }
4060 
4061 /**
4062  * Basic information about an invoice
4063  * See_Also: $(LINK https://core.telegram.org/bots/api#invoice)
4064  */
4065 struct Invoice {
4066 	/// Product name
4067 	string title;
4068 
4069 	/// Product description
4070 	string description;
4071 
4072 	/// Unique bot deep-linking parameter that can be used to generate this invoice
4073 	string start_parameter;
4074 
4075 	/// Three-letter ISO 4217 currency code
4076 	string currency;
4077 
4078 	/// Total price in the smallest units of the currency
4079 	int total_amount;
4080 
4081 	@safe pure nothrow @nogc bool isNull() { return title == typeof(title).init; }
4082 }
4083 
4084 /**
4085  * Shipping address
4086  * See_Also: $(LINK https://core.telegram.org/bots/api#shippingaddress)
4087  */
4088 struct ShippingAddress {
4089 	/// ISO 3166-1 alpha-2 country code
4090 	string country_code;
4091 
4092 	/// State, if applicable
4093 	string state;
4094 
4095 	/// City
4096 	string city;
4097 
4098 	/// First line for the address
4099 	string street_line1;
4100 
4101 	/// Second line for the address
4102 	string street_line2;
4103 
4104 	/// Address post code
4105 	string post_code;
4106 
4107 	@safe pure nothrow @nogc bool isNull() { return country_code == typeof(country_code).init; }
4108 }
4109 
4110 /**
4111  * Information about an order
4112  * See_Also: $(LINK https://core.telegram.org/bots/api#orderinfo)
4113  */
4114 struct OrderInfo {
4115 @optional:
4116 	/// User name
4117 	string name;
4118 
4119 	/// User's phone number
4120 	string phone_number;
4121 
4122 	/// User email
4123 	string email;
4124 
4125 	/// User shipping address
4126 	ShippingAddress shipping_address;
4127 
4128 	@safe pure nothrow @nogc bool isNull() { return !name.length && !phone_number.length && !email.length && shipping_address.isNull; }
4129 }
4130 
4131 /**
4132  * Shipping option
4133  * See_Also: $(LINK https://core.telegram.org/bots/api#shippingoption)
4134  */
4135 struct ShippingOption {
4136 	/// Shipping option identifier
4137 	string id;
4138 
4139 	/// Option title
4140 	string title;
4141 
4142 	/// List of price portions
4143 	LabeledPrice[] prices;
4144 
4145 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
4146 }
4147 
4148 /**
4149  * Basic information about a successful payment
4150  * See_Also: $(LINK https://core.telegram.org/bots/api#successfulpayment)
4151  */
4152 struct SuccessfulPayment {
4153 	/// Three-letter ISO 4217 currency code
4154 	string currency;
4155 
4156 	/// Total price in the smallest units of the currency
4157 	int total_amount;
4158 
4159 	/// Bot specified invoice payload
4160 	string invoice_payload;
4161 
4162 	/// Telegram payment identifier
4163 	string telegram_payment_charge_id;
4164 
4165 	/// Provider payment identifier
4166 	string provider_payment_charge_id;
4167 
4168 @optional:
4169 	/// Identifier of the shipping option chosen by the user
4170 	string shipping_option_id;
4171 
4172 	/// Order info provided by the user
4173 	OrderInfo order_info;
4174 
4175 	@safe pure nothrow @nogc bool isNull() { return currency == typeof(currency).init; }
4176 }
4177 
4178 /**
4179  * Information about an incoming shipping query
4180  * See_Also: $(LINK https://core.telegram.org/bots/api#shippingquery)
4181  */
4182 struct ShippingQuery {
4183 	/// Unique query identifier
4184 	string id;
4185 
4186 	/// User who sent the query
4187 	User from;
4188 
4189 	/// Bot specified invoice payload
4190 	string invoice_payload;
4191 
4192 	/// User specified shipping address
4193 	ShippingAddress shipping_address;
4194 
4195 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
4196 }
4197 
4198 /**
4199  * Information about an incoming pre-checkout query
4200  * See_Also: $(LINK https://core.telegram.org/bots/api#precheckoutquery)
4201  */
4202 struct PreCheckoutQuery {
4203 	/// Unique query identifier
4204 	string id;
4205 
4206 	/// User who sent the query
4207 	User from;
4208 
4209 	/// Three-letter ISO 4217 currency code
4210 	string currency;
4211 
4212 	/// Total price in the smallest units of the currency
4213 	int total_amount;
4214 
4215 	/// Bot specified invoice payload
4216 	string invoice_payload;
4217 
4218 @optional:
4219 	/// Identifier of the shipping option chosen by the user
4220 	string shipping_option_id;
4221 
4222 	/// Order info provided by the user
4223 	OrderInfo order_info;
4224 
4225 	@safe pure nothrow @nogc bool isNull() { return id == typeof(id).init; }
4226 }
4227 
4228 /**
4229  * Game
4230  * See_Also: $(LINK https://core.telegram.org/bots/api#game)
4231  */
4232 struct Game {
4233 	/// Title of the game
4234 	string title;
4235 
4236 	/// Description of the game
4237 	string description;
4238 
4239 	/// Photo that will be displayed in the game message in chats
4240 	PhotoSize[] photo;
4241 
4242 @optional:
4243 	/// Brief description of the game or high scores included in the game message
4244 	string text;
4245 
4246 	/// Special entities that appear in `text`
4247 	MessageEntity[] text_entities;
4248 
4249 	/// Animation that will be displayed in the game message in chats
4250 	Animation animation;
4251 
4252 	@safe pure nothrow @nogc bool isNull() { return title == typeof(title).init; }
4253 }
4254 
4255 /**
4256  * A placeholder, currently holds no information
4257  * See_Also: $(LINK https://core.telegram.org/bots/api#callbackgame)
4258  */
4259 struct CallbackGame {
4260 	@safe pure nothrow @nogc bool isNull() { return true; }
4261 }
4262 
4263 /**
4264  * One row of the high scores table for a game
4265  * See_Also: $(LINK https://core.telegram.org/bots/api#gamehighscore)
4266  */
4267 struct GameHighScore {
4268 	/// Position in high score table for the game
4269 	int position;
4270 
4271 	/// User
4272 	User user;
4273 
4274 	/// Score
4275 	int score;
4276 
4277 	@safe pure nothrow @nogc bool isNull() { return position == typeof(position).init; }
4278 }
4279 
4280 /// Unique identifier for the target chat or username of the target channel
4281 alias TelegramID = Algebraic!(long, string);
4282 
4283 /// Check if `T` is a valid `TelegramID`
4284 enum isTelegramID(T) = is(T == TelegramID) || is(T == long) || is(T == string);
4285 
4286 /// 
4287 @("isTelegramID")
4288 unittest {
4289 	isTelegramID!TelegramID.should.be.equal(true);
4290 	isTelegramID!long.should.be.equal(true);
4291 	isTelegramID!string.should.be.equal(true);
4292 
4293 	isTelegramID!bool.should.be.equal(false);
4294 	isTelegramID!float.should.be.equal(false);
4295 }
4296 
4297 /*                        Telegram methods                        */
4298 
4299 private mixin template TelegramMethod(string path) {
4300 	package @ignore immutable string m_path = path;
4301 }
4302 
4303 /**
4304  * Method to receive incoming updates using long polling
4305  * See_Also: `TelegramBot.getUpdates`, $(LINK https://core.telegram.org/bots/api#getupdates)
4306  */
4307 struct GetUpdatesMethod {
4308 	mixin TelegramMethod!"/getUpdates";
4309 
4310 @optional:
4311 	/// Identifier of the first update to be returned
4312 	int offset;
4313 
4314 	/// Limits the number of updates to be retrieved
4315 	int limit = 100;
4316 
4317 	/// Timeout for long polling
4318 	Duration timeout = 0.seconds;
4319 
4320 	/// List the types of updates you want your bot to receive
4321 	string[] allowed_updates = [];
4322 }
4323 
4324 /**
4325  * Method to specify a url and receive incoming updates via an outgoing webhook
4326  * See_Also: `TelegramBot.setWebhook`, $(LINK https://core.telegram.org/bots/api#setwebhook)
4327  */
4328 struct SetWebhookMethod {
4329 	mixin TelegramMethod!"/setWebhook";
4330 
4331 	/// HTTPS url to send updates to
4332 	string url;
4333 
4334 @optional:
4335 	/// Public key certificate so that the root certificate in use can be checked
4336 	InputFile certificate;
4337 
4338 	/// Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery
4339 	int max_connections = 40;
4340 
4341 	/// List the types of updates you want your bot to receive
4342 	string[] allowed_updates = [];
4343 }
4344 
4345 /**
4346  * Method to remove webhook integration
4347  * See_Also: `TelegramBot.deleteWebhook`, $(LINK https://core.telegram.org/bots/api#deletewebhook)
4348  */
4349 struct DeleteWebhookMethod {
4350 	mixin TelegramMethod!"/deleteWebhook";
4351 }
4352 
4353 /**
4354  * Method to get current webhook status
4355  * See_Also: `TelegramBot.getWebhookInfo`, $(LINK https://core.telegram.org/bots/api#getwebhookinfo)
4356  */
4357 struct GetWebhookInfoMethod {
4358 	mixin TelegramMethod!"/getWebhookInfo";
4359 }
4360 
4361 /**
4362  * Method to get basic information about the bot
4363  * See_Also: `TelegramBot.getMe`, $(LINK https://core.telegram.org/bots/api#getme)
4364  */
4365 struct GetMeMethod {
4366 	mixin TelegramMethod!"/getMe";
4367 }
4368 
4369 /**
4370  * Method to send text messages
4371  * See_Also: `TelegramBot.sendMessage`, $(LINK https://core.telegram.org/bots/api#sendmessage)
4372  */
4373 struct SendMessageMethod {
4374 	mixin TelegramMethod!"/sendMessage";
4375 
4376 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4377 	TelegramID chat_id;
4378 
4379 	/// Text of the message to be sent
4380 	string text;
4381 
4382 @optional:
4383 	/// Parse mode of the message
4384 	ParseMode parse_mode;
4385 
4386 	/// Disable link previews for links in this message
4387 	bool disable_web_page_preview;
4388 
4389 	/// Send the message silently
4390 	bool disable_notification;
4391 
4392 	/// If the message is a reply, ID of the original message
4393 	int reply_to_message_id;
4394 
4395 	/// Additional interface options
4396 	ReplyMarkup reply_markup;
4397 }
4398 
4399 /**
4400  * Method to forward messages
4401  * See_Also: `TelegramBot.forwardMessage`, $(LINK https://core.telegram.org/bots/api#forwardmessage)
4402  */
4403 struct ForwardMessageMethod {
4404 	mixin TelegramMethod!"/forwardMessage";
4405 
4406 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4407 	TelegramID chat_id;
4408 
4409 	/// Unique identifier for the chat where the original message was sent (or channel username in the format `"@channelusername"`)
4410 	TelegramID from_chat_id;
4411 
4412 	/// Message identifier in the chat specified in `from_chat_id`
4413 	int message_id;
4414 
4415 @optional:
4416 	/// Send the message silently
4417 	bool disable_notification;
4418 }
4419 
4420 /**
4421  * Method to send photos
4422  * See_Also: `TelegramBot.sendPhoto`, $(LINK https://core.telegram.org/bots/api#sendphoto)
4423  */
4424 struct SendPhotoMethod {
4425 	mixin TelegramMethod!"/sendPhoto";
4426 
4427 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4428 	TelegramID chat_id;
4429 
4430 	/**
4431 	 * Photo to send
4432 	 *
4433 	 * Pass a file_id to send a photo that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a photo from the Internet
4434 	 */
4435 	string photo;
4436 
4437 @optional:
4438 	/// Photo caption
4439 	string caption;
4440 
4441 	/// Parse mode of the caption
4442 	ParseMode parse_mode;
4443 	
4444 	/// Send the message silently
4445 	bool disable_notification;
4446 
4447 	/// If the message is a reply, ID of the original message
4448 	int reply_to_message_id;
4449 
4450 	/// Additional interface options
4451 	ReplyMarkup reply_markup;
4452 }
4453 
4454 /**
4455  * Method to send audio files
4456  * See_Also: `TelegramBot.sendAudio`, $(LINK https://core.telegram.org/bots/api#sendaudio)
4457  */
4458 struct SendAudioMethod {
4459 	mixin TelegramMethod!"/sendAudio";
4460 
4461 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4462 	TelegramID chat_id;
4463 
4464 	
4465 	/**
4466 	 * Audio ile to send
4467 	 *
4468 	 * Pass a file_id to send an audio file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get an audio file from the Internet
4469 	 */
4470 	string audio;
4471 
4472 @optional:
4473 	/// Audio caption
4474 	string caption;
4475 
4476 	/// Parse mode of the caption
4477 	ParseMode parse_mode;
4478 
4479 	/// Duration of the audio
4480 	Duration duration;
4481 
4482 	/// Performer
4483 	string performer;
4484 
4485 	/// Track name
4486 	string title;
4487 
4488 	/// Thumbnail of the file sent
4489 	string thumb;
4490 
4491 	/// Send the message silently
4492 	bool disable_notification;
4493 
4494 	/// If the message is a reply, ID of the original message
4495 	int reply_to_message_id;
4496 
4497 	/// Additional interface options
4498 	ReplyMarkup reply_markup;
4499 }
4500 
4501 /**
4502  * Method to send general files
4503  * See_Also: `TelegramBot.sendDocument`, $(LINK https://core.telegram.org/bots/api#senddocument)
4504  */
4505 struct SendDocumentMethod {
4506 	mixin TelegramMethod!"/sendDocument";
4507 
4508 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4509 	TelegramID chat_id;
4510 
4511 	/**
4512 	 * File to send
4513 	 *
4514 	 * Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet
4515 	 */
4516 	string document;
4517 
4518 @optional:
4519 	/// Thumbnail of the file sent
4520 	string thumb;
4521 
4522 	/// Document caption
4523 	string caption;
4524 
4525 	/// Parse mode of the caption
4526 	ParseMode parse_mode;
4527 
4528 	/// Send the message silently
4529 	bool disable_notification;
4530 
4531 	/// If the message is a reply, ID of the original message
4532 	int reply_to_message_id;
4533 
4534 	/// Additional interface options
4535 	ReplyMarkup reply_markup;
4536 }
4537 
4538 /**
4539  * Method to send video files
4540  * See_Also: `TelegramBot.sendVideo`, $(LINK https://core.telegram.org/bots/api#sendvideo)
4541  */
4542 struct SendVideoMethod {
4543 	mixin TelegramMethod!"/sendVideo";
4544 
4545 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4546 	TelegramID chat_id;
4547 
4548 	/**
4549 	 * Video file to send
4550 	 *
4551 	 * Pass a file_id to send a video file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a video file from the Internet
4552 	 */
4553 	string video;
4554 
4555 @optional:
4556 	/// Duration of sent video
4557 	Duration duration;
4558 
4559 	/// Video width
4560 	int width;
4561 
4562 	/// Video height
4563 	int height;
4564 
4565 	/// Thumbnail of the file sent
4566 	string thumb;
4567 
4568 	/// Video caption
4569 	string caption;
4570 
4571 	/// Parse mode of the caption
4572 	ParseMode parse_mode;
4573 
4574 	/// Pass `true`, if the uploaded video is suitable for streaming
4575 	bool supports_streaming;
4576 
4577 	/// Send the message silently
4578 	bool disable_notification;
4579 
4580 	/// If the message is a reply, ID of the original message
4581 	int reply_to_message_id;
4582 
4583 	/// Additional interface options
4584 	ReplyMarkup reply_markup;
4585 }
4586 
4587 /**
4588  * Method to send animation files
4589  * See_Also: `TelegramBot.sendAnimation`, $(LINK https://core.telegram.org/bots/api#sendanimation)
4590  */
4591 struct SendAnimationMethod {
4592 	mixin TelegramMethod!"/sendAnimation";
4593 
4594 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4595 	TelegramID chat_id;
4596 
4597 	/**
4598 	 * Animation file to send
4599 	 *
4600 	 * Pass a file_id to send an animation file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get an animation file from the Internet
4601 	 */
4602 	string animation;
4603 
4604 @optional:
4605 	/// Duration of sent animation
4606 	Duration duration;
4607 
4608 	/// Animation width
4609 	int width;
4610 
4611 	/// Animation height
4612 	int height;
4613 
4614 	/// Thumbnail of the file sent
4615 	string thumb;
4616 
4617 	/// Animation caption
4618 	string caption;
4619 
4620 	/// Parse mode of the animation
4621 	ParseMode parse_mode;
4622 
4623 	/// Send the message silently
4624 	bool disable_notification;
4625 
4626 	/// If the message is a reply, ID of the original message
4627 	int reply_to_message_id;
4628 
4629 	/// Additional interface options
4630 	ReplyMarkup reply_markup;
4631 }
4632 
4633 /**
4634  * Method to send audio files that are treated as a playable voice messages
4635  * See_Also: `TelegramBot.sendVoice`, $(LINK https://core.telegram.org/bots/api#sendvoice)
4636  */
4637 struct SendVoiceMethod {
4638 	mixin TelegramMethod!"/sendVoice";
4639 
4640 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4641 	TelegramID chat_id;
4642 
4643 	/**
4644 	 * Audio file to send
4645 	 *
4646 	 * Pass a file_id to send an audio file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get an audio file from the Internet
4647 	 */
4648 	string voice;
4649 
4650 @optional:
4651 	/// Voice message caption
4652 	string caption;
4653 
4654 	/// Parse mode of the caption
4655 	ParseMode parse_mode;
4656 
4657 	/// Duration of the voice message
4658 	Duration duration;
4659 
4660 	/// Send the message silently
4661 	bool disable_notification;
4662 
4663 	/// If the message is a reply, ID of the original message
4664 	int reply_to_message_id;
4665 
4666 	/// Additional interface options
4667 	ReplyMarkup reply_markup;
4668 }
4669 
4670 /**
4671  * Method to send video messages
4672  * See_Also: `TelegramBot.sendVideoNote`, $(LINK https://core.telegram.org/bots/api#sendvideonote)
4673  */
4674 struct SendVideoNoteMethod {
4675 	mixin TelegramMethod!"/sendVideoNote";
4676 
4677 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4678 	TelegramID chat_id;
4679 
4680 	/**
4681 	 * Video note to send
4682 	 *
4683 	 * Pass a file_id to send a video note that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a video note from the Internet
4684 	 */
4685 	string video_note;
4686 
4687 @optional:
4688 	/// Duration of sent video
4689 	Duration duration;
4690 
4691 	/// Video width and height, i.e. diameter of the video message
4692 	int length;
4693 
4694 	/// Thumbnail of the file sent
4695 	string thumb;
4696 
4697 	/// Send the message silently
4698 	bool disable_notification;
4699 
4700 	/// If the message is a reply, ID of the original message
4701 	int reply_to_message_id;
4702 
4703 	/// Additional interface options
4704 	ReplyMarkup reply_markup;
4705 }
4706 
4707 /**
4708  * Method to send a group of photos or audios as an album
4709  * See_Also: `TelegramBot.sendMediaGroup`, $(LINK https://core.telegram.org/bots/api#sendmediagroup)
4710  */
4711 struct SendMediaGroupMethod {
4712 	mixin TelegramMethod!"/sendMediaGroup";
4713 
4714 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4715 	TelegramID chat_id;
4716 
4717 	/// Photos and videos to be sent
4718 	Algebraic!(InputMediaPhoto, InputMediaVideo)[] media;
4719 
4720 @optional:
4721 	/// Send the message silently
4722 	bool disable_notification;
4723 
4724 	/// If the messages are a reply, ID of the original message
4725 	int reply_to_message_id;
4726 }
4727 
4728 /**
4729  * Method to send point on the map
4730  * See_Also: `TelegramBot.sendLocation`, $(LINK https://core.telegram.org/bots/api#sendlocation)
4731  */
4732 struct SendLocationMethod {
4733 	mixin TelegramMethod!"/sendLocation";
4734 
4735 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4736 	TelegramID chat_id;
4737 
4738 	/// Latitude of the location
4739 	float latitude;
4740 
4741 	/// Longitude of the location
4742 	float longitude;
4743 
4744 @optional:
4745 	/// Period for which the location will be updated
4746 	Duration live_period;
4747 
4748 	/// Send the message silently
4749 	bool disable_notification;
4750 
4751 	/// If the message is a reply, ID of the original message
4752 	int reply_to_message_id;
4753 
4754 	/// Additional interface options
4755 	ReplyMarkup reply_markup;
4756 }
4757 
4758 /**
4759  * Method to edit live location messages
4760  * See_Also: `TelegramBot.editLiveLocation`, $(LINK https://core.telegram.org/bots/api#editmessagelivelocation)
4761  */
4762 struct EditMessageLiveLocationMethod {
4763 	mixin TelegramMethod!"/editMessageLiveLocation";
4764 
4765 	/// Latitude of new location
4766 	float latitude;
4767 
4768 	/// Longitude of new location
4769 	float longitude;
4770 
4771 @optional:
4772 	/// Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4773 	TelegramID chat_id;
4774 
4775 	/// Required if `inline_message_id` is not specified. Identifier of the sent message
4776 	int message_id;
4777 
4778 	/// Required if `chat_id` and `message_id` are not specified. Identifier of the inline message
4779 	string inline_message_id;
4780 
4781 	/// Inline keyboard attached to a message
4782 	InlineKeyboardMarkup reply_markup;
4783 }
4784 
4785 /**
4786  * Method to stop updating a live location message
4787  * See_Also: `TelegramBot.stopMessageLiveLocation`, $(LINK https://core.telegram.org/bots/api#stopmessagelivelocation)
4788  */
4789 struct StopMessageLiveLocationMethod {
4790 	mixin TelegramMethod!"/stopMessageLiveLocation";
4791 
4792 @optional:
4793 	/// Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4794 	TelegramID chat_id;
4795 
4796 	/// Required if `inline_message_id` is not specified. Identifier of the sent message
4797 	int message_id;
4798 
4799 	/// Required if `chat_id` and `message_id` are not specified. Identifier of the inline message
4800 	string inline_message_id;
4801 
4802 	/// Inline keyboard attached to a message
4803 	InlineKeyboardMarkup reply_markup;
4804 }
4805 
4806 /**
4807  * Method to send information about a venue
4808  * See_Also: `TelegramBot.sendVenue`, $(LINK https://core.telegram.org/bots/api#sendvenue)
4809  */
4810 struct SendVenueMethod {
4811 	mixin TelegramMethod!"/sendVenue";
4812 
4813 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4814 	TelegramID chat_id;
4815 
4816 	/// Latitude of the venue
4817 	float latitude;
4818 
4819 	/// Longitude of the venue
4820 	float longitude;
4821 
4822 	/// Name of the venue
4823 	string title;
4824 
4825 	/// Address of the venue
4826 	string address;
4827 
4828 @optional:
4829 	/// Foursquare identifier of the venue
4830 	string foursquare_id;
4831 
4832 	/// Foursquare type of the venue, if known
4833 	string foursquare_type;
4834 
4835 	/// Send the message silently
4836 	bool disable_notification;
4837 
4838 	/// If the message is a reply, ID of the original message
4839 	int reply_to_message_id;
4840 
4841 	/// Additional interface options
4842 	ReplyMarkup reply_markup;
4843 }
4844 
4845 /**
4846  * Method to send phone contacts
4847  * See_Also: `TelegramBot.sendContact`, $(LINK https://core.telegram.org/bots/api#sendcontact)
4848  */
4849 struct SendContactMethod {
4850 	mixin TelegramMethod!"/sendContact";
4851 
4852 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4853 	TelegramID chat_id;
4854 
4855 	/// Contact's phone number
4856 	string phone_number;
4857 
4858 	/// Contact's first name
4859 	string first_name;
4860 
4861 @optional:
4862 	/// Contact's last name
4863 	string last_name;
4864 	
4865 	/// Additional data about the contact in the form of a vCard
4866 	string vcard;
4867 
4868 	/// Send the message silently
4869 	bool disable_notification;
4870 
4871 	/// If the message is a reply, ID of the original message
4872 	int reply_to_message_id;
4873 
4874 	/// Additional interface options
4875 	ReplyMarkup reply_markup;
4876 }
4877 
4878 /**
4879  * Method to send chat action
4880  * See_Also: `TelegramBot.sendChatAction`, $(LINK https://core.telegram.org/bots/api#sendchataction)
4881  */
4882 struct SendChatActionMethod {
4883 	mixin TelegramMethod!"/sendChatAction";
4884 
4885 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4886 	TelegramID chat_id;
4887 
4888 	/// Type of action to broadcast
4889 	ChatAction action;
4890 }
4891 
4892 /**
4893  * Method to get a list of profile pictures for a user
4894  * See_Also: `TelegramBot.getUserProfilePhotos`, $(LINK https://core.telegram.org/bots/api#getuserprofilephotos)
4895  */
4896 struct GetUserProfilePhotosMethod {
4897 	mixin TelegramMethod!"/getUserProfilePhotos";
4898 
4899 	/// Unique identifier of the target user
4900 	int user_id;
4901 
4902 @optional:
4903 	/// Sequential number of the first photo to be returned. By default, all photos are returned.
4904 	int offset;
4905 
4906 	/// Limits the number of photos to be retrieved
4907 	int limit = 100;
4908 }
4909 
4910 /**
4911  * Method to get basic info about a file and prepare it for downloading
4912  * See_Also: `TelegramBot.getFile`, $(LINK https://core.telegram.org/bots/api#getfile)
4913  */
4914 struct GetFileMethod {
4915 	mixin TelegramMethod!"/getFile";
4916 
4917 	/// File identifier to get info about
4918 	string file_id;
4919 }
4920 
4921 /**
4922  * Method to kick a user from a group, a supergroup or a channel
4923  * See_Also: `TelegramBot.kickChatMember`, $(LINK https://core.telegram.org/bots/api#kickchatmember)
4924  */
4925 struct KickChatMemberMethod {
4926 	mixin TelegramMethod!"/kickChatMember";
4927 
4928 	/// Unique identifier for the target group or username of the target supergroup or channel (in the format `"@channelusername"`)
4929 	TelegramID chat_id;
4930 
4931 	/// Unique identifier of the target user
4932 	int user_id;
4933 
4934 @optional:
4935 	/// Date when the user will be unbanned
4936 	SysTime until_date;
4937 }
4938 
4939 /**
4940  * Method to unban a previously kicked user in a supergroup or channel
4941  * See_Also: `TelegramBot.unbanChatMember`, $(LINK https://core.telegram.org/bots/api#unbanchatmember)
4942  */
4943 struct UnbanChatMemberMethod {
4944 	mixin TelegramMethod!"/unbanChatMember";
4945 
4946 	/// Unique identifier for the target group or username of the target supergroup or channel (in the format `"@username"`)
4947 	TelegramID chat_id;
4948 
4949 	/// Unique identifier of the target user
4950 	int user_id;
4951 }
4952 
4953 /**
4954  * Method to restrict a user in a supergroup
4955  * See_Also: `TelegramBot.restrictChatMember`, $(LINK https://core.telegram.org/bots/api#restrictchatmember)
4956  */
4957 struct RestrictChatMemberMethod {
4958 	mixin TelegramMethod!"/restrictChatMember";
4959 
4960 	/// Unique identifier for the target chat or username of the target supergroup (in the format `"@supergroupusername"`)
4961 	TelegramID chat_id;
4962 
4963 	/// Unique identifier of the target user
4964 	int user_id;
4965 
4966 @optional:
4967 	/// Date when restrictions will be lifted for the user
4968 	SysTime until_date;
4969 
4970 	/// Pass `true`, if the user can send text messages, contacts, locations and venues
4971 	bool can_send_messages;
4972 
4973 	//// Pass `true`, if the user can send audios, documents, photos, videos, video notes and voice notes, implies `can_send_messages`
4974 	bool can_send_media_messages;
4975 
4976 	/// Pass `true`, if the user can send animations, games, stickers and use inline bots, implies `can_send_media_messages`
4977 	bool can_send_other_messages;
4978 
4979 	/// Pass `true`, if the user may add web page previews to their messages, implies `can_send_media_messages`
4980 	bool can_add_web_page_previews;
4981 }
4982 
4983 /**
4984  * Method to promote or demote a user in a supergroup or a channel
4985  * See_Also: `TelegramBot.promoteChatMember`, $(LINK https://core.telegram.org/bots/api#promotechatmember)
4986  */
4987 struct PromoteChatMemberMethod {
4988 	mixin TelegramMethod!"/promoteChatMember";
4989 
4990 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
4991 	TelegramID chat_id;
4992 
4993 	/// Unique identifier of the target user
4994 	int user_id;
4995 
4996 @optional:
4997 	/// Pass `true`, if the administrator can change chat title, photo and other settings
4998 	bool can_change_info;
4999 
5000 	/// Pass `true`, if the administrator can create channel posts, channels only
5001 	bool can_post_messages;
5002 
5003 	/// Pass `true`, if the administrator can edit messages of other users and can pin messages, channels only
5004 	bool can_edit_messages;
5005 
5006 	/// Pass `true`, if the administrator can delete messages of other users
5007 	bool can_delete_messages;
5008 
5009 	/// Pass `true`, if the administrator can invite new users to the chat
5010 	bool can_invite_users;
5011 
5012 	/// Pass `true`, if the administrator can restrict, ban or unban chat members
5013 	bool can_restrict_members;
5014 
5015 	/// Pass `true`, if the administrator can pin messages, supergroups only
5016 	bool can_pin_messages;
5017 
5018 	/// Pass `true`, if the administrator can add new administrators 
5019 	bool can_promote_members;
5020 }
5021 
5022 /**
5023  * Method to generate a new invite link for a chat
5024  * See_Also: `TelegramBot.exportChatInviteLink`, $(LINK https://core.telegram.org/bots/api#exportchatinvitelink)
5025  */
5026 struct ExportChatInviteLinkMethod {
5027 	mixin TelegramMethod!"/exportChatInviteLink";
5028 
5029 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5030 	TelegramID chat_id;
5031 }
5032 
5033 /**
5034  * Method to set a new profile photo for the chat
5035  * See_Also: `TelegramBot.setChatPhoto`, $(LINK https://core.telegram.org/bots/api#setchatphoto)
5036  */
5037 struct SetChatPhotoMethod {
5038 	mixin TelegramMethod!"/setChatPhoto";
5039 
5040 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5041 	TelegramID chat_id;
5042 
5043 	/// New chat photo
5044 	InputFile photo;
5045 }
5046 
5047 /**
5048  * Method to delete a chat photo
5049  * See_Also: `TelegramBot.deleteChatPhoto`, $(LINK https://core.telegram.org/bots/api#deletechatphoto)
5050  */
5051 struct DeleteChatPhotoMethod {
5052 	mixin TelegramMethod!"/deleteChatPhoto";
5053 
5054 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5055 	TelegramID chat_id;
5056 }
5057 
5058 /**
5059  * Method to change the title of a chat
5060  * See_Also: `TelegramBot.setChatTitle`, $(LINK https://core.telegram.org/bots/api#setchattitle)
5061  */
5062 struct SetChatTitleMethod {
5063 	mixin TelegramMethod!"/setChatTitle";
5064 
5065 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5066 	TelegramID chat_id;
5067 
5068 	/// New chat title
5069 	string title;
5070 }
5071 
5072 /**
5073  * Method to change the description of a supergroup or a channel
5074  * See_Also: `TelegramBot.setChatDescription`, $(LINK https://core.telegram.org/bots/api#setchatdescription)
5075  */
5076 struct SetChatDescriptionMethod {
5077 	mixin TelegramMethod!"/setChatDescription";
5078 
5079 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5080 	TelegramID chat_id;
5081 
5082 @optional:
5083 	/// New chat description
5084 	string description;
5085 }
5086 
5087 /**
5088  * Method to pin a message in a supergroup or a channel
5089  * See_Also: `TelegramBot.pinChatMessage`, $(LINK https://core.telegram.org/bots/api#pinchatmessage)
5090  */
5091 struct PinChatMessageMethod {
5092 	mixin TelegramMethod!"/pinChatMessage";
5093 
5094 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5095 	TelegramID chat_id;
5096 
5097 	/// Identifier of a message to pin
5098 	int message_id;
5099 
5100 @optional:
5101 	/// Pass `true`, if it is not necessary to send a notification to all chat members about the new pinned message
5102 	bool disable_notification;
5103 }
5104 
5105 /**
5106  * Method to unpin a message in a supergroup or a channel
5107  * See_Also: `TelegramBot.unpinChatMessage`, $(LINK https://core.telegram.org/bots/api#unpinchatmessage)
5108  */
5109 struct UnpinChatMessageMethod {
5110 	mixin TelegramMethod!"/unpinChatMessage";
5111 
5112 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5113 	TelegramID chat_id;
5114 }
5115 
5116 /**
5117  * Method for your bot to leave a group, supergroup or channel
5118  * See_Also: `TelegramBot.leaveChat`, $(LINK https://core.telegram.org/bots/api#leavechat)
5119  */
5120 struct LeaveChatMethod {
5121 	mixin TelegramMethod!"/leaveChat";
5122 
5123 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5124 	TelegramID chat_id;
5125 }
5126 
5127 /**
5128  * Method to get up to date information about the chat
5129  * See_Also: `TelegramBot.getChat`, $(LINK https://core.telegram.org/bots/api#getchat)
5130  */
5131 struct GetChatMethod {
5132 	mixin TelegramMethod!"/getChat";
5133 
5134 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5135 	TelegramID chat_id;
5136 }
5137 
5138 /**
5139  * Method to get a list of administrators in a chat
5140  * See_Also: `TelegramBot.getChatAdministrators`, $(LINK https://core.telegram.org/bots/api#getchatadministrators)
5141  */
5142 struct GetChatAdministratorsMethod {
5143 	mixin TelegramMethod!"/getChatAdministrators";
5144 
5145 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5146 	TelegramID chat_id;
5147 }
5148 
5149 /**
5150  * Method to get the number of members in a chat
5151  * See_Also: `TelegramBot.getChatMembersCount`, $(LINK https://core.telegram.org/bots/api#getchatmemberscount)
5152  */
5153 struct GetChatMembersCountMethod {
5154 	mixin TelegramMethod!"/getChatMembersCount";
5155 
5156 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5157 	TelegramID chat_id;
5158 }
5159 
5160 /**
5161  * Method to get information about a member of a chat
5162  * See_Also: `TelegramBot.getChatMember`, $(LINK https://core.telegram.org/bots/api#getchatmember)
5163  */
5164 struct GetChatMemberMethod {
5165 	mixin TelegramMethod!"/getChatMember";
5166 
5167 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5168 	TelegramID chat_id;
5169 
5170 	/// Unique identifier of the target user
5171 	int user_id;
5172 }
5173 
5174 /**
5175  * Method to set a new group sticker set for a supergroup
5176  * See_Also: `TelegramBot.setChatStickerSet`, $(LINK https://core.telegram.org/bots/api#setchatstickerset)
5177  */
5178 struct SetChatStickerSetMethod {
5179 	mixin TelegramMethod!"/setChatStickerSet";
5180 
5181 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5182 	TelegramID chat_id;
5183 
5184 	/// Name of the sticker set to be set as the group sticker set
5185 	string sticker_set_name;
5186 }
5187 
5188 /**
5189  * Method to delete a group sticker set from a supergroup
5190  * See_Also: `TelegramBot.deleteChatStickerSet`, $(LINK https://core.telegram.org/bots/api#deletechatstickerset)
5191  */
5192 struct DeleteChatStickerSetMethod {
5193 	mixin TelegramMethod!"/deleteChatStickerSet";
5194 
5195 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5196 	TelegramID chat_id;
5197 }
5198 
5199 /**
5200  * Method to send answers to callback queries sent from inline keyboards
5201  * See_Also: `TelegramBot.answerCallbackQuery`, $(LINK https://core.telegram.org/bots/api#answercallbackquery)
5202  */
5203 struct AnswerCallbackQueryMethod {
5204 	mixin TelegramMethod!"/answerCallbackQuery";
5205 
5206 	/// Unique identifier for the query to be answered
5207 	string callback_query_id;
5208 
5209 @optional:
5210 	/// Text of the notification
5211 	string text;
5212 
5213 	/// If `true`, an alert will be shown by the client instead of a notification at the top of the chat screen
5214 	bool show_alert;
5215 
5216 	/// URL that will be opened by the user's client
5217 	string url;
5218 
5219 	/// The maximum amount of time that the result of the callback query may be cached client-side
5220 	Duration cache_time;
5221 }
5222 
5223 /**
5224  * Method to edit text and game messages sent by the bot or via the bot
5225  * See_Also: `TelegramBot.editMessageText`, $(LINK https://core.telegram.org/bots/api#editmessagetext)
5226  */
5227 struct EditMessageTextMethod {
5228 	mixin TelegramMethod!"/editMessageText";
5229 
5230 	/// New text of the message
5231 	string text;
5232 
5233 @optional:
5234 	/// Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5235 	TelegramID chat_id;
5236 
5237 	/// Required if `inline_message_id` is not specified. Identifier of the sent message
5238 	int message_id;
5239 
5240 	/// Required if `chat_id` and `message_id` are not specified. Identifier of the inline message
5241 	string inline_message_id;
5242 
5243 	/// Parse mode of the text
5244 	ParseMode parse_mode;
5245 
5246 	/// Disable link previews for links in this message
5247 	bool disable_web_page_preview;
5248 
5249 	/// Inline keyboard attached to the message
5250 	InlineKeyboardMarkup reply_markup;
5251 }
5252 
5253 /**
5254  * Method to edit captions of messages sent by the bot or via the bot
5255  * See_Also: `TelegramBot.editMessageCaption`, $(LINK https://core.telegram.org/bots/api#editmessagecaption)
5256  */
5257 struct EditMessageCaptionMethod {
5258 	mixin TelegramMethod!"/editMessageCaption";
5259 
5260 @optional:
5261 	/// Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5262 	TelegramID chat_id;
5263 
5264 	/// Required if `inline_message_id` is not specified. Identifier of the sent message
5265 	int message_id;
5266 
5267 	/// Required if `chat_id` and `message_id` are not specified. Identifier of the inline message
5268 	string inline_message_id;
5269 
5270 	/// New caption of the message
5271 	string caption;
5272 
5273 	/// Parse mode of the message 
5274 	ParseMode parse_mode;
5275 
5276 	/// Inline keyboard attached to the message
5277 	InlineKeyboardMarkup reply_markup;
5278 }
5279 
5280 /**
5281  * Method to edit audio, document, photo, or video messages
5282  * See_Also: `TelegramBot.editMessageMedia`, $(LINK https://core.telegram.org/bots/api#editmessagemedia)
5283  */
5284 struct EditMessageMediaMethod {
5285 	mixin TelegramMethod!"/editMessageMedia";
5286 
5287 	/// New media content of the message
5288 	InputMedia media;
5289 
5290 @optional:
5291 	/// Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5292 	TelegramID chat_id;
5293 
5294 	/// Required if `inline_message_id` is not specified. Identifier of the sent message
5295 	int message_id;
5296 
5297 	/// Required if `chat_id` and `message_id` are not specified. Identifier of the inline message
5298 	string inline_message_id;
5299 
5300 	/// Inline keyboard attached to the message
5301 	InlineKeyboardMarkup reply_markup;
5302 }
5303 
5304 /**
5305  * Method to edit only the reply markup of messages sent by the bot or via the bot
5306  * See_Also: `TelegramBot.editMessageReplyMarkup`, $(LINK https://core.telegram.org/bots/api#editmessagereplymarkup)
5307  */
5308 struct EditMessageReplyMarkupMethod {
5309 	mixin TelegramMethod!"/editMessageReplyMarkup";
5310 
5311 @optional:
5312 	/// Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5313 	TelegramID chat_id;
5314 
5315 	/// Required if `inline_message_id` is not specified. Identifier of the sent message
5316 	int message_id;
5317 
5318 	/// Required if `chat_id` and `message_id` are not specified. Identifier of the inline message
5319 	string inline_message_id;
5320 
5321 	/// Inline keyboard attached to the message
5322 	InlineKeyboardMarkup reply_markup;
5323 }
5324 
5325 /**
5326  * Method to delete a message, including service messages
5327  * See_Also: `TelegramBot.deleteMessage`, $(LINK https://core.telegram.org/bots/api#deletemessage)
5328  */
5329 struct DeleteMessageMethod {
5330 	mixin TelegramMethod!"/deleteMessage";
5331 
5332 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5333 	TelegramID chat_id;
5334 
5335 	/// Identifier of the message to delete
5336 	int message_id;
5337 }
5338 
5339 /**
5340  * Method to send .webp stickers
5341  * See_Also: `TelegramBot.sendSticker`, $(LINK https://core.telegram.org/bots/api#sendsticker)
5342  */
5343 struct SendStickerMethod {
5344 	mixin TelegramMethod!"/sendSticker";
5345 
5346 	/// Unique identifier for the target chat or username of the target channel (in the format `"@channelusername"`)
5347 	TelegramID chat_id;
5348 
5349 	/**
5350 	 * Sticker to send
5351 	 *
5352 	 * Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a .webp file from the Internet
5353 	 */
5354 	string sticker;
5355 
5356 @optional:
5357 	/// Send the message silently
5358 	bool disable_notification;
5359 
5360 	/// If the message is a reply, ID of the original message
5361 	int reply_to_message_id;
5362 
5363 	/// Additional interface options
5364 	ReplyMarkup reply_markup;
5365 }
5366 
5367 /**
5368  * Method to get a sticker set
5369  * See_Also: `TelegramBot.getStickerSet`, $(LINK https://core.telegram.org/bots/api#getstickerset)
5370  */
5371 struct GetStickerSetMethod {
5372 	mixin TelegramMethod!"/getStickerSet";
5373 
5374 	/// Name of the sticker set
5375 	string name;
5376 }
5377 
5378 /**
5379  * Method to upload a .png file with a sticker
5380  * See_Also: `TelegramBot.uploadStickerFile`, $(LINK https://core.telegram.org/bots/api#uploadstickerfile)
5381  */
5382 struct UploadStickerFileMethod {
5383 	mixin TelegramMethod!"/uploadStickerFile";
5384 
5385 	/// User identifier of sticker file owner
5386 	int user_id;
5387 
5388 	/// Png image with the sticker
5389 	InputFile png_sticker;
5390 }
5391 
5392 /**
5393  * Method to create new sticker set owned by a user
5394  * See_Also: `TelegramBot.createNewStickerSet`, $(LINK https://core.telegram.org/bots/api#createnewstickerset)
5395  */
5396 struct CreateNewStickerSetMethod {
5397 	mixin TelegramMethod!"/createNewStickerSet";
5398 
5399 	/// User identifier of created sticker set owner
5400 	int user_id;
5401 
5402 	/// Short name of sticker set, to be used in `t.me/addstickers/` URLs 
5403 	string name;
5404 
5405 	/// Sticker set title
5406 	string title;
5407 
5408 	/**
5409 	 * Png image with the sticker
5410 	 *
5411 	 * Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a .png file from the Internet
5412 	 */
5413 	string png_sticker;
5414 
5415 	/// One or more emoji corresponding to the sticker
5416 	string emojis;
5417 
5418 @optional:
5419 	/// Pass `true`, if a set of mask stickers should be created
5420 	bool contains_masks;
5421 
5422 	/// Position where the mask should be placed on faces
5423 	MaskPosition mask_position;
5424 }
5425 
5426 /**
5427  * Method to add a new sticker to a set created by the bot
5428  * See_Also: `TelegramBot.addStickerToSet`, $(LINK https://core.telegram.org/bots/api#addstickertoset)
5429  */
5430 struct AddStickerToSetMethod {
5431 	mixin TelegramMethod!"/addStickerToSet";
5432 
5433 	/// User identifier of sticker set owner
5434 	int user_id;
5435 
5436 	/// Sticker set name
5437 	string name;
5438 
5439 	/**
5440 	 * Png image with the sticker
5441 	 *
5442 	 * Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a .png file from the Internet
5443 	 */
5444 	string png_sticker;
5445 	
5446 	/// One or more emoji corresponding to the sticker
5447 	string emojis;
5448 
5449 @optional:
5450 	/// Position where the mask should be placed on faces
5451 	MaskPosition mask_position;
5452 }
5453 
5454 /**
5455  * Method to move a sticker in a set created by the bot to a specific position
5456  * See_Also: `TelegramBot.setStickerPositionInSet`, $(LINK https://core.telegram.org/bots/api#setstickerpositioninset)
5457  */
5458 struct SetStickerPositionInSetMethod {
5459 	mixin TelegramMethod!"/setStickerPositionInSet";
5460 
5461 	/// File identifier of the sticker
5462 	string sticker;
5463 
5464 	/// New sticker position in the set, zero-based
5465 	int position;
5466 }
5467 
5468 /**
5469  * Method to delete a sticker from a set created by the bot
5470  * See_Also: `TelegramBot.deleteStickerFromSet`, $(LINK https://core.telegram.org/bots/api#deletestickerfromset)
5471  */
5472 struct DeleteStickerFromSetMethod {
5473 	mixin TelegramMethod!"/deleteStickerFromSet";
5474 
5475 	/// File identifier of the sticker
5476 	string sticker;
5477 }
5478 
5479 /**
5480  * Method to send answers to an inline query
5481  * See_Also: `TelegramBot.answerInlineQuery`, $(LINK https://core.telegram.org/bots/api#answerinlinequery)
5482  */
5483 struct AnswerInlineQueryMethod {
5484 	mixin TelegramMethod!"/answerInlineQuery";
5485 
5486 	/// Unique identifier for the answered query
5487 	string inline_query_id;
5488 
5489 	/// Array of results for the inline query
5490 	InlineQueryResult[] results;
5491 
5492 @optional:
5493 	/// The maximum amount of time that the result of the inline query may be cached on the server
5494 	Duration cache_time;
5495 
5496 	/// Pass `true`, if results may be cached on the server side only for the user that sent the query
5497 	bool is_personal;
5498 
5499 	/// Pass the offset that a client should send in the next query with the same text to receive more results
5500 	string next_offset;
5501 
5502 	/// If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter `switch_pm_parameter`
5503 	string switch_pm_text;
5504 
5505 	/// Deep-linking parameter for the `/start` message sent to the bot when user presses the switch button. 1-64 characters, only `A-Z`, `a-z`, `0-9`, `_` and `-` are allowed.
5506 	string switch_pm_parameter;
5507 }
5508 
5509 private:
5510 
5511 enum optional;
5512 enum ignore;
5513 struct name {
5514 	string name;
5515 }
5516 
5517 import std.variant : Algebraic, VariantN;
5518 @trusted Json serializeToJson(T)(T value) {
5519 	import std.traits;
5520 	static if(is(T : Json)) {
5521 		return value;
5522 	} else static if(isInstanceOf!(VariantN, T)) {
5523 		if(!value.hasValue)
5524 			return Json.emptyObject;
5525 
5526 		static foreach(Type; value.AllowedTypes)
5527 			if(value.type == typeid(Type))
5528 				return value.get!Type.serializeToJson;
5529 
5530 		return Json(null);
5531 	} else static if(is(T : SysTime)) {
5532 		return Json(value.toUnixTime!long);
5533 	} else static if(is(T : Duration)) {
5534 		import std.conv : to;
5535 		return Json(value.total!"seconds".to!int);
5536 	} else static if(is(T == struct)) {
5537 		auto ret = Json.emptyObject;
5538 		foreach(i, ref field; value.tupleof) {
5539 			if(hasUDA!(value.tupleof[i], ignore) || (hasUDA!(value.tupleof[i], optional) && !field.shouldSerialize)) {
5540 				continue;
5541 			} else {
5542 				enum udas = getUDAs!(value.tupleof[i], name);
5543 				static if(udas.length) {
5544 					ret[udas[0].name] = field.serializeToJson;
5545 				} else {
5546 					ret[__traits(identifier, value.tupleof[i])] = field.serializeToJson;
5547 				}
5548 			}
5549 		}
5550 		return ret;
5551 	} else static if(isArray!T && !is(T : string)) {
5552 		auto ret = Json.emptyArray;
5553 		foreach(i; value) {
5554 			ret.appendArrayElement(i.serializeToJson);
5555 		}
5556 		return ret;
5557 	} else {
5558 		return Json(value);
5559 	}
5560 }
5561 
5562 @trusted bool shouldSerialize(T)(T value) {
5563 	static if(__traits(compiles, value.isNull)) {
5564 		return !value.isNull;
5565 	} else static if(__traits(compiles, value.hasValue)) {
5566 		return value.hasValue;
5567 	} else {
5568 		return value != typeof(value).init;
5569 	}
5570 }
5571 
5572 @("serializeToJson serializes simple values")
5573 unittest {
5574 	"Hey".serializeToJson.should.be.equal(Json("Hey"));
5575 	42.serializeToJson.should.be.equal(Json(42));
5576 	null.serializeToJson.should.be.equal(Json(null));
5577 }
5578 
5579 @("serializeToJson serializes structures and arrays")
5580 unittest {
5581 	struct S {
5582 		int n;
5583 		string t;
5584 	}
5585 	S(42, "text").serializeToJson.should.be.equal(Json([
5586 		"n": Json(42),
5587 		"t": Json("text"),
5588 	]));
5589 
5590 	["hey", "you"].serializeToJson.should.be.equal(Json([Json("hey"), Json("you")]));
5591 	[S(41, "text1"), S(42, "text2")].serializeToJson.should.be.equal(Json([
5592 		Json([
5593 			"n": Json(41),
5594 			"t": Json("text1")
5595 		]),
5596 		Json([
5597 			"n": Json(42),
5598 			"t": Json("text2")
5599 		]),
5600 	]));
5601 
5602 	struct S2 {
5603 		int n;
5604 	}
5605 
5606 	struct S1 {
5607 		S2 s2;
5608 	}
5609 	S1(S2(42)).serializeToJson.should.be.equal(Json(["s2": Json(["n": Json(42)])]));
5610 
5611 	[
5612 		[41, 42],
5613 		[43, 44],
5614 	].serializeToJson.should.be.equal(Json([
5615 		Json([
5616 			Json(41), Json(42),
5617 		]),
5618 		Json([
5619 			Json(43), Json(44),
5620 		]),
5621 	]));
5622 }
5623 
5624 @("serializeToJson ignores `@ignore` fields")
5625 unittest {
5626 	struct S {
5627 		@ignore int n;
5628 		string t;
5629 	}
5630 	S(42, "text").serializeToJson.should.be.equal(Json(["t": Json("text")]));
5631 
5632 	struct S2 {
5633 		int n;
5634 		@ignore string t;
5635 	}
5636 
5637 	struct S1 {
5638 		S2 s2;
5639 	}
5640 	S1(S2(42, "text")).serializeToJson.should.be.equal(Json(["s2": Json(["n": Json(42)])]));
5641 }
5642 
5643 @("serializeToJson ignores `@optional` fields when their value is equal to `typeof(field).init`")
5644 unittest {
5645 	struct S {
5646 		@optional @name("i") int n;
5647 		string t;
5648 	}
5649 
5650 	S(int.init, "text").serializeToJson.should.be.equal(Json(["t": Json("text")]));
5651 	S(42, "text").serializeToJson.should.be.equal(Json(["i": Json(42), "t": Json("text")]));
5652 
5653 
5654 	struct S2 {
5655 		int n;
5656 		@optional string t;
5657 	}
5658 
5659 	struct S1 {
5660 		S2 s2;
5661 	}
5662 	S1(S2(42)).serializeToJson.should.be.equal(Json(["s2": Json(["n": Json(42)])]));
5663 	S1(S2(42, "text")).serializeToJson.should.be.equal(Json(["s2": Json(["n": Json(42), "t": Json("text")])]));
5664 }
5665 
5666 @("serializeToJson serializes Algebraic values")
5667 unittest {
5668 	struct S1 {
5669 		int s1;
5670 	}
5671 
5672 	struct S2 {
5673 		string s2;
5674 	}
5675 
5676 	Algebraic!(S1, S2)(S1(42)).serializeToJson.toString
5677 		.should.be.equal(`{"s1":42}`);
5678 	Algebraic!(S1, S2)(S2("hello")).serializeToJson.toString
5679 		.should.be.equal(`{"s2":"hello"}`);
5680 }
5681 
5682 @("serializeToJson serializes complex structs")
5683 unittest {
5684 	auto data = Message(42, SysTime.fromUnixTime(0), Chat(1337, ChatType.group)).serializeToJson;
5685 
5686 	data["message_id"].should.be.equal(Json(42));
5687 	data["date"].should.be.equal(Json(0));
5688 	data["chat"]["type"].should.be.equal(Json("group"));
5689 	data["chat"]["id"].should.be.equal(Json(1337));
5690 }
5691 
5692 @("serializeToJson serializes Duration and SysTime")
5693 unittest {
5694 	3600.seconds.serializeToJson.should.be.equal(Json(3600));
5695 	SysTime.fromUnixTime(1514764800).serializeToJson.should.be.equal(Json(1514764800));
5696 
5697 	struct S {
5698 		Duration d;
5699 		SysTime t;
5700 	}
5701 
5702 	S(3600.seconds, SysTime.fromUnixTime(1514764800)).serializeToJson.should.be.equal(Json([
5703 			"d": Json(3600),
5704 			"t": Json(1514764800),
5705 		]));
5706 }
5707 
5708 @trusted T deserializeJson(T)(Json value) {
5709 	import std.traits;
5710 	import std.exception;
5711 	static if(is(T : Json)) {
5712 		return value;
5713 	} else static if(isInstanceOf!(VariantN, T)) {
5714 		return T.init;
5715 	} else static if(is(T : Duration)) {
5716 		return value.get!int.seconds;
5717 	} else static if(is(T : SysTime)) {
5718 		return SysTime.fromUnixTime(value.get!long);
5719 	} else static if(is(T == struct)) {
5720 		enforce(value.type == Json.Type.object);
5721 		T ret;
5722 		foreach(i, ref field; ret.tupleof) {
5723 			if(hasUDA!(ret.tupleof[i], ignore)) {
5724 				continue;
5725 			} else {
5726 				enum udas = getUDAs!(ret.tupleof[i], name);
5727 				static if(udas.length) {
5728 					if(value[udas[0].name].type == Json.Type.undefined) {
5729 						static if(!hasUDA!(ret.tupleof[i], optional))
5730 							throw new Exception("Missing value for non-optional field "~ udas[0].name);
5731 					} else {					
5732 						field = value[udas[0].name].deserializeJson!(typeof(field));
5733 					}
5734 				} else {
5735 					if(value[__traits(identifier, ret.tupleof[i])].type == Json.Type.undefined) {
5736 						static if(!hasUDA!(ret.tupleof[i], optional))
5737 							throw new Exception("Missing value for non-optional field "~ __traits(identifier, ret.tupleof[i]));
5738 					} else {					
5739 						field = value[__traits(identifier, ret.tupleof[i])].deserializeJson!(typeof(field));
5740 					}
5741 				}
5742 			}
5743 		}
5744 		return ret;
5745 	} else static if(isArray!T && !is(T : string)) {
5746 		enforce(value.type == Json.Type.array);
5747 		T ret;
5748 		foreach(ref e; value)
5749 			ret ~= e.deserializeJson!(ForeachType!T);
5750 		
5751 		return ret;
5752 	} else static if(is(T == enum)) {
5753 		enforce(value.type == Json.Type..string);
5754 		auto tmp = value.deserializeJson!string;
5755 
5756 		switch(tmp) {
5757 		static foreach(member; EnumMembers!T) {
5758 			case member: {
5759 				return member;
5760 			}
5761 		}
5762 		default:
5763 			assert(0);
5764 		}
5765 	} else {
5766 		return value.get!T;
5767 	}
5768 }
5769 
5770 @("deserializeJson deserializes simple values")
5771 unittest {
5772 	Json("hey").deserializeJson!string.should.be.equal("hey");
5773 	Json(42).deserializeJson!int.should.be.equal(42);
5774 	Json([
5775 		Json(42),
5776 		Json(43),
5777 	]).deserializeJson!(int[]).should.be.equal([42, 43]);
5778 }
5779 
5780 @("deserializeJson deserializes structs")
5781 unittest {
5782 	struct S {
5783 		int n;
5784 		string t;
5785 	}
5786 
5787 	Json([
5788 		"n": Json(42),
5789 		"t": Json("text"),
5790 	]).deserializeJson!S.should.be.equal(S(42, "text"));
5791 }
5792 
5793 @("deserializeJson deserializes Duration and SysTime")
5794 unittest {
5795 	Json(3600).deserializeJson!Duration.should.be.equal(3600.seconds);
5796 	Json(1514764800).deserializeJson!SysTime.should.be.equal(SysTime.fromUnixTime(1514764800));
5797 
5798 	struct S {
5799 		Duration d;
5800 		SysTime t;
5801 	}
5802 
5803 	Json([
5804 		"d": Json(3600),
5805 		"t": Json(1514764800),
5806 	]).deserializeJson!S.should.be.equal(S(3600.seconds, SysTime.fromUnixTime(1514764800)));
5807 }