├── LICENSE ├── README.md ├── example.lua ├── examples └── ipsw-bot.lua ├── install.sh ├── rockspecs ├── telegram-bot-lua-1.0.0-0.rockspec ├── telegram-bot-lua-1.0.1-0.rockspec ├── telegram-bot-lua-1.0.2-0.rockspec ├── telegram-bot-lua-1.0.2-1.rockspec ├── telegram-bot-lua-1.0.3-0.rockspec ├── telegram-bot-lua-1.0.4-0.rockspec ├── telegram-bot-lua-1.1.0-0.rockspec ├── telegram-bot-lua-1.1.0-1.rockspec ├── telegram-bot-lua-1.10-0.rockspec ├── telegram-bot-lua-1.2-0.rockspec ├── telegram-bot-lua-1.2.1-0.rockspec ├── telegram-bot-lua-1.3-0.rockspec ├── telegram-bot-lua-1.3.1-0.rockspec ├── telegram-bot-lua-1.3.2-0.rockspec ├── telegram-bot-lua-1.4-0.rockspec ├── telegram-bot-lua-1.5-0.rockspec ├── telegram-bot-lua-1.6-0.rockspec ├── telegram-bot-lua-1.7-0.rockspec ├── telegram-bot-lua-1.8-0.rockspec ├── telegram-bot-lua-1.9-0.rockspec ├── telegram-bot-lua-1.9-1.rockspec └── telegram-bot-lua-2.0-0.rockspec ├── src ├── b64url.lua ├── config.lua ├── core.lua └── tools.lua └── telegram-bot-lua-2.0-0.rockspec /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /example.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | This example will echo messages back to the user who sent them, with an inline keyboard 3 | which tells the user the JSON for the callback_query.from object. 4 | ]] 5 | 6 | local api = require('telegram-bot-lua.core').configure('') -- Enter your token 7 | local json = require('dkjson') 8 | 9 | function api.on_message(message) 10 | if message.text then 11 | api.send_message(message, message.text, nil, nil, nil, nil, false, false, nil, 12 | api.inline_keyboard():row(api.row():callback_data_button('Button', 'callback_data'))) 13 | end 14 | end 15 | 16 | function api.on_callback_query(callback_query) 17 | api.answer_callback_query(callback_query.id, json.encode(callback_query.from)) 18 | end 19 | 20 | api.run() 21 | -------------------------------------------------------------------------------- /examples/ipsw-bot.lua: -------------------------------------------------------------------------------- 1 | local ipsw = {} -- todo: update api to v4 2 | 3 | local api = require('telegram-bot-lua.core').configure('') -- Insert your token here. 4 | local tools = require('telegram-bot-lua.tools') 5 | local https = require('ssl.https') 6 | local url = require('socket.url') 7 | local json = require('dkjson') 8 | 9 | function ipsw.init() 10 | ipsw.data = {} 11 | local jstr, res = https.request('https://api.ipsw.me/v2.1/firmwares.json') 12 | if res == 200 then 13 | ipsw.data = json.decode(jstr) 14 | end 15 | ipsw.devices = {} 16 | for k, v in pairs(ipsw.data.devices) do 17 | if k:lower():match('^appletv') then 18 | if not ipsw.devices['Apple TV'] then 19 | ipsw.devices['Apple TV'] = {} 20 | end 21 | table.insert(ipsw.devices['Apple TV'], k) 22 | table.sort(ipsw.devices['Apple TV']) 23 | elseif k:lower():match('^ipad') then 24 | if not ipsw.devices['iPad'] then 25 | ipsw.devices['iPad'] = {} 26 | end 27 | table.insert(ipsw.devices['iPad'], k) 28 | table.sort(ipsw.devices['iPad']) 29 | elseif k:lower():match('^ipod') then 30 | if not ipsw.devices['iPod'] then 31 | ipsw.devices['iPod'] = {} 32 | end 33 | table.insert(ipsw.devices['iPod'], k) 34 | table.sort(ipsw.devices['iPod']) 35 | elseif k:lower():match('^iphone') then 36 | if not ipsw.devices['iPhone'] then 37 | ipsw.devices['iPhone'] = {} 38 | end 39 | table.insert(ipsw.devices['iPhone'], k) 40 | table.sort(ipsw.devices['iPhone']) 41 | elseif k:lower():match('^mac') then 42 | if not ipsw.devices['Mac'] then 43 | ipsw.devices['Mac'] = {} 44 | end 45 | table.insert(ipsw.devices['Mac'], k) 46 | table.sort(ipsw.devices['Mac']) 47 | end 48 | end 49 | end 50 | 51 | function ipsw.get_info(input) 52 | local device = input 53 | local version = 'latest' 54 | if input:match('^.- .-$') then 55 | device = input:match('^(.-) ') 56 | version = input:match(' (.-)$') 57 | end 58 | local jstr, res = https.request(string.format('https://api.ipsw.me/v2.1/%s/%s/info.json', url.escape(device), 59 | url.escape(version))) 60 | if res ~= 200 or jstr == '[]' then 61 | return false 62 | end 63 | return json.decode(jstr) 64 | end 65 | 66 | function ipsw.get_model_keyboard(device) 67 | local keyboard = { 68 | ['inline_keyboard'] = {{}} 69 | } 70 | local total = 0 71 | for _, v in pairs(ipsw.devices[device]) do 72 | total = total + 1 73 | end 74 | local count = 0 75 | local rows = math.floor(total / 20) 76 | if rows ~= total then 77 | rows = rows + 1 78 | end 79 | local row = 1 80 | for k, v in pairs(ipsw.data.devices) do 81 | if k:lower():match(device:lower():gsub(' ', '')) then 82 | count = count + 1 83 | if count == rows * row then 84 | row = row + 1 85 | table.insert(keyboard.inline_keyboard, {}) 86 | end 87 | table.insert(keyboard.inline_keyboard[row], { 88 | ['text'] = v.name:match('^.- (.-)$'), 89 | ['callback_data'] = 'model:' .. device .. ':' .. k 90 | }) 91 | end 92 | end 93 | table.insert(keyboard.inline_keyboard, {{ 94 | ['text'] = tools.symbols.back .. ' Back', 95 | ['callback_data'] = 'back' 96 | }}) 97 | return keyboard 98 | end 99 | 100 | function ipsw.get_firmware_keyboard(device, model) 101 | local keyboard = { 102 | ['inline_keyboard'] = {{}} 103 | } 104 | local total = 0 105 | for _, v in pairs(ipsw.data.devices[model].firmwares) do 106 | total = total + 1 107 | end 108 | local count = 0 109 | local rows = math.floor(total / 12) 110 | if device:lower() == 'ipad' then 111 | rows = math.floor(total / 18) 112 | end 113 | if rows ~= total then 114 | rows = rows + 1 115 | end 116 | local row = 1 117 | for k, v in pairs(ipsw.data.devices[model].firmwares) do 118 | count = count + 1 119 | if count == rows * row then 120 | row = row + 1 121 | table.insert(keyboard.inline_keyboard, {}) 122 | end 123 | table.insert(keyboard.inline_keyboard[row], { 124 | ['text'] = v.version, 125 | ['callback_data'] = 'firmware:' .. device .. ':' .. model .. ':' .. v.buildid 126 | }) 127 | end 128 | table.insert(keyboard.inline_keyboard, {{ 129 | ['text'] = tools.symbols.back .. ' Back', 130 | ['callback_data'] = 'back:device:' .. device 131 | }}) 132 | return keyboard 133 | end 134 | 135 | function api.on_callback_query(callback_query) 136 | ipsw.init() 137 | local message = callback_query.message 138 | if callback_query.data == 'back' then 139 | return api.edit_message_text(message.chat.id, message.message_id, 140 | 'This tool was created by @wrxck, and makes use of the IPSW.me API.\nBefore we begin, please select your device type:', 141 | nil, nil, nil, 142 | api.inline_keyboard():row( 143 | api.row():callback_data_button('iPod Touch', 'device:iPod'):callback_data_button('iPhone', 144 | 'device:iPhone'):callback_data_button('iPad', 'device:iPad')):row(api.row():callback_data_button( 145 | 'Apple TV', 'device:Apple TV'):callback_data_button( 146 | 'Mac', 'device:Mac'))) 147 | elseif callback_query.data:match('^back:') then 148 | callback_query.data = callback_query.data:match('^back:(.-)$') 149 | end 150 | if callback_query.data:match('^device:.-$') then 151 | callback_query.data = callback_query.data:match('^device:(.-)$') 152 | return api.edit_message_text(message.chat.id, message.message_id, 'Please select your model:', nil, nil, nil, 153 | ipsw.get_model_keyboard(callback_query.data)) 154 | elseif callback_query.data:match('^model:.-:.-$') then 155 | local device, model = callback_query.data:match('^model:(.-):(.-)$') 156 | return api.edit_message_text(message.chat.id, message.message_id, 'Please select your firmware version:', nil, nil, nil, ipsw.get_firmware_keyboard(device, model)) 157 | elseif callback_query.data:match('^firmware:.-:.-:.-$') then 158 | local device, model, firmware = callback_query.data:match('^firmware:(.-):(.-):(.-)$') 159 | firmware = model .. ' ' .. firmware 160 | local jdat = ipsw.get_info(firmware) 161 | return api.edit_message_text(message.chat.id, message.message_id, string.format( 162 | '%s iOS %s\n\nUploaded on %s at %s\n\nMD5 sum: %s\nSHA1 sum: %s\nFile size: %s GB\n\n%s This firmware is %s being signed!', 163 | jdat[1].device, jdat[1].version, jdat[1].uploaddate:match('^(.-)T'), jdat[1].uploaddate:match('T(.-)Z$'), 164 | jdat[1].md5sum, jdat[1].sha1sum, tools.round(jdat[1].size / 1000000000, 2), 165 | jdat[1].signed == false and utf8.char(10060) or utf8.char(9989), 166 | jdat[1].signed == false and 'no longer' or 'still'), 'html', nil, nil, 167 | api.inline_keyboard():row(api.row():url_button(jdat[1].filename, jdat[1].url)):row( 168 | api.row():callback_data_button(tools.symbols.back .. ' Back', 'back:model:' .. device .. ':' .. model))) 169 | end 170 | end 171 | 172 | function api.on_message(message) 173 | ipsw.init() 174 | return api.send_message(message.chat.id, 175 | 'This tool was created by @wrxck, and makes use of the IPSW.me API.\nBefore we begin, please select your device type:', 176 | nil, 'html', nil, nil, false, false, nil, 177 | api.inline_keyboard():row( 178 | api.row():callback_data_button('iPod Touch', 'device:iPod'):callback_data_button('iPhone', 'device:iPhone') 179 | :callback_data_button('iPad', 'device:iPad')):row( 180 | api.row():callback_data_button('Apple TV', 'device:Apple TV'):callback_data_button('Mac', 'device:Mac'))) 181 | end 182 | 183 | api.run() 184 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Installing Lua 5.4..." 3 | sudo apt install lua5.4 liblua5.4-dev 4 | echo "Installing LuaRocks..." 5 | sudo apt install luarocks 6 | echo "Installing telegram-bot-lua..." 7 | sudo luarocks-5.4 install telegram-bot-lua -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.0.0-0.rockspec: -------------------------------------------------------------------------------- 1 | rockspec_format = "1.0.0" 2 | package = "telegram-bot-lua" 3 | version = "1.0.0-0" 4 | 5 | source = { 6 | url = "git://github.com/wrxck/telegram-bot-lua.git", 7 | dir = "telegram-bot-lua", 8 | branch = "master" 9 | } 10 | 11 | description = { 12 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 13 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 14 | license = "GPL-3", 15 | homepage = "https://github.com/wrxck/telegram-bot-lua", 16 | maintainer = "Matthew Hesketh " 17 | } 18 | 19 | supported_platforms = { 20 | "linux" 21 | } 22 | 23 | dependencies = { 24 | "dkjson >= 2.5-2", 25 | "lpeg >= 1.0.1-1", 26 | "luasec >= 0.6-1", 27 | "luasocket >= 3.0rc1-2", 28 | "multipart-post >= 1.1-1" 29 | } 30 | 31 | build = { 32 | type = "builtin", 33 | modules = { 34 | ["telegram-bot-lua.core"] = "src/core.lua", 35 | ["telegram-bot-lua.tools"] = "src/tools.lua" 36 | } 37 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.0.1-0.rockspec: -------------------------------------------------------------------------------- 1 | rockspec_format = "1.0.1" 2 | package = "telegram-bot-lua" 3 | version = "1.0.1-0" 4 | 5 | source = { 6 | url = "git://github.com/wrxck/telegram-bot-lua.git", 7 | dir = "telegram-bot-lua", 8 | branch = "master" 9 | } 10 | 11 | description = { 12 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 13 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 14 | license = "GPL-3", 15 | homepage = "https://github.com/wrxck/telegram-bot-lua", 16 | maintainer = "Matthew Hesketh " 17 | } 18 | 19 | supported_platforms = { 20 | "linux" 21 | } 22 | 23 | dependencies = { 24 | "dkjson >= 2.5-2", 25 | "lpeg >= 1.0.1-1", 26 | "luasec >= 0.6-1", 27 | "luasocket >= 3.0rc1-2", 28 | "multipart-post >= 1.1-1" 29 | } 30 | 31 | build = { 32 | type = "builtin", 33 | modules = { 34 | ["telegram-bot-lua.core"] = "src/core.lua", 35 | ["telegram-bot-lua.tools"] = "src/tools.lua" 36 | } 37 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.0.2-0.rockspec: -------------------------------------------------------------------------------- 1 | rockspec_format = "1.0.2" 2 | package = "telegram-bot-lua" 3 | version = "1.0.2-0" 4 | 5 | source = { 6 | url = "git://github.com/wrxck/telegram-bot-lua.git", 7 | dir = "telegram-bot-lua", 8 | branch = "master" 9 | } 10 | 11 | description = { 12 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 13 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 14 | license = "GPL-3", 15 | homepage = "https://github.com/wrxck/telegram-bot-lua", 16 | maintainer = "Matthew Hesketh " 17 | } 18 | 19 | supported_platforms = { 20 | "linux" 21 | } 22 | 23 | dependencies = { 24 | "dkjson >= 2.5-2", 25 | "lpeg >= 1.0.1-1", 26 | "luasec >= 0.6-1", 27 | "luasocket >= 3.0rc1-2", 28 | "multipart-post >= 1.1-1" 29 | } 30 | 31 | build = { 32 | type = "builtin", 33 | modules = { 34 | ["telegram-bot-lua.core"] = "src/core.lua", 35 | ["telegram-bot-lua.tools"] = "src/tools.lua" 36 | } 37 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.0.2-1.rockspec: -------------------------------------------------------------------------------- 1 | rockspec_format = "1.0.2" 2 | package = "telegram-bot-lua" 3 | version = "1.0.2-1" 4 | 5 | source = { 6 | url = "git://github.com/wrxck/telegram-bot-lua.git", 7 | dir = "telegram-bot-lua", 8 | branch = "master" 9 | } 10 | 11 | description = { 12 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 13 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 14 | license = "GPL-3", 15 | homepage = "https://github.com/wrxck/telegram-bot-lua", 16 | maintainer = "Matthew Hesketh " 17 | } 18 | 19 | supported_platforms = { 20 | "linux", 21 | "macosx" 22 | } 23 | 24 | dependencies = { 25 | "dkjson >= 2.5-2", 26 | "lpeg >= 1.0.1-1", 27 | "luasec >= 0.6-1", 28 | "luasocket >= 3.0rc1-2", 29 | "multipart-post >= 1.1-1" 30 | } 31 | 32 | build = { 33 | type = "builtin", 34 | modules = { 35 | ["telegram-bot-lua.core"] = "src/core.lua", 36 | ["telegram-bot-lua.tools"] = "src/tools.lua" 37 | } 38 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.0.3-0.rockspec: -------------------------------------------------------------------------------- 1 | rockspec_format = "1.0.3" 2 | package = "telegram-bot-lua" 3 | version = "1.0.3-0" 4 | 5 | source = { 6 | url = "git://github.com/wrxck/telegram-bot-lua.git", 7 | dir = "telegram-bot-lua", 8 | branch = "master" 9 | } 10 | 11 | description = { 12 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 13 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 14 | license = "GPL-3", 15 | homepage = "https://github.com/wrxck/telegram-bot-lua", 16 | maintainer = "Matthew Hesketh " 17 | } 18 | 19 | supported_platforms = { 20 | "linux", 21 | "macosx", 22 | "unix", 23 | "bsd" 24 | } 25 | 26 | dependencies = { 27 | "dkjson >= 2.5-2", 28 | "lpeg >= 1.0.1-1", 29 | "luasec >= 0.6-1", 30 | "luasocket >= 3.0rc1-2", 31 | "multipart-post >= 1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.core"] = "src/core.lua", 38 | ["telegram-bot-lua.tools"] = "src/tools.lua" 39 | } 40 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.0.4-0.rockspec: -------------------------------------------------------------------------------- 1 | rockspec_format = "1.0.4" 2 | package = "telegram-bot-lua" 3 | version = "1.0.4-0" 4 | 5 | source = { 6 | url = "git://github.com/wrxck/telegram-bot-lua.git", 7 | dir = "telegram-bot-lua", 8 | branch = "master" 9 | } 10 | 11 | description = { 12 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 13 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 14 | license = "GPL-3", 15 | homepage = "https://github.com/wrxck/telegram-bot-lua", 16 | maintainer = "Matthew Hesketh " 17 | } 18 | 19 | supported_platforms = { 20 | "linux", 21 | "macosx", 22 | "unix", 23 | "bsd" 24 | } 25 | 26 | dependencies = { 27 | "dkjson >= 2.5-2", 28 | "lpeg >= 1.0.1-1", 29 | "luasec >= 0.6-1", 30 | "luasocket >= 3.0rc1-2", 31 | "multipart-post >= 1.1-1", 32 | "luautf8 >= 0.1.1-1" 33 | } 34 | 35 | build = { 36 | type = "builtin", 37 | modules = { 38 | ["telegram-bot-lua.core"] = "src/core.lua", 39 | ["telegram-bot-lua.tools"] = "src/tools.lua" 40 | } 41 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.1.0-0.rockspec: -------------------------------------------------------------------------------- 1 | rockspec_format = "1.1.0" 2 | package = "telegram-bot-lua" 3 | version = "1.1.0-0" 4 | 5 | source = { 6 | url = "git://github.com/wrxck/telegram-bot-lua.git", 7 | dir = "telegram-bot-lua", 8 | branch = "master" 9 | } 10 | 11 | description = { 12 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 13 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 14 | license = "GPL-3", 15 | homepage = "https://github.com/wrxck/telegram-bot-lua", 16 | maintainer = "Matthew Hesketh " 17 | } 18 | 19 | supported_platforms = { 20 | "linux", 21 | "macosx", 22 | "unix", 23 | "bsd" 24 | } 25 | 26 | dependencies = { 27 | "dkjson >= 2.5-2", 28 | "lpeg >= 1.0.1-1", 29 | "luasec >= 0.6-1", 30 | "luasocket >= 3.0rc1-2", 31 | "multipart-post >= 1.1-1", 32 | "luautf8 >= 0.1.1-1" 33 | } 34 | 35 | build = { 36 | type = "builtin", 37 | modules = { 38 | ["telegram-bot-lua.core"] = "src/core.lua", 39 | ["telegram-bot-lua.tools"] = "src/tools.lua" 40 | } 41 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.1.0-1.rockspec: -------------------------------------------------------------------------------- 1 | rockspec_format = "1.1.0" 2 | package = "telegram-bot-lua" 3 | version = "1.1.0-1" 4 | 5 | source = { 6 | url = "git://github.com/wrxck/telegram-bot-lua.git", 7 | dir = "telegram-bot-lua", 8 | branch = "master" 9 | } 10 | 11 | description = { 12 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 13 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 14 | license = "GPL-3", 15 | homepage = "https://github.com/wrxck/telegram-bot-lua", 16 | maintainer = "Matthew Hesketh " 17 | } 18 | 19 | supported_platforms = { 20 | "linux", 21 | "macosx", 22 | "unix", 23 | "bsd" 24 | } 25 | 26 | dependencies = { 27 | "dkjson >= 2.5-2", 28 | "lpeg >= 1.0.1-1", 29 | "luasec >= 0.6-1", 30 | "luasocket >= 3.0rc1-2", 31 | "multipart-post >= 1.1-1", 32 | "luautf8 >= 0.1.1-1" 33 | } 34 | 35 | build = { 36 | type = "builtin", 37 | modules = { 38 | ["telegram-bot-lua.core"] = "src/core.lua", 39 | ["telegram-bot-lua.tools"] = "src/tools.lua" 40 | } 41 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.10-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.10-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "main" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1", 32 | "html-entities >= 1.3.1-0" 33 | } 34 | 35 | build = { 36 | type = "builtin", 37 | modules = { 38 | ["telegram-bot-lua.config"] = "src/config.lua", 39 | ["telegram-bot-lua.core"] = "src/core.lua", 40 | ["telegram-bot-lua.tools"] = "src/tools.lua", 41 | ["telegram-bot-lua.b64url"] = "src/b64url.lua" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.2-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.2-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.core"] = "src/core.lua", 38 | ["telegram-bot-lua.tools"] = "src/tools.lua" 39 | } 40 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.2.1-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.2.1-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.core"] = "src/core.lua", 38 | ["telegram-bot-lua.tools"] = "src/tools.lua" 39 | } 40 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.3-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.3-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.core"] = "src/core.lua", 38 | ["telegram-bot-lua.tools"] = "src/tools.lua" 39 | } 40 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.3.1-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.3.1-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.core"] = "src/core.lua", 38 | ["telegram-bot-lua.tools"] = "src/tools.lua" 39 | } 40 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.3.2-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.3.2-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.core"] = "src/core.lua", 38 | ["telegram-bot-lua.tools"] = "src/tools.lua" 39 | } 40 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.4-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.4-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.core"] = "src/core.lua", 38 | ["telegram-bot-lua.tools"] = "src/tools.lua" 39 | } 40 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.5-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.5-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.core"] = "src/core.lua", 38 | ["telegram-bot-lua.tools"] = "src/tools.lua" 39 | } 40 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.6-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.6-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.core"] = "src/core.lua", 38 | ["telegram-bot-lua.tools"] = "src/tools.lua" 39 | } 40 | } -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.7-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.7-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.core"] = "src/core.lua", 38 | ["telegram-bot-lua.tools"] = "src/tools.lua" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.8-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.8-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.core"] = "src/core.lua", 38 | ["telegram-bot-lua.tools"] = "src/tools.lua" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.9-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.9-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1" 32 | } 33 | 34 | build = { 35 | type = "builtin", 36 | modules = { 37 | ["telegram-bot-lua.config"] = "src/config.lua", 38 | ["telegram-bot-lua.core"] = "src/core.lua", 39 | ["telegram-bot-lua.tools"] = "src/tools.lua" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-1.9-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "1.9-1" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "master" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1", 32 | "html-entities >= 1.3.1-0" 33 | } 34 | 35 | build = { 36 | type = "builtin", 37 | modules = { 38 | ["telegram-bot-lua.config"] = "src/config.lua", 39 | ["telegram-bot-lua.core"] = "src/core.lua", 40 | ["telegram-bot-lua.tools"] = "src/tools.lua" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /rockspecs/telegram-bot-lua-2.0-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "telegram-bot-lua" 2 | version = "2.0-0" 3 | 4 | source = { 5 | url = "git://github.com/wrxck/telegram-bot-lua.git", 6 | dir = "telegram-bot-lua", 7 | branch = "main" 8 | } 9 | 10 | description = { 11 | summary = "A simple yet extensive Lua library for the Telegram bot API.", 12 | detailed = "A simple yet extensive Lua library for the Telegram bot API, with many tools and API-friendly functions.", 13 | license = "GPL-3", 14 | homepage = "https://github.com/wrxck/telegram-bot-lua", 15 | maintainer = "Matthew Hesketh " 16 | } 17 | 18 | supported_platforms = { 19 | "linux", 20 | "macosx", 21 | "unix", 22 | "bsd" 23 | } 24 | 25 | dependencies = { 26 | "dkjson >= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1", 32 | "html-entities >= 1.3.1-0" 33 | } 34 | 35 | build = { 36 | type = "builtin", 37 | modules = { 38 | ["telegram-bot-lua.config"] = "src/config.lua", 39 | ["telegram-bot-lua.core"] = "src/core.lua", 40 | ["telegram-bot-lua.tools"] = "src/tools.lua", 41 | ["telegram-bot-lua.b64url"] = "src/b64url.lua" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/b64url.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | _ _ _ _ _ 4 | | | | | | | | | | | 5 | | |_ ___| | ___ __ _ _ __ __ _ _ __ ___ ______| |__ ___ | |_ ______| |_ _ __ _ 6 | | __/ _ \ |/ _ \/ _` | '__/ _` | '_ ` _ \______| '_ \ / _ \| __|______| | | | |/ _` | 7 | | || __/ | __/ (_| | | | (_| | | | | | | | |_) | (_) | |_ | | |_| | (_| | 8 | \__\___|_|\___|\__, |_| \__,_|_| |_| |_| |_.__/ \___/ \__| |_|\__,_|\__,_| 9 | __/ | 10 | |___/ 11 | 12 | Version 2.0-0 13 | Copyright (c) 2017-2024 Matthew Hesketh 14 | See LICENSE for details 15 | 16 | Adapted version of Paul Moore's base64 library (2017) updated for Lua 5.3 and upwards. 17 | ]] local b64url = {} 18 | 19 | --- octet -> char encoding. 20 | local encodable = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 21 | 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 22 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', 23 | '8', '9', '-', '_'} 24 | 25 | --- char -> octet encoding. 26 | -- Offset by 44 (from index 1). 27 | local decodable = {62, 0, 0, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 28 | 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 29 | 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51} 30 | 31 | --- Encodes a string into a Base64 string. 32 | -- The input can be any string of arbitrary bytes. 33 | -- 34 | -- @param input The input string. 35 | -- @return The Base64 representation of the input string. 36 | function b64url.encode(input) 37 | local out = {} 38 | -- Go through each triplet of 3 bytes, which produce 4 octets. 39 | local i = 1 40 | local bytes = {input:byte(i, #input)} 41 | while i <= #bytes - 2 do 42 | local buffer = 0 43 | -- Fill the buffer with the bytes, producing a 24-bit integer. 44 | local b = bytes[i] << 16 45 | buffer = buffer | (b & 0xff0000) 46 | buffer = buffer | ((bytes[i + 1] << 8) & 0xff00) 47 | buffer = buffer | (bytes[i + 2] & 0xff) 48 | -- Read out the 4 octets into the output buffer. 49 | b = (buffer >> 18) & 0x3f 50 | out[#out + 1] = encodable[b + 1] 51 | b = (buffer >> 12) & 0x3f 52 | out[#out + 1] = encodable[b + 1] 53 | b = (buffer >> 6) & 0x3f 54 | out[#out + 1] = encodable[b + 1] 55 | b = buffer & 0x3f 56 | out[#out + 1] = encodable[b + 1] 57 | i = i + 3 58 | end 59 | -- Special case 1: One byte extra, will produce 2 octets. 60 | if #bytes % 3 == 1 then 61 | local buffer = (bytes[i] << 16) & 0xff0000 62 | local b = (buffer >> 18) & 0x3f 63 | out[#out + 1] = encodable[b + 1] 64 | b = (buffer >> 12) & 0x3f 65 | out[#out + 1] = encodable[b + 1] 66 | -- Special case 2: Two bytes extra, will produce 3 octets. 67 | elseif #bytes % 3 == 2 then 68 | local buffer = 0 69 | local b = (bytes[i] << 16) & 0xff0000 70 | buffer = buffer | b 71 | b = (bytes[i + 1] << 8) & 0xff00 72 | buffer = buffer | b 73 | b = (buffer >> 18) & 0x3f 74 | out[#out + 1] = encodable[b + 1] 75 | b = (buffer >> 12) & 0x3f 76 | out[#out + 1] = encodable[b + 1] 77 | b = (buffer >> 6) & 0x3f 78 | out[#out + 1] = encodable[b + 1] 79 | end 80 | return table.concat(out) 81 | end 82 | 83 | --- Decodes a Base64 string into an output string of arbitrary bytes. 84 | -- Currently does not check the input for valid Base64, so be careful. 85 | -- 86 | -- @param input The Base64 input to decode. 87 | -- @return The decoded Base64 string, as a string of bytes. 88 | function b64url.decode(input) 89 | local out = {} 90 | -- Go through each group of 4 octets to obtain 3 bytes. 91 | local i = 1 92 | while i <= #input - 3 do 93 | -- Read the 4 octets into the buffer, producing a 24-bit integer. 94 | local b = decodable[input:byte(i) - 44] << 18 95 | local buffer = 0 | b 96 | i = i + 1 97 | b = decodable[input:byte(i) - 44] << 12 98 | buffer = buffer | b 99 | i = i + 1 100 | b = decodable[input:byte(i) - 44] << 6 101 | buffer = buffer | b 102 | i = i + 1 103 | b = decodable[input:byte(i) - 44] 104 | buffer = buffer | b 105 | i = i + 1 106 | -- Append the 3 re-constructed bytes into the output buffer. 107 | b = (buffer >> 16) & 0xff 108 | out[#out + 1] = b 109 | b = (buffer >> 8) & 0xff 110 | out[#out + 1] = b 111 | b = buffer & 0xff 112 | out[#out + 1] = b 113 | end 114 | 115 | -- Special case 1: Only 2 octets remain, producing 1 byte. 116 | if #input % 4 == 2 then 117 | local buffer = 0 118 | local b = decodable[input:byte(i) - 44] << 18 119 | buffer = buffer | b 120 | i = i + 1 121 | b = decodable[input:byte(i) - 44] << 12 122 | buffer = buffer | b 123 | i = i + 1 124 | b = (buffer >> 16) & 0xff 125 | out[#out + 1] = b 126 | -- Special case 2: Only 3 octets remain, producing 2 bytes. 127 | elseif #input % 4 == 3 then 128 | local buffer = 0 129 | local b = decodable[input:byte(i) - 44] << 18 130 | buffer = buffer | b 131 | i = i + 1 132 | b = decodable[input:byte(i) - 44] << 12 133 | buffer = buffer | b 134 | i = i + 1 135 | b = decodable[input:byte(i) - 44] << 6 136 | buffer = buffer | b 137 | i = i + 1 138 | b = (buffer >> 16) & 0xff 139 | out[#out + 1] = b 140 | b = (buffer >> 8) & 0xff 141 | out[#out + 1] = b 142 | end 143 | return string.char(table.unpack(out)) 144 | end 145 | 146 | return b64url 147 | -------------------------------------------------------------------------------- /src/config.lua: -------------------------------------------------------------------------------- 1 | return { 2 | ['endpoint'] = 'https://api.telegram.org/bot' 3 | } -------------------------------------------------------------------------------- /src/core.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | _ _ _ _ _ 4 | | | | | | | | | | | 5 | | |_ ___| | ___ __ _ _ __ __ _ _ __ ___ ______| |__ ___ | |_ ______| |_ _ __ _ 6 | | __/ _ \ |/ _ \/ _` | '__/ _` | '_ ` _ \______| '_ \ / _ \| __|______| | | | |/ _` | 7 | | || __/ | __/ (_| | | | (_| | | | | | | | |_) | (_) | |_ | | |_| | (_| | 8 | \__\___|_|\___|\__, |_| \__,_|_| |_| |_| |_.__/ \___/ \__| |_|\__,_|\__,_| 9 | __/ | 10 | |___/ 11 | 12 | Version 2.0-0 13 | Copyright (c) 2017-2024 Matthew Hesketh 14 | See LICENSE for details 15 | 16 | ]] local api = {} 17 | local https = require('ssl.https') 18 | local multipart = require('multipart-post') 19 | local ltn12 = require('ltn12') 20 | local json = require('dkjson') 21 | local html = require('htmlEntities') 22 | local config = require('telegram-bot-lua.config') 23 | 24 | function api.configure(token, debug) 25 | if not token or type(token) ~= 'string' then 26 | token = nil 27 | end 28 | api.debug = debug and true or false 29 | api.token = assert(token, 'Please specify your bot API token you received from @BotFather!') 30 | repeat 31 | api.info = api.get_me() 32 | until api.info.result 33 | api.info = api.info.result 34 | api.info.name = api.info.first_name 35 | return api 36 | end 37 | 38 | function api.request(endpoint, parameters, file) 39 | assert(endpoint, 'You must specify an endpoint to make this request to!') 40 | parameters = parameters or {} 41 | for k, v in pairs(parameters) do 42 | parameters[k] = tostring(v) 43 | end 44 | if api.debug then 45 | local output = json.encode(parameters, { 46 | ['indent'] = true 47 | }) 48 | print(output) 49 | end 50 | if file and next(file) ~= nil then 51 | local file_type, file_name = next(file) 52 | local file_res = io.open(file_name, 'r') 53 | if file_res then 54 | parameters[file_type] = { 55 | filename = file_name, 56 | data = file_res:read('*a') 57 | } 58 | file_res:close() 59 | else 60 | parameters[file_type] = file_name 61 | end 62 | end 63 | parameters = next(parameters) == nil and {''} or parameters 64 | local response = {} 65 | local body, boundary = multipart.encode(parameters) 66 | local success, res = https.request({ 67 | ['url'] = endpoint, 68 | ['method'] = 'POST', 69 | ['headers'] = { 70 | ['Content-Type'] = 'multipart/form-data; boundary=' .. boundary, 71 | ['Content-Length'] = #body 72 | }, 73 | ['source'] = ltn12.source.string(body), 74 | ['sink'] = ltn12.sink.table(response) 75 | }) 76 | if not success then 77 | print('Connection error [' .. res .. ']') 78 | return false, res 79 | end 80 | local jstr = table.concat(response) 81 | local jdat = json.decode(jstr) 82 | if not jdat then 83 | return false, res 84 | elseif not jdat.ok then 85 | local output = '\n' .. jdat.description .. ' [' .. jdat.error_code .. ']\n\nPayload: ' 86 | output = output .. json.encode(parameters, { 87 | ['indent'] = true 88 | }) .. '\n' 89 | print(output) 90 | return false, jdat 91 | end 92 | return jdat, res 93 | end 94 | 95 | function api.get_me() 96 | local success, res = api.request(config.endpoint .. api.token .. '/getMe') 97 | return success, res 98 | end 99 | 100 | function api.log_out() 101 | local success, res = api.request(config.endpoint .. api.token .. '/logOut') 102 | return success, res 103 | end 104 | 105 | function api.close() 106 | local success, res = api.request(config.endpoint .. api.token .. '/close') 107 | return success, res 108 | end 109 | 110 | ------------- 111 | -- UPDATES -- 112 | ------------- 113 | 114 | function api.get_updates(timeout, offset, limit, allowed_updates, use_beta_endpoint) -- https://core.telegram.org/bots/api#getupdates 115 | allowed_updates = type(allowed_updates) == 'table' and json.encode(allowed_updates) or allowed_updates 116 | local success, res = api.request(string.format('https://api.telegram.org/%sbot%s/getUpdates', 117 | use_beta_endpoint and 'beta/' or '', api.token), { 118 | ['timeout'] = timeout, 119 | ['offset'] = offset, 120 | ['limit'] = limit, 121 | ['allowed_updates'] = allowed_updates 122 | }) 123 | return success, res 124 | end 125 | 126 | function api.set_webhook(url, certificate, max_connections, allowed_updates) -- https://core.telegram.org/bots/api#setwebhook 127 | allowed_updates = type(allowed_updates) == 'table' and json.encode(allowed_updates) or allowed_updates 128 | local success, res = api.request(config.endpoint .. api.token .. '/setWebhook', { 129 | ['url'] = url, 130 | ['max_connections'] = max_connections, 131 | ['allowed_updates'] = allowed_updates 132 | }, { 133 | ['certificate'] = certificate 134 | }) 135 | return success, res 136 | end 137 | 138 | function api.delete_webhook() -- https://core.telegram.org/bots/api#deletewebhook 139 | local success, res = api.request(config.endpoint .. api.token .. '/deleteWebhook') 140 | return success, res 141 | end 142 | 143 | function api.get_webhook_info() -- https://core.telegram.org/bots/api#get_webhook_info 144 | local success, res = api.request(config.endpoint .. api.token .. '/getWebhookInfo') 145 | return success, res 146 | end 147 | 148 | ------------- 149 | -- METHODS -- 150 | ------------- 151 | 152 | function api.send_message(message, text, message_thread_id, parse_mode, entities, link_preview_options, 153 | disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendmessage 154 | entities = type(entities) == 'table' and json.encode(entities) or entities 155 | link_preview_options = type(link_preview_options) == 'table' and json.encode(link_preview_options) or 156 | link_preview_options 157 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 158 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 159 | message = (type(message) == 'table' and message.chat and message.chat.id) and message.chat.id or message 160 | parse_mode = (type(parse_mode) == 'boolean' and parse_mode == true) and 'MarkdownV2' or parse_mode 161 | if disable_web_page_preview == nil then 162 | disable_web_page_preview = true 163 | end 164 | local success, res = api.request(config.endpoint .. api.token .. '/sendMessage', { 165 | ['chat_id'] = message, 166 | ['message_thread_id'] = message_thread_id, 167 | ['text'] = text, 168 | ['parse_mode'] = parse_mode, 169 | ['entities'] = entities, 170 | ['link_preview_options'] = link_preview_options, 171 | ['disable_notification'] = disable_notification, 172 | ['protect_content'] = protect_content, 173 | ['reply_parameters'] = reply_parameters, 174 | ['reply_markup'] = reply_markup 175 | }) 176 | return success, res 177 | end 178 | 179 | function api.send_reply(message, text, message_thread_id, parse_mode, entities, link_preview_options, 180 | disable_notification, protect_content, reply_parameters, reply_markup) -- A variant of api.send_message(), optimised for sending a message as a reply. 181 | if type(message) ~= 'table' or not message.chat or not message.chat.id or not message.message_id then 182 | return false 183 | end 184 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 185 | parse_mode = (type(parse_mode) == 'boolean' and parse_mode == true) and 'markdown' or parse_mode 186 | if not reply_parameters then 187 | reply_parameters = api.reply_parameters(message.message_id, message.chat.id, true, nil, parse_mode, nil, nil) 188 | end 189 | local success, res = api.request(config.endpoint .. api.token .. '/sendMessage', { 190 | ['chat_id'] = message.chat.id, 191 | ['message_thread_id'] = message_thread_id, 192 | ['text'] = text, 193 | ['parse_mode'] = parse_mode, 194 | ['entities'] = entities, 195 | ['link_preview_options'] = link_preview_options, 196 | ['disable_notification'] = disable_notification, 197 | ['protect_content'] = protect_content, 198 | ['reply_parameters'] = reply_parameters, 199 | ['reply_markup'] = reply_markup 200 | }) 201 | return success, res 202 | end 203 | 204 | function api.forward_message(chat_id, from_chat_id, message_id, message_thread_id, disable_notification, protect_content) -- https://core.telegram.org/bots/api#forwardmessage 205 | local success, res = api.request(config.endpoint .. api.token .. '/forwardMessage', { 206 | ['chat_id'] = chat_id, 207 | ['message_thread_id'] = message_thread_id, 208 | ['from_chat_id'] = from_chat_id, 209 | ['disable_notification'] = disable_notification, 210 | ['protect_content'] = protect_content, 211 | ['message_id'] = message_id 212 | }) 213 | return success, res 214 | end 215 | 216 | function api.forward_messages(chat_id, from_chat_id, message_ids, message_thread_id, disable_notification, 217 | protect_content) -- https://core.telegram.org/bots/api#forwardmessages 218 | message_ids = type(message_ids) == 'table' and json.encode(message_ids) or message_ids 219 | local success, res = api.request(config.endpoint .. api.token .. '/forwardMessages', { 220 | ['chat_id'] = chat_id, 221 | ['message_thread_id'] = message_thread_id, 222 | ['from_chat_id'] = from_chat_id, 223 | ['message_ids'] = message_ids, 224 | ['disable_notification'] = disable_notification, 225 | ['protect_content'] = protect_content 226 | }) 227 | return success, res 228 | end 229 | 230 | function api.copy_message(chat_id, from_chat_id, message_id, message_thread_id, caption, parse_mode, caption_entities, 231 | disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#copymessage 232 | caption_entities = type(caption_entities) == 'table' and json.encode(caption_entities) or caption_entities 233 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 234 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 235 | local success, res = api.request(config.endpoint .. api.token .. '/copyMessage', { 236 | ['chat_id'] = chat_id, 237 | ['message_thread_id'] = message_thread_id, 238 | ['from_chat_id'] = from_chat_id, 239 | ['message_id'] = message_id, 240 | ['caption'] = caption, 241 | ['parse_mode'] = parse_mode, 242 | ['caption_entities'] = caption_entities, 243 | ['disable_notification'] = disable_notification, 244 | ['protect_content'] = protect_content, 245 | ['reply_parameters'] = reply_parameters, 246 | ['reply_markup'] = reply_markup 247 | }) 248 | return success, res 249 | end 250 | 251 | function api.copy_messages(chat_id, from_chat_id, message_ids, message_thread_id, disable_notification, protect_content, 252 | remove_caption) -- https://core.telegram.org/bots/api#copymessages 253 | message_ids = type(message_ids) == 'table' and json.encode(message_ids) or message_ids 254 | local success, res = api.request(config.endpoint .. api.token .. '/copyMessages', { 255 | ['chat_id'] = chat_id, 256 | ['message_thread_id'] = message_thread_id, 257 | ['from_chat_id'] = from_chat_id, 258 | ['message_ids'] = message_ids, 259 | ['disable_notification'] = disable_notification, 260 | ['protect_content'] = protect_content, 261 | ['remove_caption'] = remove_caption 262 | }) 263 | return success, res 264 | end 265 | 266 | function api.send_photo(chat_id, photo, message_thread_id, caption, parse_mode, caption_entities, has_spoiler, 267 | disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendphoto 268 | caption_entities = type(caption_entities) == 'table' and json.encode(caption_entities) or caption_entities 269 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 270 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 271 | local success, res = api.request(config.endpoint .. api.token .. '/sendPhoto', { 272 | ['chat_id'] = chat_id, 273 | ['message_thread_id'] = message_thread_id, 274 | ['caption'] = caption, 275 | ['parse_mode'] = parse_mode, 276 | ['caption_entities'] = caption_entities, 277 | ['has_spoiler'] = has_spoiler, 278 | ['disable_notification'] = disable_notification, 279 | ['protect_content'] = protect_content, 280 | ['reply_parameters'] = reply_to_message_id, 281 | ['reply_markup'] = reply_markup 282 | }, { 283 | ['photo'] = photo 284 | }) 285 | return success, res 286 | end 287 | -- documentation update point 288 | function api.send_audio(chat_id, audio, message_thread_id, caption, parse_mode, caption_entities, duration, performer, 289 | title, thumbnail, disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendaudio 290 | caption_entities = type(caption_entities) == 'table' and json.encode(caption_entities) or caption_entities 291 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 292 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 293 | local success, res = api.request(config.endpoint .. api.token .. '/sendAudio', { 294 | ['chat_id'] = chat_id, 295 | ['message_thread_id'] = message_thread_id, 296 | ['caption'] = caption, 297 | ['parse_mode'] = parse_mode, 298 | ['caption_entities'] = caption_entities, 299 | ['duration'] = duration, 300 | ['performer'] = performer, 301 | ['title'] = title, 302 | ['disable_notification'] = disable_notification, 303 | ['protect_content'] = protect_content, 304 | ['reply_parameters'] = reply_parameters, 305 | ['reply_markup'] = reply_markup 306 | }, { 307 | ['audio'] = audio, 308 | ['thumbnail'] = thumbnail 309 | }) 310 | return success, res 311 | end 312 | 313 | function api.send_document(chat_id, document, message_thread_id, thumbnail, caption, parse_mode, caption_entities, 314 | disable_content_type_detection, disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#senddocument 315 | caption_entities = type(caption_entities) == 'table' and json.encode(caption_entities) or caption_entities 316 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 317 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 318 | local success, res = api.request(config.endpoint .. api.token .. '/sendDocument', { 319 | ['chat_id'] = chat_id, 320 | ['message_thread_id'] = message_thread_id, 321 | ['caption'] = caption, 322 | ['parse_mode'] = parse_mode, 323 | ['caption_entities'] = caption_entities, 324 | ['disable_content_type_detection'] = disable_content_type_detection, 325 | ['disable_notification'] = disable_notification, 326 | ['protect_content'] = protect_content, 327 | ['reply_parameters'] = reply_parameters, 328 | ['reply_markup'] = reply_markup 329 | }, { 330 | ['document'] = document, 331 | ['thumbnail'] = thumbnail 332 | }) 333 | return success, res 334 | end 335 | 336 | function api.send_video(chat_id, video, message_thread_id, duration, width, height, caption, parse_mode, has_spoiler, 337 | supports_streaming, disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendvideo 338 | caption_entities = type(caption_entities) == 'table' and json.encode(caption_entities) or caption_entities 339 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 340 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 341 | local success, res = api.request(config.endpoint .. api.token .. '/sendVideo', { 342 | ['chat_id'] = chat_id, 343 | ['message_thread_id'] = message_thread_id, 344 | ['duration'] = duration, 345 | ['width'] = width, 346 | ['height'] = height, 347 | ['caption'] = caption, 348 | ['parse_mode'] = parse_mode, 349 | ['caption_entities'] = caption_entities, 350 | ['has_spoiler'] = has_spoiler, 351 | ['supports_streaming'] = supports_streaming, 352 | ['disable_notification'] = disable_notification, 353 | ['protect_content'] = protect_content, 354 | ['reply_parameters'] = reply_parameters, 355 | ['reply_markup'] = reply_markup 356 | }, { 357 | ['video'] = video, 358 | ['thumbnail'] = thumbnail 359 | }) 360 | return success, res 361 | end 362 | 363 | function api.send_animation(chat_id, animation, message_thread_id, duration, width, height, thumbnail, caption, 364 | parse_mode, caption_entities, has_spoiler, disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendanimation 365 | caption_entities = type(caption_entities) == 'table' and json.encode(caption_entities) or caption_entities 366 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 367 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 368 | local success, res = api.request(config.endpoint .. api.token .. '/sendAnimation', { 369 | ['chat_id'] = chat_id, 370 | ['message_thread_id'] = message_thread_id, 371 | ['duration'] = duration, 372 | ['width'] = width, 373 | ['height'] = height, 374 | ['caption'] = caption, 375 | ['parse_mode'] = parse_mode, 376 | ['caption_entities'] = caption_entities, 377 | ['has_spoiler'] = has_spoiler, 378 | ['disable_notification'] = disable_notification, 379 | ['protect_content'] = protect_content, 380 | ['reply_parameters'] = reply_parameters, 381 | ['reply_markup'] = reply_markup 382 | }, { 383 | ['animation'] = animation, 384 | ['thumbnail'] = thumbnail 385 | }) 386 | return success, res 387 | end 388 | 389 | function api.send_voice(chat_id, voice, message_thread_id, caption, parse_mode, caption_entities, duration, 390 | disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendvoice 391 | caption_entities = type(caption_entities) == 'table' and json.encode(caption_entities) or caption_entities 392 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 393 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 394 | local success, res = api.request(config.endpoint .. api.token .. '/sendVoice', { 395 | ['chat_id'] = chat_id, 396 | ['message_thread_id'] = message_thread_id, 397 | ['caption'] = caption, 398 | ['parse_mode'] = parse_mode, 399 | ['caption_entities'] = caption_entities, 400 | ['duration'] = duration, 401 | ['disable_notification'] = disable_notification, 402 | ['protect_content'] = protect_content, 403 | ['reply_parameters'] = reply_parameters, 404 | ['reply_markup'] = reply_markup 405 | }, { 406 | ['voice'] = voice 407 | }) 408 | return success, res 409 | end 410 | 411 | function api.send_video_note(chat_id, video_note, message_thread_id, duration, length, thumbnail, disable_notification, 412 | protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendvideonote 413 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 414 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 415 | local success, res = api.request(config.endpoint .. api.token .. '/sendVideoNote', { 416 | ['chat_id'] = chat_id, 417 | ['message_thread_id'] = message_thread_id, 418 | ['duration'] = duration, 419 | ['length'] = length, 420 | ['disable_notification'] = disable_notification, 421 | ['protect_content'] = protect_content, 422 | ['reply_parameters'] = reply_parameters, 423 | ['reply_markup'] = reply_markup 424 | }, { 425 | ['video_note'] = video_note, 426 | ['thumbnail'] = thumbnail 427 | }) 428 | return success, res 429 | end 430 | 431 | function api.send_media_group(chat_id, media, message_thread_id, disable_notification, protect_content, reply_parameters) -- https://core.telegram.org/bots/api#sendmediagroup 432 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 433 | local success, res = api.request(config.endpoint .. api.token .. '/sendMediaGroup', { 434 | ['chat_id'] = chat_id, 435 | ['message_thread_id'] = message_thread_id, 436 | ['media'] = media, 437 | ['disable_notification'] = disable_notification, 438 | ['protect_content'] = protect_content, 439 | ['reply_parameters'] = reply_parameters 440 | }) 441 | return success, res 442 | end 443 | 444 | function api.send_location(chat_id, latitude, longitude, message_thread_id, horizontal_accuracy, live_period, heading, 445 | proximity_alert_radius, disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendlocation 446 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 447 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 448 | local success, res = api.request(config.endpoint .. api.token .. '/sendLocation', { 449 | ['chat_id'] = chat_id, 450 | ['message_thread_id'] = message_thread_id, 451 | ['latitude'] = latitude, 452 | ['longitude'] = longitude, 453 | ['horizontal_accuracy'] = horizontal_accuracy, 454 | ['live_period'] = live_period, 455 | ['heading'] = heading, 456 | ['proximity_alert_radius'] = proximity_alert_radius, 457 | ['disable_notification'] = disable_notification, 458 | ['protect_content'] = protect_content, 459 | ['reply_parameters'] = reply_parameters, 460 | ['reply_markup'] = reply_markup 461 | }) 462 | return success, res 463 | end 464 | 465 | function api.edit_message_live_location(chat_id, message_id, inline_message_id, latitude, longitude, 466 | horizontal_accuracy, heading, proximity_alert_radius, reply_markup) -- https://core.telegram.org/bots/api#editmessagelivelocation 467 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 468 | local success, res = api.request(config.endpoint .. api.token .. '/editMessageLiveLocation', { 469 | ['chat_id'] = chat_id, 470 | ['message_id'] = message_id, 471 | ['inline_message_id'] = inline_message_id, 472 | ['latitude'] = latitude, 473 | ['longitude'] = longitude, 474 | ['horizontal_accuracy'] = horizontal_accuracy, 475 | ['heading'] = heading, 476 | ['proximity_alert_radius'] = proximity_alert_radius, 477 | ['reply_markup'] = reply_markup 478 | }) 479 | return success, res 480 | end 481 | 482 | function api.stop_message_live_location(chat_id, message_id, inline_message_id, reply_markup) -- https://core.telegram.org/bots/api#stopmessagelivelocation 483 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 484 | local success, res = api.request(config.endpoint .. api.token .. '/stopMessageLiveLocation', { 485 | ['chat_id'] = chat_id, 486 | ['message_id'] = message_id, 487 | ['inline_message_id'] = inline_message_id, 488 | ['reply_markup'] = reply_markup 489 | }) 490 | return success, res 491 | end 492 | 493 | function api.send_venue(chat_id, latitude, longitude, title, address, message_thread_id, foursquare_id, foursquare_type, 494 | google_place_id, google_place_type, disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendvenue 495 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 496 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 497 | local success, res = api.request(config.endpoint .. api.token .. '/sendVenue', { 498 | ['chat_id'] = chat_id, 499 | ['message_thread_id'] = message_thread_id, 500 | ['latitude'] = latitude, 501 | ['longitude'] = longitude, 502 | ['title'] = title, 503 | ['address'] = address, 504 | ['foursquare_id'] = foursquare_id, 505 | ['foursquare_type'] = foursquare_type, 506 | ['google_place_id'] = google_place_id, 507 | ['google_place_type'] = google_place_type, 508 | ['disable_notification'] = disable_notification, 509 | ['protect_content'] = protect_content, 510 | ['reply_parameters'] = reply_parameters, 511 | ['reply_markup'] = reply_markup 512 | }) 513 | return success, res 514 | end 515 | 516 | function api.send_contact(chat_id, phone_number, first_name, last_name, message_thread_id, vcard, disable_notification, 517 | protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendcontact 518 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 519 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 520 | local success, res = api.request(config.endpoint .. api.token .. '/sendContact', { 521 | ['chat_id'] = chat_id, 522 | ['message_thread_id'] = message_thread_id, 523 | ['phone_number'] = phone_number, 524 | ['first_name'] = first_name, 525 | ['last_name'] = last_name, 526 | ['vcard'] = vcard, 527 | ['disable_notification'] = disable_notification, 528 | ['protect_content'] = protect_content, 529 | ['reply_parameters'] = reply_parameters, 530 | ['reply_markup'] = reply_markup 531 | }) 532 | return success, res 533 | end 534 | 535 | function api.send_poll(chat_id, question, options, message_thread_id, is_anonymous, poll_type, allows_multiple_answers, 536 | correct_option_id, explanation, explanation_parse_mode, explanation_entities, open_period, close_date, is_closed, 537 | disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendpoll 538 | explanation_entities = type(explanation_entities) == 'table' and json.encode(explanation_entities) or 539 | explanation_entities 540 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 541 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 542 | is_anonymous = type(is_anonymous) == 'boolean' and is_anonymous or false 543 | allows_multiple_answers = type(allows_multiple_answers) == 'boolean' and allows_multiple_answers or false 544 | local success, res = api.request(config.endpoint .. api.token .. '/sendPoll', { 545 | ['chat_id'] = chat_id, 546 | ['message_thread_id'] = message_thread_id, 547 | ['question'] = question, 548 | ['options'] = options, 549 | ['is_anonymous'] = is_anonymous, 550 | ['type'] = poll_type, 551 | ['allows_multiple_answers'] = allows_multiple_answers, 552 | ['correct_option_id'] = correct_option_id, 553 | ['explanation'] = explanation, 554 | ['explanation_parse_mode'] = explanation_parse_mode, 555 | ['explanation_entities'] = explanation_entities, 556 | ['open_period'] = open_period, 557 | ['close_date'] = close_date, 558 | ['is_closed'] = is_closed, 559 | ['disable_notification'] = disable_notification, 560 | ['protect_content'] = protect_content, 561 | ['reply_parameters'] = reply_parameters, 562 | ['reply_markup'] = reply_markup 563 | }) 564 | return success, res 565 | end 566 | 567 | function api.send_dice(chat_id, emoji, message_thread_id, disable_notification, protect_content, reply_parameters, 568 | reply_markup) -- https://core.telegram.org/bots/api#senddice 569 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 570 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 571 | local success, res = api.request(config.endpoint .. api.token .. '/sendDice', { 572 | ['chat_id'] = chat_id, 573 | ['message_thread_id'] = message_thread_id, 574 | ['emoji'] = emoji, 575 | ['disable_notification'] = disable_notification, 576 | ['protect_content'] = protect_content, 577 | ['reply_parameters'] = reply_parameters, 578 | ['reply_markup'] = reply_markup 579 | }) 580 | return success, res 581 | end 582 | 583 | function api.send_chat_action(chat_id, action, message_thread_id) -- https://core.telegram.org/bots/api#sendchataction 584 | local success, res = api.request(config.endpoint .. api.token .. '/sendChatAction', { 585 | ['chat_id'] = chat_id, 586 | ['action'] = action or 'typing', -- Fallback to `typing` as the default action. 587 | ['message_thread_id'] = message_thread_id 588 | }) 589 | return success, res 590 | end 591 | 592 | function api.set_message_reaction(chat_id, message_id, reaction, is_big) -- https://core.telegram.org/bots/api#setmessagereaction 593 | reaction = type(reaction) == 'table' and json.encode(reaction) or reaction 594 | is_big = type(is_big) == 'boolean' and is_big or false 595 | local success, res = api.request(config.endpoint .. api.token .. '/setMessageReaction', { 596 | ['chat_id'] = chat_id, 597 | ['message_id'] = message_id, 598 | ['reaction'] = reaction, 599 | ['is_big'] = is_big 600 | }) 601 | return success, res 602 | end 603 | 604 | function api.get_user_profile_photos(user_id, offset, limit) -- https://core.telegram.org/bots/api#getuserprofilephotos 605 | local success, res = api.request(config.endpoint .. api.token .. '/getUserProfilePhotos', { 606 | ['user_id'] = user_id, 607 | ['offset'] = offset, 608 | ['limit'] = limit 609 | }) 610 | return success, res 611 | end 612 | 613 | function api.get_file(file_id) -- https://core.telegram.org/bots/api#getfile 614 | local success, res = api.request(config.endpoint .. api.token .. '/getFile', { 615 | ['file_id'] = file_id 616 | }) 617 | return success, res 618 | end 619 | 620 | function api.ban_chat_member(chat_id, user_id, until_date, revoke_messages) -- https://core.telegram.org/bots/api#kickchatmember 621 | local success, res = api.request(config.endpoint .. api.token .. '/kickChatMember', { 622 | ['chat_id'] = chat_id, 623 | ['user_id'] = user_id, 624 | ['until_date'] = until_date, 625 | ['revoke_messages'] = revoke_messages 626 | }) 627 | return success, res 628 | end 629 | 630 | function api.kick_chat_member(chat_id, user_id) 631 | local success, res = api.request(config.endpoint .. api.token .. '/unbanChatMember', { 632 | ['chat_id'] = chat_id, 633 | ['user_id'] = user_id, 634 | ['only_if_banned'] = false 635 | }) 636 | return success, res 637 | end 638 | 639 | function api.unban_chat_member(chat_id, user_id, only_if_banned) -- https://core.telegram.org/bots/api#unbanchatmember 640 | local success, res 641 | for _ = 1, 3 do -- Repeat 3 times to ensure the user was unbanned (I've encountered issues before so 642 | -- this is for precautionary measures.) 643 | success, res = api.request(config.endpoint .. api.token .. '/unbanChatMember', { 644 | ['chat_id'] = chat_id, 645 | ['user_id'] = user_id, 646 | ['only_if_banned'] = only_if_banned 647 | }) 648 | if success then -- If one of the first 3 attempts is a success (which it typically will be), we can just stop the loop 649 | return success, res 650 | end 651 | end 652 | return success, res 653 | end 654 | 655 | function api.restrict_chat_member(chat_id, user_id, permissions, use_independent_chat_permissions, until_date) -- https://core.telegram.org/bots/api#restrictchatmember 656 | permissions = type(permissions) == 'table' and json.encode(permissions) or permissions 657 | if until_date == ('forever' or 'permanently') then 658 | until_date = os.time() - 1000 -- indefinite restriction 659 | end 660 | local success, res = api.request(config.endpoint .. api.token .. '/restrictChatMember', { 661 | ['chat_id'] = chat_id, 662 | ['user_id'] = user_id, 663 | ['permissions'] = permissions, 664 | ['use_independent_chat_permissions'] = use_independent_chat_permissions, 665 | ['until_date'] = until_date 666 | }) 667 | return success, res 668 | end 669 | 670 | function api.promote_chat_member(chat_id, user_id, is_anonymous, can_manage_chat, can_delete_messages, 671 | can_manage_video_chats, can_restrict_members, can_promote_members, can_change_info, can_invite_users, 672 | can_post_messages, can_edit_messages, can_pin_messages, can_post_stories, can_edit_stories, can_delete_stories, 673 | can_manage_topics) -- https://core.telegram.org/bots/api#promotechatmember 674 | local success, res = api.request(config.endpoint .. api.token .. '/promoteChatMember', { 675 | ['chat_id'] = chat_id, 676 | ['user_id'] = user_id, 677 | ['is_anonymous'] = is_anonymous, 678 | ['can_manage_chat'] = can_manage_chat, 679 | ['can_delete_messages'] = can_delete_messages, 680 | ['can_manage_video_chats'] = can_manage_video_chats, 681 | ['can_restrict_members'] = can_restrict_members, 682 | ['can_promote_members'] = can_promote_members, 683 | ['can_change_info'] = can_change_info, 684 | ['can_invite_users'] = can_invite_users, 685 | ['can_post_messages'] = can_post_messages, 686 | ['can_edit_messages'] = can_edit_messages, 687 | ['can_pin_messages'] = can_pin_messages, 688 | ['can_post_stories'] = can_post_stories, 689 | ['can_edit_stories'] = can_edit_stories, 690 | ['can_delete_stories'] = can_delete_stories, 691 | ['can_manage_topics'] = can_manage_topics 692 | }) 693 | return success, res 694 | end 695 | 696 | function api.set_chat_administrator_custom_title(chat_id, user_id, custom_title) -- https://core.telegram.org/bots/api#setchatadministratorcustomtitle 697 | if custom_title:len() > 16 then -- telegram doesn't allow custom titles longer than 16 chars 698 | custom_title = custom_title:sub(1, 16) 699 | end 700 | local success, res = api.request(config.endpoint .. api.token .. '/setChatAdministratorCustomTitle', { 701 | ['chat_id'] = chat_id, 702 | ['user_id'] = user_id, 703 | ['custom_title'] = custom_title 704 | }) 705 | return success, res 706 | end 707 | 708 | function api.ban_chat_sender_chat(chat_id, sender_chat_id) -- https://core.telegram.org/bots/api#banchatsenderchat 709 | local success, res = api.request(config.endpoint .. api.token .. '/banChatSenderChat', { 710 | ['chat_id'] = chat_id, 711 | ['sender_chat_id'] = sender_chat_id 712 | }) 713 | return success, res 714 | end 715 | 716 | function api.unban_chat_sender_chat(chat_id, sender_chat_id) -- https://core.telegram.org/bots/api#unbanchatsenderchat 717 | local success, res = api.request(config.endpoint .. api.token .. '/unbanChatSenderChat', { 718 | ['chat_id'] = chat_id, 719 | ['sender_chat_id'] = sender_chat_id 720 | }) 721 | return success, res 722 | end 723 | 724 | function api.set_chat_permissions(chat_id, permissions, use_independent_chat_permissions) -- https://core.telegram.org/bots/api#setchatpermissions 725 | permissions = type(permissions) == 'table' and json.encode(permissions) or permissions 726 | local success, res = api.request(config.endpoint .. api.token .. '/setChatPermissions', { 727 | ['chat_id'] = chat_id, 728 | ['permissions'] = permissions, 729 | ['use_independent_chat_permissions'] = use_independent_chat_permissions 730 | }) 731 | return success, res 732 | end 733 | 734 | function api.export_chat_invite_link(chat_id) -- https://core.telegram.org/bots/api#exportchatinvitelink 735 | local success, res = api.request(config.endpoint .. api.token .. '/exportChatInviteLink', { 736 | ['chat_id'] = chat_id 737 | }) 738 | return success, res 739 | end 740 | 741 | function api.create_chat_invite_link(chat_id, name, expire_date, member_limit, creates_join_request) -- https://core.telegram.org/bots/api#createchatinvitelink 742 | if type(member_limit) == 'number' or tonumber(member_limit) ~= nil then 743 | member_limit = tonumber(member_limit) 744 | if member_limit < 1 then 745 | member_limit = 1 746 | elseif member_limit < 99999 then 747 | member_limit = 99999 748 | end 749 | end 750 | name = tostring(name) 751 | if name:len() > 32 then 752 | name = name:sub(1, 32) 753 | end 754 | local success, res = api.request(config.endpoint .. api.token .. '/createChatInviteLink', { 755 | ['chat_id'] = chat_id, 756 | ['name'] = name, 757 | ['expire_date'] = expire_date, 758 | ['member_limit'] = member_limit, 759 | ['creates_join_request'] = creates_join_request 760 | }) 761 | return success, res 762 | end 763 | 764 | function api.edit_chat_invite_link(chat_id, invite_link, name, expire_date, member_limit, creates_join_request) -- https://core.telegram.org/bots/api#editchatinvitelink 765 | if type(member_limit) == 'number' or tonumber(member_limit) ~= nil then 766 | member_limit = tonumber(member_limit) 767 | if member_limit < 1 then 768 | member_limit = 1 769 | elseif member_limit < 99999 then 770 | member_limit = 99999 771 | end 772 | end 773 | name = tostring(name) 774 | if name:len() > 32 then 775 | name = name:sub(1, 32) 776 | end 777 | local success, res = api.request(config.endpoint .. api.token .. '/editChatInviteLink', { 778 | ['chat_id'] = chat_id, 779 | ['invite_link'] = invite_link, 780 | ['name'] = name, 781 | ['expire_date'] = expire_date, 782 | ['member_limit'] = member_limit, 783 | ['creates_join_request'] = creates_join_request 784 | }) 785 | return success, res 786 | end 787 | 788 | function api.revoke_chat_invite_link(chat_id, invite_link) -- https://core.telegram.org/bots/api#revokechatinvitelink 789 | local success, res = api.request(config.endpoint .. api.token .. '/revokeChatInviteLink', { 790 | ['chat_id'] = chat_id, 791 | ['invite_link'] = invite_link 792 | }) 793 | return success, res 794 | end 795 | 796 | function api.approve_chat_join_request(chat_id, user_id) -- https://core.telegram.org/bots/api#approvechatjoinrequest 797 | local success, res = api.request(config.endpoint .. api.token .. '/approveChatJoinRequest', { 798 | ['chat_id'] = chat_id, 799 | ['user_id'] = user_id 800 | }) 801 | return success, res 802 | end 803 | 804 | function api.decline_chat_join_request(chat_id, user_id) -- https://core.telegram.org/bots/api#declinechatjoinrequest 805 | local success, res = api.request(config.endpoint .. api.token .. '/declineChatJoinRequest', { 806 | ['chat_id'] = chat_id, 807 | ['user_id'] = user_id 808 | }) 809 | return success, res 810 | end 811 | 812 | function api.set_chat_photo(chat_id, photo) -- https://core.telegram.org/bots/api#setchatphoto 813 | local success, res = api.request(config.endpoint .. api.token .. '/setChatPhoto', { 814 | ['chat_id'] = chat_id 815 | }, { 816 | ['photo'] = photo 817 | }) 818 | return success, res 819 | end 820 | 821 | function api.delete_chat_photo(chat_id) -- https://core.telegram.org/bots/api#deletechatphoto 822 | local success, res = api.request(config.endpoint .. api.token .. '/deleteChatPhoto', { 823 | ['chat_id'] = chat_id 824 | }) 825 | return success, res 826 | end 827 | 828 | function api.set_chat_title(chat_id, title) -- https://core.telegram.org/bots/api#setchattitle 829 | title = tostring(title) 830 | if title:len() > 128 then -- telegram won't allow chat titles greater than 128 chars 831 | title = title:sub(1, 128) 832 | end 833 | local success, res = api.request(config.endpoint .. api.token .. '/setChatTitle', { 834 | ['chat_id'] = chat_id, 835 | ['title'] = title 836 | }) 837 | return success, res 838 | end 839 | 840 | function api.set_chat_description(chat_id, description) -- https://core.telegram.org/bots/api#setchatdescription 841 | description = tostring(description) 842 | if description:len() > 255 then -- telegram won't allow chat descriptions greater than 255 chars 843 | description = description:sub(1, 255) 844 | end 845 | local success, res = api.request(config.endpoint .. api.token .. '/setChatDescription', { 846 | ['chat_id'] = chat_id, 847 | ['description'] = description 848 | }) 849 | return success, res 850 | end 851 | 852 | function api.pin_chat_message(chat_id, message_id, disable_notification) -- https://core.telegram.org/bots/api#pinchatmessage 853 | local success, res = api.request(config.endpoint .. api.token .. '/pinChatMessage', { 854 | ['chat_id'] = chat_id, 855 | ['message_id'] = message_id, 856 | ['disable_notification'] = disable_notification 857 | }) 858 | return success, res 859 | end 860 | 861 | function api.unpin_chat_message(chat_id, message_id) -- https://core.telegram.org/bots/api#unpinchatmessage 862 | local success, res = api.request(config.endpoint .. api.token .. '/unpinChatMessage', { 863 | ['chat_id'] = chat_id, 864 | ['message_id'] = message_id 865 | }) 866 | return success, res 867 | end 868 | 869 | function api.unpin_all_chat_messages(chat_id) -- https://core.telegram.org/bots/api#unpinallchatmessages 870 | local success, res = api.request(config.endpoint .. api.token .. '/unpinAllChatMessages', { 871 | ['chat_id'] = chat_id 872 | }) 873 | return success, res 874 | end 875 | 876 | function api.leave_chat(chat_id) -- https://core.telegram.org/bots/api#leavechat 877 | local success, res = api.request(config.endpoint .. api.token .. '/leaveChat', { 878 | ['chat_id'] = chat_id 879 | }) 880 | return success, res 881 | end 882 | 883 | function api.get_chat(chat_id) -- https://core.telegram.org/bots/api#getchat 884 | local success, result = api.request(config.endpoint .. api.token .. '/getChat', { 885 | ['chat_id'] = chat_id 886 | }) 887 | if not success or not success.result then 888 | return success, result 889 | end 890 | -- Little workaround to try and get more information! 891 | if success.result.username and success.result.type == 'private' then 892 | local old_timeout = https.TIMEOUT 893 | https.TIMEOUT = 1 894 | local scrape, scrape_res = https.request('https://t.me/' .. success.result.username) 895 | https.TIMEOUT = old_timeout 896 | if scrape_res ~= 200 then 897 | return success, result 898 | end 899 | local bio = scrape:match('%
(.-)%') 900 | if not bio then 901 | return success 902 | end 903 | bio = bio:gsub('%b<>', '') 904 | bio = html.decode(bio) 905 | success.result.bio = bio 906 | end 907 | return success, result 908 | end 909 | 910 | function api.get_chat_administrators(chat_id) -- https://core.telegram.org/bots/api#getchatadministrators 911 | local success, res = api.request(config.endpoint .. api.token .. '/getChatAdministrators', { 912 | ['chat_id'] = chat_id 913 | }) 914 | return success, res 915 | end 916 | 917 | function api.get_chat_members_count(chat_id) -- https://core.telegram.org/bots/api#getchatmemberscount 918 | local success, res = api.request(config.endpoint .. api.token .. '/getChatMembersCount', { 919 | ['chat_id'] = chat_id 920 | }) 921 | return success, res 922 | end 923 | 924 | function api.get_chat_member(chat_id, user_id) -- https://core.telegram.org/bots/api#getchatmember 925 | local success, res = api.request(config.endpoint .. api.token .. '/getChatMember', { 926 | ['chat_id'] = chat_id, 927 | ['user_id'] = user_id 928 | }) 929 | return success, res 930 | end 931 | 932 | function api.set_chat_sticker_set(chat_id, sticker_set_name) -- https://core.telegram.org/bots/api#setchatstickerset 933 | local success, res = api.request(config.endpoint .. api.token .. '/setChatStickerSet', { 934 | ['chat_id'] = chat_id, 935 | ['sticker_set_name'] = sticker_set_name 936 | }) 937 | return success, res 938 | end 939 | 940 | function api.delete_chat_sticker_set(chat_id) -- https://core.telegram.org/bots/api#deletechatstickerset 941 | local success, res = api.request(config.endpoint .. api.token .. '/deleteChatStickerSet', { 942 | ['chat_id'] = chat_id 943 | }) 944 | return success, res 945 | end 946 | 947 | function api.get_forum_topic_icon_stickers() -- https://core.telegram.org/bots/api#getforumtopiciconstickers 948 | local success, res = api.request(config.endpoint .. api.token .. '/getForumTopicIconStickers') 949 | return success, res 950 | end 951 | 952 | function api.create_forum_topic(chat_id, name, icon_color, icon_custom_emoji_id) -- https://core.telegram.org/bots/api#createforumtopic 953 | name = tostring(name) 954 | if name:len() > 128 then -- telegram won't allow chat descriptions greater than 255 chars 955 | name = name:sub(1, 128) 956 | end 957 | local success, res = api.request(config.endpoint .. api.token .. '/createForumTopic', { 958 | ['chat_id'] = chat_id, 959 | ['name'] = name, 960 | ['icon_color'] = icon_color, 961 | ['icon_custom_emoji_id'] = icon_custom_emoji_id 962 | }) 963 | return success, res 964 | end 965 | 966 | function api.edit_forum_topic(chat_id, message_thread_id, name, icon_custom_emoji_id) -- https://core.telegram.org/bots/api#editforumtopic 967 | name = tostring(name) 968 | if name:len() > 128 then -- telegram won't allow chat descriptions greater than 255 chars 969 | name = name:sub(1, 128) 970 | end 971 | local success, res = api.request(config.endpoint .. api.token .. '/editForumTopic', { 972 | ['chat_id'] = chat_id, 973 | ['message_thread_id'] = message_thread_id, 974 | ['name'] = name, 975 | ['icon_custom_emoji_id'] = icon_custom_emoji_id 976 | }) 977 | return success, res 978 | end 979 | 980 | function api.close_forum_topic(chat_id, message_thread_id) -- https://core.telegram.org/bots/api#closeforumtopic 981 | local success, res = api.request(config.endpoint .. api.token .. '/closeForumTopic', { 982 | ['chat_id'] = chat_id, 983 | ['message_thread_id'] = message_thread_id 984 | }) 985 | return success, res 986 | end 987 | 988 | function api.reopen_forum_topic(chat_id, message_thread_id) -- https://core.telegram.org/bots/api#reopenforumtopic 989 | local success, res = api.request(config.endpoint .. api.token .. '/reopenForumTopic', { 990 | ['chat_id'] = chat_id, 991 | ['message_thread_id'] = message_thread_id 992 | }) 993 | return success, res 994 | end 995 | 996 | function api.delete_forum_topic(chat_id, message_thread_id) -- https://core.telegram.org/bots/api#deleteforumtopic 997 | local success, res = api.request(config.endpoint .. api.token .. '/deleteForumTopic', { 998 | ['chat_id'] = chat_id, 999 | ['message_thread_id'] = message_thread_id 1000 | }) 1001 | return success, res 1002 | end 1003 | 1004 | function api.unpin_all_forum_topic_messages(chat_id, message_thread_id) -- https://core.telegram.org/bots/api#unpinallforumtopicmessages 1005 | local success, res = api.request(config.endpoint .. api.token .. '/unpinAllForumTopicMessages', { 1006 | ['chat_id'] = chat_id, 1007 | ['message_thread_id'] = message_thread_id 1008 | }) 1009 | return success, res 1010 | end 1011 | 1012 | function api.edit_general_forum_topic(chat_id, name) -- https://core.telegram.org/bots/api#editgeneralforumtopic 1013 | name = tostring(name) 1014 | if name:len() > 128 then -- telegram won't allow chat descriptions greater than 255 chars 1015 | name = name:sub(1, 128) 1016 | end 1017 | local success, res = api.request(config.endpoint .. api.token .. '/editGeneralForumTopic', { 1018 | ['chat_id'] = chat_id, 1019 | ['name'] = tostring(name) 1020 | }) 1021 | return success, res 1022 | end 1023 | 1024 | function api.close_general_forum_topic(chat_id) -- https://core.telegram.org/bots/api#closegeneralforumtopic 1025 | local success, res = api.request(config.endpoint .. api.token .. '/closeGeneralForumTopic', { 1026 | ['chat_id'] = chat_id 1027 | }) 1028 | return success, res 1029 | end 1030 | 1031 | function api.reopen_general_forum_topic(chat_id) -- https://core.telegram.org/bots/api#reopengeneralforumtopic 1032 | local success, res = api.request(config.endpoint .. api.token .. '/reopenGeneralForumTopic', { 1033 | ['chat_id'] = chat_id 1034 | }) 1035 | return success, res 1036 | end 1037 | 1038 | function api.hide_general_forum_topic(chat_id) -- https://core.telegram.org/bots/api#hidegeneralforumtopic 1039 | local success, res = api.request(config.endpoint .. api.token .. '/hideGeneralForumTopic', { 1040 | ['chat_id'] = chat_id 1041 | }) 1042 | return success, res 1043 | end 1044 | 1045 | function api.unhide_general_forum_topic(chat_id) -- https://core.telegram.org/bots/api#unhidegeneralforumtopic 1046 | local success, res = api.request(config.endpoint .. api.token .. '/unhideGeneralForumTopic', { 1047 | ['chat_id'] = chat_id 1048 | }) 1049 | return success, res 1050 | end 1051 | 1052 | function api.unpin_all_general_forum_topic_messages(chat_id) -- https://core.telegram.org/bots/api#unpinallgeneralforumtopicmessages 1053 | local success, res = api.request(config.endpoint .. api.token .. '/unpinAllGeneralForumTopicMessages', { 1054 | ['chat_id'] = chat_id 1055 | }) 1056 | return success, res 1057 | end 1058 | 1059 | function api.answer_callback_query(callback_query_id, text, show_alert, url, cache_time) -- https://core.telegram.org/bots/api#answercallbackquery 1060 | local success, res = api.request(config.endpoint .. api.token .. '/answerCallbackQuery', { 1061 | ['callback_query_id'] = callback_query_id, 1062 | ['text'] = text, 1063 | ['show_alert'] = show_alert, 1064 | ['url'] = url, 1065 | ['cache_time'] = cache_time 1066 | }) 1067 | return success, res 1068 | end 1069 | 1070 | function api.get_user_chat_boosts(chat_id, user_id) -- https://core.telegram.org/bots/api#getUserChatBoosts 1071 | local success, res = api.request(config.endpoint .. api.token .. '/getUserChatBoosts', { 1072 | ['chat_id'] = chat_id, 1073 | ['user_id'] = user_id 1074 | }) 1075 | return success, res 1076 | end 1077 | 1078 | function api.set_my_commands(commands, scope, language_code) -- https://core.telegram.org/bots/api#setmycommands 1079 | commands = type(commands) == 'table' and json.encode(commands) or commands 1080 | scope = type(scope) == 'table' and json.encode(scope) or scope 1081 | local success, res = api.request(config.endpoint .. api.token .. '/setMyCommands', { 1082 | ['commands'] = commands, 1083 | ['scope'] = scope, 1084 | ['language_code'] = language_code 1085 | }) 1086 | return success, res 1087 | end 1088 | 1089 | function api.delete_my_commands(scope, language_code) -- https://core.telegram.org/bots/api#deletemycommands 1090 | scope = type(scope) == 'table' and json.encode(scope) or scope 1091 | local success, res = api.request(config.endpoint .. api.token .. '/deleteMyCommands', { 1092 | ['scope'] = scope, 1093 | ['language_code'] = language_code 1094 | }) 1095 | return success, res 1096 | end 1097 | 1098 | function api.get_my_commands(scope, language_code) -- https://core.telegram.org/bots/api#getmycommands 1099 | scope = type(scope) == 'table' and json.encode(scope) or scope 1100 | local success, res = api.request(config.endpoint .. api.token .. '/getMyCommands', { 1101 | ['scope'] = scope, 1102 | ['language_code'] = language_code 1103 | }) 1104 | return success, res 1105 | end 1106 | 1107 | function api.set_my_name(name, language_code) -- https://core.telegram.org/bots/api#setmyname 1108 | name = tostring(name) 1109 | if name:len() > 64 then 1110 | name = name:sub(1, 64) 1111 | end 1112 | local success, res = api.request(config.endpoint .. api.token .. '/setMyName', { 1113 | ['name'] = name, 1114 | ['language_code'] = language_code 1115 | }) 1116 | return success, res 1117 | end 1118 | 1119 | function api.get_my_name(language_code) -- https://core.telegram.org/bots/api#getmyname 1120 | local success, res = api.request(config.endpoint .. api.token .. '/getMyName', { 1121 | ['language_code'] = language_code 1122 | }) 1123 | return success, res 1124 | end 1125 | 1126 | function api.set_my_description(description, language_code) -- https://core.telegram.org/bots/api#setmydescription 1127 | description = tostring(description) 1128 | if description:len() > 512 then 1129 | description = description:sub(1, 512) 1130 | end 1131 | local success, res = api.request(config.endpoint .. api.token .. '/setMyDescription', { 1132 | ['description'] = description, 1133 | ['language_code'] = language_code 1134 | }) 1135 | return success, res 1136 | end 1137 | 1138 | function api.get_my_description(language_code) -- https://core.telegram.org/bots/api#getmydescription 1139 | local success, res = api.request(config.endpoint .. api.token .. '/getMyDescription', { 1140 | ['language_code'] = language_code 1141 | }) 1142 | return success, res 1143 | end 1144 | 1145 | function api.set_my_short_description(short_description, language_code) -- https://core.telegram.org/bots/api#setmyshortdescription 1146 | short_description = tostring(short_description) 1147 | if short_description:len() > 120 then 1148 | short_description = short_description:sub(1, 120) 1149 | end 1150 | local success, res = api.request(config.endpoint .. api.token .. '/setMyShortDescription', { 1151 | ['short_description'] = short_description, 1152 | ['language_code'] = language_code 1153 | }) 1154 | return success, res 1155 | end 1156 | 1157 | function api.get_my_short_description(language_code) -- https://core.telegram.org/bots/api#getmyshortdescription 1158 | local success, res = api.request(config.endpoint .. api.token .. '/getMyShortDescription', { 1159 | ['language_code'] = language_code 1160 | }) 1161 | return success, res 1162 | end 1163 | 1164 | function api.set_chat_menu_button(chat_id, menu_button) -- https://core.telegram.org/bots/api#setchatmenubutton 1165 | menu_button = type(menu_button) == 'table' and json.encode(menu_button) or menu_button 1166 | local success, res = api.request(config.endpoint .. api.token .. '/setChatMenuButton', { 1167 | ['chat_id'] = chat_id, 1168 | ['menu_button'] = menu_button 1169 | }) 1170 | return success, res 1171 | end 1172 | 1173 | function api.get_chat_menu_button(chat_id) -- https://core.telegram.org/bots/api#getchatmenubutton 1174 | local success, res = api.request(config.endpoint .. api.token .. '/getChatMenuButton', { 1175 | ['chat_id'] = chat_id 1176 | }) 1177 | return success, res 1178 | end 1179 | 1180 | function api.set_my_default_administrator_rights(rights, for_channels) -- https://core.telegram.org/bots/api#setmydefaultadministratorrights 1181 | rights = type(rights) == 'table' and json.encode(rights) or rights 1182 | local success, res = api.request(config.endpoint .. api.token .. '/setMyDefaultAdministratorRights', { 1183 | ['rights'] = rights, 1184 | ['for_channels'] = for_channels 1185 | }) 1186 | return success, res 1187 | end 1188 | 1189 | function api.get_my_default_administrator_rights(for_channels) -- https://core.telegram.org/bots/api#getmydefaultadministratorrights 1190 | local success, res = api.request(config.endpoint .. api.token .. '/getMyDefaultAdministratorRights', { 1191 | ['for_channels'] = for_channels 1192 | }) 1193 | return success, res 1194 | end 1195 | 1196 | function api.edit_message_text(chat_id, message_id, text, parse_mode, entities, link_preview_options, reply_markup, 1197 | inline_message_id) -- https://core.telegram.org/bots/api#editmessagetext 1198 | entities = type(entities) == 'table' and json.encode(entities) or entities 1199 | link_preview_options = type(link_preview_options) == 'table' and json.encode(link_preview_options) or 1200 | link_preview_options 1201 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 1202 | parse_mode = (type(parse_mode) == 'boolean' and parse_mode == true) and 'MarkdownV2' or parse_mode 1203 | local success, res = api.request(config.endpoint .. api.token .. '/editMessageText', { 1204 | ['chat_id'] = chat_id, 1205 | ['message_id'] = message_id, 1206 | ['inline_message_id'] = inline_message_id, 1207 | ['text'] = text, 1208 | ['parse_mode'] = parse_mode, 1209 | ['entities'] = entities, 1210 | ['link_preview_options'] = link_preview_options, 1211 | ['reply_markup'] = reply_markup 1212 | }) 1213 | -- Try as an inline message 1214 | if not success then 1215 | success, res = api.request(config.endpoint .. api.token .. '/editMessageText', { 1216 | ['chat_id'] = chat_id, 1217 | ['message_id'] = inline_message_id, 1218 | ['inline_message_id'] = message_id, 1219 | ['text'] = text, 1220 | ['parse_mode'] = parse_mode, 1221 | ['entities'] = entities, 1222 | ['link_preview_options'] = link_preview_options, 1223 | ['reply_markup'] = reply_markup 1224 | }) 1225 | end 1226 | return success, res 1227 | end 1228 | 1229 | function api.edit_message_caption(chat_id, message_id, caption, parse_mode, caption_entities, reply_markup, 1230 | inline_message_id) -- https://core.telegram.org/bots/api#editmessagecaption 1231 | caption_entities = type(caption_entities) == 'table' and json.encode(caption_entities) or caption_entities 1232 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 1233 | parse_mode = (type(parse_mode) == 'boolean' and parse_mode == true) and 'MarkdownV2' or parse_mode 1234 | local success, res = api.request(config.endpoint .. api.token .. '/editMessageCaption', { 1235 | ['chat_id'] = chat_id, 1236 | ['message_id'] = message_id, 1237 | ['inline_message_id'] = inline_message_id, 1238 | ['caption'] = caption, 1239 | ['parse_mode'] = parse_mode, 1240 | ['caption_entities'] = caption_entities, 1241 | ['reply_markup'] = reply_markup 1242 | }) 1243 | -- Try as an inline message 1244 | if not success then 1245 | success, res = api.request(config.endpoint .. api.token .. '/editMessageCaption', { 1246 | ['chat_id'] = chat_id, 1247 | ['message_id'] = inline_message_id, 1248 | ['inline_message_id'] = message_id, 1249 | ['caption'] = caption, 1250 | ['parse_mode'] = parse_mode, 1251 | ['caption_entities'] = caption_entities, 1252 | ['reply_markup'] = reply_markup 1253 | }) 1254 | end 1255 | return success, res 1256 | end 1257 | 1258 | function api.edit_message_media(chat_id, message_id, media, reply_markup, inline_message_id) -- https://core.telegram.org/bots/api#editmessagemedia 1259 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 1260 | media = type(media) == 'table' and json.encode(media) or media 1261 | local success, res = api.request(config.endpoint .. api.token .. '/editMessageMedia', { 1262 | ['chat_id'] = chat_id, 1263 | ['message_id'] = message_id, 1264 | ['inline_message_id'] = inline_message_id, 1265 | ['media'] = media, 1266 | ['reply_markup'] = reply_markup 1267 | }) 1268 | if not success then 1269 | success, res = api.request(config.endpoint .. api.token .. '/editMessageMedia', { 1270 | ['chat_id'] = chat_id, 1271 | ['message_id'] = inline_message_id, 1272 | ['inline_message_id'] = message_id, 1273 | ['media'] = media, 1274 | ['reply_markup'] = reply_markup 1275 | }) 1276 | end 1277 | return success, res 1278 | end 1279 | 1280 | function api.edit_message_reply_markup(chat_id, message_id, inline_message_id, reply_markup) -- https://core.telegram.org/bots/api#editmessagereplymarkup 1281 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 1282 | local success, res = api.request(config.endpoint .. api.token .. '/editMessageReplyMarkup', { 1283 | ['chat_id'] = chat_id, 1284 | ['message_id'] = message_id, 1285 | ['inline_message_id'] = inline_message_id, 1286 | ['reply_markup'] = reply_markup 1287 | }) 1288 | -- Try as an inline message 1289 | if not success then 1290 | success, res = api.request(config.endpoint .. api.token .. '/editMessageReplyMarkup', { 1291 | ['chat_id'] = chat_id, 1292 | ['message_id'] = inline_message_id, 1293 | ['inline_message_id'] = message_id, 1294 | ['reply_markup'] = reply_markup 1295 | }) 1296 | end 1297 | return success, res 1298 | end 1299 | 1300 | function api.stop_poll(chat_id, message_id, reply_markup) -- https://core.telegram.org/bots/api#stoppoll 1301 | local success, res = api.request(config.endpoint .. api.token .. '/stopPoll', { 1302 | ['chat_id'] = chat_id, 1303 | ['message_id'] = message_id, 1304 | ['reply_markup'] = reply_markup 1305 | }) 1306 | return success, res 1307 | end 1308 | 1309 | function api.delete_message(chat_id, message_id) -- https://core.telegram.org/bots/api#deletemessage 1310 | local success, res = api.request(config.endpoint .. api.token .. '/deleteMessage', { 1311 | ['chat_id'] = chat_id, 1312 | ['message_id'] = message_id 1313 | }) 1314 | return success, res 1315 | end 1316 | 1317 | function api.delete_messages(chat_id, message_ids) -- https://core.telegram.org/bots/api#deletemessages 1318 | message_ids = type(message_ids) == 'table' and json.encode(message_ids) or message_ids 1319 | local success, res = api.request(config.endpoint .. api.token .. '/deleteMessages', { 1320 | ['chat_id'] = chat_id, 1321 | ['message_ids'] = message_ids 1322 | }) 1323 | return success, res 1324 | end 1325 | 1326 | -------------- 1327 | -- STICKERS -- 1328 | -------------- 1329 | 1330 | function api.send_sticker(chat_id, sticker, message_thread_id, emoji, disable_notification, protect_content, 1331 | reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendsticker 1332 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 1333 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 1334 | local success, res = api.request(config.endpoint .. api.token .. '/sendSticker', { 1335 | ['chat_id'] = chat_id, 1336 | ['message_thread_id'] = message_thread_id, 1337 | ['emoji'] = emoji, 1338 | ['disable_notification'] = disable_notification, 1339 | ['protect_content'] = protect_content, 1340 | ['reply_parameters'] = reply_parameters, 1341 | ['reply_markup'] = reply_markup 1342 | }, { 1343 | ['sticker'] = sticker 1344 | }) 1345 | return success, res 1346 | end 1347 | 1348 | function api.get_sticker_set(name) -- https://core.telegram.org/bots/api#getstickerset 1349 | local success, res = api.request(config.endpoint .. api.token .. '/getStickerSet', { 1350 | ['name'] = name 1351 | }) 1352 | return success, res 1353 | end 1354 | 1355 | function api.get_custom_emoji_stickers(custom_emoji_ids) -- https://core.telegram.org/bots/api#getcustomemojistickers 1356 | custom_emoji_ids = type(custom_emoji_ids) == 'table' and json.encode(custom_emoji_ids) or custom_emoji_ids 1357 | local success, res = api.request(config.endpoint .. api.token .. '/getCustomEmojiStickers', { 1358 | ['custom_emoji_ids'] = custom_emoji_ids 1359 | }) 1360 | return success, res 1361 | end 1362 | 1363 | function api.upload_sticker_file(user_id, sticker, sticker_format) -- https://core.telegram.org/bots/api#uploadstickerfile 1364 | local success, res = api.request(config.endpoint .. api.token .. '/uploadStickerFile', { 1365 | ['user_id'] = user_id, 1366 | ['sticker_format'] = sticker_format 1367 | }, { 1368 | ['sticker'] = sticker 1369 | }) 1370 | return success, res 1371 | end 1372 | 1373 | function api.create_new_sticker_set(user_id, name, title, stickers, sticker_format, sticker_type, needs_repainting) -- https://core.telegram.org/bots/api#createnewstickerset 1374 | stickers = type(stickers) == 'table' and json.encode(stickers) or stickers 1375 | local success, res = api.request(config.endpoint .. api.token .. '/createNewStickerSet', { 1376 | ['user_id'] = user_id, 1377 | ['name'] = name, 1378 | ['title'] = title, 1379 | ['stickers'] = stickers, 1380 | ['sticker_format'] = sticker_format, 1381 | ['sticker_type'] = sticker_type, 1382 | ['needs_repainting'] = needs_repainting 1383 | }) 1384 | return success, res 1385 | end 1386 | 1387 | function api.add_sticker_to_set(user_id, name, sticker) -- https://core.telegram.org/bots/api#addstickertoset 1388 | sticker = type(sticker) == 'table' and json.encode(sticker) or sticker 1389 | local success, res = api.request(config.endpoint .. api.token .. '/addStickerToSet', { 1390 | ['user_id'] = user_id, 1391 | ['name'] = name, 1392 | ['sticker'] = sticker 1393 | }) 1394 | return success, res 1395 | end 1396 | 1397 | function api.set_sticker_position_in_set(sticker, position) -- https://core.telegram.org/bots/api#setstickerpositioninset 1398 | local success, res = api.request(config.endpoint .. api.token .. '/setStickerPositionInSet', { 1399 | ['sticker'] = sticker, 1400 | ['position'] = position 1401 | }) 1402 | return success, res 1403 | end 1404 | 1405 | function api.delete_sticker_from_set(sticker) -- https://core.telegram.org/bots/api#deletestickerfromset 1406 | local success, res = api.request(config.endpoint .. api.token .. '/deleteStickerFromSet', { 1407 | ['sticker'] = sticker 1408 | }) 1409 | return success, res 1410 | end 1411 | 1412 | function api.set_sticker_emoji_list(sticker, emoji_list) -- https://core.telegram.org/bots/api#setstickeremojilist 1413 | emoji_list = type(emoji_list) == 'table' and json.encode(emoji_list) or emoji_list 1414 | local success, res = api.request(config.endpoint .. api.token .. '/setStickerEmojiList', { 1415 | ['sticker'] = sticker, 1416 | ['emoji_list'] = emoji_list 1417 | }) 1418 | return success, res 1419 | end 1420 | 1421 | function api.set_sticker_keywords(sticker, keywords) -- https://core.telegram.org/bots/api#setstickerkeywords 1422 | keywords = type(keywords) == 'table' and json.encode(keywords) or keywords 1423 | local success, res = api.request(config.endpoint .. api.token .. '/setStickerKeywords', { 1424 | ['sticker'] = sticker, 1425 | ['keywords'] = keywords 1426 | }) 1427 | return success, res 1428 | end 1429 | 1430 | function api.set_sticker_mask_position(sticker, mask_position) -- https://core.telegram.org/bots/api#setstickermaskposition 1431 | mask_position = type(mask_position) == 'table' and json.encode(mask_position) or mask_position 1432 | local success, res = api.request(config.endpoint .. api.token .. '/setStickerMaskPosition', { 1433 | ['sticker'] = sticker, 1434 | ['mask_position'] = mask_position 1435 | }) 1436 | return success, res 1437 | end 1438 | 1439 | function api.set_sticker_set_title(name, title) -- https://core.telegram.org/bots/api#setstickersettitle 1440 | title = tostring(title) 1441 | if title:len() > 64 then 1442 | title = title:sub(1, 64) 1443 | end 1444 | local success, res = api.request(config.endpoint .. api.token .. '/setStickerSetTitle', { 1445 | ['name'] = name, 1446 | ['title'] = title 1447 | }) 1448 | return success, res 1449 | end 1450 | 1451 | function api.set_sticker_set_thumbnail(name, user_id, thumbnail) -- https://core.telegram.org/bots/api#setstickersetthumbnail 1452 | local success, res = api.request(config.endpoint .. api.token .. '/setStickerSetThumbnail', { 1453 | ['name'] = name, 1454 | ['user_id'] = user_id 1455 | }, { 1456 | ['thumbnail'] = thumbnail 1457 | }) 1458 | return success, res 1459 | end 1460 | 1461 | function api.set_custom_emoji_sticker_set_thumbnail(name, custom_emoji_id) -- https://core.telegram.org/bots/api#setcustomemojistickersetthumbnail 1462 | local success, res = api.request(config.endpoint .. api.token .. '/setCustomEmojiStickerSetThumbnail', { 1463 | ['name'] = name, 1464 | ['custom_emoji_id'] = custom_emoji_id 1465 | }) 1466 | return success, res 1467 | end 1468 | 1469 | function api.delete_sticker_set(name) -- https://core.telegram.org/bots/api#deletestickerset 1470 | local success, res = api.request(config.endpoint .. api.token .. '/deleteStickerSet', { 1471 | ['name'] = name 1472 | }) 1473 | return success, res 1474 | end 1475 | 1476 | function api.answer_inline_query(inline_query_id, results, cache_time, is_personal, next_offset, button) -- https://core.telegram.org/bots/api#answerinlinequery 1477 | button = type(button) == 'table' and json.encode(button) or button 1478 | if results and type(results) == 'table' then 1479 | if results.id then 1480 | results = {results} 1481 | end 1482 | results = json.encode(results) 1483 | end 1484 | local success, res = api.request(config.endpoint .. api.token .. '/answerInlineQuery', { 1485 | ['inline_query_id'] = inline_query_id, 1486 | ['results'] = results, 1487 | ['cache_time'] = cache_time, 1488 | ['is_personal'] = is_personal, 1489 | ['next_offset'] = next_offset, 1490 | ['button'] = button 1491 | }) 1492 | return success, res 1493 | end 1494 | 1495 | function api.answer_web_app_query(web_app_query_id, result) -- https://core.telegram.org/bots/api#answerwebappquery 1496 | result = type(result) == 'table' and json.encode(result) or result 1497 | local success, res = api.request(config.endpoint .. api.token .. '/answerWebAppQuery', { 1498 | ['web_app_query_id'] = web_app_query_id, 1499 | ['result'] = result 1500 | }) 1501 | return success, res 1502 | end 1503 | 1504 | -------------- 1505 | -- PAYMENTS -- 1506 | -------------- 1507 | 1508 | function api.send_invoice(chat_id, title, description, payload, provider_token, currency, prices, message_thread_id, 1509 | max_tip_amount, suggested_tip_amounts, start_parameter, provider_data, photo_url, photo_size, photo_width, 1510 | photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, 1511 | send_email_to_provider, is_flexible, disable_notification, protect_content, reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendinvoice 1512 | prices = type(prices) == 'table' and json.encode(prices) or prices 1513 | suggested_tip_amounts = type(suggested_tip_amounts) == 'table' and json.encode(suggested_tip_amounts) or 1514 | suggested_tip_amounts 1515 | provider_data = type(provider_data) == 'table' and json.encode(provider_data) or provider_data 1516 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 1517 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 1518 | local success, res = api.request(config.endpoint .. api.token .. '/sendInvoice', { 1519 | ['chat_id'] = chat_id, 1520 | ['message_thread_id'] = message_thread_id, 1521 | ['title'] = title, 1522 | ['description'] = description, 1523 | ['payload'] = payload, 1524 | ['provider_token'] = provider_token, 1525 | ['currency'] = currency, 1526 | ['prices'] = prices, 1527 | ['max_tip_amount'] = max_tip_amount, 1528 | ['suggested_tip_amounts'] = suggested_tip_amounts, 1529 | ['start_parameter'] = start_parameter, 1530 | ['provider_data'] = provider_data, 1531 | ['photo_url'] = photo_url, 1532 | ['photo_size'] = photo_size, 1533 | ['photo_width'] = photo_width, 1534 | ['photo_height'] = photo_height, 1535 | ['need_name'] = need_name, 1536 | ['need_phone_number'] = need_phone_number, 1537 | ['need_email'] = need_email, 1538 | ['need_shipping_address'] = need_shipping_address, 1539 | ['send_phone_number_to_provider'] = send_phone_number_to_provider, 1540 | ['send_email_to_provider'] = send_email_to_provider, 1541 | ['is_flexible'] = is_flexible, 1542 | ['disable_notification'] = disable_notification, 1543 | ['protect_content'] = protect_content, 1544 | ['reply_parameters'] = reply_parameters, 1545 | ['reply_markup'] = reply_markup 1546 | }) 1547 | return success, res 1548 | end 1549 | 1550 | function api.create_invoice_link(title, description, payload, provider_token, currency, prices, max_tip_amount, 1551 | suggested_tip_amounts, provider_data, photo_url, photo_size, photo_width, photo_height, need_name, 1552 | need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, send_email_to_provider, 1553 | is_flexible) -- https://core.telegram.org/bots/api#createinvoicelink 1554 | prices = type(prices) == 'table' and json.encode(prices) or prices 1555 | suggested_tip_amounts = type(suggested_tip_amounts) == 'table' and json.encode(suggested_tip_amounts) or 1556 | suggested_tip_amounts 1557 | provider_data = type(provider_data) == 'table' and json.encode(provider_data) or provider_data 1558 | local success, res = api.request(config.endpoint .. api.token .. '/createInvoiceLink', { 1559 | ['title'] = title, 1560 | ['description'] = description, 1561 | ['payload'] = payload, 1562 | ['provider_token'] = provider_token, 1563 | ['currency'] = currency, 1564 | ['prices'] = prices, 1565 | ['max_tip_amount'] = max_tip_amount, 1566 | ['suggested_tip_amounts'] = suggested_tip_amounts, 1567 | ['provider_data'] = provider_data, 1568 | ['photo_url'] = photo_url, 1569 | ['photo_size'] = photo_size, 1570 | ['photo_width'] = photo_width, 1571 | ['photo_height'] = photo_height, 1572 | ['need_name'] = need_name, 1573 | ['need_phone_number'] = need_phone_number, 1574 | ['need_email'] = need_email, 1575 | ['need_shipping_address'] = need_shipping_address, 1576 | ['send_phone_number_to_provider'] = send_phone_number_to_provider, 1577 | ['send_email_to_provider'] = send_email_to_provider, 1578 | ['is_flexible'] = is_flexible 1579 | }) 1580 | return success, res 1581 | end 1582 | 1583 | function api.answer_shipping_query(shipping_query_id, ok, shipping_options, error_message) -- https://core.telegram.org/bots/api#answershippingquery 1584 | shipping_options = type(shipping_options) == 'table' and json.encode(shipping_options) or shipping_options 1585 | -- Ensure an error_message is given when ok is false 1586 | if type(ok) == 'boolean' and ok == false and not error_message then 1587 | error_message = 'Unspecified issue occurred! Please contact the person you received this invoice from!' 1588 | end 1589 | local success, res = api.request(config.endpoint .. api.token .. '/answerShippingQuery', { 1590 | ['shipping_query_id'] = shipping_query_id, 1591 | ['ok'] = ok, 1592 | ['shipping_options'] = shipping_options, 1593 | ['error_message'] = error_message 1594 | }) 1595 | return success, res 1596 | end 1597 | 1598 | function api.answer_pre_checkout_query(pre_checkout_query_id, ok, error_message) -- https://core.telegram.org/bots/api#answerprecheckoutquery 1599 | -- Ensure an error_message is given when ok is false 1600 | if type(ok) == 'boolean' and ok == false and not error_message then 1601 | error_message = 'Unspecified issue occurred! Please contact the person you received this invoice from!' 1602 | end 1603 | local success, res = api.request(config.endpoint .. api.token .. '/answerPreCheckoutQuery', { 1604 | ['pre_checkout_query_id'] = pre_checkout_query_id, 1605 | ['ok'] = ok, 1606 | ['error_message'] = error_message 1607 | }) 1608 | return success, res 1609 | end 1610 | 1611 | --------------- 1612 | -- PASSPORTS -- 1613 | --------------- 1614 | 1615 | function api.set_passport_data_errors(user_id, errors) -- https://core.telegram.org/bots/api#setpassportdataerrors 1616 | errors = type(errors) == 'table' and json.encode(errors) or errors 1617 | local success, res = api.request(config.endpoint .. api.token .. '/setPassportDataErrors', { 1618 | ['user_id'] = user_id, 1619 | ['errors'] = errors 1620 | }) 1621 | return success, res 1622 | end 1623 | 1624 | ----------- 1625 | -- GAMES -- 1626 | ----------- 1627 | 1628 | function api.send_game(chat_id, game_short_name, message_thread_id, disable_notification, protect_content, 1629 | reply_parameters, reply_markup) -- https://core.telegram.org/bots/api#sendgame 1630 | reply_parameters = type(reply_parameters) == 'table' and json.encode(reply_parameters) or reply_parameters 1631 | reply_markup = type(reply_markup) == 'table' and json.encode(reply_markup) or reply_markup 1632 | local success, res = api.request(config.endpoint .. api.token .. '/sendGame', { 1633 | ['chat_id'] = chat_id, 1634 | ['message_thread_id'] = message_thread_id, 1635 | ['game_short_name'] = game_short_name, 1636 | ['disable_notification'] = disable_notification, 1637 | ['protect_content'] = protect_content, 1638 | ['reply_parameters'] = reply_parameters, 1639 | ['reply_markup'] = reply_markup 1640 | }) 1641 | return success, res 1642 | end 1643 | 1644 | function api.set_game_score(user_id, score, force, disable_edit_message, chat_id, message_id, inline_message_id) -- https://core.telegram.org/bots/api#setgamescore 1645 | local success, res = api.request(config.endpoint .. api.token .. '/setGameScore', { 1646 | ['user_id'] = user_id, 1647 | ['score'] = score, 1648 | ['force'] = force, 1649 | ['disable_edit_message'] = disable_edit_message, 1650 | ['chat_id'] = chat_id, 1651 | ['message_id'] = message_id, 1652 | ['inline_message_id'] = inline_message_id 1653 | }) 1654 | return success, res 1655 | end 1656 | 1657 | function api.get_game_high_scores(chat_id, user_id, message_id, inline_message_id) -- https://core.telegram.org/bots/api#getgamehighscores 1658 | local success, res = api.request(config.endpoint .. api.token .. '/getGameHighScores', { 1659 | ['user_id'] = user_id, 1660 | ['chat_id'] = chat_id, 1661 | ['message_id'] = message_id, 1662 | ['inline_message_id'] = inline_message_id 1663 | }) 1664 | return success, res 1665 | end 1666 | 1667 | function api.on_update(_) 1668 | end 1669 | function api.on_message(_) 1670 | end 1671 | function api.on_private_message(_) 1672 | end 1673 | function api.on_group_message(_) 1674 | end 1675 | function api.on_supergroup_message(_) 1676 | end 1677 | function api.on_callback_query(_) 1678 | end 1679 | function api.on_inline_query(_) 1680 | end 1681 | function api.on_channel_post(_) 1682 | end 1683 | function api.on_edited_message(_) 1684 | end 1685 | function api.on_edited_private_message(_) 1686 | end 1687 | function api.on_edited_group_message(_) 1688 | end 1689 | function api.on_edited_supergroup_message(_) 1690 | end 1691 | function api.on_edited_channel_post(_) 1692 | end 1693 | function api.on_chosen_inline_result(_) 1694 | end 1695 | function api.on_shipping_query(_) 1696 | end 1697 | function api.on_pre_checkout_query(_) 1698 | end 1699 | function api.on_poll(_) 1700 | end 1701 | function api.on_poll_answer(_) 1702 | end 1703 | function api.on_message_reaction(_) 1704 | end 1705 | function api.on_message_reaction_count(_) 1706 | end 1707 | function api.on_my_chat_member(_) 1708 | end 1709 | function api.on_chat_member(_) 1710 | end 1711 | function api.on_chat_join_request(_) 1712 | end 1713 | function api.on_chat_boost(_) 1714 | end 1715 | function api.on_removed_chat_boost(_) 1716 | end 1717 | 1718 | function api.process_update(update) 1719 | if update then 1720 | api.on_update(update) 1721 | end 1722 | if update.message then 1723 | if update.message.chat.type == 'private' then 1724 | api.on_private_message(update.message) 1725 | elseif update.message.chat.type == 'group' then 1726 | api.on_group_message(update.message) 1727 | elseif update.message.chat.type == 'supergroup' then 1728 | api.on_supergroup_message(update.message) 1729 | end 1730 | return api.on_message(update.message) 1731 | elseif update.edited_message then 1732 | if update.edited_message.chat.type == 'private' then 1733 | api.on_edited_private_message(update.edited_message) 1734 | elseif update.edited_message.chat.type == 'group' then 1735 | api.on_edited_group_message(update.edited_message) 1736 | elseif update.edited_message.chat.type == 'supergroup' then 1737 | api.on_edited_supergroup_message(update.edited_message) 1738 | end 1739 | return api.on_edited_message(update.edited_message) 1740 | elseif update.callback_query then 1741 | return api.on_callback_query(update.callback_query) 1742 | elseif update.inline_query then 1743 | return api.on_inline_query(update.inline_query) 1744 | elseif update.channel_post then 1745 | return api.on_channel_post(update.channel_post) 1746 | elseif update.edited_channel_post then 1747 | return api.on_edited_channel_post(update.edited_channel_post) 1748 | elseif update.chosen_inline_result then 1749 | return api.on_chosen_inline_result(update.chosen_inline_result) 1750 | elseif update.shipping_query then 1751 | return api.on_shipping_query(update.shipping_query) 1752 | elseif update.pre_checkout_query then 1753 | return api.on_pre_checkout_query(update.pre_checkout_query) 1754 | elseif update.poll then 1755 | return api.on_poll(update.poll) 1756 | elseif update.poll_answer then 1757 | return api.on_poll_answer 1758 | elseif update.message_reaction then 1759 | return api.on_message_reaction 1760 | elseif update.message_reaction_count then 1761 | return api.on_message_reaction_count 1762 | elseif update.my_chat_member then 1763 | return api.on_my_chat_member 1764 | elseif update.chat_member then 1765 | return api.on_chat_member 1766 | elseif update.chat_join_request then 1767 | return api.on_chat_join_request 1768 | elseif update.chat_boost then 1769 | return api.on_chat_boost 1770 | elseif update.removed_chat_boost then 1771 | return api.on_removed_chat_boost 1772 | end 1773 | return false 1774 | end 1775 | 1776 | function api.run(limit, timeout, offset, allowed_updates, use_beta_endpoint) 1777 | limit = tonumber(limit) ~= nil and limit or 1 1778 | timeout = tonumber(timeout) ~= nil and timeout or 0 1779 | offset = tonumber(offset) ~= nil and offset or 0 1780 | while true do 1781 | local updates = api.get_updates(timeout, offset, limit, allowed_updates, use_beta_endpoint) 1782 | if updates and type(updates) == 'table' and updates.result then 1783 | for _, v in pairs(updates.result) do 1784 | api.process_update(v) 1785 | offset = v.update_id + 1 1786 | end 1787 | end 1788 | end 1789 | end 1790 | 1791 | function api.input_text_message_content(message_text, parse_mode, disable_web_page_preview, encoded) 1792 | parse_mode = (type(parse_mode) == 'boolean' and parse_mode == true) and 'markdown' or parse_mode 1793 | local input_message_content = { 1794 | ['message_text'] = tostring(message_text), 1795 | ['parse_mode'] = parse_mode, 1796 | ['disable_web_page_preview'] = disable_web_page_preview 1797 | } 1798 | input_message_content = encoded and json.encode(input_message_content) or input_message_content 1799 | return input_message_content 1800 | end 1801 | 1802 | function api.input_location_message_content(latitude, longitude, encoded) 1803 | local input_message_content = { 1804 | ['latitude'] = tonumber(latitude), 1805 | ['longitude'] = tonumber(longitude) 1806 | } 1807 | input_message_content = encoded and json.encode(input_message_content) or input_message_content 1808 | return input_message_content 1809 | end 1810 | 1811 | function api.input_venue_message_content(latitude, longitude, title, address, foursquare_id, encoded) 1812 | local input_message_content = { 1813 | ['latitude'] = tonumber(latitude), 1814 | ['longitude'] = tonumber(longitude), 1815 | ['title'] = tostring(title), 1816 | ['address'] = tostring(address), 1817 | ['foursquare_id'] = foursquare_id 1818 | } 1819 | input_message_content = encoded and json.encode(input_message_content) or input_message_content 1820 | return input_message_content 1821 | end 1822 | 1823 | function api.input_contact_message_content(phone_number, first_name, last_name, encoded) 1824 | local input_message_content = { 1825 | ['phone_number'] = tostring(phone_number), 1826 | ['first_name'] = tonumber(first_name), 1827 | ['last_name'] = last_name 1828 | } 1829 | input_message_content = encoded and json.encode(input_message_content) or input_message_content 1830 | return input_message_content 1831 | end 1832 | 1833 | function api.input_media_photo(media, caption, parse_mode) 1834 | return { 1835 | ['type'] = 'photo', 1836 | ['caption'] = caption, 1837 | ['parse_mode'] = parse_mode 1838 | }, { 1839 | ['media'] = media 1840 | } 1841 | end 1842 | 1843 | function api.input_media_video(media, thumb, caption, parse_mode, width, height, duration, supports_streaming) 1844 | return { 1845 | ['type'] = 'video', 1846 | ['caption'] = caption, 1847 | ['parse_mode'] = parse_mode, 1848 | ['width'] = tonumber(width), 1849 | ['height'] = tonumber(height), 1850 | ['duration'] = tonumber(duration), 1851 | ['supports_streaming'] = supports_streaming 1852 | }, { 1853 | ['media'] = media, 1854 | ['thumb'] = thumb 1855 | } 1856 | end 1857 | 1858 | function api.input_media_animation(media, thumb, caption, parse_mode, width, height, duration) 1859 | return { 1860 | ['type'] = 'animation', 1861 | ['caption'] = caption, 1862 | ['parse_mode'] = parse_mode, 1863 | ['width'] = tonumber(width), 1864 | ['height'] = tonumber(height), 1865 | ['duration'] = tonumber(duration) 1866 | }, { 1867 | ['media'] = media, 1868 | ['thumb'] = thumb 1869 | } 1870 | end 1871 | 1872 | function api.input_media_audio(media, thumb, caption, parse_mode, duration, performer, title) 1873 | return { 1874 | ['type'] = 'audio', 1875 | ['caption'] = caption, 1876 | ['parse_mode'] = parse_mode, 1877 | ['duration'] = tonumber(duration), 1878 | ['performer'] = performer, 1879 | ['title'] = title 1880 | }, { 1881 | ['media'] = media, 1882 | ['thumb'] = thumb 1883 | } 1884 | end 1885 | 1886 | function api.input_media_document(media, thumb, caption, parse_mode) 1887 | return { 1888 | ['type'] = 'document', 1889 | ['caption'] = caption, 1890 | ['parse_mode'] = parse_mode 1891 | }, { 1892 | ['media'] = media, 1893 | ['thumb'] = thumb 1894 | } 1895 | end 1896 | 1897 | -- Functions to handle ChatPermissions 1898 | 1899 | function api.chat_permissions(can_send_messages, can_send_media_messages, can_send_polls, can_send_other_messages, 1900 | can_add_web_page_previews, can_change_info, can_invite_users, can_pin_messages) 1901 | return { 1902 | ['can_send_messages'] = can_send_messages, 1903 | ['can_send_media_messages'] = can_send_media_messages, 1904 | ['can_send_polls'] = can_send_polls, 1905 | ['can_send_other_messages'] = can_send_other_messages, 1906 | ['can_add_web_page_previews'] = can_add_web_page_previews, 1907 | ['can_change_info'] = can_change_info, 1908 | ['can_invite_users'] = can_invite_users, 1909 | ['can_pin_messages'] = can_pin_messages 1910 | } 1911 | end 1912 | 1913 | -- Functions and meta-methods for handling mask positioning arrays to use with various 1914 | -- sticker-related functions. 1915 | 1916 | api.mask_position_meta = {} 1917 | api.mask_position_meta.__index = api.mask_position_meta 1918 | 1919 | function api.mask_position_meta:position(point, x_shift, y_shift, scale) 1920 | table.insert(self, { 1921 | ['point'] = tostring(point), -- Available points include "forehead", "eyes", "mouth" or "chin". 1922 | ['x_shift'] = tonumber(x_shift), 1923 | ['y_shift'] = tonumber(y_shift), 1924 | ['scale'] = tonumber(scale) 1925 | }) 1926 | return self 1927 | end 1928 | 1929 | function api.mask_position() 1930 | local output = setmetatable({}, api.mask_position_meta) 1931 | return output 1932 | end 1933 | 1934 | -- Functions for handling inline objects to use with api.answer_inline_query(). 1935 | 1936 | function api.send_inline_article(inline_query_id, title, description, message_text, parse_mode, reply_markup) 1937 | description = description or title 1938 | message_text = message_text or description 1939 | parse_mode = (type(parse_mode) == 'boolean' and parse_mode == true) and 'markdown' or parse_mode 1940 | return api.answer_inline_query(inline_query_id, json.encode({{ 1941 | ['type'] = 'article', 1942 | ['id'] = '1', 1943 | ['title'] = title, 1944 | ['description'] = description, 1945 | ['input_message_content'] = { 1946 | ['message_text'] = message_text, 1947 | ['parse_mode'] = parse_mode 1948 | }, 1949 | ['reply_markup'] = reply_markup 1950 | }})) 1951 | end 1952 | 1953 | function api.send_inline_article_url(inline_query_id, title, url, hide_url, input_message_content, reply_markup, id) 1954 | return api.answer_inline_query(inline_query_id, json.encode({{ 1955 | ['type'] = 'article', 1956 | ['id'] = tonumber(id) ~= nil and tostring(id) or '1', 1957 | ['title'] = tostring(title), 1958 | ['url'] = tostring(url), 1959 | ['hide_url'] = hide_url or false, 1960 | ['input_message_content'] = input_message_content, 1961 | ['reply_markup'] = reply_markup 1962 | }})) 1963 | end 1964 | 1965 | api.inline_result_meta = {} 1966 | api.inline_result_meta.__index = api.inline_result_meta 1967 | 1968 | function api.inline_result_meta:type(type) 1969 | self['type'] = tostring(type) 1970 | return self 1971 | end 1972 | 1973 | function api.inline_result_meta:id(id) 1974 | self['id'] = id and tostring(id) or '1' 1975 | return self 1976 | end 1977 | 1978 | function api.inline_result_meta:title(title) 1979 | self['title'] = tostring(title) 1980 | return self 1981 | end 1982 | 1983 | function api.inline_result_meta:input_message_content(input_message_content) 1984 | self['input_message_content'] = input_message_content 1985 | return self 1986 | end 1987 | 1988 | function api.inline_result_meta:reply_markup(reply_markup) 1989 | self['reply_markup'] = reply_markup 1990 | return self 1991 | end 1992 | 1993 | function api.inline_result_meta:url(url) 1994 | self['url'] = tostring(url) 1995 | return self 1996 | end 1997 | 1998 | function api.inline_result_meta:hide_url(hide_url) 1999 | self['hide_url'] = hide_url or false 2000 | return self 2001 | end 2002 | 2003 | function api.inline_result_meta:description(description) 2004 | self['description'] = tostring(description) 2005 | return self 2006 | end 2007 | 2008 | function api.inline_result_meta:thumb_url(thumb_url) 2009 | self['thumb_url'] = tostring(thumb_url) 2010 | return self 2011 | end 2012 | 2013 | function api.inline_result_meta:thumb_width(thumb_width) 2014 | self['thumb_width'] = tonumber(thumb_width) 2015 | return self 2016 | end 2017 | 2018 | function api.inline_result_meta:thumb_height(thumb_height) 2019 | self['thumb_height'] = tonumber(thumb_height) 2020 | return self 2021 | end 2022 | 2023 | function api.inline_result_meta:photo_url(photo_url) 2024 | self['photo_url'] = tostring(photo_url) 2025 | return self 2026 | end 2027 | 2028 | function api.inline_result_meta:photo_width(photo_width) 2029 | self['photo_width'] = tonumber(photo_width) 2030 | return self 2031 | end 2032 | 2033 | function api.inline_result_meta:photo_height(photo_height) 2034 | self['photo_height'] = tonumber(photo_height) 2035 | return self 2036 | end 2037 | 2038 | function api.inline_result_meta:caption(caption) 2039 | self['caption'] = tostring(caption) 2040 | return self 2041 | end 2042 | 2043 | function api.inline_result_meta:gif_url(gif_url) 2044 | self['gif_url'] = tostring(gif_url) 2045 | return self 2046 | end 2047 | 2048 | function api.inline_result_meta:gif_width(gif_width) 2049 | self['gif_width'] = tonumber(gif_width) 2050 | return self 2051 | end 2052 | 2053 | function api.inline_result_meta:gif_height(gif_height) 2054 | self['gif_height'] = tonumber(gif_height) 2055 | return self 2056 | end 2057 | 2058 | function api.inline_result_meta:mpeg4_url(mpeg4_url) 2059 | self['mpeg4_url'] = tostring(mpeg4_url) 2060 | return self 2061 | end 2062 | 2063 | function api.inline_result_meta:mpeg4_width(mpeg4_width) 2064 | self['mpeg4_width'] = tonumber(mpeg4_width) 2065 | return self 2066 | end 2067 | 2068 | function api.inline_result_meta:mpeg4_height(mpeg4_height) 2069 | self['mpeg4_height'] = tonumber(mpeg4_height) 2070 | return self 2071 | end 2072 | 2073 | function api.inline_result_meta:video_url(video_url) 2074 | self['video_url'] = tostring(video_url) 2075 | return self 2076 | end 2077 | 2078 | function api.inline_result_meta:mime_type(mime_type) 2079 | self['mime_type'] = tostring(mime_type) 2080 | return self 2081 | end 2082 | 2083 | function api.inline_result_meta:video_width(video_width) 2084 | self['video_width'] = tonumber(video_width) 2085 | return self 2086 | end 2087 | 2088 | function api.inline_result_meta:video_height(video_height) 2089 | self['video_height'] = tonumber(video_height) 2090 | return self 2091 | end 2092 | 2093 | function api.inline_result_meta:video_duration(video_duration) 2094 | self['video_duration'] = tonumber(video_duration) 2095 | return self 2096 | end 2097 | 2098 | function api.inline_result_meta:audio_url(audio_url) 2099 | self['audio_url'] = tostring(audio_url) 2100 | return self 2101 | end 2102 | 2103 | function api.inline_result_meta:performer(performer) 2104 | self['performer'] = tostring(performer) 2105 | return self 2106 | end 2107 | 2108 | function api.inline_result_meta:audio_duration(audio_duration) 2109 | self['audio_duration'] = tonumber(audio_duration) 2110 | return self 2111 | end 2112 | 2113 | function api.inline_result_meta:voice_url(voice_url) 2114 | self['voice_url'] = tostring(voice_url) 2115 | return self 2116 | end 2117 | 2118 | function api.inline_result_meta:voice_duration(voice_duration) 2119 | self['voice_duration'] = tonumber(voice_duration) 2120 | return self 2121 | end 2122 | 2123 | function api.inline_result_meta:document_url(document_url) 2124 | self['document_url'] = tostring(document_url) 2125 | return self 2126 | end 2127 | 2128 | function api.inline_result_meta:latitude(latitude) 2129 | self['latitude'] = tonumber(latitude) 2130 | return self 2131 | end 2132 | 2133 | function api.inline_result_meta:longitude(longitude) 2134 | self['longitude'] = tonumber(longitude) 2135 | return self 2136 | end 2137 | 2138 | function api.inline_result_meta:live_period(live_period) 2139 | self['live_period'] = tonumber(live_period) 2140 | return self 2141 | end 2142 | 2143 | function api.inline_result_meta:address(address) 2144 | self['address'] = tostring(address) 2145 | return self 2146 | end 2147 | 2148 | function api.inline_result_meta:foursquare_id(foursquare_id) 2149 | self['foursquare_id'] = tostring(foursquare_id) 2150 | return self 2151 | end 2152 | 2153 | function api.inline_result_meta:phone_number(phone_number) 2154 | self['phone_number'] = tostring(phone_number) 2155 | return self 2156 | end 2157 | 2158 | function api.inline_result_meta:first_name(first_name) 2159 | self['first_name'] = tostring(first_name) 2160 | return self 2161 | end 2162 | 2163 | function api.inline_result_meta:last_name(last_name) 2164 | self['last_name'] = tostring(last_name) 2165 | return self 2166 | end 2167 | 2168 | function api.inline_result_meta:game_short_name(game_short_name) 2169 | self['game_short_name'] = tostring(game_short_name) 2170 | return self 2171 | end 2172 | 2173 | function api.inline_result() 2174 | local output = setmetatable({}, api.inline_result_meta) 2175 | return output 2176 | end 2177 | 2178 | function api.send_inline_photo(inline_query_id, photo_url, caption, reply_markup) 2179 | return api.answer_inline_query(inline_query_id, json.encode({{ 2180 | ['type'] = 'photo', 2181 | ['id'] = '1', 2182 | ['photo_url'] = photo_url, 2183 | ['thumb_url'] = photo_url, 2184 | ['caption'] = caption, 2185 | ['reply_markup'] = reply_markup 2186 | }})) 2187 | end 2188 | 2189 | function api.send_inline_cached_photo(inline_query_id, photo_file_id, caption, reply_markup) 2190 | return api.answer_inline_query(inline_query_id, json.encode({{ 2191 | ['type'] = 'photo', 2192 | ['id'] = '1', 2193 | ['photo_file_id'] = photo_file_id, 2194 | ['caption'] = caption, 2195 | ['reply_markup'] = reply_markup 2196 | }})) 2197 | end 2198 | 2199 | function api.url_button(text, url, encoded) 2200 | if not text or not url then 2201 | return false 2202 | end 2203 | local button = { 2204 | ['text'] = tostring(text), 2205 | ['url'] = tostring(url) 2206 | } 2207 | if encoded then 2208 | button = json.encode(button) 2209 | end 2210 | return button 2211 | end 2212 | 2213 | function api.callback_data_button(text, callback_data, encoded) 2214 | if not text or not callback_data then 2215 | return false 2216 | end 2217 | local button = { 2218 | ['text'] = tostring(text), 2219 | ['callback_data'] = tostring(callback_data) 2220 | } 2221 | if encoded then 2222 | button = json.encode(button) 2223 | end 2224 | return button 2225 | end 2226 | 2227 | function api.switch_inline_query_button(text, switch_inline_query, encoded) 2228 | if not text or not switch_inline_query then 2229 | return false 2230 | end 2231 | local button = { 2232 | ['text'] = tostring(text), 2233 | ['switch_inline_query'] = tostring(switch_inline_query) 2234 | } 2235 | if encoded then 2236 | button = json.encode(button) 2237 | end 2238 | return button 2239 | end 2240 | 2241 | function api.switch_inline_query_current_chat_button(text, switch_inline_query_current_chat, encoded) 2242 | if not text or not switch_inline_query_current_chat then 2243 | return false 2244 | end 2245 | local button = { 2246 | ['text'] = tostring(text), 2247 | ['switch_inline_query_current_chat'] = tostring(switch_inline_query_current_chat) 2248 | } 2249 | if encoded then 2250 | button = json.encode(button) 2251 | end 2252 | return button 2253 | end 2254 | 2255 | function api.callback_game_button(text, callback_game, encoded) 2256 | if not text or not callback_game then 2257 | return false 2258 | end 2259 | local button = { 2260 | ['text'] = tostring(text), 2261 | ['callback_game'] = tostring(callback_game) 2262 | } 2263 | if encoded then 2264 | button = json.encode(button) 2265 | end 2266 | return button 2267 | end 2268 | 2269 | function api.pay_button(text, pay, encoded) 2270 | if not text or pay == nil then 2271 | return false 2272 | end 2273 | local button = { 2274 | ['text'] = tostring(text), 2275 | ['pay'] = pay 2276 | } 2277 | if encoded then 2278 | button = json.encode(button) 2279 | end 2280 | return button 2281 | end 2282 | 2283 | function api.remove_keyboard(selective) 2284 | return { 2285 | ['remove_keyboard'] = true, 2286 | ['selective'] = selective or false 2287 | } 2288 | end 2289 | 2290 | api.keyboard_meta = {} 2291 | api.keyboard_meta.__index = api.keyboard_meta 2292 | 2293 | function api.keyboard_meta:row(row) 2294 | table.insert(self.keyboard, row) 2295 | return self 2296 | end 2297 | 2298 | function api.keyboard(resize_keyboard, one_time_keyboard, selective) 2299 | return setmetatable({ 2300 | ['keyboard'] = {}, 2301 | ['resize_keyboard'] = resize_keyboard or false, 2302 | ['one_time_keyboard'] = one_time_keyboard or false, 2303 | ['selective'] = selective or false 2304 | }, api.keyboard_meta) 2305 | end 2306 | 2307 | api.inline_keyboard_meta = {} 2308 | api.inline_keyboard_meta.__index = api.inline_keyboard_meta 2309 | 2310 | function api.inline_keyboard_meta:row(row) 2311 | table.insert(self.inline_keyboard, row) 2312 | return self 2313 | end 2314 | 2315 | function api.inline_keyboard() 2316 | return setmetatable({ 2317 | ['inline_keyboard'] = {} 2318 | }, api.inline_keyboard_meta) 2319 | end 2320 | 2321 | api.prices_meta = {} 2322 | api.prices_meta.__index = api.prices_meta 2323 | 2324 | function api.prices_meta:labeled_price(label, amount) 2325 | table.insert(self, { 2326 | ['label'] = tostring(label), 2327 | ['amount'] = tonumber(amount) 2328 | }) 2329 | return self 2330 | end 2331 | 2332 | function api.prices() 2333 | return setmetatable({}, api.prices_meta) 2334 | end 2335 | 2336 | api.shipping_options_meta = {} 2337 | api.shipping_options_meta.__index = api.shipping_options_meta 2338 | 2339 | function api.shipping_options_meta:shipping_option(id, title, prices) 2340 | table.insert(self, { 2341 | ['id'] = tostring(id), 2342 | ['title'] = tostring(title), 2343 | ['prices'] = prices 2344 | }) 2345 | return self 2346 | end 2347 | 2348 | function api.shipping_options() 2349 | return setmetatable({}, api.shipping_options_meta) 2350 | end 2351 | 2352 | api.row_meta = {} 2353 | api.row_meta.__index = api.row_meta 2354 | 2355 | function api.row_meta:url_button(text, url) 2356 | table.insert(self, { 2357 | ['text'] = tostring(text), 2358 | ['url'] = tostring(url) 2359 | }) 2360 | return self 2361 | end 2362 | 2363 | function api.row_meta:callback_data_button(text, callback_data) 2364 | table.insert(self, { 2365 | ['text'] = tostring(text), 2366 | ['callback_data'] = tostring(callback_data) 2367 | }) 2368 | return self 2369 | end 2370 | 2371 | function api.row_meta:switch_inline_query_button(text, switch_inline_query) 2372 | table.insert(self, { 2373 | ['text'] = tostring(text), 2374 | ['switch_inline_query'] = tostring(switch_inline_query) 2375 | }) 2376 | return self 2377 | end 2378 | 2379 | function api.row_meta:switch_inline_query_current_chat_button(text, switch_inline_query_current_chat) 2380 | table.insert(self, { 2381 | ['text'] = tostring(text), 2382 | ['switch_inline_query_current_chat'] = tostring(switch_inline_query_current_chat) 2383 | }) 2384 | return self 2385 | end 2386 | 2387 | function api.row_meta:pay_button(text, pay) 2388 | table.insert(self, { 2389 | ['text'] = tostring(text), 2390 | ['pay'] = pay 2391 | }) 2392 | return self 2393 | end 2394 | 2395 | function api.row(_) 2396 | return setmetatable({}, api.row_meta) 2397 | end 2398 | 2399 | api.input_media_meta = {} 2400 | api.input_media_meta.__index = api.input_media_meta 2401 | 2402 | function api.input_media_meta:photo(media, caption) 2403 | table.insert(self, { 2404 | ['type'] = 'photo', 2405 | ['media'] = tostring(media), 2406 | ['caption'] = caption 2407 | }) 2408 | return self 2409 | end 2410 | 2411 | function api.input_media_meta:video(media, caption, width, height, duration) 2412 | table.insert(self, { 2413 | ['type'] = 'video', 2414 | ['media'] = tostring(media), 2415 | ['caption'] = caption, 2416 | ['width'] = width, 2417 | ['height'] = height, 2418 | ['duration'] = duration 2419 | }) 2420 | return self 2421 | end 2422 | 2423 | function api.input_media(_) 2424 | return setmetatable({}, api.input_media_meta) 2425 | end 2426 | 2427 | function api.labeled_price(label, amount, encoded) 2428 | if not label or not amount or tonumber(amount) == nil then 2429 | return false 2430 | end 2431 | local button = { 2432 | ['label'] = tostring(label), 2433 | ['amount'] = tonumber(amount) 2434 | } 2435 | if encoded then 2436 | button = json.encode(button) 2437 | end 2438 | return button 2439 | end 2440 | 2441 | function api.get_chat_member_permissions(chat_id, user_id) 2442 | if not chat_id or not user_id then 2443 | return false 2444 | end 2445 | local success = api.get_chat_member(chat_id, user_id) 2446 | if not success then 2447 | return success 2448 | end 2449 | local p = success.result 2450 | return { 2451 | ['can_be_edited'] = p.can_be_edited or false, 2452 | ['can_post_messages'] = p.can_post_messages or false, 2453 | ['can_edit_messages'] = p.can_edit_messages or false, 2454 | ['can_delete_messages'] = p.can_delete_messages or false, 2455 | ['can_restrict_members'] = p.can_restrict_members or false, 2456 | ['can_promote_members'] = p.can_promote_members or false, 2457 | ['can_change_info'] = p.can_change_info or false, 2458 | ['can_invite_users'] = p.can_invite_users or false, 2459 | ['can_pin_messages'] = p.can_pin_messages or false, 2460 | ['can_send_messages'] = p.can_send_messages or false, 2461 | ['can_send_media_messages'] = p.can_send_media_messages or false, 2462 | ['can_send_polls'] = p.can_send_polls or false, 2463 | ['can_send_other_messages'] = p.can_send_other_messages or false, 2464 | ['can_add_web_page_previews'] = p.can_add_web_page_previews or false 2465 | } 2466 | end 2467 | 2468 | function api.is_user_kicked(chat_id, user_id) 2469 | if not chat_id or not user_id then 2470 | return false 2471 | end 2472 | local user, res = api.get_chat_member(chat_id, user_id) 2473 | if not user or not user.result then 2474 | return false, res 2475 | elseif user.result.status == 'kicked' then 2476 | return true, res 2477 | end 2478 | return false, user.result.status 2479 | end 2480 | 2481 | function api.is_user_group_admin(chat_id, user_id) 2482 | if not chat_id or not user_id then 2483 | return false 2484 | end 2485 | local user, res = api.get_chat_member(chat_id, user_id) 2486 | if not user or not user.result then 2487 | return false, res 2488 | elseif user.result.status == ('administrator' or 'creator') then 2489 | return true, res 2490 | end 2491 | return false, user.result.status 2492 | end 2493 | 2494 | function api.is_user_group_creator(chat_id, user_id) 2495 | if not chat_id or not user_id then 2496 | return false 2497 | end 2498 | local user, res = api.get_chat_member(chat_id, user_id) 2499 | if not user or not user.result then 2500 | return false, res 2501 | elseif user.result.status == 'creator' then 2502 | return true, res 2503 | end 2504 | return false, user.result.status 2505 | end 2506 | 2507 | function api.is_user_restricted(chat_id, user_id) 2508 | if not chat_id or not user_id then 2509 | return false 2510 | end 2511 | local user, res = api.get_chat_member(chat_id, user_id) 2512 | if not user or not user.result then 2513 | return false, res 2514 | elseif user.result.status == 'kicked' then 2515 | return true, res 2516 | end 2517 | return false, user.result.status 2518 | end 2519 | 2520 | function api.has_user_left(chat_id, user_id) 2521 | if not chat_id or not user_id then 2522 | return false 2523 | end 2524 | local user, res = api.get_chat_member(chat_id, user_id) 2525 | if not user or not user.result then 2526 | return false, res 2527 | elseif user.result.status == 'left' then 2528 | return true, res 2529 | end 2530 | return false, user.result.status 2531 | end 2532 | 2533 | -- New stuff, to be sorted 2534 | 2535 | function api.reaction_type_emoji(emoji) 2536 | return { 2537 | ['type'] = 'emoji', 2538 | ['emoji'] = emoji 2539 | } 2540 | end 2541 | 2542 | function api.reaction_type_custom_emoji(custom_emoji_id) 2543 | return { 2544 | ['type'] = 'custom_emoji', 2545 | ['custom_emoji_id'] = custom_emoji_id 2546 | } 2547 | end 2548 | 2549 | function api.chat_permissions(can_send_messages, can_send_audios, can_send_documents, can_send_photos, can_send_videos, 2550 | can_send_video_notes, can_send_voice_notes, can_send_polls, can_send_other_messages, can_add_web_page_previews, 2551 | can_change_info, can_invite_users, can_pin_messages, can_manage_topics) 2552 | return { 2553 | ['can_send_messages'] = can_send_messages, 2554 | ['can_send_audios'] = can_send_audios, 2555 | ['can_send_documents'] = can_send_documents, 2556 | ['can_send_photos'] = can_send_photos, 2557 | ['can_send_videos'] = can_send_videos, 2558 | ['can_send_video_notes'] = can_send_video_notes, 2559 | ['can_send_voice_notes'] = can_send_voice_notes, 2560 | ['can_send_polls'] = can_send_polls, 2561 | ['can_send_other_messages'] = can_send_other_messages, 2562 | ['can_add_web_page_previews'] = can_add_web_page_previews, 2563 | ['can_change_info'] = can_change_info, 2564 | ['can_invite_users'] = can_invite_users, 2565 | ['can_pin_messages'] = can_pin_messages, 2566 | ['can_manage_topics'] = can_manage_topics 2567 | } 2568 | end 2569 | 2570 | function api.bot_command(command, description) 2571 | command = tostring(command) 2572 | description = tostring(description) 2573 | if command:len() > 32 then 2574 | command = command:sub(1, 32) 2575 | end 2576 | if description:len() > 256 then 2577 | description = description:sub(1, 256) 2578 | end 2579 | return { 2580 | ['command'] = command, 2581 | ['description'] = description 2582 | } 2583 | end 2584 | 2585 | function api.bot_command_scope_default() 2586 | return { 2587 | ['type'] = 'default' 2588 | } 2589 | end 2590 | 2591 | function api.bot_command_scope_all_private_chats() 2592 | return { 2593 | ['type'] = 'all_private_chats' 2594 | } 2595 | end 2596 | 2597 | function api.bot_command_scope_all_group_chats() 2598 | return { 2599 | ['type'] = 'all_group_chats' 2600 | } 2601 | end 2602 | 2603 | function api.bot_command_scope_all_chat_administrators() 2604 | return { 2605 | ['type'] = 'all_chat_administrators' 2606 | } 2607 | end 2608 | 2609 | function api.bot_command_scope_chat(chat_id) 2610 | return { 2611 | ['type'] = 'chat', 2612 | ['chat_id'] = chat_id 2613 | } 2614 | end 2615 | 2616 | function api.bot_command_scope_chat_administrators(chat_id) 2617 | return { 2618 | ['type'] = 'chat_administrators', 2619 | ['chat_id'] = chat_id 2620 | } 2621 | end 2622 | 2623 | function api.bot_command_scope_chat_member(chat_id, user_id) 2624 | return { 2625 | ['type'] = 'chat_member', 2626 | ['chat_id'] = chat_id, 2627 | ['user_id'] = user_id 2628 | } 2629 | end 2630 | 2631 | function api.menu_button_commands() 2632 | return { 2633 | ['type'] = 'commands' 2634 | } 2635 | end 2636 | 2637 | function api.menu_button_web_app(text, web_app) 2638 | return { 2639 | ['type'] = 'web_app', 2640 | ['text'] = text, 2641 | ['web_app'] = web_app 2642 | } 2643 | end 2644 | 2645 | function api.menu_button_default() 2646 | return { 2647 | ['type'] = 'default' 2648 | } 2649 | end 2650 | 2651 | function api.chat_administrator_rights(is_anonymous, can_manage_chat, can_delete_messages, can_manage_video_chats, 2652 | can_restrict_members, can_promote_members, can_change_info, can_invite_users, can_post_messages, can_edit_messages, 2653 | can_pin_messages, can_post_stories, can_edit_stories, can_delete_stories, can_manage_topics) 2654 | return { 2655 | ['is_anonymous'] = is_anonymous, 2656 | ['can_manage_chat'] = can_manage_chat, 2657 | ['can_delete_messages'] = can_delete_messages, 2658 | ['can_manage_video_chats'] = can_manage_video_chats, 2659 | ['can_restrict_members'] = can_restrict_members, 2660 | ['can_promote_members'] = can_promote_members, 2661 | ['can_change_info'] = can_change_info, 2662 | ['can_invite_users'] = can_invite_users, 2663 | ['can_post_messages'] = can_post_messages, 2664 | ['can_edit_messages'] = can_edit_messages, 2665 | ['can_pin_messages'] = can_pin_messages, 2666 | ['can_post_stories'] = can_post_stories, 2667 | ['can_edit_stories'] = can_edit_stories, 2668 | ['can_delete_stories'] = can_delete_stories, 2669 | ['can_manage_topics'] = can_manage_topics 2670 | } 2671 | end 2672 | 2673 | function api.link_preview_options(is_disabled, url, prefer_small_media, prefer_large_media, show_above_text) 2674 | return { 2675 | ['is_disabled'] = is_disabled, 2676 | ['url'] = url, 2677 | ['prefer_small_media'] = prefer_small_media, 2678 | ['prefer_large_media'] = prefer_large_media, 2679 | ['show_above_text'] = show_above_text 2680 | } 2681 | end 2682 | 2683 | function api.message_entity(entity_type, offset, length, url, user, language, custom_emoji_id) 2684 | return { 2685 | ['type'] = tostring(entity_type), 2686 | ['offset'] = tonumber(offset), 2687 | ['length'] = tonumber(length), 2688 | ['url'] = tostring(url), 2689 | ['user'] = type(user) == 'table' and user or nil, 2690 | ['language'] = tostring(language), 2691 | ['custom_emoji_id'] = tostring(custom_emoji_id) 2692 | } 2693 | end 2694 | 2695 | function api.reply_parameter(message_id, chat_id, allow_sending_without_reply, quote, quote_parse_mode, quote_entities, 2696 | quote_position) 2697 | quote_entities = type(quote_entities) == 'table' and json.encode(quote_entities) or quote_entities 2698 | return { 2699 | ['message_id'] = tonumber(message_id), 2700 | ['chat_id'] = chat_id, 2701 | ['allow_sending_without_reply'] = allow_sending_without_reply, 2702 | ['quote'] = quote, 2703 | ['quote_parse_mode'] = quote_parse_mode, 2704 | ['quote_entities'] = quote_entities, 2705 | ['quote_position'] = tonumber(quote_position) 2706 | } 2707 | end 2708 | 2709 | function api.input_sticker(sticker, emoji_list, mask_position, keywords) 2710 | return { 2711 | ['sticker'] = sticker, 2712 | ['emoji_list'] = emoji_list, 2713 | ['mask_position'] = mask_position, 2714 | ['keywords'] = keywords 2715 | } 2716 | end 2717 | 2718 | function api.inline_query_results_button(text, web_app, start_parameter) 2719 | return { 2720 | ['text'] = text, 2721 | ['web_app'] = web_app, 2722 | ['start_parameter'] = start_parameter 2723 | } 2724 | end 2725 | 2726 | function api.labeled_price(label, amount) 2727 | return { 2728 | ['label'] = label, 2729 | ['amount'] = amount 2730 | } 2731 | end 2732 | 2733 | function api.passport_element_error_data_field(error_type, field_name, data_hash, message) 2734 | return { 2735 | ['source'] = 'data', 2736 | ['type'] = error_type, 2737 | ['field_name'] = field_name, 2738 | ['data_hash'] = data_hash, 2739 | ['message'] = message 2740 | } 2741 | end 2742 | 2743 | function api.passport_element_error_front_side(error_type, file_hash, message) 2744 | return { 2745 | ['source'] = 'front_side', 2746 | ['type'] = error_type, 2747 | ['file_hash'] = file_hash, 2748 | ['message'] = message 2749 | } 2750 | end 2751 | 2752 | function api.passport_element_error_reverse_side(error_type, file_hash, message) 2753 | return { 2754 | ['source'] = 'reverse_side', 2755 | ['type'] = error_type, 2756 | ['file_hash'] = file_hash, 2757 | ['message'] = message 2758 | } 2759 | end 2760 | 2761 | function api.passport_element_error_selfie(error_type, file_hash, message) 2762 | return { 2763 | ['source'] = 'selfie', 2764 | ['type'] = error_type, 2765 | ['file_hash'] = file_hash, 2766 | ['message'] = message 2767 | } 2768 | end 2769 | 2770 | function api.passport_element_error_file(error_type, file_hash, message) 2771 | return { 2772 | ['source'] = 'file', 2773 | ['type'] = error_type, 2774 | ['file_hash'] = file_hash, 2775 | ['message'] = message 2776 | } 2777 | end 2778 | 2779 | function api.passport_element_error_files(error_type, file_hashes, message) 2780 | return { 2781 | ['source'] = 'files', 2782 | ['type'] = error_type, 2783 | ['file_hashes'] = file_hashes, 2784 | ['message'] = message 2785 | } 2786 | end 2787 | 2788 | function api.passport_element_error_translation_file(error_type, file_hash, message) 2789 | return { 2790 | ['source'] = 'translation_file', 2791 | ['type'] = error_type, 2792 | ['file_hash'] = file_hash, 2793 | ['message'] = message 2794 | } 2795 | end 2796 | 2797 | function api.passport_element_error_translation_files(error_type, file_hashes, message) 2798 | return { 2799 | ['source'] = 'translation_files', 2800 | ['type'] = error_type, 2801 | ['file_hashes'] = file_hashes, 2802 | ['message'] = message 2803 | } 2804 | end 2805 | 2806 | function api.passport_element_error_unspecified(error_type, element_hash, message) 2807 | return { 2808 | ['source'] = 'unspecified', 2809 | ['type'] = error_type, 2810 | ['element_hash'] = element_hash, 2811 | ['message'] = message 2812 | } 2813 | end 2814 | 2815 | function api.web_app_info(url) 2816 | return { 2817 | ['url'] = url 2818 | } 2819 | end 2820 | 2821 | return api 2822 | -------------------------------------------------------------------------------- /src/tools.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | _ _ _ _ _ 4 | | | | | | | | | | | 5 | | |_ ___| | ___ __ _ _ __ __ _ _ __ ___ ______| |__ ___ | |_ ______| |_ _ __ _ 6 | | __/ _ \ |/ _ \/ _` | '__/ _` | '_ ` _ \______| '_ \ / _ \| __|______| | | | |/ _` | 7 | | || __/ | __/ (_| | | | (_| | | | | | | | |_) | (_) | |_ | | |_| | (_| | 8 | \__\___|_|\___|\__, |_| \__,_|_| |_| |_| |_.__/ \___/ \__| |_|\__,_|\__,_| 9 | __/ | 10 | |___/ 11 | 12 | Version 2.0-0 13 | Copyright (c) 2017-2024 Matthew Hesketh 14 | See LICENSE for details 15 | 16 | ]] local tools = {} 17 | local api = require('telegram-bot-lua.core') 18 | local https = require('ssl.https') 19 | local http = require('socket.http') 20 | local socket = require('socket') 21 | local ltn12 = require('ltn12') 22 | local json = require('dkjson') 23 | local utf8 = utf8 or require('lua-utf8') -- Lua 5.2 compatibility. 24 | local b64url = require('telegram-bot-lua.b64url') 25 | 26 | function tools.comma_value(amount) 27 | amount = tostring(amount) 28 | local k 29 | while true do 30 | amount, k = amount:gsub('^(-?%d+)(%d%d%d)', '%1,%2') 31 | if k == 0 then 32 | break 33 | end 34 | end 35 | return amount 36 | end 37 | 38 | function tools.format_ms(milliseconds) 39 | local total_seconds = math.floor(milliseconds / 1000) 40 | local seconds = total_seconds % 60 41 | local minutes = math.floor(total_seconds / 60) % 60 42 | local hours = math.floor(minutes / 60) 43 | return string.format('%02d:%02d:%02d', hours, minutes, seconds) 44 | end 45 | 46 | function tools.format_time(seconds) 47 | if not seconds or tonumber(seconds) == nil then 48 | return false 49 | end 50 | seconds = tonumber(seconds) -- Make sure we're handling a numerical value 51 | local minutes = math.floor(seconds / 60) 52 | if minutes == 0 then 53 | return seconds ~= 1 and seconds .. ' seconds' or seconds .. ' second' 54 | elseif minutes < 60 then 55 | return minutes ~= 1 and minutes .. ' minutes' or minutes .. ' minute' 56 | end 57 | local hours = math.floor(seconds / 3600) 58 | if hours == 0 then 59 | return minutes ~= 1 and minutes .. ' minutes' or minutes .. ' minute' 60 | elseif hours < 24 then 61 | return hours ~= 1 and hours .. ' hours' or hours .. ' hour' 62 | end 63 | local days = math.floor(seconds / 86400) 64 | if days == 0 then 65 | return hours ~= 1 and hours .. ' hours' or hours .. ' hour' 66 | elseif days < 7 then 67 | return days ~= 1 and days .. ' days' or days .. ' day' 68 | end 69 | local weeks = math.floor(seconds / 604800) 70 | if weeks == 0 then 71 | return days ~= 1 and days .. ' days' or days .. ' day' 72 | else 73 | return weeks ~= 1 and weeks .. ' weeks' or weeks .. ' week' 74 | end 75 | end 76 | 77 | function tools.round(num, idp) 78 | if idp and idp > 0 then 79 | local mult = 10 ^ idp 80 | return math.floor(num * mult + .5) / mult 81 | end 82 | return math.floor(num + .5) 83 | end 84 | 85 | function tools.pretty_print(table) 86 | return json.encode(table, { 87 | ['indent'] = true 88 | }) 89 | end 90 | 91 | tools.commands_meta = {} 92 | tools.commands_meta.__index = tools.commands_meta 93 | 94 | function tools.commands_meta:command(command) 95 | table.insert(self.table, '^[/!#]' .. command .. '$') 96 | table.insert(self.table, '^[/!#]' .. command .. '@' .. self.username .. '$') 97 | table.insert(self.table, '^[/!#]' .. command .. '%s+[^%s]*') 98 | table.insert(self.table, '^[/!#]' .. command .. '@' .. self.username .. '%s+[^%s]*') 99 | return self 100 | end 101 | 102 | function tools.commands(username, command_table) 103 | local self = setmetatable({}, tools.commands_meta) 104 | self.username = username 105 | self.table = command_table or {} 106 | return self 107 | end 108 | 109 | function tools.table_size(t) 110 | local i = 0 111 | for _ in pairs(t) do 112 | i = i + 1 113 | end 114 | return i 115 | end 116 | 117 | function tools.escape_markdown(str) 118 | return tostring(str):gsub('_', '\\_'):gsub('%[', '\\['):gsub('*', '\\*'):gsub('`', '\\`') 119 | end 120 | 121 | function tools.escape_html(str) 122 | return tostring(str):gsub('&', '&'):gsub('<', '<'):gsub('>', '>') 123 | end 124 | 125 | function tools.escape_bash(str) 126 | return tostring(str):gsub('%$', ''):gsub('%^', ''):gsub('&', ''):gsub('|', ''):gsub(';', '') 127 | end 128 | 129 | function tools.utf8_len(str) 130 | local chars = 0 131 | for i = 1, str:len() do 132 | local byte = str:byte(i) 133 | if byte < 128 or byte >= 192 then 134 | chars = chars + 1 135 | end 136 | end 137 | return chars 138 | end 139 | 140 | function tools.get_linked_name(id) 141 | local success = api.get_chat(id) 142 | if not success or not success.result then 143 | return false 144 | end 145 | local output = tools.escape_html(success.result.first_name) 146 | if success.result.username then 147 | output = '' .. output .. '' 148 | end 149 | return output 150 | end 151 | 152 | function tools.get_word(str, i) 153 | if not str then 154 | return false 155 | end 156 | i = i or 1 157 | local n = 1 158 | for word in str:gmatch('%g+') do 159 | if n == i then 160 | return word 161 | end 162 | n = n + 1 163 | end 164 | return false 165 | end 166 | 167 | function tools.input(s) 168 | if not s then 169 | return false 170 | end 171 | local input = s:find(' ') 172 | if not input then 173 | return false 174 | end 175 | return s:sub(input + 1) 176 | end 177 | 178 | function tools.trim(str) 179 | return str:gsub('^%s*(.-)%s*$', '%1') 180 | end 181 | 182 | tools.symbols = { 183 | ['back'] = utf8.char(8592), 184 | ['previous'] = utf8.char(8592), 185 | ['forward'] = utf8.char(8594), 186 | ['next'] = utf8.char(8594), 187 | ['bullet'] = utf8.char(8226), 188 | ['bullet_point'] = utf8.char(8226) 189 | } 190 | 191 | function tools.create_link(text, link, parse_mode) 192 | text = tostring(text) 193 | parse_mode = parse_mode == true and 'markdown' or tostring(parse_mode) 194 | if not link then 195 | return text 196 | elseif parse_mode:lower() == 'markdown' then 197 | return '[' .. tools.escape_markdown(text) .. '](' .. tools.escape_markdown(link) .. ')' 198 | end 199 | return '' .. tools.escape_html(text) .. '' 200 | end 201 | 202 | function tools.download_file(url, name, path) 203 | name = name or os.time() .. '.' .. url:match('.+%/%.(.-)$') 204 | local body = {} 205 | local _, res 206 | if url:match('^https') then 207 | _, res = https.request({ 208 | ['url'] = url, 209 | ['sink'] = ltn12.sink.table(body) 210 | }) 211 | else 212 | _, res = http.request({ 213 | ['url'] = url, 214 | ['sink'] = ltn12.sink.table(body), 215 | ['redirect'] = true 216 | }) 217 | end 218 | if res ~= 200 then 219 | error(res) 220 | return false 221 | end 222 | path = path and tostring(path) or '/tmp/' 223 | if not path:match('^/') then 224 | path = '/' .. path 225 | end 226 | if not path:match('/$') then 227 | path = path .. '/' 228 | end 229 | local file = io.open(path .. name, 'w+') 230 | local contents = table.concat(body) 231 | file:write(contents) 232 | file:close() 233 | path = path .. name 234 | return path 235 | end 236 | 237 | function tools.save_to_file(data, filename, append) 238 | if not data or not filename then 239 | return false 240 | end 241 | local mode = append and 'a+' or 'w+' 242 | if not filename:match('^/') then 243 | filename = '/tmp/' .. filename 244 | end 245 | local file = io.open(filename, mode) 246 | file:write(data) 247 | file:close() 248 | return filename 249 | end 250 | 251 | function tools.file_exists(path) 252 | local file = io.open(path, 'rb') 253 | if file then 254 | file:close() 255 | end 256 | return file ~= nil 257 | end 258 | 259 | function tools.get_file_as_table(path) 260 | if not path or not tools.file_exists(path) then 261 | return {} 262 | end 263 | local file = {} 264 | for line in io.lines(file) do 265 | file[#file + 1] = line 266 | end 267 | return file 268 | end 269 | 270 | function tools.read_file(path) 271 | if not path then 272 | return false 273 | end 274 | local file = io.open(path, 'rb') 275 | if not file then 276 | return false 277 | end 278 | local data = file:read('*all') 279 | file:close() 280 | return data 281 | end 282 | 283 | function tools.json_to_table(path) 284 | if not path then 285 | return {} 286 | end 287 | local parsed = tools.read_file(path) 288 | if not parsed then 289 | return {} 290 | end 291 | parsed = json.decode(parsed) 292 | return type(parsed) == 'table' and parsed or {} 293 | end 294 | 295 | function tools.get_formatted_user(user_id, name, parse_mode) 296 | if not user_id or not name then 297 | return false 298 | end 299 | if not parse_mode or type(parse_mode) == ('nil' or 'boolean') then 300 | parse_mode = 'MarkdownV2' 301 | end 302 | local user_id_string = '[%s](tg://user?id=%s)' 303 | if parse_mode:lower() == 'html' then 304 | user_id_string = '%s' 305 | return string.format(user_id_string, user_id, tools.escape_html(name)) 306 | end 307 | return string.format(user_id_string, tools.escape_markdown(name), user_id) 308 | end 309 | 310 | tools.random_string_charset = {} 311 | 312 | for i = 65, 90 do 313 | table.insert(tools.random_string_charset, string.char(i)) 314 | end 315 | 316 | for i = 97, 122 do 317 | table.insert(tools.random_string_charset, string.char(i)) 318 | end 319 | 320 | function tools.random_string(length, amount) 321 | if not length or tonumber(length) <= 0 then 322 | return '' 323 | end 324 | local command = io.popen('shuf -i 1-100000 -n 1') -- uses shuf for another random value because everything in lua is shocking 325 | local seed = command:read('*all') 326 | command:close() 327 | seed = tonumber(seed) * socket.gettime() 328 | math.randomseed(seed) 329 | if amount and tonumber(amount) ~= nil then 330 | local output = {} 331 | for _ = 1, tonumber(amount) do 332 | local value = tools.random_string(length - 1) .. 333 | tools.random_string_charset[math.random(1, #tools.random_string_charset)] 334 | table.insert(output, value) 335 | end 336 | return output 337 | end 338 | return tools.random_string(length - 1) .. tools.random_string_charset[math.random(1, #tools.random_string_charset)] 339 | end 340 | 341 | function tools.string_hexdump(data, length, size, space) 342 | data = tostring(data) 343 | size = (tonumber(size) == nil or tonumber(size) < 1) and 1 or tonumber(size) 344 | space = (tonumber(space) == nil or tonumber(space) < 1) and 8 or tonumber(space) 345 | length = (tonumber(length) == nil or tonumber(length) < 1) and 32 or tonumber(length) 346 | local output = {} 347 | local column = 0 348 | for i = 1, #data, size do 349 | for j = size, 1, -1 do 350 | local sub = string.sub(data, i + j - 1, i + j - 1) 351 | if #sub > 0 then 352 | local byte = string.byte(sub) 353 | local formatted = string.format('%.2x', byte) 354 | table.insert(output, formatted) 355 | end 356 | end 357 | if column % space == 0 then 358 | table.insert(output, ' ') 359 | end 360 | if (i + size - 1) % length == 0 then 361 | table.insert(output, '\n') 362 | end 363 | column = column + 1 364 | end 365 | return table.concat(output) 366 | end 367 | 368 | function tools.table_contains(tab, match) 369 | if type(tab) ~= 'table' then 370 | return false 371 | end 372 | for _, val in pairs(tab) do 373 | if val == match then 374 | return true 375 | end 376 | end 377 | return false 378 | end 379 | 380 | function tools.table_random(tab, seed) 381 | if seed and tonumber(seed) ~= nil then 382 | math.randomseed(seed) 383 | end 384 | tab = type(tab) == 'table' and tab or {tostring(tab)} 385 | local total = 0 386 | for _, chance in pairs(tab) do 387 | total = total + chance 388 | end 389 | local choice = math.random() * total 390 | for key, chance in pairs(tab) do 391 | choice = choice - chance 392 | if choice < 0 then 393 | return key 394 | end 395 | end 396 | end 397 | 398 | function tools.get_word(str, i) 399 | if not str then 400 | return false 401 | end 402 | local n = 1 403 | for word in str:gmatch('%g+') do 404 | i = i or 1 405 | if n == i then 406 | return word 407 | end 408 | n = n + 1 409 | end 410 | return false 411 | end 412 | 413 | function tools.service_message(message) 414 | if message.new_chat_member then 415 | return true, 'new_chat_member' 416 | elseif message.left_chat_member then 417 | return true, 'left_chat_member' 418 | elseif message.new_chat_title then 419 | return true, 'new_chat_title' 420 | elseif message.new_chat_photo then 421 | return true, 'new_chat_photo' 422 | elseif message.delete_chat_photo then 423 | return true, 'delete_chat_photo' 424 | elseif message.group_chat_created then 425 | return true, 'group_chat_created' 426 | elseif message.supergroup_chat_created then 427 | return true, 'supergroup_chat_created' 428 | elseif message.channel_chat_created then 429 | return true, 'channel_chat_created' 430 | elseif message.migrate_to_chat_id then 431 | return true, 'migrate_to_chat_id' 432 | elseif message.migrate_from_chat_id then 433 | return true, 'migrate_from_chat_id' 434 | elseif message.pinned_message then 435 | return true, 'pinned_message' 436 | elseif message.successful_payment then 437 | return true, 'successful_payment' 438 | end 439 | return false 440 | end 441 | 442 | function tools.is_media(message) 443 | if message.audio or message.document or message.game or message.photo or message.sticker or message.video or 444 | message.voice or message.video_note or message.contact or message.location or message.venue or message.invoice or 445 | message.poll or message.dice then 446 | return true 447 | end 448 | return false 449 | end 450 | 451 | function tools.media_type(message) 452 | if message.audio then 453 | return 'audio' 454 | elseif message.document then 455 | return 'document' 456 | elseif message.game then 457 | return 'game' 458 | elseif message.photo then 459 | return 'photo' 460 | elseif message.sticker then 461 | return 'sticker' 462 | elseif message.video then 463 | return 'video' 464 | elseif message.voice then 465 | return 'voice' 466 | elseif message.video_note then 467 | return 'video note' 468 | elseif message.contact then 469 | return 'contact' 470 | elseif message.location then 471 | return 'location' 472 | elseif message.venue then 473 | return 'venue' 474 | elseif message.invoice then 475 | return 'invoice' 476 | elseif message.forward_from or message.forward_from_chat then 477 | return 'forwarded' 478 | elseif message.dice then 479 | return 'dice' 480 | elseif message.poll then 481 | return 'poll' 482 | elseif message.text then 483 | return (message.text:match('[\216-\219][\128-\191]') or message.text:match(utf8.char(0x202e)) or 484 | message.text:match(utf8.char(0x200f))) and 'rtl' or 'text' 485 | end 486 | return '' 487 | end 488 | 489 | function tools.file_id(message, unique) 490 | if message.audio then 491 | if unique then 492 | return message.audio.file_unique_id 493 | end 494 | return message.audio.file_id 495 | elseif message.document then 496 | if unique then 497 | return message.document.file_unique_id 498 | end 499 | return message.document.file_id 500 | elseif message.sticker then 501 | if unique then 502 | return message.sticker.file_unique_id 503 | end 504 | return message.sticker.file_id 505 | elseif message.video then 506 | if unique then 507 | return message.video.file_unique_id 508 | end 509 | return message.video.file_id 510 | elseif message.voice then 511 | if unique then 512 | return message.voice.file_unique_id 513 | end 514 | return message.voice.file_id 515 | elseif message.video_note then 516 | if unique then 517 | return message.video_note.file_unique_id 518 | end 519 | return message.video_note.file_id 520 | elseif message.photo then -- Get the highest resolution for photos. 521 | if unique then 522 | return message.photo[#message.photo].file_unique_id 523 | end 524 | return message.photo[#message.photo].file_id 525 | end 526 | return '' 527 | end 528 | 529 | function tools.is_duplicate(tab, val) 530 | local seen = {} 531 | local duplicated = {} 532 | for i = 1, #tab do 533 | local element = tab[i] 534 | if seen[element] then 535 | duplicated[element] = true 536 | else 537 | seen[element] = true 538 | end 539 | end 540 | if val and duplicated[val] then 541 | return true 542 | elseif val then 543 | return false 544 | end 545 | return duplicated 546 | end 547 | 548 | function tools.is_valid_url(original_url, parts, any) 549 | if not original_url then 550 | return false 551 | end 552 | original_url = tostring(original_url) 553 | if not original_url:match('^[Hh][Tt][Tt][Pp][Ss]?://') and not any then 554 | original_url = 'http://' .. original_url 555 | end 556 | -- Thanks to https://stackoverflow.com/questions/23590304/finding-a-url-in-a-string-lua-pattern 557 | local url, protocol, subdomain, tld, colon, port, slash, path = string.match(original_url, 558 | '^(([%w_.~!*:@&+$/?%%#-]-)(%w[-.%w]*%.)(%w+)(:?)(%d*)(/?)([%w_.~!*:@&+$/?%%#=-]*))$') 559 | if parts then 560 | return { 561 | ['url'] = url, 562 | ['protocol'] = protocol, 563 | ['subdomain'] = subdomain, 564 | ['tld'] = tld, 565 | ['colon'] = colon, 566 | ['port'] = port, 567 | ['slash'] = slash, 568 | ['path'] = path 569 | } 570 | end 571 | return url and true or false, url 572 | end 573 | 574 | function tools.file_size(file) 575 | local is_path = false 576 | if type(file) ~= 'userdata' and type(file) ~= 'string' then 577 | return false, 'No file/path given!' 578 | elseif type(file) == 'string' then 579 | is_path = true 580 | if not file:match('^/') then 581 | file = '/' .. file 582 | end 583 | if file:match('/$') then 584 | file = file:match('^(.-)/$') 585 | end 586 | file = io.open(file, 'r') 587 | end 588 | local current = file:seek() 589 | local size = file:seek('end') 590 | file:seek('set', current) 591 | if is_path then 592 | file:close() 593 | end 594 | return tonumber(size) 595 | end 596 | 597 | function tools.rle_encode(s) 598 | local new, count = '', 0 599 | for i = 1, #s do 600 | local current = s:sub(i, i) 601 | if current == string.char(0) then 602 | count = count + 1 603 | else 604 | if count > 0 then 605 | new = new .. string.char(0) .. string.char(count) 606 | count = 0 607 | end 608 | new = new .. current 609 | end 610 | end 611 | return new 612 | end 613 | 614 | function tools.rle_decode(input) 615 | local new = '' 616 | local last = '' 617 | local length = #input 618 | for i = 1, length do 619 | local current = input:sub(i, i) 620 | if last == string.char(0) then 621 | new = new .. string.rep(last, string.byte(current)) 622 | last = '' 623 | else 624 | new = new .. last 625 | last = current 626 | end 627 | end 628 | return new .. last 629 | end 630 | 631 | function tools.unpack_telegram_invite_link(link) 632 | if not link then 633 | return false, 'No link given!' 634 | elseif link:match('joinchat/') then 635 | link = link:match('joinchat/(.-)$') 636 | end 637 | local append = '' 638 | local amount = (string.len(link) % 4) 639 | for _ = 1, amount do 640 | append = append .. '=' 641 | end 642 | local decoded = b64url.decode(link .. append) 643 | if not decoded then 644 | return false, 'Could not decode!' 645 | end 646 | local user_id, chat_id, rand_long = string.unpack('>IIL', decoded) 647 | return { 648 | ['user_id'] = user_id, 649 | ['chat_id'] = chat_id, 650 | ['rand_long'] = rand_long 651 | } 652 | end 653 | 654 | function tools.unpack_file_id(file_id, media_type) 655 | if not file_id then 656 | return false, 'No file_id given!' 657 | elseif not media_type then 658 | media_type = '' 659 | end 660 | local append = '' 661 | local amount = (string.len(file_id) % 4) 662 | for _ = 1, amount do 663 | append = append .. '=' 664 | end 665 | local decoded = tools.rle_decode(b64url.decode(file_id .. append)) 666 | if not decoded then 667 | return false, 'Could not decode!' 668 | end 669 | local file_type = string.unpack('> 32 708 | end 709 | return payload 710 | end 711 | 712 | function tools.unpack_inline_message_id(inline_message_id) 713 | if not inline_message_id then 714 | return false, 'No inline_message_id given!' 715 | end 716 | local append = '' 717 | local amount = (string.len(inline_message_id) % 4) 718 | for _ = 1, amount do 719 | append = append .. '=' 720 | end 721 | local decoded = b64url.decode(inline_message_id .. append) 722 | if not decoded then 723 | return false, 'Could not decode!' 724 | end 725 | local dc_id, message_id, chat_id, access_hash = string.unpack('= 2.5-2", 27 | "lpeg >= 1.0.1-1", 28 | "luasec >= 0.6-1", 29 | "luasocket >= 3.0rc1-2", 30 | "multipart-post >= 1.1-1", 31 | "luautf8 >= 0.1.1-1", 32 | "html-entities >= 1.3.1-0" 33 | } 34 | 35 | build = { 36 | type = "builtin", 37 | modules = { 38 | ["telegram-bot-lua.config"] = "src/config.lua", 39 | ["telegram-bot-lua.core"] = "src/core.lua", 40 | ["telegram-bot-lua.tools"] = "src/tools.lua", 41 | ["telegram-bot-lua.b64url"] = "src/b64url.lua" 42 | } 43 | } 44 | --------------------------------------------------------------------------------