diff --git a/addons/.gitignore b/addons/.gitignore new file mode 100644 index 0000000..dfacaa7 --- /dev/null +++ b/addons/.gitignore @@ -0,0 +1,3 @@ +.vscode/* +__pycache__ +ignore* \ No newline at end of file diff --git a/addons/LICENSE b/addons/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/addons/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/addons/README.md b/addons/README.md new file mode 100644 index 0000000..f92e6fb --- /dev/null +++ b/addons/README.md @@ -0,0 +1,46 @@ +#

+

+ +# UltroidAddons +Plugins repository for [@TheUltroid](https://github.com/TeamUltroid/Ultroid). + + +# Contributing +If you want to contribute to this repository (adding your plugins/porting from other bots), use the format given below and create a pull request. +โš ๏ธ First check whether the stuff you push works. Also, if the pull request doesn't follow the below format, it will be closed without prior notice. + +```python +# Credits @username (creator of plugin and who ported) + +# Ported from (if ported else skip) + +# Ported for Ultroid < https://github.com/TeamUltroid/Ultroid > +``` + +Kindly do not **steal** others works without credits.
+ +# Example Plugin + Required Import are Automatically Done. + +This Example Works Everywhere. (e.g. Groups, Personal Chats ...) +```python +@ultroid_cmd(pattern="hoi") +async def hello_world_example(event): + # As telethon is an asyncio based lib, you will have to use `async`/`await` Syntax. + await event.reply("Hello **World**.") +``` + +This Example Works Only In Groups. +```python +@ultroid_cmd(pattern="hoi", groups_only=True,) +async def hello_world_example(event): + await event.reply("Hello **World**.") +``` + +If Your plugin need any additional requirements, it can be added to addons.txt

+ +
+ +> For More Information See [The Pypi Page](https://pypi.org/project/py-Ultroid). + +> Made with ๐Ÿ’• by [@TeamUltroid](https://t.me/TeamUltroid). diff --git a/addons/__init__.py b/addons/__init__.py new file mode 100644 index 0000000..adc471f --- /dev/null +++ b/addons/__init__.py @@ -0,0 +1,11 @@ +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + + +from plugins import * + +bot = ultroid_bot diff --git a/addons/__pycache__/__init__.cpython-312.pyc b/addons/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7f4500e Binary files /dev/null and b/addons/__pycache__/__init__.cpython-312.pyc differ diff --git a/addons/__pycache__/activitygen.cpython-312.pyc b/addons/__pycache__/activitygen.cpython-312.pyc new file mode 100644 index 0000000..f84782f Binary files /dev/null and b/addons/__pycache__/activitygen.cpython-312.pyc differ diff --git a/addons/__pycache__/afk.cpython-312.pyc b/addons/__pycache__/afk.cpython-312.pyc new file mode 100644 index 0000000..a2ca0ab Binary files /dev/null and b/addons/__pycache__/afk.cpython-312.pyc differ diff --git a/addons/__pycache__/animations.cpython-312.pyc b/addons/__pycache__/animations.cpython-312.pyc new file mode 100644 index 0000000..c1f8ff8 Binary files /dev/null and b/addons/__pycache__/animations.cpython-312.pyc differ diff --git a/addons/__pycache__/anime.cpython-312.pyc b/addons/__pycache__/anime.cpython-312.pyc new file mode 100644 index 0000000..06a5924 Binary files /dev/null and b/addons/__pycache__/anime.cpython-312.pyc differ diff --git a/addons/__pycache__/animechan.cpython-312.pyc b/addons/__pycache__/animechan.cpython-312.pyc new file mode 100644 index 0000000..be320b6 Binary files /dev/null and b/addons/__pycache__/animechan.cpython-312.pyc differ diff --git a/addons/__pycache__/animedb.cpython-312.pyc b/addons/__pycache__/animedb.cpython-312.pyc new file mode 100644 index 0000000..23c50a1 Binary files /dev/null and b/addons/__pycache__/animedb.cpython-312.pyc differ diff --git a/addons/__pycache__/antiflood.cpython-312.pyc b/addons/__pycache__/antiflood.cpython-312.pyc new file mode 100644 index 0000000..fdacc74 Binary files /dev/null and b/addons/__pycache__/antiflood.cpython-312.pyc differ diff --git a/addons/__pycache__/asstcmd.cpython-312.pyc b/addons/__pycache__/asstcmd.cpython-312.pyc new file mode 100644 index 0000000..aa34f2a Binary files /dev/null and b/addons/__pycache__/asstcmd.cpython-312.pyc differ diff --git a/addons/__pycache__/astronomy.cpython-312.pyc b/addons/__pycache__/astronomy.cpython-312.pyc new file mode 100644 index 0000000..d23a861 Binary files /dev/null and b/addons/__pycache__/astronomy.cpython-312.pyc differ diff --git a/addons/__pycache__/audiotools.cpython-312.pyc b/addons/__pycache__/audiotools.cpython-312.pyc new file mode 100644 index 0000000..3861537 Binary files /dev/null and b/addons/__pycache__/audiotools.cpython-312.pyc differ diff --git a/addons/__pycache__/autoban.cpython-312.pyc b/addons/__pycache__/autoban.cpython-312.pyc new file mode 100644 index 0000000..6a88617 Binary files /dev/null and b/addons/__pycache__/autoban.cpython-312.pyc differ diff --git a/addons/__pycache__/autocorrect.cpython-312.pyc b/addons/__pycache__/autocorrect.cpython-312.pyc new file mode 100644 index 0000000..4806bc1 Binary files /dev/null and b/addons/__pycache__/autocorrect.cpython-312.pyc differ diff --git a/addons/__pycache__/autopic.cpython-312.pyc b/addons/__pycache__/autopic.cpython-312.pyc new file mode 100644 index 0000000..dbf53bf Binary files /dev/null and b/addons/__pycache__/autopic.cpython-312.pyc differ diff --git a/addons/__pycache__/autoprofile.cpython-312.pyc b/addons/__pycache__/autoprofile.cpython-312.pyc new file mode 100644 index 0000000..fed08e3 Binary files /dev/null and b/addons/__pycache__/autoprofile.cpython-312.pyc differ diff --git a/addons/__pycache__/beautify.cpython-312.pyc b/addons/__pycache__/beautify.cpython-312.pyc new file mode 100644 index 0000000..d02fcc2 Binary files /dev/null and b/addons/__pycache__/beautify.cpython-312.pyc differ diff --git a/addons/__pycache__/blacklist.cpython-312.pyc b/addons/__pycache__/blacklist.cpython-312.pyc new file mode 100644 index 0000000..32f2a5e Binary files /dev/null and b/addons/__pycache__/blacklist.cpython-312.pyc differ diff --git a/addons/__pycache__/brainfuck.cpython-312.pyc b/addons/__pycache__/brainfuck.cpython-312.pyc new file mode 100644 index 0000000..095e625 Binary files /dev/null and b/addons/__pycache__/brainfuck.cpython-312.pyc differ diff --git a/addons/__pycache__/broadcast.cpython-312.pyc b/addons/__pycache__/broadcast.cpython-312.pyc new file mode 100644 index 0000000..758797e Binary files /dev/null and b/addons/__pycache__/broadcast.cpython-312.pyc differ diff --git a/addons/__pycache__/button.cpython-312.pyc b/addons/__pycache__/button.cpython-312.pyc new file mode 100644 index 0000000..3527bf1 Binary files /dev/null and b/addons/__pycache__/button.cpython-312.pyc differ diff --git a/addons/__pycache__/calculator.cpython-312.pyc b/addons/__pycache__/calculator.cpython-312.pyc new file mode 100644 index 0000000..73b7103 Binary files /dev/null and b/addons/__pycache__/calculator.cpython-312.pyc differ diff --git a/addons/__pycache__/channelhacks.cpython-312.pyc b/addons/__pycache__/channelhacks.cpython-312.pyc new file mode 100644 index 0000000..b4a5995 Binary files /dev/null and b/addons/__pycache__/channelhacks.cpython-312.pyc differ diff --git a/addons/__pycache__/chatbot.cpython-312.pyc b/addons/__pycache__/chatbot.cpython-312.pyc new file mode 100644 index 0000000..d27d73e Binary files /dev/null and b/addons/__pycache__/chatbot.cpython-312.pyc differ diff --git a/addons/__pycache__/cleanaction.cpython-312.pyc b/addons/__pycache__/cleanaction.cpython-312.pyc new file mode 100644 index 0000000..735c8b9 Binary files /dev/null and b/addons/__pycache__/cleanaction.cpython-312.pyc differ diff --git a/addons/__pycache__/clone.cpython-312.pyc b/addons/__pycache__/clone.cpython-312.pyc new file mode 100644 index 0000000..6cae2f9 Binary files /dev/null and b/addons/__pycache__/clone.cpython-312.pyc differ diff --git a/addons/__pycache__/compressor.cpython-312.pyc b/addons/__pycache__/compressor.cpython-312.pyc new file mode 100644 index 0000000..b1fbc1e Binary files /dev/null and b/addons/__pycache__/compressor.cpython-312.pyc differ diff --git a/addons/__pycache__/converter.cpython-312.pyc b/addons/__pycache__/converter.cpython-312.pyc new file mode 100644 index 0000000..bec3032 Binary files /dev/null and b/addons/__pycache__/converter.cpython-312.pyc differ diff --git a/addons/__pycache__/covid.cpython-312.pyc b/addons/__pycache__/covid.cpython-312.pyc new file mode 100644 index 0000000..ecbacc0 Binary files /dev/null and b/addons/__pycache__/covid.cpython-312.pyc differ diff --git a/addons/__pycache__/echo.cpython-312.pyc b/addons/__pycache__/echo.cpython-312.pyc new file mode 100644 index 0000000..2cd1421 Binary files /dev/null and b/addons/__pycache__/echo.cpython-312.pyc differ diff --git a/addons/__pycache__/encodedecode.cpython-312.pyc b/addons/__pycache__/encodedecode.cpython-312.pyc new file mode 100644 index 0000000..8766be0 Binary files /dev/null and b/addons/__pycache__/encodedecode.cpython-312.pyc differ diff --git a/addons/__pycache__/extra.cpython-312.pyc b/addons/__pycache__/extra.cpython-312.pyc new file mode 100644 index 0000000..d38837c Binary files /dev/null and b/addons/__pycache__/extra.cpython-312.pyc differ diff --git a/addons/__pycache__/fakeaction.cpython-312.pyc b/addons/__pycache__/fakeaction.cpython-312.pyc new file mode 100644 index 0000000..d15ccba Binary files /dev/null and b/addons/__pycache__/fakeaction.cpython-312.pyc differ diff --git a/addons/__pycache__/fastly.cpython-312.pyc b/addons/__pycache__/fastly.cpython-312.pyc new file mode 100644 index 0000000..c5740bc Binary files /dev/null and b/addons/__pycache__/fastly.cpython-312.pyc differ diff --git a/addons/__pycache__/figlet.cpython-312.pyc b/addons/__pycache__/figlet.cpython-312.pyc new file mode 100644 index 0000000..30bd193 Binary files /dev/null and b/addons/__pycache__/figlet.cpython-312.pyc differ diff --git a/addons/__pycache__/filter.cpython-312.pyc b/addons/__pycache__/filter.cpython-312.pyc new file mode 100644 index 0000000..43a76b5 Binary files /dev/null and b/addons/__pycache__/filter.cpython-312.pyc differ diff --git a/addons/__pycache__/findsong.cpython-312.pyc b/addons/__pycache__/findsong.cpython-312.pyc new file mode 100644 index 0000000..5fb5f18 Binary files /dev/null and b/addons/__pycache__/findsong.cpython-312.pyc differ diff --git a/addons/__pycache__/flaticon.cpython-312.pyc b/addons/__pycache__/flaticon.cpython-312.pyc new file mode 100644 index 0000000..3ed2bee Binary files /dev/null and b/addons/__pycache__/flaticon.cpython-312.pyc differ diff --git a/addons/__pycache__/fontgen.cpython-312.pyc b/addons/__pycache__/fontgen.cpython-312.pyc new file mode 100644 index 0000000..245d509 Binary files /dev/null and b/addons/__pycache__/fontgen.cpython-312.pyc differ diff --git a/addons/__pycache__/fontsnew.cpython-312.pyc b/addons/__pycache__/fontsnew.cpython-312.pyc new file mode 100644 index 0000000..2d90bd4 Binary files /dev/null and b/addons/__pycache__/fontsnew.cpython-312.pyc differ diff --git a/addons/__pycache__/forcesubscribe.cpython-312.pyc b/addons/__pycache__/forcesubscribe.cpython-312.pyc new file mode 100644 index 0000000..b145cfd Binary files /dev/null and b/addons/__pycache__/forcesubscribe.cpython-312.pyc differ diff --git a/addons/__pycache__/fun.cpython-312.pyc b/addons/__pycache__/fun.cpython-312.pyc new file mode 100644 index 0000000..2c3996b Binary files /dev/null and b/addons/__pycache__/fun.cpython-312.pyc differ diff --git a/addons/__pycache__/gdrive.cpython-312.pyc b/addons/__pycache__/gdrive.cpython-312.pyc new file mode 100644 index 0000000..3665aa6 Binary files /dev/null and b/addons/__pycache__/gdrive.cpython-312.pyc differ diff --git a/addons/__pycache__/giftools.cpython-312.pyc b/addons/__pycache__/giftools.cpython-312.pyc new file mode 100644 index 0000000..0b37137 Binary files /dev/null and b/addons/__pycache__/giftools.cpython-312.pyc differ diff --git a/addons/__pycache__/glitch.cpython-312.pyc b/addons/__pycache__/glitch.cpython-312.pyc new file mode 100644 index 0000000..f7e66b4 Binary files /dev/null and b/addons/__pycache__/glitch.cpython-312.pyc differ diff --git a/addons/__pycache__/globaltools.cpython-312.pyc b/addons/__pycache__/globaltools.cpython-312.pyc new file mode 100644 index 0000000..c04303a Binary files /dev/null and b/addons/__pycache__/globaltools.cpython-312.pyc differ diff --git a/addons/__pycache__/greetings.cpython-312.pyc b/addons/__pycache__/greetings.cpython-312.pyc new file mode 100644 index 0000000..aa2afe4 Binary files /dev/null and b/addons/__pycache__/greetings.cpython-312.pyc differ diff --git a/addons/__pycache__/hack.cpython-312.pyc b/addons/__pycache__/hack.cpython-312.pyc new file mode 100644 index 0000000..8a74010 Binary files /dev/null and b/addons/__pycache__/hack.cpython-312.pyc differ diff --git a/addons/__pycache__/howto.cpython-312.pyc b/addons/__pycache__/howto.cpython-312.pyc new file mode 100644 index 0000000..1d917a6 Binary files /dev/null and b/addons/__pycache__/howto.cpython-312.pyc differ diff --git a/addons/__pycache__/imagetools.cpython-312.pyc b/addons/__pycache__/imagetools.cpython-312.pyc new file mode 100644 index 0000000..6ac74f1 Binary files /dev/null and b/addons/__pycache__/imagetools.cpython-312.pyc differ diff --git a/addons/__pycache__/imdb.cpython-312.pyc b/addons/__pycache__/imdb.cpython-312.pyc new file mode 100644 index 0000000..679c7d9 Binary files /dev/null and b/addons/__pycache__/imdb.cpython-312.pyc differ diff --git a/addons/__pycache__/inlinefun.cpython-312.pyc b/addons/__pycache__/inlinefun.cpython-312.pyc new file mode 100644 index 0000000..213036c Binary files /dev/null and b/addons/__pycache__/inlinefun.cpython-312.pyc differ diff --git a/addons/__pycache__/limited.cpython-312.pyc b/addons/__pycache__/limited.cpython-312.pyc new file mode 100644 index 0000000..b249fcb Binary files /dev/null and b/addons/__pycache__/limited.cpython-312.pyc differ diff --git a/addons/__pycache__/locks.cpython-312.pyc b/addons/__pycache__/locks.cpython-312.pyc new file mode 100644 index 0000000..5a1d08c Binary files /dev/null and b/addons/__pycache__/locks.cpython-312.pyc differ diff --git a/addons/__pycache__/logo.cpython-312.pyc b/addons/__pycache__/logo.cpython-312.pyc new file mode 100644 index 0000000..86fffa9 Binary files /dev/null and b/addons/__pycache__/logo.cpython-312.pyc differ diff --git a/addons/__pycache__/memify.cpython-312.pyc b/addons/__pycache__/memify.cpython-312.pyc new file mode 100644 index 0000000..658f3e0 Binary files /dev/null and b/addons/__pycache__/memify.cpython-312.pyc differ diff --git a/addons/__pycache__/misc.cpython-312.pyc b/addons/__pycache__/misc.cpython-312.pyc new file mode 100644 index 0000000..83b3058 Binary files /dev/null and b/addons/__pycache__/misc.cpython-312.pyc differ diff --git a/addons/__pycache__/morsecode.cpython-312.pyc b/addons/__pycache__/morsecode.cpython-312.pyc new file mode 100644 index 0000000..5dbda33 Binary files /dev/null and b/addons/__pycache__/morsecode.cpython-312.pyc differ diff --git a/addons/__pycache__/mute.cpython-312.pyc b/addons/__pycache__/mute.cpython-312.pyc new file mode 100644 index 0000000..60af3e9 Binary files /dev/null and b/addons/__pycache__/mute.cpython-312.pyc differ diff --git a/addons/__pycache__/ncode.cpython-312.pyc b/addons/__pycache__/ncode.cpython-312.pyc new file mode 100644 index 0000000..59b6cbe Binary files /dev/null and b/addons/__pycache__/ncode.cpython-312.pyc differ diff --git a/addons/__pycache__/nightmode.cpython-312.pyc b/addons/__pycache__/nightmode.cpython-312.pyc new file mode 100644 index 0000000..aa576cf Binary files /dev/null and b/addons/__pycache__/nightmode.cpython-312.pyc differ diff --git a/addons/__pycache__/notes.cpython-312.pyc b/addons/__pycache__/notes.cpython-312.pyc new file mode 100644 index 0000000..fed3ab4 Binary files /dev/null and b/addons/__pycache__/notes.cpython-312.pyc differ diff --git a/addons/__pycache__/nsfwfilter.cpython-312.pyc b/addons/__pycache__/nsfwfilter.cpython-312.pyc new file mode 100644 index 0000000..d1fd8a0 Binary files /dev/null and b/addons/__pycache__/nsfwfilter.cpython-312.pyc differ diff --git a/addons/__pycache__/ocr.cpython-312.pyc b/addons/__pycache__/ocr.cpython-312.pyc new file mode 100644 index 0000000..59cc600 Binary files /dev/null and b/addons/__pycache__/ocr.cpython-312.pyc differ diff --git a/addons/__pycache__/other.cpython-312.pyc b/addons/__pycache__/other.cpython-312.pyc new file mode 100644 index 0000000..e80bb68 Binary files /dev/null and b/addons/__pycache__/other.cpython-312.pyc differ diff --git a/addons/__pycache__/pdftools.cpython-312.pyc b/addons/__pycache__/pdftools.cpython-312.pyc new file mode 100644 index 0000000..2a3c727 Binary files /dev/null and b/addons/__pycache__/pdftools.cpython-312.pyc differ diff --git a/addons/__pycache__/pokedex.cpython-312.pyc b/addons/__pycache__/pokedex.cpython-312.pyc new file mode 100644 index 0000000..204dbd2 Binary files /dev/null and b/addons/__pycache__/pokedex.cpython-312.pyc differ diff --git a/addons/__pycache__/polls.cpython-312.pyc b/addons/__pycache__/polls.cpython-312.pyc new file mode 100644 index 0000000..aaccf7d Binary files /dev/null and b/addons/__pycache__/polls.cpython-312.pyc differ diff --git a/addons/__pycache__/profanityfilter.cpython-312.pyc b/addons/__pycache__/profanityfilter.cpython-312.pyc new file mode 100644 index 0000000..6575d12 Binary files /dev/null and b/addons/__pycache__/profanityfilter.cpython-312.pyc differ diff --git a/addons/__pycache__/qrcode.cpython-312.pyc b/addons/__pycache__/qrcode.cpython-312.pyc new file mode 100644 index 0000000..61accd5 Binary files /dev/null and b/addons/__pycache__/qrcode.cpython-312.pyc differ diff --git a/addons/__pycache__/quote.cpython-312.pyc b/addons/__pycache__/quote.cpython-312.pyc new file mode 100644 index 0000000..8eea0bc Binary files /dev/null and b/addons/__pycache__/quote.cpython-312.pyc differ diff --git a/addons/__pycache__/quotefancy.cpython-312.pyc b/addons/__pycache__/quotefancy.cpython-312.pyc new file mode 100644 index 0000000..081df5b Binary files /dev/null and b/addons/__pycache__/quotefancy.cpython-312.pyc differ diff --git a/addons/__pycache__/random.cpython-312.pyc b/addons/__pycache__/random.cpython-312.pyc new file mode 100644 index 0000000..75b2c91 Binary files /dev/null and b/addons/__pycache__/random.cpython-312.pyc differ diff --git a/addons/__pycache__/resize.cpython-312.pyc b/addons/__pycache__/resize.cpython-312.pyc new file mode 100644 index 0000000..610d05c Binary files /dev/null and b/addons/__pycache__/resize.cpython-312.pyc differ diff --git a/addons/__pycache__/schedulemsg.cpython-312.pyc b/addons/__pycache__/schedulemsg.cpython-312.pyc new file mode 100644 index 0000000..ce9e04f Binary files /dev/null and b/addons/__pycache__/schedulemsg.cpython-312.pyc differ diff --git a/addons/__pycache__/search.cpython-312.pyc b/addons/__pycache__/search.cpython-312.pyc new file mode 100644 index 0000000..eeb17a6 Binary files /dev/null and b/addons/__pycache__/search.cpython-312.pyc differ diff --git a/addons/__pycache__/searchmsgs.cpython-312.pyc b/addons/__pycache__/searchmsgs.cpython-312.pyc new file mode 100644 index 0000000..5ca2501 Binary files /dev/null and b/addons/__pycache__/searchmsgs.cpython-312.pyc differ diff --git a/addons/__pycache__/snips.cpython-312.pyc b/addons/__pycache__/snips.cpython-312.pyc new file mode 100644 index 0000000..a715e3e Binary files /dev/null and b/addons/__pycache__/snips.cpython-312.pyc differ diff --git a/addons/__pycache__/song.cpython-312.pyc b/addons/__pycache__/song.cpython-312.pyc new file mode 100644 index 0000000..5d2f6ec Binary files /dev/null and b/addons/__pycache__/song.cpython-312.pyc differ diff --git a/addons/__pycache__/spam.cpython-312.pyc b/addons/__pycache__/spam.cpython-312.pyc new file mode 100644 index 0000000..c26bf57 Binary files /dev/null and b/addons/__pycache__/spam.cpython-312.pyc differ diff --git a/addons/__pycache__/specialtools.cpython-312.pyc b/addons/__pycache__/specialtools.cpython-312.pyc new file mode 100644 index 0000000..3350343 Binary files /dev/null and b/addons/__pycache__/specialtools.cpython-312.pyc differ diff --git a/addons/__pycache__/speechtool.cpython-312.pyc b/addons/__pycache__/speechtool.cpython-312.pyc new file mode 100644 index 0000000..3c2964d Binary files /dev/null and b/addons/__pycache__/speechtool.cpython-312.pyc differ diff --git a/addons/__pycache__/spellcheck.cpython-312.pyc b/addons/__pycache__/spellcheck.cpython-312.pyc new file mode 100644 index 0000000..b24902c Binary files /dev/null and b/addons/__pycache__/spellcheck.cpython-312.pyc differ diff --git a/addons/__pycache__/stickerspam.cpython-312.pyc b/addons/__pycache__/stickerspam.cpython-312.pyc new file mode 100644 index 0000000..4c51db4 Binary files /dev/null and b/addons/__pycache__/stickerspam.cpython-312.pyc differ diff --git a/addons/__pycache__/stickertools.cpython-312.pyc b/addons/__pycache__/stickertools.cpython-312.pyc new file mode 100644 index 0000000..e8f2aef Binary files /dev/null and b/addons/__pycache__/stickertools.cpython-312.pyc differ diff --git a/addons/__pycache__/sticklet.cpython-312.pyc b/addons/__pycache__/sticklet.cpython-312.pyc new file mode 100644 index 0000000..51891c1 Binary files /dev/null and b/addons/__pycache__/sticklet.cpython-312.pyc differ diff --git a/addons/__pycache__/tag.cpython-312.pyc b/addons/__pycache__/tag.cpython-312.pyc new file mode 100644 index 0000000..bca6d4b Binary files /dev/null and b/addons/__pycache__/tag.cpython-312.pyc differ diff --git a/addons/__pycache__/taglogger.cpython-312.pyc b/addons/__pycache__/taglogger.cpython-312.pyc new file mode 100644 index 0000000..b66e1f0 Binary files /dev/null and b/addons/__pycache__/taglogger.cpython-312.pyc differ diff --git a/addons/__pycache__/test.cpython-312.pyc b/addons/__pycache__/test.cpython-312.pyc new file mode 100644 index 0000000..b3129cb Binary files /dev/null and b/addons/__pycache__/test.cpython-312.pyc differ diff --git a/addons/__pycache__/totalmsgs.cpython-312.pyc b/addons/__pycache__/totalmsgs.cpython-312.pyc new file mode 100644 index 0000000..f30622c Binary files /dev/null and b/addons/__pycache__/totalmsgs.cpython-312.pyc differ diff --git a/addons/__pycache__/truthdare.cpython-312.pyc b/addons/__pycache__/truthdare.cpython-312.pyc new file mode 100644 index 0000000..0f32ecc Binary files /dev/null and b/addons/__pycache__/truthdare.cpython-312.pyc differ diff --git a/addons/__pycache__/twitter.cpython-312.pyc b/addons/__pycache__/twitter.cpython-312.pyc new file mode 100644 index 0000000..f5e69b9 Binary files /dev/null and b/addons/__pycache__/twitter.cpython-312.pyc differ diff --git a/addons/__pycache__/typing.cpython-312.pyc b/addons/__pycache__/typing.cpython-312.pyc new file mode 100644 index 0000000..1b55e1a Binary files /dev/null and b/addons/__pycache__/typing.cpython-312.pyc differ diff --git a/addons/__pycache__/unsplash.cpython-312.pyc b/addons/__pycache__/unsplash.cpython-312.pyc new file mode 100644 index 0000000..8e755d4 Binary files /dev/null and b/addons/__pycache__/unsplash.cpython-312.pyc differ diff --git a/addons/__pycache__/usernamelogger.cpython-312.pyc b/addons/__pycache__/usernamelogger.cpython-312.pyc new file mode 100644 index 0000000..cea2157 Binary files /dev/null and b/addons/__pycache__/usernamelogger.cpython-312.pyc differ diff --git a/addons/__pycache__/videotools.cpython-312.pyc b/addons/__pycache__/videotools.cpython-312.pyc new file mode 100644 index 0000000..7a5b3c6 Binary files /dev/null and b/addons/__pycache__/videotools.cpython-312.pyc differ diff --git a/addons/__pycache__/waifu.cpython-312.pyc b/addons/__pycache__/waifu.cpython-312.pyc new file mode 100644 index 0000000..a965197 Binary files /dev/null and b/addons/__pycache__/waifu.cpython-312.pyc differ diff --git a/addons/__pycache__/warn.cpython-312.pyc b/addons/__pycache__/warn.cpython-312.pyc new file mode 100644 index 0000000..e6097e4 Binary files /dev/null and b/addons/__pycache__/warn.cpython-312.pyc differ diff --git a/addons/__pycache__/weather.cpython-312.pyc b/addons/__pycache__/weather.cpython-312.pyc new file mode 100644 index 0000000..b806d13 Binary files /dev/null and b/addons/__pycache__/weather.cpython-312.pyc differ diff --git a/addons/__pycache__/webupload.cpython-312.pyc b/addons/__pycache__/webupload.cpython-312.pyc new file mode 100644 index 0000000..a2a9d30 Binary files /dev/null and b/addons/__pycache__/webupload.cpython-312.pyc differ diff --git a/addons/__pycache__/whichsong.cpython-312.pyc b/addons/__pycache__/whichsong.cpython-312.pyc new file mode 100644 index 0000000..6a6ddc6 Binary files /dev/null and b/addons/__pycache__/whichsong.cpython-312.pyc differ diff --git a/addons/__pycache__/whisper.cpython-312.pyc b/addons/__pycache__/whisper.cpython-312.pyc new file mode 100644 index 0000000..2c6d7b5 Binary files /dev/null and b/addons/__pycache__/whisper.cpython-312.pyc differ diff --git a/addons/__pycache__/wikipedia.cpython-312.pyc b/addons/__pycache__/wikipedia.cpython-312.pyc new file mode 100644 index 0000000..a0ed008 Binary files /dev/null and b/addons/__pycache__/wikipedia.cpython-312.pyc differ diff --git a/addons/__pycache__/words.cpython-312.pyc b/addons/__pycache__/words.cpython-312.pyc new file mode 100644 index 0000000..a7485d4 Binary files /dev/null and b/addons/__pycache__/words.cpython-312.pyc differ diff --git a/addons/__pycache__/wreplace.cpython-312.pyc b/addons/__pycache__/wreplace.cpython-312.pyc new file mode 100644 index 0000000..d90a555 Binary files /dev/null and b/addons/__pycache__/wreplace.cpython-312.pyc differ diff --git a/addons/__pycache__/writer.cpython-312.pyc b/addons/__pycache__/writer.cpython-312.pyc new file mode 100644 index 0000000..32980b5 Binary files /dev/null and b/addons/__pycache__/writer.cpython-312.pyc differ diff --git a/addons/__pycache__/youtube.cpython-312.pyc b/addons/__pycache__/youtube.cpython-312.pyc new file mode 100644 index 0000000..cb07e1f Binary files /dev/null and b/addons/__pycache__/youtube.cpython-312.pyc differ diff --git a/addons/__pycache__/ziptools.cpython-312.pyc b/addons/__pycache__/ziptools.cpython-312.pyc new file mode 100644 index 0000000..39e4378 Binary files /dev/null and b/addons/__pycache__/ziptools.cpython-312.pyc differ diff --git a/addons/activitygen.py b/addons/activitygen.py new file mode 100644 index 0000000..2f094bd --- /dev/null +++ b/addons/activitygen.py @@ -0,0 +1,31 @@ +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +""" +โœ˜ Commands Available - + +โ€ข `{i}bored` + Get some activity to do when you get bored +""" + +from . import async_searcher, ultroid_cmd + + +@ultroid_cmd(pattern="bored$") +async def bored_cmd(event): + msg = await event.eor("`Generating an Activity for You!`") + content = await async_searcher( + "https://bored-api.appbrewery.com/random", re_json=True + ) + m = f"**Activity:** `{content['activity']}`" + if content.get("link"): + m += f"\n**Read More:** {content['link']}" + if content.get("participants"): + m += f"\n**Participants Required:** `{content['participants']}`" + if content.get("price"): + m += f"\n**Price:** `{content['price']}`" + await msg.edit(m) \ No newline at end of file diff --git a/addons/addons.txt b/addons/addons.txt new file mode 100644 index 0000000..7f2f21f --- /dev/null +++ b/addons/addons.txt @@ -0,0 +1,18 @@ +covid +emoji +fonttools +gtts +gingerit +jikanpy +git+https://github.com/New-dev0/AsyncLyricsExtractor.git +phlogo +pokedex.py +pyfiglet +pygments +pyjokes +quotefancy +shazamio +speechrecognition +speedtest-cli +textblob +wikipedia diff --git a/addons/afk.py b/addons/afk.py new file mode 100644 index 0000000..74e06eb --- /dev/null +++ b/addons/afk.py @@ -0,0 +1,165 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_afk") + + +import asyncio + +from telethon import events + +from pyUltroid.dB.afk_db import add_afk, del_afk, is_afk +from pyUltroid.dB.base import KeyManager + +from . import ( + LOG_CHANNEL, + NOSPAM_CHAT, + Redis, + asst, + get_string, + mediainfo, + udB, + ultroid_bot, + ultroid_cmd, + upload_file +) + +old_afk_msg = [] + +is_approved = KeyManager("PMPERMIT", cast=list).contains + + +@ultroid_cmd(pattern="afk( (.*)|$)", owner_only=True) +async def set_afk(event): + if event.client._bot or is_afk(): + return + text, media, media_type = None, None, None + if event.pattern_match.group(1).strip(): + text = event.text.split(maxsplit=1)[1] + reply = await event.get_reply_message() + if reply: + if reply.text and not text: + text = reply.text + if reply.media: + media_type = mediainfo(reply.media) + if media_type.startswith(("pic", "gif")): + file = await event.client.download_media(reply.media) + media = upload_file(file) + else: + media = reply.file.id + await event.eor("`Done`", time=2) + add_afk(text, media_type, media) + ultroid_bot.add_handler(remove_afk, events.NewMessage(outgoing=True)) + ultroid_bot.add_handler( + on_afk, + events.NewMessage( + incoming=True, func=lambda e: bool(e.mentioned or e.is_private) + ), + ) + msg1, msg2 = None, None + if text and media: + if "sticker" in media_type: + msg1 = await ultroid_bot.send_file(event.chat_id, file=media) + msg2 = await ultroid_bot.send_message( + event.chat_id, get_string("afk_5").format(text) + ) + else: + msg1 = await ultroid_bot.send_message( + event.chat_id, get_string("afk_5").format(text), file=media + ) + elif media: + if "sticker" in media_type: + msg1 = await ultroid_bot.send_file(event.chat_id, file=media) + msg2 = await ultroid_bot.send_message(event.chat_id, get_string("afk_6")) + else: + msg1 = await ultroid_bot.send_message( + event.chat_id, get_string("afk_6"), file=media + ) + elif text: + msg1 = await event.respond(get_string("afk_5").format(text)) + else: + msg1 = await event.respond(get_string("afk_6")) + old_afk_msg.append(msg1) + if msg2: + old_afk_msg.append(msg2) + return await asst.send_message(LOG_CHANNEL, msg2.text) + await asst.send_message(LOG_CHANNEL, msg1.text) + + +async def remove_afk(event): + if event.is_private and udB.get_key("PMSETTING") and not is_approved(event.chat_id): + return + elif "afk" in event.text.lower(): + return + elif event.chat_id in NOSPAM_CHAT: + return + if is_afk(): + _, _, _, afk_time = is_afk() + del_afk() + off = await event.reply(get_string("afk_1").format(afk_time)) + await asst.send_message(LOG_CHANNEL, get_string("afk_2").format(afk_time)) + for x in old_afk_msg: + try: + await x.delete() + except BaseException: + pass + await asyncio.sleep(10) + await off.delete() + + +async def on_afk(event): + if event.is_private and Redis("PMSETTING") and not is_approved(event.chat_id): + return + elif "afk" in event.text.lower(): + return + elif not is_afk(): + return + if event.chat_id in NOSPAM_CHAT: + return + sender = await event.get_sender() + if sender.bot or sender.verified: + return + text, media_type, media, afk_time = is_afk() + msg1, msg2 = None, None + if text and media: + if "sticker" in media_type: + msg1 = await event.reply(file=media) + msg2 = await event.reply(get_string("afk_3").format(afk_time, text)) + else: + msg1 = await event.reply( + get_string("afk_3").format(afk_time, text), file=media + ) + elif media: + if "sticker" in media_type: + msg1 = await event.reply(file=media) + msg2 = await event.reply(get_string("afk_4").format(afk_time)) + else: + msg1 = await event.reply(get_string("afk_4").format(afk_time), file=media) + elif text: + msg1 = await event.reply(get_string("afk_3").format(afk_time, text)) + else: + msg1 = await event.reply(get_string("afk_4").format(afk_time)) + for x in old_afk_msg: + try: + await x.delete() + except BaseException: + pass + old_afk_msg.append(msg1) + if msg2: + old_afk_msg.append(msg2) + + +if udB.get_key("AFK_DB"): + ultroid_bot.add_handler(remove_afk, events.NewMessage(outgoing=True)) + ultroid_bot.add_handler( + on_afk, + events.NewMessage( + incoming=True, func=lambda e: bool(e.mentioned or e.is_private) + ), + ) diff --git a/addons/animations.py b/addons/animations.py new file mode 100644 index 0000000..4c7d722 --- /dev/null +++ b/addons/animations.py @@ -0,0 +1,46 @@ +# Ultroid Userbot +# 2020 Copyright (c) + +# Animation Plugin + +""" + Animation Plugin + +โœ˜ Commands Available + +โ€ข `{i}kill` +โ€ข `{i}fp` + +""" + +import asyncio + + +@ultroid_cmd(pattern="kill$") +async def _(event): + animation_interval = 0.7 + animation_ttl = range(0, 12) + a = await event.eor("`ready to die dude.....`") + animation_chars = [ + "๏ผฆ๏ฝ‰๏ฝ‰๏ฝ‰๏ฝ‰๏ฝ‰๏ฝ’๏ฝ…", + "(ใ€€๏ฝฅเธดฯ‰๏ฝฅเธด)๏ธปใƒ‡โ•ไธ€-->", + "---->____________โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ ", + "------>__________โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ ", + "-------->โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ _________", + "---------->โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ _______", + "------------>โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ _____", + "-------------->โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ ____", + "------------------>", + "------>;(^ใ€‚^)ใƒŽ", + "(๏ฟฃใƒผ๏ฟฃ) DEAD", + """`Targeted user killed by Headshot๐Ÿ˜ˆ.๐Ÿ˜ˆ.๐Ÿ˜ˆ.๐Ÿ˜ˆ.๐Ÿ˜ˆ.๐Ÿ˜ˆ.๐Ÿ˜ˆ......` + `#Sad_Reacts_Offline`\n""", + ] + for i in animation_ttl: + await asyncio.sleep(animation_interval) + await a.edit(animation_chars[i % 12]) + + +@ultroid_cmd(pattern="fp$") +async def a(e): + await e.eor("๐Ÿคฆโ€โ™‚") diff --git a/addons/anime.py b/addons/anime.py new file mode 100644 index 0000000..dd3b230 --- /dev/null +++ b/addons/anime.py @@ -0,0 +1,45 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2022 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +""" +โœ˜ Commands Available - + +โ€ข `{i}character ` + Fetch anime character details. +""" + +import jikanpy + +from . import * + + +@ultroid_cmd(pattern="character ?(.*)") +async def anime_char_search(event): + xx = await event.eor(get_string("com_1")) + char_name = event.pattern_match.group(1) + if not char_name: + await eod(xx, "`Enter the name of a character too please!`", time=5) + jikan = jikanpy.jikan.Jikan() + try: + s = jikan.search("character", char_name) + except jikanpy.exceptions.APIException: + return await eod(xx, "`Couldn't find character!`", time=5) + a = s["results"][0]["mal_id"] + char_json = jikan.character(a) + pic = char_json["image_url"] + msg = f"**[{char_json['name']}]({char_json['url']})**" + if char_json["name_kanji"] != "Japanese": + msg += f" [{char_json['name_kanji']}]\n" + else: + msg += "\n" + if char_json["nicknames"]: + nicknames_string = ", ".join(char_json["nicknames"]) + msg += f"\n**Nicknames** : `{nicknames_string}`\n" + about = char_json["about"].split("\n", 1)[0].strip().replace("\n", "") + msg += f"\n**About**: __{about}__" + await event.reply(msg, file=pic, force_document=False) + await xx.delete() diff --git a/addons/animechan.py b/addons/animechan.py new file mode 100644 index 0000000..c7796c0 --- /dev/null +++ b/addons/animechan.py @@ -0,0 +1,25 @@ +# Ultroid - UserBot +# +# This file is a part of < https://github.com/TeamUltroid/UltroidAddons/> + +""" +Fetch Random anime quotes + +Command : `{i}aniquote` +""" + +from . import ultroid_cmd, async_searcher + + +@ultroid_cmd(pattern="aniquote") +async def _(ult): + u = await ult.eor("...") + try: + resp = await async_searcher( + "https://animechan.vercel.app/api/random", re_json=True + ) + results = f"**{resp['quote']}**\n" + results += f" โ€” __{resp['character']} ({resp['anime']})__" + return await u.edit(results) + except Exception: + await u.edit("`Something went wrong LOL ...`") diff --git a/addons/animedb.py b/addons/animedb.py new file mode 100644 index 0000000..2e538bc --- /dev/null +++ b/addons/animedb.py @@ -0,0 +1,35 @@ +# Made by : @Arnab431 || github.com/ArnabXD +# Made For : https://github.com/TeamUltroid/UltroidAddons + +""" +Search animes and manga from anilist.co using @animedb_bot + +โœ˜ Commands Available + +โ€ข `{i}manga ` + To get manga info +""" + + +from . import * + + +@ultroid_cmd( + pattern="manga ?(.*)", +) +async def manga(ult): + msg = await ult.eor("`Searching ...`") + keyword = ult.pattern_match.group(1) + if keyword is None: + return await msg.edit("`Provide a Keyword to search`") + try: + animes = await ult.client.inline_query("animedb_bot", f" {keyword}") + await animes[0].click( + ult.chat_id, + reply_to=ult.reply_to_msg_id, + silent=True if ult.is_reply else False, + hide_via=True, + ) + return await msg.delete() + except Exception: + return await msg.edit("`No Results Found ...`") diff --git a/addons/antiflood.py b/addons/antiflood.py new file mode 100644 index 0000000..b62c253 --- /dev/null +++ b/addons/antiflood.py @@ -0,0 +1,144 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_antiflood") + + +import re + +from telethon.events import NewMessage as NewMsg + +from pyUltroid.dB import DEVLIST +from pyUltroid.fns.admins import admin_check + +from . import Button, Redis, asst, callback, eod, get_string, udB, ultroid_bot, ultroid_cmd + +# Functions moved from antiflood_db.py +def get_flood(): + return udB.get_key("ANTIFLOOD") or {} + + +def set_flood(chat_id, limit): + omk = get_flood() + omk.update({chat_id: limit}) + return udB.set_key("ANTIFLOOD", omk) + + +def get_flood_limit(chat_id): + omk = get_flood() + if chat_id in omk.keys(): + return omk[chat_id] + + +def rem_flood(chat_id): + omk = get_flood() + if chat_id in omk.keys(): + del omk[chat_id] + return udB.set_key("ANTIFLOOD", omk) + + +_check_flood = {} + +if Redis("ANTIFLOOD"): + + @ultroid_bot.on( + NewMsg( + chats=list(get_flood().keys()), + ), + ) + async def flood_checm(event): + count = 1 + chat = (await event.get_chat()).title + if event.chat_id in _check_flood.keys(): + if event.sender_id == list(_check_flood[event.chat_id].keys())[0]: + count = _check_flood[event.chat_id][event.sender_id] + _check_flood[event.chat_id] = {event.sender_id: count + 1} + else: + _check_flood[event.chat_id] = {event.sender_id: count} + else: + _check_flood[event.chat_id] = {event.sender_id: count} + if await admin_check(event, silent=True) or getattr(event.sender, "bot", None): + return + if event.sender_id in DEVLIST: + return + if _check_flood[event.chat_id][event.sender_id] >= int( + get_flood_limit(event.chat_id) + ): + try: + name = event.sender.first_name + await event.client.edit_permissions( + event.chat_id, event.sender_id, send_messages=False + ) + del _check_flood[event.chat_id] + await event.reply(f"#AntiFlood\n\n{get_string('antiflood_3')}") + await asst.send_message( + int(Redis("LOG_CHANNEL")), + f"#Antiflood\n\n`Muted `[{name}](tg://user?id={event.sender_id})` in {chat}`", + buttons=Button.inline( + "Unmute", data=f"anti_{event.sender_id}_{event.chat_id}" + ), + ) + except BaseException: + pass + + +@callback( + re.compile( + "anti_(.*)", + ), +) +async def unmuting(e): + ino = (e.data_match.group(1)).decode("UTF-8").split("_") + user = int(ino[0]) + chat = int(ino[1]) + user_name = (await ultroid_bot.get_entity(user)).first_name + chat_title = (await ultroid_bot.get_entity(chat)).title + await ultroid_bot.edit_permissions(chat, user, send_messages=True) + await e.edit( + f"#Antiflood\n\n`Unmuted `[{user_name}](tg://user?id={user})` in {chat_title}`" + ) + + +@ultroid_cmd( + pattern="setflood ?(\\d+)", + admins_only=True, +) +async def setflood(e): + input_ = e.pattern_match.group(1).strip() + if not input_: + return await e.eor("`What?`", time=5) + if not input_.isdigit(): + return await e.eor(get_string("com_3"), time=5) + if m := set_flood(e.chat_id, input_): + return await eod(e, get_string("antiflood_4").format(input_)) + + +@ultroid_cmd( + pattern="remflood$", + admins_only=True, +) +async def remove_flood(e): + hmm = rem_flood(e.chat_id) + try: + del _check_flood[e.chat_id] + except BaseException: + pass + if hmm: + return await e.eor(get_string("antiflood_1"), time=5) + await e.eor(get_string("antiflood_2"), time=5) + + +@ultroid_cmd( + pattern="getflood$", + admins_only=True, +) +async def getflood(e): + if ok := get_flood_limit(e.chat_id): + return await e.eor(get_string("antiflood_5").format(ok), time=5) + await e.eor(get_string("antiflood_2"), time=5) diff --git a/addons/asstcmd.py b/addons/asstcmd.py new file mode 100644 index 0000000..5957b09 --- /dev/null +++ b/addons/asstcmd.py @@ -0,0 +1,129 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_asstcmd") + +import os + +from pyUltroid.fns.tools import create_tl_btn, format_btn, get_msg_button +from telethon import events, utils + +from . import asst, get_string, mediainfo, udB, ultroid_cmd, upload_file + +# Functions moved from asstcmd_db.py +def get_stuff(): + return udB.get_key("ASST_CMDS") or {} + + +def add_cmd(cmd, msg, media, button): + ok = get_stuff() + ok.update({cmd: {"msg": msg, "media": media, "button": button}}) + return udB.set_key("ASST_CMDS", ok) + + +def rem_cmd(cmd): + ok = get_stuff() + if ok.get(cmd): + ok.pop(cmd) + return udB.set_key("ASST_CMDS", ok) + + +def cmd_reply(cmd): + ok = get_stuff() + if ok.get(cmd): + okk = ok[cmd] + return okk["msg"], okk["media"], okk["button"] if ok.get("button") else None + return + + +def list_cmds(): + ok = get_stuff() + return ok.keys() + + +@ultroid_cmd(pattern="addcmd( (.*)|$)") +async def ac(e): + wrd = (e.pattern_match.group(1).strip()).lower() + wt = await e.get_reply_message() + if not (wt and wrd): + return await e.eor(get_string("asstcmd_1"), time=5) + if "/" in wrd: + wrd = wrd.replace("/", "") + btn = format_btn(wt.buttons) if wt.buttons else None + if wt and wt.media: + wut = mediainfo(wt.media) + if wut.startswith(("pic", "gif")): + dl = await e.client.download_media(wt.media) + m = upload_file(dl) + os.remove(dl) + elif wut == "video": + if wt.media.document.size > 8 * 1000 * 1000: + return await e.eor(get_string("com_4"), time=5) + dl = await e.client.download_media(wt.media) + m = upload_file(dl) + os.remove(dl) + else: + m = utils.pack_bot_file_id(wt.media) + if wt.text: + txt = wt.text + if not btn: + txt, btn = get_msg_button(wt.text) + add_cmd(wrd, txt, m, btn) + else: + add_cmd(wrd, None, m, btn) + else: + txt = wt.text + if not btn: + txt, btn = get_msg_button(wt.text) + add_cmd(wrd, txt, None, btn) + asst.add_handler( + astcmds, + events.NewMessage( + func=lambda x: x.text.startswith("/") and x.text[1:] in list(list_cmds()) + ), + ) + await e.eor(get_string("asstcmd_4").format(wrd)) + + +@ultroid_cmd(pattern="remcmd( (.*)|$)") +async def rc(e): + wrd = (e.pattern_match.group(1).strip()).lower() + if not wrd: + return await e.eor(get_string("asstcmd_2"), time=5) + wrd = wrd.replace("/", "") + rem_cmd(wrd) + await e.eor(get_string("asstcmd_3").format(wrd)) + + +@ultroid_cmd(pattern="listcmd$") +async def lscmd(e): + if list_cmds(): + ok = get_string("asstcmd_6") + for x in list_cmds(): + ok += f"/{x}" + "\n" + return await e.eor(ok) + return await e.eor(get_string("asstcmd_5")) + + +async def astcmds(e): + xx = (e.text.replace("/", "")).lower().split()[0] + if cmd_reply(xx): + msg, media, bt = cmd_reply(xx) + if bt: + bt = create_tl_btn(bt) + await e.reply(msg, file=media, buttons=bt) + + +if udB.get_key("ASST_CMDS"): + asst.add_handler( + astcmds, + events.NewMessage( + func=lambda x: x.text.startswith("/") and x.text[1:] in list(list_cmds()) + ), + ) diff --git a/addons/astronomy.py b/addons/astronomy.py new file mode 100644 index 0000000..6c3fd70 --- /dev/null +++ b/addons/astronomy.py @@ -0,0 +1,39 @@ +# Ultroid Userbot +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +""" +โœ˜ Commands Available - + +โ€ข `{i}apod`` + Get Astronomy Picture of Day by NASA +""" + +from bs4 import BeautifulSoup as bs + +from . import ultroid_cmd, async_searcher + + +@ultroid_cmd(pattern="apod$") +async def aposj(e): + link = "https://apod.nasa.gov/apod/" + C = await async_searcher(link) + m = bs(C, "html.parser", from_encoding="utf-8") + try: + try: + img = m.find_all("img")[0]["src"] + img = link + img + except IndexError: + img = None + expla = m.find_all("p")[2].text.replace("\n", " ") + expla = expla.split(" ")[0] + if len(expla) > 900: + expla = expla[:900] + "..." + expla = "__" + expla + "__" + await e.reply(expla, file=img) + if e.out: + await e.delete() + except Exception as E: + return await eod(e, str(E)) diff --git a/addons/audiotools.py b/addons/audiotools.py new file mode 100644 index 0000000..40343bb --- /dev/null +++ b/addons/audiotools.py @@ -0,0 +1,163 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + + +import os +import time +from datetime import datetime as dt + +from pyUltroid.fns.tools import set_attributes + +from . import ( + LOGS, + ULTConfig, + bash, + downloader, + eod, + eor, + genss, + get_help, + get_string, + humanbytes, + mediainfo, + stdr, + time_formatter, + ultroid_cmd +) + +__doc__ = get_help("help_audiotools") + + +@ultroid_cmd(pattern="makevoice$") +async def vnc(e): + if not e.reply_to: + return await eod(e, get_string("audiotools_1")) + r = await e.get_reply_message() + if not mediainfo(r.media).startswith(("audio", "video")): + return await eod(e, get_string("spcltool_1")) + xxx = await e.eor(get_string("com_1")) + file, _ = await e.client.fast_downloader( + r.document, + ) + await xxx.edit(get_string("audiotools_2")) + await bash( + f"ffmpeg -i '{file.name}' -map 0:a -codec:a libopus -b:a 100k -vbr on out.opus" + ) + try: + await e.client.send_message( + e.chat_id, file="out.opus", force_document=False, reply_to=r + ) + except Exception as er: + LOGS.exception(er) + return await xxx.edit("`Failed to convert in Voice...`") + await xxx.delete() + os.remove(file.name) + os.remove("out.opus") + + +@ultroid_cmd(pattern="atrim( (.*)|$)") +async def trim_aud(e): + sec = e.pattern_match.group(1).strip() + if not sec or "-" not in sec: + return await eod(e, get_string("audiotools_3")) + a, b = sec.split("-") + if int(a) >= int(b): + return await eod(e, get_string("audiotools_4")) + vido = await e.get_reply_message() + if vido and vido.media and mediainfo(vido.media).startswith(("video", "audio")): + if hasattr(vido.media, "document"): + vfile = vido.media.document + name = vido.file.name + else: + vfile = vido.media + name = "" + if not name: + name = dt.now().isoformat("_", "seconds") + ".mp4" + xxx = await e.eor(get_string("audiotools_5")) + c_time = time.time() + file = await downloader( + f"resources/downloads/{name}", + vfile, + xxx, + c_time, + f"Downloading {name}...", + ) + + o_size = os.path.getsize(file.name) + d_time = time.time() + diff = time_formatter((d_time - c_time) * 1000) + file_name = (file.name).split("/")[-1] + out = file_name.replace(file_name.split(".")[-1], "_trimmed.aac") + if int(b) > int(await genss(file.name)): + os.remove(file.name) + return await eod(xxx, get_string("audiotools_6")) + ss, dd = stdr(int(a)), stdr(int(b)) + xxx = await xxx.edit( + f"Downloaded `{file.name}` of `{humanbytes(o_size)}` in `{diff}`.\n\nNow Trimming Audio from `{ss}` to `{dd}`..." + ) + cmd = f'ffmpeg -i "{file.name}" -preset ultrafast -ss {ss} -to {dd} -vn -acodec copy "{out}" -y' + await bash(cmd) + os.remove(file.name) + f_time = time.time() + n_file, _ = await e.client.fast_uploader( + out, show_progress=True, event=e, message="Uploading...", to_delete=True + ) + attributes = await set_attributes(out) + + caption = get_string("audiotools_7").format(ss, dd) + await e.client.send_file( + e.chat_id, + n_file, + thumb=ULTConfig.thumb, + caption=caption, + attributes=attributes, + force_document=False, + reply_to=e.reply_to_msg_id, + ) + await xxx.delete() + else: + await e.eor(get_string("audiotools_1"), time=5) + + +@ultroid_cmd(pattern="extractaudio$") +async def ex_aud(e): + reply = await e.get_reply_message() + if not (reply and reply.media and mediainfo(reply.media).startswith("video")): + return await e.eor(get_string("audiotools_8")) + name = reply.file.name or "video.mp4" + vfile = reply.media.document + msg = await e.eor(get_string("com_1")) + c_time = time.time() + file = await downloader( + f"resources/downloads/{name}", + vfile, + msg, + c_time, + f"Downloading {name}...", + ) + + out_file = f"{file.name}.aac" + cmd = f"ffmpeg -i {file.name} -vn -acodec copy {out_file}" + o, err = await bash(cmd) + os.remove(file.name) + attributes = await set_attributes(out_file) + + f_time = time.time() + try: + n_file, _ = await e.client.fast_uploader( + out_file, show_progress=True, event=e, message="Uploading...", to_delete=True + ) + + except FileNotFoundError: + return await eor(msg, get_string("audiotools_9")) + await e.reply( + get_string("audiotools_10"), + file=n_file, + thumb=ULTConfig.thumb, + attributes=attributes, + ) + await msg.delete() diff --git a/addons/autoban.py b/addons/autoban.py new file mode 100644 index 0000000..ab002ba --- /dev/null +++ b/addons/autoban.py @@ -0,0 +1,59 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_autoban") + +from telethon import events + +from pyUltroid.dB.base import KeyManager + +from . import LOGS, asst, ultroid_bot, ultroid_cmd + +Keym = KeyManager("DND_CHATS", cast=list) + + +def join_func(e): + return e.user_joined and Keym.contains(e.chat_id) + + +async def dnd_func(event): + for user in event.users: + try: + await (await event.client.kick_participant(event.chat_id, user)).delete() + except Exception as ex: + LOGS.error("Error in DND:") + LOGS.exception(ex) + await event.delete() + + +@ultroid_cmd( + pattern="autokick (on|off)$", + admins_only=True, + manager=True, + require="ban_users", + fullsudo=True, +) +async def _(event): + match = event.pattern_match.group(1) + if match == "on": + if Keym.contains(event.chat_id): + return await event.eor("`Chat already in do not disturb mode.`", time=3) + Keym.add(event.chat_id) + event.client.add_handler(dnd_func, events.ChatAction(func=join_func)) + await event.eor("`Do not disturb mode activated for this chat.`", time=3) + elif match == "off": + if not Keym.contains(event.chat_id): + return await event.eor("`Chat is not in do not disturb mode.`", time=3) + Keym.remove(event.chat_id) + await event.eor("`Do not disturb mode deactivated for this chat.`", time=3) + + +if Keym.get(): + ultroid_bot.add_handler(dnd_func, events.ChatAction(func=join_func)) + asst.add_handler(dnd_func, events.ChatAction(func=join_func)) diff --git a/addons/autocorrect.py b/addons/autocorrect.py new file mode 100644 index 0000000..edb4116 --- /dev/null +++ b/addons/autocorrect.py @@ -0,0 +1,59 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2022 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_autocorrect") + +import string + +from . import HNDLR, LOGS, get_string, udB, ultroid_bot, ultroid_cmd # ignore: pylint + +try: + from gingerit.gingerit import GingerIt +except ImportError: + LOGS.info("GingerIt not found") + GingerIt = None + +from google_trans_new import google_translator +from telethon import events + + +@ultroid_cmd(pattern="autocorrect", fullsudo=True) +async def acc(e): + if not udB.get_key("AUTOCORRECT"): + udB.set_key("AUTOCORRECT", "True") + ultroid_bot.add_handler( + gramme, events.NewMessage(outgoing=True, func=lambda x: x.text) + ) + return await e.eor(get_string("act_1"), time=5) + udB.del_key("AUTOCORRECT") + await e.eor(get_string("act_2"), time=5) + + +async def gramme(event): + if not udB.get_key("AUTOCORRECT"): + return + t = event.text + if t[0] == HNDLR or t[0].lower() not in string.ascii_lowercase or t.endswith(".."): + return + tt = google_translator().detect(t) + if tt[0] != "en": + return + xx = GingerIt() + x = xx.parse(t) + res = x["result"] + try: + await event.edit(res) + except BaseException: + pass + + +if GingerIt and udB.get_key("AUTOCORRECT"): + ultroid_bot.add_handler( + gramme, events.NewMessage(outgoing=True, func=lambda x: x.text) + ) diff --git a/addons/autopic.py b/addons/autopic.py new file mode 100644 index 0000000..30a048b --- /dev/null +++ b/addons/autopic.py @@ -0,0 +1,156 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + + +import asyncio +import os +import random +from random import shuffle +import aiohttp +import re +from telethon.tl.functions.photos import UploadProfilePhotoRequest +from PIL import Image + +from pyUltroid.fns.helper import download_file, fast_download + +from . import LOGS, get_help, get_string, udB, ultroid_bot, ultroid_cmd + +__doc__ = get_help("help_autopic") + + +async def get_google_images(query: str): + """Extract image URLs from Google Images search results with fallbacks""" + + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" + } + + search_url = f"https://www.google.com/search?q={query}&tbm=isch" + + # Domains to exclude + excluded_domains = [ + 'gstatic.com', + 'google.com', + 'googleusercontent.com', + 'ssl.google.com' + ] + + def is_valid_url(url): + return not any(domain in url.lower() for domain in excluded_domains) + + try: + async with aiohttp.ClientSession() as session: + async with session.get(search_url, headers=headers) as response: + html = await response.text() + + # Try to extract from search results div first + img_urls = [] + search_pattern = r'

' + search_match = re.search(search_pattern, html, re.DOTALL) + if search_match: + search_content = search_match.group(1) + url_pattern = r'https://[^\"]*?\.(?:jpg|jpeg|png|webp)' + url_matches = re.finditer(url_pattern, search_content, re.IGNORECASE) + for url_match in url_matches: + url = url_match.group(0) + if url not in img_urls and is_valid_url(url): + img_urls.append(url) + + # Fallback to tdeeNb div if no results + if not img_urls: + pattern = r'
]*>(.*?)
' + matches = re.finditer(pattern, html, re.DOTALL) + for match in matches: + div_content = match.group(1) + url_pattern = r'https://[^\"]*?\.(?:jpg|jpeg|png|webp)' + url_matches = re.finditer(url_pattern, div_content, re.IGNORECASE) + for url_match in url_matches: + url = url_match.group(0) + if url not in img_urls and is_valid_url(url): + img_urls.append(url) + + # Fallback to general image search if still no results + if not img_urls: + pattern = r"https://[^\"]*?\.(?:jpg|jpeg|png|webp)" + matches = re.finditer(pattern, html, re.IGNORECASE) + for match in matches: + url = match.group(0) + if url not in img_urls and is_valid_url(url): + img_urls.append(url) + + # Final fallback to data URLs if still no results + if not img_urls: + pattern = r'data:image/(?:jpeg|png|webp);base64,[^\"]*' + matches = re.finditer(pattern, html, re.IGNORECASE) + for match in matches: + url = match.group(0) + if url not in img_urls: + img_urls.append(url) + + return img_urls + + except Exception as e: + print(f"Error fetching Google images: {e}") + return [] + + +@ultroid_cmd(pattern="autopic( (.*)|$)") +async def autopic(e): + search = e.pattern_match.group(1).strip() + if udB.get_key("AUTOPIC") and not search: + udB.del_key("AUTOPIC") + return await e.eor(get_string("autopic_5")) + if not search: + return await e.eor(get_string("autopic_1"), time=5) + e = await e.eor(get_string("com_1")) + images = await get_google_images(search) + if not images: + return await e.eor(get_string("autopic_2").format(search), time=5) + await e.eor(get_string("autopic_3").format(search)) + udB.set_key("AUTOPIC", search) + SLEEP_TIME = udB.get_key("SLEEP_TIME") or 1221 + while True: + for lie in images: + if udB.get_key("AUTOPIC") != search: + return + download_path, stime = await fast_download(lie, "resources/downloads/autopic.jpg") + img = Image.open(download_path) + img.save("resources/downloads/autopic.jpg") + file = await e.client.upload_file("resources/downloads/autopic.jpg") + await e.client(UploadProfilePhotoRequest(file=file)) + os.remove("resources/downloads/autopic.jpg") + await asyncio.sleep(SLEEP_TIME) + + shuffle(images) + + +if search := udB.get_key("AUTOPIC"): + images = {} + sleep = udB.get_key("SLEEP_TIME") or 1221 + + async def autopic_func(): + search = udB.get_key("AUTOPIC") + if images.get(search) is None: + images[search] = await get_google_images(search) + if not images.get(search): + return + img = random.choice(images[search]) + filee, stime = await fast_download(img, "resources/downloads/autopic.jpg") + img = Image.open(filee) + img.save("resources/downloads/autopic.jpg") + file = await ultroid_bot.upload_file("resources/downloads/autopic.jpg") + await ultroid_bot(UploadProfilePhotoRequest(file=file)) + os.remove(filee) + + try: + from apscheduler.schedulers.asyncio import AsyncIOScheduler + + schedule = AsyncIOScheduler() + schedule.add_job(autopic_func, "interval", seconds=sleep) + schedule.start() + except ModuleNotFoundError as er: + LOGS.error(f"autopic: '{er.name}' not installed.") diff --git a/addons/autoprofile.py b/addons/autoprofile.py new file mode 100644 index 0000000..7ccc518 --- /dev/null +++ b/addons/autoprofile.py @@ -0,0 +1,82 @@ +# +# Ultroid - UserBot +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +# Ported Plugin + +""" +โœ˜ Commands Available - + +โ€ข `{i}autoname` + `Starts AUTONAME`. + +โ€ข `{i}stopname` + `Stops AUTONAME.` + +โ€ข `{i}autobio` + `Starts AUTOBIO.` + +โ€ข `{i}stopbio` + `Stops AUTOBIO.` +""" + +import random + +from telethon.tl.functions.account import UpdateProfileRequest + +from . import * + + +@ultroid_cmd(pattern="(auto|stop)name$") +async def autoname_(event): + match = event.pattern_match.group(1) + if match == "stop": + udB.del_key("AUTONAME") + await event.eor("`AUTONAME has been Stopped !`") + return + udB.set_key("AUTONAME", "True") + await eod(event, "`Started AUTONAME`") + while True: + getn = udB.get_key("AUTONAME") + if not getn: + return + DM = time.strftime("%d-%m-%y") + HM = time.strftime("%H:%M") + name = f"๐Ÿ•’{HM} โšก{OWNER_NAME}โšก {DM} ๐Ÿ—“๏ธ" + await event.client(UpdateProfileRequest(first_name=name)) + await asyncio.sleep(1111) + + +@ultroid_cmd(pattern="(auto|stop)bio$") +async def autoname_(event): + match = event.pattern_match.group(1) + if match == "stop": + udB.del_key("AUTOBIO") + await event.eor("`AUTOBIO has been Stopped !`") + return + udB.set_key("AUTOBIO", "True") + await eod(event, "`Started AUTOBIO`") + BIOS = [ + "Busy Today !", + "ULTROID USER", + "Enjoying Life!", + "Unique as Always!" "Sprinkling a bit of magic", + "Intelligent !", + ] + while True: + getn = udB.get_key("AUTOBIO") + if not getn: + return + BIOMSG = random.choice(BIOS) + DM = time.strftime("%d-%m-%y") + HM = time.strftime("%H:%M") + name = f"๐Ÿ“…{DM} | {BIOMSG} | โŒš๏ธ{HM}" + await event.client( + UpdateProfileRequest( + about=name, + ) + ) + await asyncio.sleep(1111) diff --git a/addons/beautify.py b/addons/beautify.py new file mode 100644 index 0000000..33609f5 --- /dev/null +++ b/addons/beautify.py @@ -0,0 +1,197 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_beautify") + + +import os +import random + +from telethon.utils import get_display_name +from urllib.parse import urlencode +from . import Carbon, ultroid_cmd, get_string, inline_mention, LOGS +from secrets import token_hex + +_colorspath = "resources/colorlist.txt" + +if os.path.exists(_colorspath): + with open(_colorspath, "r") as f: + all_col = f.read().split() +else: + all_col = [] + + +@ultroid_cmd( + pattern="(rc|c)arbon", +) +async def cr_bn(event): + xxxx = await event.eor(get_string("com_1")) + te = event.pattern_match.group(1) + col = random.choice(all_col) if te[0] == "r" else "White" + if event.reply_to_msg_id: + temp = await event.get_reply_message() + if temp.media: + b = await event.client.download_media(temp) + with open(b) as a: + code = a.read() + os.remove(b) + else: + code = temp.message + else: + try: + code = event.text.split(" ", maxsplit=1)[1] + except IndexError: + return await xxxx.eor(get_string("carbon_2")) + xx = await Carbon(code=code, file_name="ultroid_carbon", backgroundColor=col) + if isinstance(xx, dict): + await xxxx.edit(f"`{xx}`") + return + await xxxx.delete() + await event.reply( + f"Carbonised by {inline_mention(event.sender)}", + file=xx, + ) + + +@ultroid_cmd( + pattern="ccarbon( (.*)|$)", +) +async def crbn(event): + match = event.pattern_match.group(1).strip() + if not match: + return await event.eor(get_string("carbon_3")) + msg = await event.eor(get_string("com_1")) + if event.reply_to_msg_id: + temp = await event.get_reply_message() + if temp.media: + b = await event.client.download_media(temp) + with open(b) as a: + code = a.read() + os.remove(b) + else: + code = temp.message + else: + try: + match = match.split(" ", maxsplit=1) + code = match[1] + match = match[0] + except IndexError: + return await msg.eor(get_string("carbon_2")) + xx = await Carbon(code=code, backgroundColor=match) + await msg.delete() + await event.reply( + f"Carbonised by {inline_mention(event.sender)}", + file=xx, + ) + + +RaySoTheme = [ + "meadow", + "breeze", + "raindrop", + "candy", + "crimson", + "falcon", + "sunset", + "noir", + "midnight", + "bitmap", + "ice", + "sand", + "forest", + "mono", +] + + +@ultroid_cmd(pattern="rayso") +async def pass_on(ult): + try: + from playwright.async_api import async_playwright + except ImportError: + await ult.eor( + "`playwright` is not installed!\nPlease install it to use this command.." + ) + return + + proc = await ult.eor(get_string("com_1")) + spli = ult.text.split() + theme, dark, title, text = None, True, get_display_name(ult.chat), None + if len(spli) > 1: + if spli[1] in RaySoTheme: + theme = spli[1] + if len(spli) > 2: + text = " ".join(spli[2:]) + else: + text = " ".join(spli[1:]) + if ult.is_reply: + try: + msg = await ult.get_reply_message() + text = msg.message if not text else text + title = get_display_name(msg.sender) + if not theme and spli[1] in RaySoTheme: + theme = spli[1] + except Exception as sam: + LOGS.exception(sam) + if not text: + await proc.eor("No text to beautify!") + return + if not theme: + theme = random.choice(RaySoTheme) + cleaned_text = "\n".join([line.strip() for line in text.splitlines()]) + name = token_hex(8) + ".png" + data = {"darkMode": dark, "theme": theme, "title": title} + url = f"https://ray.so/#{urlencode(data)}" + async with async_playwright() as play: + try: + browser = await play.chromium.launch() + page = await browser.new_page() + await page.goto(url) + await page.wait_for_load_state("networkidle") + try: + await page.wait_for_selector( + "div[class*='Editor_editor__']", timeout=60000 + ) + editor = await page.query_selector("div[class*='Editor_editor__']") + await editor.focus() + await editor.click() + + for line in cleaned_text.split("\n"): + await page.keyboard.type(line) + await page.keyboard.press("Enter") + + await page.evaluate( + """() => { + const button = document.querySelector('button[aria-label="Export as PNG"]'); + button.click(); + }""" + ) + + async with page.expect_download() as download_info: + download = await download_info.value + await download.save_as(name) + except playwright._impl._errors.TimeoutError: + LOGS.error("Timeout error: Selector not found within 60 seconds.") + await proc.eor("Failed to find the editor within 60 seconds.") + return + except Exception as e: + LOGS.error(f"Error occurred during playwright operation: {e}") + await proc.eor("An error occurred during the operation.") + return + finally: + if os.path.exists(name): + try: + await ult.reply(file=name) + await proc.try_delete() + os.remove(name) + except Exception as e: + LOGS.error(f"Error occurred while replying with the file: {e}") + await proc.eor("Failed to send the file.") + else: + LOGS.error(f"Error: File {name} not found or inaccessible.") + await proc.eor("Failed to save the file.") diff --git a/addons/blacklist.py b/addons/blacklist.py new file mode 100644 index 0000000..9de1258 --- /dev/null +++ b/addons/blacklist.py @@ -0,0 +1,109 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_blacklist") + +from telethon.tl.types import Channel + +from . import events, get_string, udB, ultroid_bot, ultroid_cmd + +# Functions moved from blacklist_db.py +def get_stuff(): + return udB.get_key("BLACKLIST_DB") or {} + + +def add_blacklist(chat, word): + ok = get_stuff() + if ok.get(chat): + for z in word.split(): + if z not in ok[chat]: + ok[chat].append(z) + else: + ok.update({chat: [word]}) + return udB.set_key("BLACKLIST_DB", ok) + + +def rem_blacklist(chat, word): + ok = get_stuff() + if ok.get(chat) and word in ok[chat]: + ok[chat].remove(word) + return udB.set_key("BLACKLIST_DB", ok) + + +def list_blacklist(chat): + ok = get_stuff() + if ok.get(chat): + txt = "".join(f"๐Ÿ‘‰`{z}`\n" for z in ok[chat]) + if txt: + return txt + + +def get_blacklist(chat): + ok = get_stuff() + if ok.get(chat): + return ok[chat] + + +@ultroid_cmd(pattern="blacklist( (.*)|$)", admins_only=True) +async def af(e): + wrd = e.pattern_match.group(1).strip() + chat = e.chat_id + if not (wrd): + return await e.eor(get_string("blk_1")) + wrd = e.text.split(maxsplit=1)[1] + add_blacklist(chat, wrd) + await e.eor(get_string("blk_2").format(wrd)) + + +@ultroid_cmd(pattern="remblacklist( (.*)|$)", admins_only=True) +async def rf(e): + wrd = e.pattern_match.group(1).strip() + chat = e.chat_id + if not wrd: + return await e.eor(get_string("blk_3")) + wrd = e.text.split(maxsplit=1)[1] + if rem_blacklist(chat, wrd): + await e.eor(get_string("blk_4").format(wrd)) + else: + await e.eor(get_string("blk_5")) + + +@ultroid_cmd(pattern="listblacklist$", admins_only=True) +async def lsnote(e): + if x := list_blacklist(e.chat_id): + await e.eor(get_string("blk_6").format(x)) + else: + await e.eor(get_string("blk_7")) + + +async def blacklist(e): + if not get_blacklist(e.chat_id): + return + xx = get_blacklist(e.chat_id) + if getattr(e, "sender", None) and isinstance(e.sender, Channel): + return + if not ( + isinstance(e.text, str) + and ("chat.whatsapp.com" in e.text.lower()) + and e.sender + and hasattr(e.sender, "username") + and e.sender.username == "Channel_Bot" + ): + text = e.text.lower().split() + for x in xx: + if x.lower() in text: + try: + await e.delete() + except Exception: + pass + break + + +if get_stuff(): + ultroid_bot.add_handler(blacklist, events.NewMessage(incoming=True)) diff --git a/addons/brainfuck.py b/addons/brainfuck.py new file mode 100644 index 0000000..91c9079 --- /dev/null +++ b/addons/brainfuck.py @@ -0,0 +1,201 @@ +# Made by : @Hackintush || github.com/ToxygenX +# Made For : https://github.com/TeamUltroid/UltroidAddons + +""" +โœ˜ Commands Available + +โ€ข `{i}bf` + Text to Brainfuck String Generator with text or reply. + +โ€ข `{i}rbf` + Brainfuck Interpreter with string or reply. +""" + +from . import * + + +def evaluate(commands): + interpreter = BrainfuckInterpreter(commands) + while interpreter.available(): + interpreter.step() + + return interpreter.output.read() + + +__all__ = "BrainfuckInterpreter" + + +class IOStream: + def __init__(self, data=None): + self._buffer = data or "" + + def __len__(self): + return len(self._buffer) + + def read(self, length=None): + if not length: + data = self._buffer + self._buffer = "" + else: + data = self._buffer[:length] + self._buffer = self._buffer[length:] + + return data + + def write(self, data): + self._buffer += data + + +class IncrementalByteCellArray: + def __init__(self): + self.byte_cells = [0] + self.data_pointer = 0 + + def __getitem__(self, item): + cell_amount = len(self.byte_cells) + if item > cell_amount - 1: + self.extend(item - cell_amount + 1) + + return self.byte_cells[item] + + def __setitem__(self, key: int, value: int): + cell_amount = len(self.byte_cells) + if key > cell_amount - 1: + self.extend(key - cell_amount + 1) + + self.byte_cells[key] = value + + def __len__(self): + return len(self.byte_cells) + + def __repr__(self): + return self.byte_cells.__repr__() + + def extend(self, size: int): + self.byte_cells += [0] * size + + def increment(self): + new_val = (self.get() + 1) % 256 + self.set(new_val) + + def decrement(self): + new_val = self.get() - 1 + if new_val < 0: + new_val = 255 + + self.set(new_val) + + def set(self, value: int): + self.__setitem__(self.data_pointer, value) + + def get(self): + return self.__getitem__(self.data_pointer) + + +class BrainfuckInterpreter: + def __init__(self, commands: str): + self._commands = commands + + self.input = IOStream() + self.output = IOStream() + + self.instruction_pointer = 0 + self.cells = IncrementalByteCellArray() + + self._opening_bracket_indexes = [] + + def _look_forward(self): + remaining_commands = self._commands[self.instruction_pointer :] + loop_counter = 0 + index = self.instruction_pointer + + for command in remaining_commands: + if command == "[": + loop_counter += 1 + elif command == "]": + loop_counter -= 1 + + if loop_counter == 0: + return index + + index += 1 + + def _interpret(self): + instruction = self._commands[self.instruction_pointer] + + if instruction == ">": + self.cells.data_pointer += 1 + elif instruction == "<": + self.cells.data_pointer -= 1 + elif instruction == "+": + self.cells.increment() + elif instruction == "-": + self.cells.decrement() + elif instruction == ".": + self.output.write(chr(self.cells.get())) + elif instruction == ",": + self.cells.set(self.input.read(1)) + elif instruction == "[": + if self.cells.get() == 0: + loop_end = self._look_forward() + self.instruction_pointer = loop_end + else: + self._opening_bracket_indexes.append(self.instruction_pointer) + elif instruction == "]": + if self.cells.get() != 0: + opening_bracket_index = self._opening_bracket_indexes.pop(-1) + + self.instruction_pointer = opening_bracket_index - 1 + else: + self._opening_bracket_indexes.pop(-1) + + def step(self) -> None: + self._interpret() + self.instruction_pointer += 1 + + def available(self) -> bool: + return not self.instruction_pointer >= len(self._commands) + + def command(self): + return self._commands[self.instruction_pointer] + + +def bf(text): + items = [] + for c in text: + items.append( + "[-]>[-]<" + + ("+" * (ord(c) // 10)) + + "[>++++++++++<-]>" + + ("+" * (ord(c) % 10)) + + ".<" + ) + return "".join(items) + + +@ultroid_cmd( + pattern="bf", +) +async def _(event): + input_ = event.text[4:] + if not input_: + if event.reply_to_msg_id: + previous_message = await event.get_reply_message() + input_ = previous_message.message + else: + return await eod(event, "Give me some text lol", time=5) + await event.eor(bf(input_)) + + +@ultroid_cmd( + pattern="rbf", +) +async def _(event): + input_ = event.text[5:] + if not input_: + if event.reply_to_msg_id: + previous_message = await event.get_reply_message() + input_ = previous_message.message + else: + return await eod(event, "Give me some text lol", time=5) + await event.eor(f"{evaluate(input_)}") diff --git a/addons/broadcast.py b/addons/broadcast.py new file mode 100644 index 0000000..4eb1d36 --- /dev/null +++ b/addons/broadcast.py @@ -0,0 +1,216 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + + +from . import get_help + +__doc__ = get_help("help_broadcast") + +import asyncio +import io + +from telethon.utils import get_display_name + +from pyUltroid.dB.base import KeyManager + +from . import HNDLR, LOGS, eor, get_string, udB, ultroid_bot, ultroid_cmd + +KeyM = KeyManager("BROADCAST", cast=list) + + +@ultroid_cmd( + pattern="addch( (.*)|$)", + allow_sudo=False, +) +async def broadcast_adder(event): + msgg = event.pattern_match.group(1).strip() + x = await event.eor(get_string("bd_1")) + if msgg == "all": + await x.edit(get_string("bd_2")) + chats = [ + e.entity + for e in await event.client.get_dialogs() + if (e.is_group or e.is_channel) + ] + new = 0 + for i in chats: + try: + if ( + i.broadcast + and (i.creator or i.admin_rights) + and not KeyM.contains(i.id) + ): + new += 1 + cid = f"-100{i.id}" + KeyM.add(int(cid)) + except Exception as Ex: + LOGS.exception(Ex) + await x.edit(get_string("bd_3").format(KeyM.count(), new)) + return + if event.reply_to_msg_id: + previous_message = await event.get_reply_message() + raw_text = previous_message.text + lines = raw_text.split("\n") + length = len(lines) + for line_number in range(1, length - 2): + channel_id = lines[line_number][4:-1] + if not KeyM.contains(channel_id): + KeyM.add(channel_id) + await x.edit(get_string("bd_4")) + await asyncio.sleep(3) + await event.delete() + return + chat_id = event.chat_id + if chat_id == udB.get_key("LOG_CHANNEL"): + return + if KeyM.contains(chat_id): + await x.edit(get_string("bd_6")) + elif xx := KeyM.add(chat_id): + await x.edit(get_string("bd_5")) + else: + await x.edit(get_string("sf_8")) + await asyncio.sleep(3) + await x.delete() + + +@ultroid_cmd( + pattern="remch( (.*)|$)", + allow_sudo=False, +) +async def broadcast_remover(event): + chat_id = event.pattern_match.group(1).strip() or event.chat_id + x = await event.eor(get_string("com_1")) + if chat_id == "all": + await x.edit(get_string("bd_8")) + udB.del_key("BROADCAST") + await x.edit("Database cleared.") + return + if KeyM.contains(chat_id): + KeyM.remove(chat_id) + await x.edit(get_string("bd_7")) + else: + await x.edit(get_string("bd_9")) + await asyncio.sleep(3) + await x.delete() + + +@ultroid_cmd( + pattern="listchannels$", +) +async def list_all(event): + x = await event.eor(get_string("com_1")) + channels = KeyM.get() + num = KeyM.count() + if not channels: + return await eor(x, "No chats were added.", time=5) + msg = "Channels in database:\n" + for channel in channels: + name = "" + try: + name = get_display_name(await event.client.get_entity(channel)) + except ValueError: + name = "" + msg += f"=> **{name}** [`{channel}`]\n" + msg += f"\nTotal {num} channels." + if len(msg) > 4096: + MSG = msg.replace("*", "").replace("`", "") + with io.BytesIO(str.encode(MSG)) as out_file: + out_file.name = "channels.txt" + await event.reply( + "Channels in Database", + file=out_file, + force_document=True, + allow_cache=False, + ) + await x.delete() + else: + await x.edit(msg) + + +@ultroid_cmd( + pattern="forward$", + allow_sudo=False, +) +async def forw(event): + if not event.is_reply: + return await event.eor(get_string("ex_1")) + ultroid_bot = event.client + channels = KeyM.get() + x = await event.eor("Sending...") + if not channels: + return await x.edit(f"Please add channels by using `{HNDLR}add` in them.") + error_count = 0 + sent_count = 0 + previous_message = await event.get_reply_message() + error_count = 0 + for channel in channels: + try: + await ultroid_bot.forward_messages(channel, previous_message) + sent_count += 1 + await x.edit( + f"Sent : {sent_count}\nError : {error_count}\nTotal : {len(channels)}", + ) + except Exception: + try: + await ultroid_bot.send_message( + udB.get_key("LOG_CHANNEL"), + f"Error in sending at {channel}.", + ) + except Exception as Em: + LOGS.info(Em) + error_count += 1 + await x.edit( + f"Sent : {sent_count}\nError : {error_count}\nTotal : {len(channels)}", + ) + await x.edit(f"{sent_count} messages sent with {error_count} errors.") + if error_count > 0: + await ultroid_bot.send_message( + udB.get_key("LOG_CHANNEL"), f"{error_count} Errors" + ) + + +@ultroid_cmd( + pattern="broadcast( (.*)|$)", + allow_sudo=False, +) +async def sending(event): + x = await event.eor(get_string("com_1")) + if not event.is_reply: + return await x.edit(get_string("ex_1")) + channels = KeyM.get() + if not channels: + return await x.edit(f"Please add channels by using `{HNDLR}add` in them.") + await x.edit("Sending....") + if event.reply_to_msg_id: + previous_message = await event.get_reply_message() + if previous_message.poll: + return await x.edit(f"Reply `{HNDLR}forward` for polls.") + if previous_message: + error_count = 0 + sent_count = 0 + for channel in channels: + try: + await ultroid_bot.send_message(channel, previous_message) + sent_count += 1 + await x.edit( + f"Sent : {sent_count}\nError : {error_count}\nTotal : {len(channels)}", + ) + except Exception as error: + await ultroid_bot.send_message( + udB.get_key("LOG_CHANNEL"), + f"Error in sending at {channel}.\n\n{error}", + ) + error_count += 1 + await x.edit( + f"Sent : {sent_count}\nError : {error_count}\nTotal : {len(channels)}", + ) + await x.edit(f"{sent_count} messages sent with {error_count} errors.") + if error_count > 0: + await ultroid_bot.send_message( + udB.get_key("LOG_CHANNEL"), + f"{error_count} Errors", + ) diff --git a/addons/button.py b/addons/button.py new file mode 100644 index 0000000..ef1fbb2 --- /dev/null +++ b/addons/button.py @@ -0,0 +1,54 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_button") + +import os + +from . import upload_file as uf +from telethon.utils import pack_bot_file_id + +from pyUltroid.fns.tools import create_tl_btn, get_msg_button + +from . import HNDLR, get_string, mediainfo, ultroid_cmd +from ._inline import something + + +@ultroid_cmd(pattern="button") +async def butt(event): + media, wut, text = None, None, None + if event.reply_to: + wt = await event.get_reply_message() + if wt.text: + text = wt.text + if wt.media: + wut = mediainfo(wt.media) + if wut and wut.startswith(("pic", "gif")): + dl = await wt.download_media() + media = uf(dl) + elif wut == "video": + if wt.media.document.size > 8 * 1000 * 1000: + return await event.eor(get_string("com_4"), time=5) + dl = await wt.download_media() + media = uf(dl) + os.remove(dl) + else: + media = pack_bot_file_id(wt.media) + try: + text = event.text.split(maxsplit=1)[1] + except IndexError: + if not text: + return await event.eor( + f"**Please give some text in correct format.**\n\n`{HNDLR}help button`", + ) + text, buttons = get_msg_button(text) + if buttons: + buttons = create_tl_btn(buttons) + await something(event, text, media, buttons) + await event.delete() diff --git a/addons/calculator.py b/addons/calculator.py new file mode 100644 index 0000000..3514c29 --- /dev/null +++ b/addons/calculator.py @@ -0,0 +1,153 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + + +from . import get_help + +__doc__ = get_help("help_calculator") + +import re + +from . import Button, asst, callback, get_string, in_pattern, udB, ultroid_cmd + +CALC = {} + +m = [ + "AC", + "C", + "โŒซ", + "%", + "7", + "8", + "9", + "+", + "4", + "5", + "6", + "-", + "1", + "2", + "3", + "x", + "00", + "0", + ".", + "รท", +] +tultd = [Button.inline(f"{x}", data=f"calc{x}") for x in m] +lst = list(zip(tultd[::4], tultd[1::4], tultd[2::4], tultd[3::4])) +lst.append([Button.inline("=", data="calc=")]) + + +@ultroid_cmd(pattern="calc") +async def icalc(e): + udB.del_key("calc") + if e.client._bot: + return await e.reply(get_string("calc_1"), buttons=lst) + results = await e.client.inline_query(asst.me.username, "calc") + await results[0].click(e.chat_id, silent=True, hide_via=True) + await e.delete() + + +@in_pattern("calc", owner=True) +async def _(e): + calc = e.builder.article("Calc", text=get_string("calc_1"), buttons=lst) + await e.answer([calc]) + + +@callback(re.compile("calc(.*)"), owner=True) +async def _(e): + x = (e.data_match.group(1)).decode() + user = e.query.user_id + get = None + if x == "AC": + if CALC.get(user): + CALC.pop(user) + await e.edit( + get_string("calc_1"), + buttons=[Button.inline(get_string("calc_2"), data="recalc")], + ) + elif x == "C": + if CALC.get(user): + CALC.pop(user) + await e.answer("cleared") + elif x == "โŒซ": + if CALC.get(user): + get = CALC[user] + if get: + CALC.update({user: get[:-1]}) + await e.answer(str(get[:-1])) + elif x == "%": + if CALC.get(user): + get = CALC[user] + if get: + CALC.update({user: f"{get}/100"}) + await e.answer(str(f"{get}/100")) + elif x == "รท": + if CALC.get(user): + get = CALC[user] + if get: + CALC.update({user: f"{get}/"}) + await e.answer(str(f"{get}/")) + elif x == "x": + if CALC.get(user): + get = CALC[user] + if get: + CALC.update({user: f"{get}*"}) + await e.answer(str(f"{get}*")) + elif x == "=": + if CALC.get(user): + get = CALC[user] + if get: + if get.endswith(("*", ".", "/", "-", "+")): + get = get[:-1] + out = eval(get) + try: + num = float(out) + await e.answer(f"Answer : {num}", cache_time=0, alert=True) + except BaseException: + CALC.pop(user) + await e.answer(get_string("sf_8"), cache_time=0, alert=True) + await e.answer("None") + else: + if CALC.get(user): + get = CALC[user] + if get: + CALC.update({user: get + x}) + return await e.answer(str(get + x)) + CALC.update({user: x}) + await e.answer(str(x)) + + +@callback("recalc", owner=True) +async def _(e): + m = [ + "AC", + "C", + "โŒซ", + "%", + "7", + "8", + "9", + "+", + "4", + "5", + "6", + "-", + "1", + "2", + "3", + "x", + "00", + "0", + ".", + "รท", + ] + tultd = [Button.inline(f"{x}", data=f"calc{x}") for x in m] + lst = list(zip(tultd[::4], tultd[1::4], tultd[2::4], tultd[3::4])) + lst.append([Button.inline("=", data="calc=")]) + await e.edit(get_string("calc_1"), buttons=lst) diff --git a/addons/channelhacks.py b/addons/channelhacks.py new file mode 100644 index 0000000..561517a --- /dev/null +++ b/addons/channelhacks.py @@ -0,0 +1,224 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +from . import get_help + +__doc__ = get_help("help_channelhacks") + + +import asyncio +import io + +from telethon.errors.rpcerrorlist import FloodWaitError +from telethon.utils import get_display_name, get_peer_id + +from pyUltroid.dB.base import KeyManager + +from . import LOGS, asst, eor, events, get_string, udB, ultroid_bot, ultroid_cmd + +ERROR = {} +SourceM = KeyManager("CH_SOURCE", cast=list) +DestiM = KeyManager("CH_DESTINATIONS", cast=list) + + +async def autopost_func(e): + if not udB.get_key("AUTOPOST"): + return + x = SourceM.get() + th = await e.get_chat() + if get_peer_id(th) not in x: + return + y = DestiM.get() + for ys in y: + try: + await e.client.send_message(int(ys), e.message) + except Exception as ex: + try: + ERROR[str(ex)] + except KeyError: + ERROR.update({str(ex): ex}) + Error = f"**Error on AUTOPOST**\n\n`{ex}`" + await asst.send_message(udB.get_key("LOG_CHANNEL"), Error) + + +@ultroid_cmd(pattern="shift (.*)") +async def _(e): + x = e.pattern_match.group(1).strip() + z = await e.eor(get_string("com_1")) + a, b = x.split("|") + try: + c = await e.client.parse_id(a) + except Exception: + await z.edit(get_string("cha_1")) + return + try: + d = await e.client.parse_id(b) + except Exception as er: + LOGS.exception(er) + await z.edit(get_string("cha_1")) + return + async for msg in e.client.iter_messages(int(c), reverse=True): + try: + await asyncio.sleep(2) + await e.client.send_message(int(d), msg) + except FloodWaitError as er: + await asyncio.sleep(er.seconds + 5) + await e.client.send_message(int(d), msg) + except BaseException as er: + LOGS.exception(er) + await z.edit("Done") + + +@ultroid_cmd(pattern="asource (.*)") +async def source(e): + if x := e.pattern_match.group(1).strip(): + try: + y = await e.client.parse_id(x) + except Exception as er: + LOGS.exception(er) + return + else: + y = e.chat_id + if not SourceM.contains(y): + SourceM.add(y) + await e.eor(get_string("cha_2")) + ultroid_bot.add_handler(autopost_func, events.NewMessage()) + else: + await e.eor(get_string("cha_3")) + + +@ultroid_cmd(pattern="dsource( (.*)|$)") +async def dd(event): + chat_id = event.pattern_match.group(1).strip() + x = await event.eor(get_string("com_1")) + if chat_id == "all": + await x.edit(get_string("bd_8")) + udB.del_key("CH_SOURCE") + await x.edit(get_string("cha_4")) + return + if chat_id: + try: + y = await event.client.parse_id(chat_id) + except Exception as er: + LOGS.exception(er) + return + else: + y = event.chat_id + if SourceM.contains(y): + SourceM.remove(y) + await eor(x, get_string("cha_5"), time=5) + else: + await eor(x, "Source channel is already removed from database. ", time=3) + + +@ultroid_cmd(pattern="listsource") +async def list_all(event): + x = await event.eor(get_string("com_1")) + num = SourceM.count() + if not num: + return await eor(x, "No chats were added.", time=5) + msg = get_string("cha_8") + channels = SourceM.get() + for channel in channels: + name = "" + try: + name = get_display_name(await event.client.get_entity(int(channel))) + except BaseException: + name = "" + msg += f"\n=> **{name}** [`{channel}`]" + msg += f"\nTotal {num} channels." + if len(msg) > 4096: + MSG = msg.replace("*", "").replace("`", "") + with io.BytesIO(str.encode(MSG)) as out_file: + out_file.name = "channels.txt" + await event.reply( + "Channels in database", + file=out_file, + force_document=True, + allow_cache=False, + ) + await x.delete() + else: + await x.edit(msg) + + +@ultroid_cmd(pattern="adest (.*)") +async def destination(e): + if x := e.pattern_match.group(1).strip(): + try: + y = await e.client.parse_id(x) + except Exception as er: + LOGS.exception(er) + return + else: + y = e.chat_id + if not DestiM.contains(y): + DestiM.add(y) + await e.eor("Destination added succesfully") + else: + await e.eor("Destination channel already added") + + +@ultroid_cmd(pattern="ddest( (.*)|$)") +async def dd(event): + chat_id = event.pattern_match.group(1).strip() + x = await event.eor(get_string("com_1")) + if chat_id == "all": + await x.edit(get_string("bd_8")) + udB.del_key("CH_DESTINATION") + await x.edit("Destinations database cleared.") + return + if chat_id: + try: + y = await event.client.parse_id(chat_id) + except Exception as er: + LOGS.exception(er) + return + else: + y = event.chat_id + if DestiM.contains(y): + DestiM.remove(y) + await eor(x, "Destination removed from database") + else: + await eor(x, "Destination channel is already removed from database. ", time=5) + + +@ultroid_cmd(pattern="listdest") +async def list_all(event): + ultroid_bot = event.client + x = await event.eor(get_string("com_1")) + channels = DestiM.get() + num = len(channels) + if not num: + return await eor(x, "No chats were added.", time=5) + msg = get_string("cha_7") + for channel in channels: + name = "" + try: + name = get_display_name(await ultroid_bot.get_entity(int(channel))) + except BaseException: + name = "" + msg += f"\n=> **{name}** [`{channel}`]" + msg += f"\nTotal {num} channels." + if len(msg) > 4096: + MSG = msg.replace("*", "").replace("`", "") + with io.BytesIO(str.encode(MSG)) as out_file: + out_file.name = "channels.txt" + await ultroid_bot.send_file( + event.chat_id, + out_file, + force_document=True, + allow_cache=False, + caption="Destination channels in database", + reply_to=event, + ) + await x.delete() + else: + await x.edit(msg) + + +if udB.get_key("AUTOPOST"): + ultroid_bot.add_handler(autopost_func, events.NewMessage()) diff --git a/addons/chatbot.py b/addons/chatbot.py new file mode 100644 index 0000000..02b0ddb --- /dev/null +++ b/addons/chatbot.py @@ -0,0 +1,109 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_chatbot") + +import asyncio + +from pyUltroid.fns.tools import get_chatbot_reply + +from telethon.tl.types import Channel + +from . import LOGS, eod, get_string, inline_mention, udB, ultroid_cmd, ultroid_bot, events + + +@ultroid_cmd(pattern="repai") +async def im_lonely_chat_with_me(event): + if event.reply_to: + message = (await event.get_reply_message()).message + else: + try: + message = event.text.split(" ", 1)[1] + except IndexError: + return await eod(event, get_string("tban_1"), time=10) + reply_ = await get_chatbot_reply(message=message) + await event.eor(reply_) + + +@ultroid_cmd(pattern="addai") +async def add_chatBot(event): + await chat_bot_fn(event, type_="add") + + +@ultroid_cmd(pattern="remai") +async def rem_chatBot(event): + await chat_bot_fn(event, type_="remov") + + +@ultroid_cmd(pattern="listai") +async def lister(event): + key = udB.get_key("CHATBOT_USERS") or {} + users = key.get(event.chat_id, []) + if not users: + return await event.eor(get_string("chab_2"), time=5) + msg = "**Total List Of AI Enabled Users In This Chat :**\n\n" + for i in users: + try: + user = await event.client.get_entity(int(i)) + user = inline_mention(user) + except BaseException: + user = f"`{i}`" + msg += f"โ€ข {user}\n" + await event.eor(msg, link_preview=False) + + +async def chat_bot_fn(event, type_): + if event.reply_to: + user_ = (await event.get_reply_message()).sender + else: + temp = event.text.split(maxsplit=1) + try: + user_ = await event.client.get_entity(await event.client.parse_id(temp[1])) + except BaseException as er: + LOGS.exception(er) + user_ = event.chat if event.is_private else None + if not user_: + return await eod( + event, + get_string("chab_1"), + ) + key = udB.get_key("CHATBOT_USERS") or {} + chat = event.chat_id + user = user_.id + if type_ == "add": + if key.get(chat): + if user not in key[chat]: + key[chat].append(user) + else: + key.update({chat: [user]}) + elif type_ == "remov": + if key.get(chat): + if user in key[chat]: + key[chat].remove(user) + if chat in key and not key[chat]: + del key[chat] + udB.set_key("CHATBOT_USERS", key) + await event.eor(f"**ChatBot:**\n{type_}ed {inline_mention(user_)}") + + + +@ultroid_bot.on(events.NewMessage(incoming=True)) +async def chatBot_replies(e): + sender = await e.get_sender() +if isinstance(sender, Channel): + return + if sender.bot: + return + key = udB.get_key("CHATBOT_USERS") or {} + if e.text and key.get(e.chat_id) and sender.id in key[e.chat_id]: + msg = await get_chatbot_reply(e.message.message) + if msg: + sleep = udB.get_key("CHATBOT_SLEEP") or 1.5 + await asyncio.sleep(sleep) + await e.reply(msg) diff --git a/addons/cleanaction.py b/addons/cleanaction.py new file mode 100644 index 0000000..ffb7e78 --- /dev/null +++ b/addons/cleanaction.py @@ -0,0 +1,60 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_cleanaction") + + +from telethon.utils import get_display_name + +from . import get_string, udB, ultroid_cmd, events, ultroid_bot + + +@ultroid_cmd(pattern="addclean$", admins_only=True) +async def _(e): + key = udB.get_key("CLEANCHAT") or [] + if e.chat_id in key: + return await e.eor(get_string("clan_5")) + key.append(e.chat_id) + udB.set_key("CLEANCHAT", key) + await e.eor(get_string("clan_1"), time=5) + + +@ultroid_cmd(pattern="remclean$") +async def _(e): + key = udB.get_key("CLEANCHAT") or [] + if e.chat_id in key: + key.remove(e.chat_id) + udB.set_key("CLEANCHAT", key) + await e.eor(get_string("clan_2"), time=5) + + +@ultroid_cmd(pattern="listclean$") +async def _(e): + if k := udB.get_key("CLEANCHAT"): + o = "" + for x in k: + try: + title = get_display_name(await e.client.get_entity(x)) + except BaseException: + title = get_string("clan_3") + o += f"{x} {title}\n" + return await e.eor(o) + await e.eor(get_string("clan_4"), time=5) + +# Add chat action handler for clean action +async def clean_action(event): + key = udB.get_key("CLEANCHAT") or [] + if event.chat_id in key: + try: + await event.delete() + except BaseException: + pass + +# Register the handler +ultroid_bot.add_handler(clean_action, events.ChatAction()) diff --git a/addons/clone.py b/addons/clone.py new file mode 100644 index 0000000..dc66932 --- /dev/null +++ b/addons/clone.py @@ -0,0 +1,143 @@ +# Ported From DarkCobra , Originally By Uniborg +# Ultroid - UserBot +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +""" +โœ˜ Commands Available + +โ€ข `{i}clone ` + clone the identity of user. + +โ€ข `{i}revert` + Revert to your original identity +""" +import html +import io +import os + +from telethon.tl.functions.account import UpdateProfileRequest +from telethon.tl.functions.photos import DeletePhotosRequest, UploadProfilePhotoRequest +from telethon.tl.functions.users import GetFullUserRequest +from telethon.tl.types import MessageEntityMentionName + +from . import * + + +@ultroid_cmd(pattern="clone ?(.*)", fullsudo=True) +async def _(event): + eve = await event.eor("Processing...") + reply_message = await event.get_reply_message() + whoiam = await event.client(GetFullUserRequest(ultroid_bot.uid)) + if whoiam.full_user.about: + mybio = f"{str(ultroid_bot.me.id)}01" + # saving bio for revert + udB.set_key(f"{mybio}", whoiam.full_user.about) + udB.set_key(f"{ultroid_bot.uid}02", whoiam.users[0].first_name) + if whoiam.users[0].last_name: + udB.set_key(f"{ultroid_bot.uid}03", whoiam.users[0].last_name) + replied_user, error_i_a = await get_full_user(event) + if replied_user is None: + await eve.edit(str(error_i_a)) + return + user_id = replied_user.users[0].id + profile_pic = await event.client.download_profile_photo(user_id) + first_name = html.escape(replied_user.users[0].first_name) + if first_name is not None: + first_name = first_name.replace("\u2060", "") + last_name = replied_user.users[0].last_name + if last_name is not None: + last_name = html.escape(last_name) + last_name = last_name.replace("\u2060", "") + if last_name is None: + last_name = "โชโฌโฎโฎโฎ" + user_bio = replied_user.full_user.about + await event.client(UpdateProfileRequest(first_name=first_name)) + await event.client(UpdateProfileRequest(last_name=last_name)) + await event.client(UpdateProfileRequest(about=user_bio)) + if profile_pic: + pfile = await event.client.upload_file(profile_pic) + await event.client(UploadProfilePhotoRequest(file=pfile)) + os.remove(profile_pic) + await eve.delete() + await event.client.send_message( + event.chat_id, f"I am {first_name} from now...", reply_to=reply_message + ) + + +@ultroid_cmd(pattern="revert$") +async def _(event): + name = OWNER_NAME + mybio = f"{str(ultroid_bot.me.id)}01" + bio = chc if (chc := udB.get_key(mybio)) else "Error : Bio Lost" + fname = udB.get_key(f"{ultroid_bot.uid}02") + lname = udB.get_key(f"{ultroid_bot.uid}03") + if fname: + name = fname + ok = lname if lname else "" + n = 1 + client = event.client + await client( + DeletePhotosRequest(await event.client.get_profile_photos("me", limit=n)) + ) + await client(UpdateProfileRequest(about=bio)) + await client(UpdateProfileRequest(first_name=name)) + await client(UpdateProfileRequest(last_name=ok)) + await event.eor("Succesfully reverted to your account back !") + udB.del_key(f"{ultroid_bot.uid}01") + udB.del_key(f"{ultroid_bot.uid}02") + udB.del_key(f"{ultroid_bot.uid}03") + + +async def get_full_user(event): + if event.reply_to_msg_id: + previous_message = await event.get_reply_message() + if previous_message.forward: + replied_user = await event.client( + GetFullUserRequest( + previous_message.forward.sender_id + or previous_message.forward.channel_id + ) + ) + return replied_user, None + replied_user = await event.client( + GetFullUserRequest(previous_message.sender_id) + ) + return replied_user, None + else: + input_str = None + try: + input_str = event.pattern_match.group(1) + except IndexError as e: + return None, e + if event.message.entities is not None: + mention_entity = event.message.entities + probable_user_mention_entity = mention_entity[0] + if isinstance(probable_user_mention_entity, MessageEntityMentionName): + user_id = probable_user_mention_entity.user_id + replied_user = await event.client(GetFullUserRequest(user_id)) + return replied_user, None + try: + user_object = await event.client.get_entity(input_str) + user_id = user_object.id + replied_user = await event.client(GetFullUserRequest(user_id)) + return replied_user, None + except Exception as e: + return None, e + elif event.is_private: + try: + user_id = event.chat_id + replied_user = await event.client(GetFullUserRequest(user_id)) + return replied_user, None + except Exception as e: + return None, e + else: + try: + user_object = await event.client.get_entity(int(input_str)) + user_id = user_object.id + replied_user = await event.client(GetFullUserRequest(user_id)) + return replied_user, None + except Exception as e: + return None, e diff --git a/addons/compressor.py b/addons/compressor.py new file mode 100644 index 0000000..a221d8f --- /dev/null +++ b/addons/compressor.py @@ -0,0 +1,177 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_compressor") + + +import asyncio +import os +import re +import time +from datetime import datetime as dt + +from telethon.errors.rpcerrorlist import MessageNotModifiedError +from telethon.tl.types import DocumentAttributeVideo + +from pyUltroid.fns.tools import metadata + +from . import ( + ULTConfig, + bash, + downloader, + get_string, + humanbytes, + math, + mediainfo, + time_formatter, + ultroid_cmd, + uploader, +) + + +@ultroid_cmd(pattern="compress( (.*)|$)") +async def _(e): + cr = e.pattern_match.group(1).strip() + crf = 27 + to_stream = False + if cr: + k = e.text.split() + if len(k) == 2: + crf = int(k[1]) if k[1].isdigit() else 27 + elif len(k) > 2: + crf = int(k[1]) if k[1].isdigit() else 27 + to_stream = "stream" in k[2] + vido = await e.get_reply_message() + if vido and vido.media and "video" in mediainfo(vido.media): + if hasattr(vido.media, "document"): + vfile = vido.media.document + name = vido.file.name + else: + vfile = vido.media + name = "" + if not name: + name = "video_" + dt.now().isoformat("_", "seconds") + ".mp4" + xxx = await e.eor(get_string("audiotools_5")) + c_time = time.time() + file = await downloader( + f"resources/downloads/{name}", + vfile, + xxx, + c_time, + f"Downloading {name}...", + ) + + o_size = os.path.getsize(file.name) + d_time = time.time() + diff = time_formatter((d_time - c_time) * 1000) + file_name = (file.name).split("/")[-1] + out = file_name.replace(file_name.split(".")[-1], "compressed.mkv") + await xxx.edit( + f"`Downloaded {file.name} of {humanbytes(o_size)} in {diff}.\nNow Compressing...`" + ) + x, y = await bash( + f'mediainfo --fullscan """{file.name}""" | grep "Frame count"' + ) + if y and y.endswith("NOT_FOUND"): + return await xxx.edit(f"ERROR: `{y}`") + total_frames = x.split(":")[1].split("\n")[0] + progress = f"progress-{c_time}.txt" + with open(progress, "w"): + pass + proce = await asyncio.create_subprocess_shell( + f'ffmpeg -hide_banner -loglevel quiet -progress {progress} -i """{file.name}""" -preset ultrafast -vcodec libx265 -crf {crf} -c:a copy """{out}""" -y', + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + while proce.returncode != 0: + await asyncio.sleep(3) + with open(progress, "r+") as fil: + text = fil.read() + frames = re.findall("frame=(\\d+)", text) + size = re.findall("total_size=(\\d+)", text) + speed = 0 + if len(frames): + elapse = int(frames[-1]) + if len(size): + size = int(size[-1]) + per = elapse * 100 / int(total_frames) + time_diff = time.time() - int(d_time) + speed = round(elapse / time_diff, 2) + if int(speed) != 0: + some_eta = ((int(total_frames) - elapse) / speed) * 1000 + text = f"`Compressing {file_name} at {crf} CRF.\n`" + progress_str = "`[{0}{1}] {2}%\n\n`".format( + "".join("โ—" for _ in range(math.floor(per / 5))), + "".join("" for _ in range(20 - math.floor(per / 5))), + round(per, 2), + ) + + e_size = f"{humanbytes(size)} of ~{humanbytes((size / per) * 100)}" + eta = f"~{time_formatter(some_eta)}" + try: + await xxx.edit( + text + + progress_str + + "`" + + e_size + + "`" + + "\n\n`" + + eta + + "`" + ) + except MessageNotModifiedError: + pass + os.remove(file.name) + c_size = os.path.getsize(out) + f_time = time.time() + difff = time_formatter((f_time - d_time) * 1000) + await xxx.edit( + f"`Compressed {humanbytes(o_size)} to {humanbytes(c_size)} in {difff}\nTrying to Upload...`" + ) + differ = 100 - ((c_size / o_size) * 100) + caption = f"**Original Size: **`{humanbytes(o_size)}`\n" + caption += f"**Compressed Size: **`{humanbytes(c_size)}`\n" + caption += f"**Compression Ratio: **`{differ:.2f}%`\n" + caption += f"\n**Time Taken To Compress: **`{difff}`" + n_file, _ = await e.client.fast_uploader( + out, show_progress=True, event=e, message="Uploading...", to_delete=True + ) + if to_stream: + data = await metadata(out) + width = data["width"] + height = data["height"] + duration = data["duration"] + attributes = [ + DocumentAttributeVideo( + duration=duration, w=width, h=height, supports_streaming=True + ) + ] + await e.client.send_file( + e.chat_id, + n_file, + thumb=ULTConfig.thumb, + caption=caption, + attributes=attributes, + force_document=False, + reply_to=e.reply_to_msg_id, + ) + else: + await e.client.send_file( + e.chat_id, + n_file, + thumb=ULTConfig.thumb, + caption=caption, + force_document=True, + reply_to=e.reply_to_msg_id, + ) + await xxx.delete() + os.remove(out) + os.remove(progress) + else: + await e.eor(get_string("audiotools_8"), time=5) diff --git a/addons/converter.py b/addons/converter.py new file mode 100644 index 0000000..201ff76 --- /dev/null +++ b/addons/converter.py @@ -0,0 +1,196 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_converter") + +import os +import time + +from . import LOGS + +try: + import cv2 +except ImportError: + cv2 = None + +try: + from PIL import Image +except ImportError: + LOGS.info(f"{__file__}: PIL not Installed.") + Image = None + +from . import upload_file as uf + +from . import ( + ULTConfig, + bash, + con, + downloader, + get_paste, + get_string, + udB, + ultroid_cmd, + uploader, +) + +opn = [] + + +@ultroid_cmd( + pattern="thumbnail$", +) +async def _(e): + r = await e.get_reply_message() + if r.photo: + dl = await r.download_media() + elif r.document and r.document.thumbs: + dl = await r.download_media(thumb=-1) + else: + return await e.eor("`Reply to Photo or media with thumb...`") + nn = uf(dl) + os.remove(dl) + udB.set_key("CUSTOM_THUMBNAIL", str(nn)) + await bash(f"wget {nn} -O resources/extras/ultroid.jpg") + await e.eor(get_string("cvt_6").format(nn), link_preview=False) + + +@ultroid_cmd( + pattern="rename( (.*)|$)", +) +async def imak(event): + reply = await event.get_reply_message() + t = time.time() + if not reply: + return await event.eor(get_string("cvt_1")) + inp = event.pattern_match.group(1).strip() + if not inp: + return await event.eor(get_string("cvt_2")) + xx = await event.eor(get_string("com_1")) + if reply.media: + if hasattr(reply.media, "document"): + file = reply.media.document + image = await downloader( + reply.file.name or str(time.time()), + reply.media.document, + xx, + t, + get_string("com_5"), + ) + + file = image.name + else: + file = await event.client.download_media(reply.media) + if os.path.exists(inp): + os.remove(inp) + await bash(f'mv """{file}""" """{inp}"""') + if not os.path.exists(inp) or os.path.exists(inp) and not os.path.getsize(inp): + os.rename(file, inp) + k = time.time() + n_file, _ = await event.client.fast_uploader( + inp, show_progress=True, event=event, message="Uploading...", to_delete=True + ) + await event.reply( + f"`{n_file.name}`", + file=n_file, + force_document=True, + thumb=ULTConfig.thumb, + ) + os.remove(inp) + await xx.delete() + + +conv_keys = { + "img": "png", + "sticker": "webp", + "webp": "webp", + "image": "png", + "webm": "webm", + "gif": "gif", + "json": "json", + "tgs": "tgs", +} + + +@ultroid_cmd( + pattern="convert( (.*)|$)", +) +async def uconverter(event): + xx = await event.eor(get_string("com_1")) + a = await event.get_reply_message() + if a is None: + return await event.eor("`Reply to Photo or media with thumb...`") + input_ = event.pattern_match.group(1).strip() + b = await a.download_media("resources/downloads/") + if not b and (a.document and a.document.thumbs): + b = await a.download_media(thumb=-1) + if not b: + return await xx.edit(get_string("cvt_3")) + try: + convert = conv_keys[input_] + except KeyError: + return await xx.edit(get_string("sts_3").format("gif/img/sticker/webm")) + file = await con.convert(b, outname="ultroid", convert_to=convert) + print(file) + + if file: + await event.client.send_file( + event.chat_id, file, reply_to=event.reply_to_msg_id or event.id + ) + os.remove(file) + else: + await xx.edit("`Failed to convert`") + return + await xx.delete() + +@ultroid_cmd( + pattern="doc( (.*)|$)", +) +async def _(event): + input_str = event.pattern_match.group(1).strip() + if not (input_str and event.is_reply): + return await event.eor(get_string("cvt_1"), time=5) + xx = await event.eor(get_string("com_1")) + a = await event.get_reply_message() + if not a.message: + return await xx.edit(get_string("ex_1")) + with open(input_str, "w") as b: + b.write(str(a.message)) + await xx.edit(f"**Packing into** `{input_str}`") + await event.reply(file=input_str, thumb=ULTConfig.thumb) + await xx.delete() + os.remove(input_str) + + +@ultroid_cmd( + pattern="open( (.*)|$)", +) +async def _(event): + a = await event.get_reply_message() + b = event.pattern_match.group(1).strip() + if not ((a and a.media) or (b and os.path.exists(b))): + return await event.eor(get_string("cvt_7"), time=5) + xx = await event.eor(get_string("com_1")) + rem = None + if not b: + b = await a.download_media() + rem = True + try: + with open(b) as c: + d = c.read() + except UnicodeDecodeError: + return await xx.eor(get_string("cvt_8"), time=5) + try: + await xx.edit(f"```{d}```") + except BaseException: + what, data = await get_paste(d) + await xx.edit( + f"**MESSAGE EXCEEDS TELEGRAM LIMITS**\n\nSo Pasted It On [SPACEBIN]({data['link']})" + ) + if rem: + os.remove(b) diff --git a/addons/covid.py b/addons/covid.py new file mode 100644 index 0000000..a7bffe1 --- /dev/null +++ b/addons/covid.py @@ -0,0 +1,39 @@ +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +""" +โœ˜ Commands Available + +โ€ข `{i}covid country name` + Gets the Covid-19 Status of a given Country. +""" + +from covid import Covid + +from . import ultroid_cmd + + +@ultroid_cmd(pattern="covid") +async def coronish(event): + covid = Covid() + okie = event.text.split(maxsplit=1) + try: + country = okie[1] + except IndexError: + await event.eor("Give a country name to Search for it's Covid Cases!") + return + try: + cases = covid.get_status_by_country_name((country).lower()) + act = cases["active"] + conf = cases["confirmed"] + dec = cases["deaths"] + rec = cases["recovered"] + await event.eor( + f"**Country:** **{country.capitalize()}**\n**Active:** {act}\n**Confirmed:** {conf}\n**Recovered:** {rec}\n**Deceased:** {dec}", + ) + except ValueError: + await event.eor(f"It seems that Country {country} is invalid!") diff --git a/addons/echo.py b/addons/echo.py new file mode 100644 index 0000000..d76a425 --- /dev/null +++ b/addons/echo.py @@ -0,0 +1,145 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_echo") + +from telethon.utils import get_display_name + +from . import events, udB, ultroid_bot, ultroid_cmd, LOGS, types + +# Functions moved from echo_db.py +def get_stuff(): + return udB.get_key("ECHO") or {} + + +def add_echo(chat, user): + x = get_stuff() + if k := x.get(int(chat)): + if user not in k: + k.append(int(user)) + x.update({int(chat): k}) + else: + x.update({int(chat): [int(user)]}) + return udB.set_key("ECHO", x) + + +def rem_echo(chat, user): + x = get_stuff() + if k := x.get(int(chat)): + if user in k: + k.remove(int(user)) + x.update({int(chat): k}) + return udB.set_key("ECHO", x) + + +def check_echo(chat, user): + x = get_stuff() + if (k := x.get(int(chat))) and int(user) in k: + return True + + +def list_echo(chat): + x = get_stuff() + return x.get(int(chat)) + + +@ultroid_cmd(pattern="addecho( (.*)|$)") +async def echo(e): + r = await e.get_reply_message() + if r: + user = r.sender_id + else: + try: + user = e.text.split()[1] + if user.startswith("@"): + ok = await e.client.get_entity(user) + user = ok.id + else: + user = int(user) + except BaseException: + return await e.eor("Reply To A user.", time=5) + if check_echo(e.chat_id, user): + return await e.eor("Echo already activated for this user.", time=5) + add_echo(e.chat_id, user) + ok = await e.client.get_entity(user) + user = f"{ok.first_name} {ok.last_name}" if ok.last_name else ok.first_name + await e.eor(f"Activated Echo For {user}.") + + +@ultroid_cmd(pattern="remecho( (.*)|$)") +async def rm(e): + r = await e.get_reply_message() + if r: + user = r.sender_id + else: + try: + user = e.text.split()[1] + if user.startswith("@"): + ok = await e.client.get_entity(user) + user = ok.id + else: + user = int(user) + except BaseException: + return await e.eor("Reply To A User.", time=5) + if check_echo(e.chat_id, user): + rem_echo(e.chat_id, user) + ok = await e.client.get_entity(user) + user = f"{ok.first_name} {ok.last_name}" if ok.last_name else ok.first_name + return await e.eor(f"Deactivated Echo For {user}.") + await e.eor("Echo not activated for this user") + + +@ultroid_cmd(pattern="listecho$") +async def lstecho(e): + k = list_echo(e.chat_id) + if k: + user = "**Activated Echo For Users:**\n\n" + for x in k: + try: + ok = await e.client.get_entity(x) + kk = f"{ok.first_name} {ok.last_name}" if ok.last_name else ok.first_name + user += f"โ€ข{kk}\n" + except BaseException: + user += f"โ€ข[{x}](tg://user?id={x})\n" + await e.eor(user) + else: + await e.eor("`No echo activated here!`", time=5) + + +@ultroid_bot.on(events.NewMessage(incoming=True)) +async def samereply(e): + if check_echo(e.chat_id, e.sender_id): + if e.text: + text = e.text + if e.reply_to: + if e.reply_to.reply_to_top_id: + reply_msg = await e.get_reply_message() + if reply_msg.text: + text = reply_msg.text + try: + await e.client.send_message(e.chat_id, text, reply_to=e.id) + except Exception as er: + print(er) + if e.media: + await e.client.send_file(e.chat_id, e.media, reply_to=e.id) + + +# Add a separate handler for echo replies +async def echo_handler(e): + sender = await e.get_sender() + if not isinstance(sender, types.User) or sender.bot: + return + if check_echo(e.chat_id, e.sender_id): + try: + await e.respond(e.message) + except Exception as er: + LOGS.exception(er) + +# Register the handler +ultroid_bot.add_handler(echo_handler, events.NewMessage(incoming=True)) diff --git a/addons/encodedecode.py b/addons/encodedecode.py new file mode 100644 index 0000000..aa95fdf --- /dev/null +++ b/addons/encodedecode.py @@ -0,0 +1,53 @@ +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +""" +โœ˜ Commands Available - + +โ€ข {i}encode + encode the text + + โ€ข {i}decode + decode the text +""" + +import base64 + +from . import ultroid_cmd + + +@ultroid_cmd(pattern="encode ?(.*)") +async def encod(e): + match = e.pattern_match.group(1) + if not match and e.is_reply: + gt = await e.get_reply_message() + if gt.text: + match = gt.text + if not (match or e.is_reply): + return await e.eor("`Give me Something to Encode..`") + byt = match.encode("ascii") + et = base64.b64encode(byt) + atc = et.decode("ascii") + await e.eor(f"**=>> Encoded Text :** `{match}`\n\n**=>> OUTPUT :**\n`{atc}`") + + +@ultroid_cmd(pattern="decode ?(.*)") +async def encod(e): + match = e.pattern_match.group(1) + if not match and e.is_reply: + gt = await e.get_reply_message() + if gt.text: + match = gt.text + if not (match or e.is_reply): + return await e.eor("`Give me Something to Decode..`") + byt = match.encode("ascii") + try: + et = base64.b64decode(byt) + atc = et.decode("ascii") + await e.eor(f"**=>> Decoded Text :** `{match}`\n\n**=>> OUTPUT :**\n`{atc}`") + except Exception as p: + await e.eor("**ERROR :** " + str(p)) diff --git a/addons/extra.py b/addons/extra.py new file mode 100644 index 0000000..7fa9ac8 --- /dev/null +++ b/addons/extra.py @@ -0,0 +1,85 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("extra") + +import asyncio + +from . import get_string, ultroid_cmd + + +@ultroid_cmd( + pattern="del$", + manager=True, +) +async def delete_it(delme): + msg_src = await delme.get_reply_message() + if not msg_src: + return + await msg_src.try_delete() + await delme.try_delete() + + +@ultroid_cmd( + pattern="copy$", +) +async def copy(e): + reply = await e.get_reply_message() + if reply: + await reply.reply(reply) + return await e.try_delete() + await e.eor(get_string("ex_1"), time=5) + + +@ultroid_cmd( + pattern="edit", +) +async def editer(edit): + message = edit.text + chat = await edit.get_input_chat() + string = str(message[6:]) + reply = await edit.get_reply_message() + if reply and reply.text: + try: + await reply.edit(string) + await edit.delete() + except BaseException: + pass + else: + i = 1 + async for message in edit.client.iter_messages(chat, from_user="me", limit=2): + if i == 2: + await message.edit(string) + await edit.delete() + break + i += 1 + + +@ultroid_cmd( + pattern="reply$", +) +async def _(e): + if e.reply_to_msg_id: + chat = e.chat_id + try: + msg = (await e.client.get_messages(e.chat_id, limit=1, max_id=e.id))[0] + except IndexError: + return await e.eor( + "`You have previously sent no message to reply again...`", time=5 + ) + except BaseException as er: + return await e.eor(f"**ERROR:** `{er}`") + await asyncio.wait( + [ + e.client.delete_messages(chat, [e.id, msg.id]), + e.client.send_message(chat, msg, reply_to=e.reply_to_msg_id), + ] + ) + else: + await e.try_delete() diff --git a/addons/fakeaction.py b/addons/fakeaction.py new file mode 100644 index 0000000..7e88941 --- /dev/null +++ b/addons/fakeaction.py @@ -0,0 +1,36 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_fakeaction") + +import math +import time + +from pyUltroid.fns.admins import ban_time + +from . import asyncio, get_string, ultroid_cmd + + +@ultroid_cmd( + pattern="f(typing|audio|contact|document|game|location|sticker|photo|round|video)( (.*)|$)" +) +async def _(e): + act = e.pattern_match.group(1).strip() + t = e.pattern_match.group(2) + if act in ["audio", "round", "video"]: + act = f"record-{act}" + if t.isdigit(): + t = int(t) + elif t.endswith(("s", "h", "d", "m")): + t = math.ceil((ban_time(t)) - time.time()) + else: + t = 60 + await e.eor(get_string("fka_1").format(str(t)), time=5) + async with e.client.action(e.chat_id, act): + await asyncio.sleep(t) diff --git a/addons/fastly.py b/addons/fastly.py new file mode 100644 index 0000000..cb118de --- /dev/null +++ b/addons/fastly.py @@ -0,0 +1,84 @@ +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +""" +Fasly Bot Cheat. + +โ€ข `{i}fastly` - On/Off command. + +โ€ข Also Required : `OCR_API`. Add it using the command `.setdb OCR_API api_key` +โ€ข To get the API visit 'https://ocr.space/ocrapi' +The bot will try to auto reply first to the messages by @FastlyWriteBot + +โ€ข Add User id of fastly clone to `FASTLY_CLONES` to allow this plugin work with them. +""" + +from telegraph import upload_file +from telethon import events +from . import udB, LOGS, ultroid_bot, ultroid_cmd, async_searcher +from os import remove + +base_url = "https://api.ocr.space/parse/imageurl?apikey={api}&url={tgraph}" + +BotList = [1806208310] + +if udB.get_key("FASTLY_CLONES"): + for i in udB.get_key("FASTLY_CLONES").split(): + try: + BotList.append(int(i)) + except TypeError: + LOGS.exception(f"Invalid Value in 'FASTLY_CLONES': {i}") + + +async def fastly_bot(event): + if not udB.get_key("FASTLY"): + return + api = udB.get_key("OCR_API") + if not (api and event.photo): + return + med = await event.download_media() + upload = upload_file(med) + link = "https://telegra.ph" + upload[0] + out = await async_searcher(base_url.format(api=api, tgraph=link), re_json=True) + try: + txt = out["ParsedResults"][0]["ParsedText"] + except (KeyError, IndexError): + return + txt = txt.split("By@")[0].replace("\n", "").replace("\r", "") + if txt: + try: + await event.reply(txt) + except Exception as er: + LOGS.exception(er) + try: + remove(med) + except Exception as e: + LOGS.exception(e) + + +@ultroid_cmd(pattern="fastly$") +async def fastOnOff(event): + xx = await event.eor("`...`") + get_ = udB.get_key("FASTLY") + if not get_: + if not udB.get_key("OCR_API"): + return await xx.edit("`OCR_API` is missing.\nAdd it before using this..") + udB.set_key("FASTLY", True) + ultroid_bot.add_handler( + fastly_bot, + events.NewMessage(incoming=True, from_users=BotList), + ) + return await xx.edit("`Auto Fastly Response Activated`") + udB.del_key("FASTLY") + await xx.edit("`Fastly Stopped!`") + + +if udB.get_key("FASTLY"): + ultroid_bot.add_handler( + fastly_bot, + events.NewMessage(incoming=True, from_users=BotList), + ) diff --git a/addons/figlet.py b/addons/figlet.py new file mode 100644 index 0000000..6291359 --- /dev/null +++ b/addons/figlet.py @@ -0,0 +1,506 @@ +""" +โœ˜ Commands Available + +โ€ข `{i}figlet ` + Make a text a figlet. +""" + +import pyfiglet + +from . import ultroid_cmd, split_list + +CMD_SET = { + "slant": "slant", + "3D": "3-d", + "5line": "5lineoblique", + "alpha": "alphabet", + "banner": "banner3-D", + "doh": "doh", + "iso": "isometric1", + "letters": "letters", + "allig": "alligator", + "dotm": "dotmatrix", + "bubble": "bubble", + "bulb": "bulbhead", + "digi": "digital", + "3x5": "3x5", + "1943": "1943____", + "4x4": "4x4_offr", + "5x7": "5x7", + "5x8": "5x8", + "64f1": "64f1____", + "6x10": "6x10", + "6x9": "6x9", + "zooloo": "a_zooloo", + "acro": "acrobatic", + "aveng": "advenger", + "allig2": "alligator2", + "aqua": "aquaplan", + "arrows": "arrows", + "asc": "asc_____", + "ascii12": "ascii12", + "ascii9": "ascii9", + "ascii": "ascii___", + "assalt": "assalt_m", + "asslt": "asslt__m", + "atc": "atc_____", + "atcg": "atc_gran", + "avatar": "avatar", + "bm200": "b_m__200", + "banner3": "banner3", + "banner4": "banner4", + "barb": "barbwire", + "basic": "basic", + "battles": "battle_s", + "battlesh": "battlesh", + "baz": "baz__bil", + "beer": "beer_pub", + "bell": "bell", + "big": "big", + "bigascii12": "bigascii12", + "bigascii9": "bigascii9", + "bigchief": "bigchief", + "bigmono12": "bigmono12", + "bigmono9": "bigmono9", + "binary": "binary", + "block": "block", + "brite": "brite", + "briteb": "briteb", + "britebi": "britebi", + "britei": "britei", + "broadway": "broadway", + "bubbles": "bubble__", + "buble": "bubble_b", + "bhead": "bulbhead", + "c1": "c1______", + "c2": "c2______", + "cascii": "c_ascii_", + "cconsen": "c_consen", + "calgphy2": "calgphy2", + "caligraphy": "caligraphy", + "catwalk": "catwalk", + "causin": "caus_in_", + "char1": "char1___", + "char2": "char2___", + "char3": "char3___", + "char4": "char4___", + "charact1": "charact1", + "charact2": "charact2", + "charact3": "charact3", + "charact4": "charact4", + "charact5": "charact5", + "charact6": "charact6", + "characte": "characte", + "charset": "charset_", + "chartr": "chartr", + "chartri": "chartri", + "chunky": "chunky", + "circle": "circle", + "clb6x10": "clb6x10", + "clb8x10": "clb8x10", + "clb8x8": "clb8x8", + "clr4x6": "clr4x6", + "clr5x10": "clr5x10", + "clr5x6": "clr5x6", + "clr5x8": "clr5x8", + "clr6x10": "clr6x10", + "clr6x6": "clr6x6", + "clr6x8": "clr6x8", + "clr7x10": "clr7x10", + "clr7x8": "clr7x8", + "clr8x10": "clr8x10", + "clr8x8": "clr8x8", + "coilcop": "coil_cop", + "coinstak": "coinstak", + "colossal": "colossal", + "comsen": "com_sen_", + "computer": "computer", + "contessa": "contessa", + "contrast": "contrast", + "convoy": "convoy__", + "cosmic": "cosmic", + "cosmike": "cosmike", + "cour": "cour", + "courb": "courb", + "courbi": "courbi", + "couri": "couri", + "crawford": "crawford", + "cricket": "cricket", + "cursive": "cursive", + "cyberlarge": "cyberlarge", + "cybermedium": "cybermedium", + "cybersmall": "cybersmall", + "ddragon": "d_dragon", + "dcsbfmo": "dcs_bfmo", + "decimal": "decimal", + "deepstr": "deep_str", + "defleppard": "defleppard", + "demo1": "demo_1__", + "demo2": "demo_2__", + "demom": "demo_m__", + "devilish": "devilish", + "diamond": "diamond", + "doom": "doom", + "double": "double", + "drpepper": "drpepper", + "druid": "druid___", + "efist": "e__fist_", + "ebbs1": "ebbs_1__", + "ebbs2": "ebbs_2__", + "eca": "eca_____", + "eftichess": "eftichess", + "eftifont": "eftifont", + "eftipiti": "eftipiti", + "eftirobot": "eftirobot", + "eftitalic": "eftitalic", + "eftiwall": "eftiwall", + "eftiwater": "eftiwater", + "emboss": "emboss", + "emboss2": "emboss2", + "epic": "epic", + "etcrvs": "etcrvs__", + "f15": "f15_____", + "facesof": "faces_of", + "fairmea": "fair_mea", + "fairligh": "fairligh", + "fantasy": "fantasy_", + "fbr12": "fbr12___", + "fbr1": "fbr1____", + "fbr2": "fbr2____", + "fbrstri": "fbr_stri", + "fbrtilt": "fbr_tilt", + "fender": "fender", + "finalass": "finalass", + "fireing": "fireing_", + "flynsh": "flyn_sh", + "fourtops": "fourtops", + "fp1": "fp1_____", + "fp2": "fp2_____", + "fraktur": "fraktur", + "funkydr": "funky_dr", + "future": "future", + "future1": "future_1", + "future2": "future_2", + "future3": "future_3", + "future4": "future_4", + "future5": "future_5", + "future6": "future_6", + "future7": "future_7", + "future8": "future_8", + "fuzzy": "fuzzy", + "gauntlet": "gauntlet", + "ghostbo": "ghost_bo", + "goofy": "goofy", + "gothic": "gothic", + "gothics": "gothic__", + "graceful": "graceful", + "gradient": "gradient", + "graffiti": "graffiti", + "grandpr": "grand_pr", + "greek": "greek", + "greenbe": "green_be", + "hades": "hades___", + "heavyme": "heavy_me", + "helv": "helv", + "helvb": "helvb", + "helvbi": "helvbi", + "helvi": "helvi", + "heroboti": "heroboti", + "hex": "hex", + "highnoo": "high_noo", + "hills": "hills___", + "holly": "hollywood", + "homepak": "home_pak", + "houseof": "house_of", + "hypabal": "hypa_bal", + "hyper": "hyper___", + "incraw": "inc_raw_", + "invita": "invita", + "iso2": "isometric2", + "iso3": "isometric3", + "iso4": "isometric4", + "italic": "italic", + "italics": "italics_", + "ivrit": "ivrit", + "jazmine": "jazmine", + "jerusalem": "jerusalem", + "joust": "joust___", + "ktk": "katakana", + "kban": "kban", + "kgamesi": "kgames_i", + "kikstar": "kik_star", + "krakout": "krak_out", + "larry3d": "larry3d", + "lazyjon": "lazy_jon", + "lcd": "lcd", + "lean": "lean", + "letter": "letter", + "letterr": "letter_", + "letterw3": "letterw3", + "lexible": "lexible_", + "linux": "linux", + "lockergnome": "lockergnome", + "lower": "lower", + "madnurs": "mad_nurs", + "madrid": "madrid", + "magicma": "magic_ma", + "marquee": "marquee", + "mastero": "master_o", + "maxfour": "maxfour", + "mayhemd": "mayhem_d", + "mcg": "mcg_____", + "migally": "mig_ally", + "mike": "mike", + "mini": "mini", + "mirror": "mirror", + "mnemonic": "mnemonic", + "modern": "modern__", + "mono12": "mono12", + "mono9": "mono9", + "morse": "morse", + "moscow": "moscow", + "mshebrew210": "mshebrew210", + "nancyjf": "nancyj-fancy", + "nancyju": "nancyj-underlined", + "nancyj": "nancyj", + "newasci": "new_asci", + "nfi1": "nfi1____", + "nipl": "nipples", + "notieca": "notie_ca", + "npn": "npn_____", + "ntgreek": "ntgreek", + "null": "null", + "nvscript": "nvscript", + "o8": "o8", + "octal": "octal", + "odellak": "odel_lak", + "ogre": "ogre", + "okbeer": "ok_beer_", + "os2": "os2", + "outrun": "outrun__", + "pshm": "p_s_h_m_", + "pskateb": "p_skateb", + "pacospe": "pacos_pe", + "pagga": "pagga", + "panther": "panther_", + "pawnins": "pawn_ins", + "pawp": "pawp", + "peaks": "peaks", + "pebbles": "pebbles", + "pepper": "pepper", + "phonix": "phonix__", + "platoon2": "platoon2", + "platoon": "platoon_", + "pod": "pod_____", + "poison": "poison", + "puffy": "puffy", + "pyramid": "pyramid", + "r2d2": "r2-d2___", + "rad": "rad_____", + "radphan": "rad_phan", + "radical": "radical_", + "rainbow": "rainbow_", + "rallys2": "rally_s2", + "rallysp": "rally_sp", + "rampage": "rampage_", + "rastan": "rastan__", + "rawrecu": "raw_recu", + "rci": "rci_____", + "rectangles": "rectangles", + "relief": "relief", + "relief2": "relief2", + "rev": "rev", + "ripper": "ripper!_", + "roadrai": "road_rai", + "rockbox": "rockbox_", + "rok": "rok_____", + "roman": "roman", + "romans": "roman___", + "rot13": "rot13", + "rounded": "rounded", + "rowancap": "rowancap", + "rozzo": "rozzo", + "runic": "runic", + "runyc": "runyc", + "sans": "sans", + "sansb": "sansb", + "sansbi": "sansbi", + "sansi": "sansi", + "sblood": "sblood", + "sbook": "sbook", + "sbookb": "sbookb", + "sbookbi": "sbookbi", + "sbooki": "sbooki", + "script": "script", + "scripts": "script__", + "serifcap": "serifcap", + "shadow": "shadow", + "shimrod": "shimrod", + "short": "short", + "skatero": "skate_ro", + "skateord": "skateord", + "skateroc": "skateroc", + "sketchs": "sketch_s", + "slide": "slide", + "slscript": "slscript", + "sm": "sm______", + "small": "small", + "smascii12": "smascii12", + "smascii9": "smascii9", + "smblock": "smblock", + "smbraille": "smbraille", + "smisome1": "smisome1", + "smkeyboard": "smkeyboard", + "smmono12": "smmono12", + "smmono9": "smmono9", + "smscript": "smscript", + "smshadow": "smshadow", + "smslant": "smslant", + "smtengwar": "smtengwar", + "spaceop": "space_op", + "spcdemo": "spc_demo", + "speed": "speed", + "stacey": "stacey", + "stampatello": "stampatello", + "standard": "standard", + "starwar": "star_war", + "starwars": "starwars", + "stealth": "stealth_", + "stellar": "stellar", + "stencil1": "stencil1", + "stencil2": "stencil2", + "stop": "stop", + "straight": "straight", + "street_s": "street_s", + "subteran": "subteran", + "superte": "super_te", + "tofap": "t__of_ap", + "tanja": "tanja", + "tav1": "tav1____", + "taxi": "taxi____", + "tec1": "tec1____", + "tec7000": "tec_7000", + "tecrvs": "tecrvs__", + "tengwar": "tengwar", + "term": "term", + "thick": "thick", + "thin": "thin", + "threepoint": "threepoint", + "tipan": "ti_pan__", + "ticks": "ticks", + "ticksslant": "ticksslant", + "tiles": "tiles", + "times": "times", + "timesofl": "timesofl", + "tinkertoy": "tinker-toy", + "tomahawk": "tomahawk", + "tombstone": "tombstone", + "top_duck": "top_duck", + "trashman": "trashman", + "trek": "trek", + "triadst": "triad_st", + "ts1": "ts1_____", + "tsalagi": "tsalagi", + "tsm": "tsm_____", + "tsnbase": "tsn_base", + "tty": "tty", + "ttyb": "ttyb", + "tubular": "tubular", + "twincob": "twin_cob", + "twopoint": "twopoint", + "typeset": "type_set", + "ucffan": "ucf_fan_", + "ugalympi": "ugalympi", + "unarmed": "unarmed_", + "univers": "univers", + "upper": "upper", + "usa": "usa_____", + "usapq": "usa_pq__", + "usaflag": "usaflag", + "utopia": "utopia", + "utopiab": "utopiab", + "utopiabi": "utopiabi", + "utopiai": "utopiai", + "vortron": "vortron_", + "warofw": "war_of_w", + "wavy": "wavy", + "weird": "weird", + "whimsy": "whimsy", + "wideterm": "wideterm", + "xbrite": "xbrite", + "xbriteb": "xbriteb", + "xbritebi": "xbritebi", + "xbritei": "xbritei", + "xchartr": "xchartr", + "xchartri": "xchartri", + "xcour": "xcour", + "xcourb": "xcourb", + "xcourbi": "xcourbi", + "xcouri": "xcouri", + "xhelv": "xhelv", + "xhelvb": "xhelvb", + "xhelvbi": "xhelvbi", + "xhelvi": "xhelvi", + "xsans": "xsans", + "xsansb": "xsansb", + "xsansbi": "xsansbi", + "xsansi": "xsansi", + "xsbook": "xsbook", + "xsbookb": "xsbookb", + "xsbookbi": "xsbookbi", + "xsbooki": "xsbooki", + "xtimes": "xtimes", + "xtty": "xtty", + "xttyb": "xttyb", + "yiear": "yie-ar__", + "yieark": "yie_ar_k", + "zpilot": "z-pilot_", + "zigzag": "zig_zag_", + "zone7": "zone7___", +} + +DataList = sorted(list(CMD_SET.keys())) +Split = split_list(DataList, 42) +offset = 0 + + +@ultroid_cmd(pattern="figlet( ?(.*)|$)") +async def figlet(event): + input_str = event.pattern_match.group(1).strip() + if not input_str: + return await event.eor("`Provide some text to make figlet...`") + if input_str == "list": + global offset + if offset == len(Split): + offset = 0 + All = Split[offset] + Text = "**List of Figlet Fonts :**\n\n" + while All: + c = 3 + Nline = "โ€ขโ€ข " + " ".join([f"`{a}`" for a in All[:3]]) + while (c < len(All) - 1) and len(Nline) < 32: + c += 1 + Nline += f" `{All[c]}`" + Text += Nline + "\n" + All = All[c:] + await event.eor(Text) + offset += 1 + return + if "|" in input_str: + text, cmd = input_str.split("|", maxsplit=1) + elif input_str is not None: + cmd = None + text = input_str + else: + await event.eor("Please add some text to figlet") + return + if cmd is not None: + try: + font = CMD_SET[cmd] + except KeyError: + await event.eor("Invalid selected font.") + return + result = pyfiglet.figlet_format(text, font=font) + else: + result = pyfiglet.figlet_format(text) + await event.eor(f"โ€Œโ€Œโ€Ž`{result}`") diff --git a/addons/filter.py b/addons/filter.py new file mode 100644 index 0000000..c25c41c --- /dev/null +++ b/addons/filter.py @@ -0,0 +1,140 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_filter") + +import os +import re + +from telethon.tl.types import User +from telethon.utils import get_display_name + +from pyUltroid.fns.tools import create_tl_btn, format_btn, get_msg_button + +from . import events, get_string, mediainfo, udB, ultroid_bot, ultroid_cmd, upload_file +from ._inline import something + +# Functions moved from filter_db.py +def get_stuff(): + return udB.get_key("FILTERS") or {} + + +def add_filter(chat, word, msg, media, button): + ok = get_stuff() + if ok.get(chat): + ok[chat].update({word: {"msg": msg, "media": media, "button": button}}) + else: + ok.update({chat: {word: {"msg": msg, "media": media, "button": button}}}) + udB.set_key("FILTERS", ok) + + +def rem_filter(chat, word): + ok = get_stuff() + if ok.get(chat) and ok[chat].get(word): + ok[chat].pop(word) + udB.set_key("FILTERS", ok) + + +def rem_all_filter(chat): + ok = get_stuff() + if ok.get(chat): + ok.pop(chat) + udB.set_key("FILTERS", ok) + + +def get_filter(chat): + ok = get_stuff() + if ok.get(chat): + return ok[chat] + + +def list_filter(chat): + ok = get_stuff() + if ok.get(chat): + return "".join(f"๐Ÿ‘‰ `{z}`\n" for z in ok[chat]) + + +@ultroid_cmd(pattern="addfilter( (.*)|$)") +async def af(e): + wrd = (e.pattern_match.group(1).strip()).lower() + wt = await e.get_reply_message() + chat = e.chat_id + if not (wt and wrd): + return await e.eor(get_string("flr_1")) + btn = format_btn(wt.buttons) if wt.buttons else None + if wt and wt.media: + wut = mediainfo(wt.media) + if wut.startswith(("pic", "gif")): + dl = await wt.download_media() + variable = upload_file(dl) + os.remove(dl) + elif wut == "video": + if wt.media.document.size > 8 * 1000 * 1000: + return await e.eor(get_string("com_4")) + dl = await wt.download_media() + variable = upload_file(dl) + os.remove(dl) + else: + variable = None + else: + variable = None + if not wt.text and not variable: + return await e.eor(get_string("flr_2")) + txt = wt.text or None + if txt: + if button := get_msg_button(txt): + txt = button[0] + button = button[1] + elif btn: + button = btn + else: + button = None + add_filter(chat, wrd, txt, variable, button) + await e.eor(get_string("flr_3").format(wrd)) + ultroid_bot.add_handler(filter_func, events.NewMessage()) + + +@ultroid_cmd(pattern="remfilter( (.*)|$)") +async def rf(e): + wrd = (e.pattern_match.group(1).strip()).lower() + chat = e.chat_id + if not wrd: + return await e.eor(get_string("flr_4")) + rem_filter(chat, wrd) + await e.eor(get_string("flr_5").format(wrd)) + + +@ultroid_cmd(pattern="listfilter$") +async def lsnote(e): + if x := list_filter(e.chat_id): + await e.eor(get_string("flr_6").format(x)) + else: + await e.eor(get_string("flr_7")) + + +async def filter_func(e): + if isinstance(e.sender, User) and e.sender.bot: + return + xx = (e.text).lower() + chat = e.chat_id + if x := get_filter(chat): + for c in x: + pat = r"( |^|[^\w])" + re.escape(c) + r"( |$|[^\w])" + if re.search(pat, xx): + if k := x.get(c): + msg = k["msg"] + media = k["media"] + if k.get("button"): + btn = create_tl_btn(k["button"]) + return await something(e, msg, media, btn) + await e.reply(msg, file=media) + + +if get_stuff(): + ultroid_bot.add_handler(filter_func, events.NewMessage()) diff --git a/addons/findsong.py b/addons/findsong.py new file mode 100644 index 0000000..355eb70 --- /dev/null +++ b/addons/findsong.py @@ -0,0 +1,41 @@ +# Ultroid Userbot +# Made by senku + +""" +โœ˜ Commands Available + +โ€ข `{i}findsong ` + Identify the song name +""" + +from telethon.errors.rpcerrorlist import YouBlockedUserError + +from . import * + + +@ultroid_cmd(pattern="findsong$") +async def _(event): + if not event.reply_to_msg_id: + return await event.eor("Reply to an audio message.") + reply_message = await event.get_reply_message() + chat = "@auddbot" + snku = await event.eor("Identifying the song") + async with event.client.conversation(chat) as conv: + try: + await conv.send_message("/start") + await conv.get_response() + await conv.send_message(reply_message) + check = await conv.get_response() + if not check.text.startswith("Audio received"): + return await snku.edit( + "An error while identifying the song. Try to use a 5-10s long audio message." + ) + await snku.edit("Wait just a sec...") + result = await conv.get_response() + await event.client.send_read_acknowledge(conv.chat_id) + except YouBlockedUserError: + await snku.edit("Please unblock (@auddbot) and try again") + return + namem = f"**Song Name : **{result.text.splitlines()[0]}\ + \n\n**Details : **__{result.text.splitlines()[2]}__" + await snku.edit(namem) diff --git a/addons/flaticon.py b/addons/flaticon.py new file mode 100644 index 0000000..0abff1a --- /dev/null +++ b/addons/flaticon.py @@ -0,0 +1,45 @@ +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + + +""" +โœ˜ Commands Available + +โ€ข `{i}icon ` + Icon search from flaticon.com and uploading as sticker. +""" + +import os +import random + +from bs4 import BeautifulSoup as bs + +from . import LOGS, ultroid_cmd, download_file, async_searcher, get_string + + +@ultroid_cmd(pattern="icon ?(.*)") +async def www(e): + a = e.pattern_match.group(1) + if not a: + return await e.eor("Give some Text to Get Icon from Flaticon.com") + tt = await e.eor(get_string("com_1")) + query = a.replace(" ", "%20") + try: + link = f"https://www.flaticon.com/search?word={query}" + ge = await async_searcher(link) + cl = bs(ge, "html.parser", from_encoding="utf-8") + results = cl.find_all( + "img", src="https://media.flaticon.com/dist/min/img/loader.gif" + ) + dome = results[random.randrange(0, len(results) - 1)]["data-src"] + await download_file(dome, "sticker.webp") + await e.reply(file="sticker.webp") + os.remove("sticker.webp") + await tt.delete() + except Exception as E: + LOGS.info(E) + await tt.edit("`No Results Found`") diff --git a/addons/fontgen.py b/addons/fontgen.py new file mode 100644 index 0000000..d686c75 --- /dev/null +++ b/addons/fontgen.py @@ -0,0 +1,60 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from . import get_help + +__doc__ = get_help("help_fontgen") + +import string + +from . import eod, ultroid_cmd + +_default = string.ascii_letters +Fonts = { + "small caps": "แด€ส™แด„แด…แด‡า“ษขสœษชแดŠแด‹สŸแดษดแดแด˜ฯ™ส€sแด›แดœแด แดกxสแดขABCDEFGHIJKLMNOPQRSTUVWXYZ", + "monospace": "๐šŠ๐š‹๐šŒ๐š๐šŽ๐š๐š๐š‘๐š’๐š“๐š”๐š•๐š–๐š—๐š˜๐š™๐šš๐š›๐šœ๐š๐šž๐šŸ๐š ๐šก๐šข๐šฃ๐™ฐ๐™ฑ๐™ฒ๐™ณ๐™ด๐™ต๐™ถ๐™ท๐™ธ๐™น๐™บ๐™ป๐™ผ๐™ฝ๐™พ๐™ฟ๐š€๐š๐š‚๐šƒ๐š„๐š…๐š†๐š‡๐šˆ๐š‰", + "double stroke": "๐•’๐•“๐•”๐••๐•–๐•—๐•˜๐•™๐•š๐•›๐•œ๐•๐•ž๐•Ÿ๐• ๐•ก๐•ข๐•ฃ๐•ค๐•ฅ๐•ฆ๐•ง๐•จ๐•ฉ๐•ช๐•ซ๐”ธ๐”นโ„‚๐”ป๐”ผ๐”ฝ๐”พโ„๐•€๐•๐•‚๐•ƒ๐•„โ„•๐•†โ„™โ„šโ„๐•Š๐•‹๐•Œ๐•๐•Ž๐•๐•โ„ค", + "script royal": "๐’ถ๐’ท๐’ธ๐’น๐‘’๐’ป๐‘”๐’ฝ๐’พ๐’ฟ๐“€๐“๐“‚๐“ƒ๐‘œ๐“…๐“†๐“‡๐“ˆ๐“‰๐“Š๐“‹๐“Œ๐“๐“Ž๐“๐’œโ„ฌ๐’ž๐’Ÿโ„ฐโ„ฑ๐’ขโ„‹โ„๐’ฅ๐’ฆโ„’โ„ณ๐’ฉ๐’ช๐’ซ๐’ฌโ„›๐’ฎ๐’ฏ๐’ฐ๐’ฑ๐’ฒ๐’ณ๐’ด๐’ต", +} + + +@ultroid_cmd( + pattern="font( (.*)|$)", +) +async def _(e): + input = e.pattern_match.group(1).strip() + reply = await e.get_reply_message() + if not input: + m = "**Available Fonts**\n\n" + for x in Fonts.keys(): + m += f"โ€ข `{x}`\n" + return await e.eor(m, time=5) + if not reply: + try: + _ = input.split(":", maxsplit=1) + font = _[0][:-1] + text = _[1] + except IndexError: + return await eod(e, help) + elif not input: + return await eod(e, "`Give font dude :/`") + else: + font = input + text = reply.message + if font not in Fonts.keys(): + return await e.eor(f"`{font} not in font list`.", time=5) + msg = gen_font(text, Fonts[font]) + await e.eor(msg) + + +def gen_font(text, new_font): + new_font = " ".join(new_font).split() + for q in text: + if q in _default: + new = new_font[_default.index(q)] + text = text.replace(q, new) + return text diff --git a/addons/fontsnew.py b/addons/fontsnew.py new file mode 100644 index 0000000..2cc2c54 --- /dev/null +++ b/addons/fontsnew.py @@ -0,0 +1,517 @@ +# +# Credits @chewmo +# +# Ported for Ultroid < https://github.com/TeamUltroid/Ultroid > +# + +""" +โœ˜ Commands Available - + +โ€ข `{i}weeb ` + turns text to ๅฑฑไน‡ไน‡ไนƒ font + +โ€ข `{i}tantext ` + turns text to แŽฟแŽฏแแŽฟแ‹๏พ’แŽฟ font + +โ€ข `{i}linetext ` + turns text to ๐•ƒ๐•€โ„•๐”ผ๐•‹๐”ผ๐•๐•‹ + +โ€ข `{i}boxtext ` + turns text to ๐Ÿ„ฑ๐Ÿ„พ๐Ÿ…‡๐Ÿ…ƒ๐Ÿ„ด๐Ÿ…‡๐Ÿ…ƒ + +โ€ข `{i}bubbletext ` + turns text to โ’ทโ“Šโ’ทโ’ทโ“โ’บโ“‰โ’บโ“โ“‰ + +โ€ข `{i}cursive ` + turns text to ๐“ฌ๐“พ๐“ป๐“ผ๐“ฒ๐“ฟ๐“ฎ font + +โ€ข `{i}greekify ` + turns text to ฯ‘ะณฮตฮตฮบฮนา“ฯˆ font + +โ€ข `{i}sorcify ` + turns text to ึ†ึ…ส€ฦˆษ›ส€ษ›ส€ font + +โ€ข `{i}fraktify ` + turns text to ๐–‹๐–—๐–†๐–๐–™๐–š๐–—๐–Š font + +โ€ข `{i}rusify ` + turns text to ัั†$ั—fั‡ font +""" + + +normiefont = [ + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", +] +weebyfont = [ + "ๅ‚", + "ไนƒ", + "ๅŒš", + "ๅˆ€", + "ไน‡", + "ไธ‹", + "ๅŽถ", + "ๅ„", + "ๅทฅ", + "ไธ", + "้•ฟ", + "ไนš", + "ไปŽ", + "๐ ˜จ", + "ๅฃ", + "ๅฐธ", + "ใ”ฟ", + "ๅฐบ", + "ไธ‚", + "ไธ…", + "ๅ‡ต", + "ใƒช", + "ๅฑฑ", + "ไน‚", + "ไธซ", + "ไน™", +] +tantextfont = [ + "แŽฏ", + "แฐ", + "แฃ", + "แŽด", + "แ‹", + "แŽด", + "แŽถ", + "แ‚", + "i", + "แ ", + "แฆ", + "l", + "m", + "แ", + "แซ", + "แŽต", + "แ„", + "แ–‡", + "แŽฆ", + "แŽฟ", + "แŒ", + "แ‰", + "แฏ", + "๏พ’", + "แŽฉ", + "แƒ", +] +linetextfont = [ + "๐”ธ", + "๐”น", + "โ„‚", + "๐”ป", + "๐”ผ", + "๐”ฝ", + "๐”พ", + "โ„", + "๐•€", + "๐•", + "๐•‚", + "๐•ƒ", + "๐•„", + "โ„•", + "๐•†", + "โ„™", + "โ„š", + "โ„", + "๐•Š", + "๐•‹", + "๐•Œ", + "๐•", + "๐•Ž", + "๐•", + "๐•", + "โ„ค", +] +boxtextfont = [ + "๐Ÿ„ฐ", + "๐Ÿ„ฑ", + "๐Ÿ„ฒ", + "๐Ÿ„ณ", + "๐Ÿ„ด", + "๐Ÿ„ต", + "๐Ÿ„ถ", + "๐Ÿ„ท", + "๐Ÿ„ธ", + "๐Ÿ„น", + "๐Ÿ„บ", + "๐Ÿ„ป", + "๐Ÿ„ผ", + "๐Ÿ„ฝ", + "๐Ÿ„พ", + "๐Ÿ„ฟ", + "๐Ÿ…€", + "๐Ÿ…", + "๐Ÿ…‚", + "๐Ÿ…ƒ", + "๐Ÿ…„", + "๐Ÿ……", + "๐Ÿ…†", + "๐Ÿ…‡", + "๐Ÿ…ˆ", + "๐Ÿ…‰", +] +bubbletextfont = [ + "โ’ถ", + "โ’ท", + "โ’ธ", + "โ’น", + "โ’บ", + "โ’ป", + "โ’ผ", + "โ’ฝ", + "โ’พ", + "โ’ฟ", + "โ“€", + "โ“", + "โ“‚", + "โ“ƒ", + "โ“„", + "โ“…", + "โ“†", + "โ“‡", + "โ“ˆ", + "โ“‰", + "โ“Š", + "โ“‹", + "โ“Œ", + "โ“", + "โ“Ž", + "โ“", +] +cursivefont = [ + "๐“ช", + "๐“ซ", + "๐“ฌ", + "๐“ญ", + "๐“ฎ", + "๐“ฏ", + "๐“ฐ", + "๐“ฑ", + "๐“ฒ", + "๐“ณ", + "๐“ด", + "๐“ต", + "๐“ถ", + "๐“ท", + "๐“ธ", + "๐“น", + "๐“บ", + "๐“ป", + "๐“ผ", + "๐“ฝ", + "๐“พ", + "๐“ฟ", + "๐”€", + "๐”", + "๐”‚", + "๐”ƒ", +] +greekfont = [ + "ฮป", + "ฯ", + "ฯ‚", + "d", + "ฮต", + "า“", + "ฯ‘", + "ะฝ", + "ฮน", + "ฯณ", + "ฮบ", + "l", + "ฯป", + "ฯ€", + "ฯƒ", + "ฯ", + "ฯ†", + "ะณ", + "s", + "ฯ„", + "ฯ…", + "v", + "ัˆ", + "ฯฐ", + "ฯˆ", + "z", +] +sorcererfont = [ + "วŸ", + "ษฎ", + "ฦˆ", + "ษ–", + "ษ›", + "ส„", + "ษข", + "ษฆ", + "ษจ", + "ส", + "ำ„", + "สŸ", + "ส", + "ีผ", + "ึ…", + "ึ„", + "ีฆ", + "ส€", + "ึ†", + "ศถ", + "สŠ", + "ส‹", + "ีก", + "ำผ", + "ส", + "ส", +] +frakturfont = [ + "๐–†", + "๐–‡", + "๐–ˆ", + "๐–‰", + "๐–Š", + "๐–‹", + "๐–Œ", + "๐–", + "๐–Ž", + "๐–", + "๐–", + "๐–‘", + "๐–’", + "๐–“", + "๐–”", + "๐–•", + "๐––", + "๐–—", + "๐–˜", + "๐–™", + "๐–š", + "๐–›", + "๐–œ", + "๐–", + "๐–ž", + "๐–Ÿ", +] +rusifont = [ + "ะฐ", + "ะฑ", + "c", + "ะด", + "ั‘", + "f", + "g", + "ะฝ", + "ั—", + "j", + "ะบ", + "ะณ", + "ัซ", + "ะฟ", + "ัณ", + "p", + "ั„", + "ั", + "$", + "ั‚", + "ั†", + "ัต", + "ั‰", + "ะถ", + "ั‡", + "ะท", +] + + +@ultroid_cmd(pattern="weeb ?(.*)") +async def weebify(ult): + args = ult.pattern_match.group(1) + if not args and ult.is_reply: + get = await ult.get_reply_message() + args = get.text + if not args: + await ult.edit("What I am Supposed to Weebify? Please Give Text Sir") + return + string = "".join(args).lower() + for normiecharacter in string: + if normiecharacter in normiefont: + weebycharacter = weebyfont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, weebycharacter) + await ult.eor(string) + + +@ultroid_cmd(pattern="tantext ?(.*)") +async def tantxt(ult): + args = ult.pattern_match.group(1) + if not args and ult.is_reply: + get = await ult.get_reply_message() + args = get.text + if not args: + await ult.edit("What I am Supposed to tanify? Please Give Text Sir") + return + string = "".join(args).lower() + for normiecharacter in string: + if normiecharacter in normiefont: + tanycharacter = tantextfont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, tanycharacter) + await ult.eor(string) + + +@ultroid_cmd(pattern="linetext ?(.*)") +async def linetxt(ult): + args = ult.pattern_match.group(1) + if not args and ult.is_reply: + get = await ult.get_reply_message() + args = get.text + if not args: + await ult.edit("What I am Supposed to linefy? Please Give Text Sir") + return + string = "".join(args).lower() + for normiecharacter in string: + if normiecharacter in normiefont: + linecharacter = linetextfont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, linecharacter) + await ult.edit(string) + + +@ultroid_cmd(pattern="boxtext ?(.*)") +async def boxtxt(ult): + args = ult.pattern_match.group(1) + if not args and ult.is_reply: + get = await ult.get_reply_message() + args = get.text + if not args: + return await ult.edit("What I am Supposed to boxify? Please Give Text Sir") + string = "".join(args).lower() + for normiecharacter in string: + if normiecharacter in normiefont: + boxcharacter = boxtextfont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, boxcharacter) + await ult.eor(string) + + +@ultroid_cmd(pattern="bubbletext ?(.*)") +async def bubbletxt(ult): + args = ult.pattern_match.group(1) + if not args and ult.is_reply: + get = await ult.get_reply_message() + args = get.text + if not args: + return await ult.edit("What I am Supposed to bubblify? Please Give Text Sir") + string = "".join(args).lower() + for normiecharacter in string: + if normiecharacter in normiefont: + bubblecharacter = bubbletextfont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, bubblecharacter) + await ult.eor(string) + + +@ultroid_cmd(pattern="cursive ?(.*)") +async def cursive(ult): + args = ult.pattern_match.group(1) + if not args and ult.is_reply: + get = await ult.get_reply_message() + args = get.text + if not args: + return await ult.edit( + "What I am Supposed to write in cursive? Please Give Text Sir" + ) + string = "".join(args).lower() + for normiecharacter in string: + if normiecharacter in normiefont: + cursivecharacter = cursivefont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, cursivecharacter) + await ult.eor(string) + + +@ultroid_cmd(pattern="greekify ?(.*)") +async def greektext(ult): + args = ult.pattern_match.group(1) + if not args and ult.is_reply: + get = await ult.get_reply_message() + args = get.text + if not args: + return await ult.edit("What I am Supposed to greekify? Please Give Text Sir") + string = "".join(args).lower() + for normiecharacter in string: + if normiecharacter in normiefont: + greekcharacter = greekfont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, greekcharacter) + await ult.eor(string) + + +@ultroid_cmd(pattern="sorcify ?(.*)") +async def sorcerertext(ult): + + args = ult.pattern_match.group(1) + if not args and ult.is_reply: + get = await ult.get_reply_message() + args = get.text + if not args: + await ult.edit("What I am Supposed to sorcify? Please Give Text Sir") + return + string = "".join(args).lower() + for normiecharacter in string: + if normiecharacter in normiefont: + sorcerercharacter = sorcererfont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, sorcerercharacter) + await ult.eor(string) + + +@ultroid_cmd(pattern="fraktify ?(.*)") +async def frakturtext(ult): + args = ult.pattern_match.group(1) + if not args and ult.is_reply: + get = await ult.get_reply_message() + args = get.text + if not args: + await ult.edit("What I am Supposed to fraktify? Please Give Text Sir") + return + string = "".join(args).lower() + for normiecharacter in string: + if normiecharacter in normiefont: + frakturcharacter = frakturfont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, frakturcharacter) + await ult.eor(string) + + +@ultroid_cmd(pattern="rusify ?(.*)") +async def rusitext(ult): + args = ult.pattern_match.group(1) + if not args and ult.is_reply: + get = await ult.get_reply_message() + args = get.text + if not args: + return await ult.edit("What I am Supposed to rusify? Please Give Text Sir") + string = "".join(args).lower() + for normiecharacter in string: + if normiecharacter in normiefont: + rusicharacter = rusifont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, rusicharacter) + await ult.eor(string) diff --git a/addons/forcesubscribe.py b/addons/forcesubscribe.py new file mode 100644 index 0000000..3245053 --- /dev/null +++ b/addons/forcesubscribe.py @@ -0,0 +1,229 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +""" +โœ˜ Commands Available - + +โ€ข `{i}fsub ` + Enable ForceSub in Used Chat ! + +โ€ข `{i}checkfsub` + Check/Get Active ForceSub Setting of Used Chat. + +โ€ข `{i}remfsub` + Remove ForceSub from Used Chat ! + + Note - You Need to be Admin in Both Channel/Chats + in order to Use ForceSubscribe. +""" + +import re + +from telethon.errors.rpcerrorlist import ChatAdminRequiredError, UserNotParticipantError +from telethon.tl.custom import Button +from telethon.tl.functions.channels import GetParticipantRequest +from telethon.tl.functions.messages import ExportChatInviteRequest +from telethon.tl.types import ( + Channel, + ChannelParticipantBanned, + ChannelParticipantLeft, + User, +) + +from . import ( + LOGS, + asst, + callback, + events, + get_string, + in_pattern, + inline_mention, + udB, + ultroid_bot, + ultroid_cmd, +) + +# Functions moved from forcesub_db.py +def get_chats(): + return udB.get_key("FORCESUB") or {} + + +def add_forcesub(chat_id, chattojoin): + omk = get_chats() + omk.update({chat_id: chattojoin}) + return udB.set_key("FORCESUB", omk) + + +def get_forcesetting(chat_id): + omk = get_chats() + if chat_id in omk.keys(): + return omk[chat_id] + + +def rem_forcesub(chat_id): + omk = get_chats() + if chat_id in omk.keys(): + try: + del omk[chat_id] + return udB.set_key("FORCESUB", omk) + except KeyError: + return False + + +CACHE = {} + + +@ultroid_cmd(pattern="fsub( (.*)|$)", admins_only=True, groups_only=True) +async def addfor(e): + match = e.pattern_match.group(1).strip() + if not match: + return await e.eor(get_string("fsub_1"), time=5) + try: + match = await e.client.parse_id(match) + except BaseException: + return await e.eor(get_string("fsub_2"), time=5) + add_forcesub(e.chat_id, match) + await e.eor("Added ForceSub in This Chat !") + ultroid_bot.add_handler(force_sub, events.NewMessage(incoming=True)) + + +@ultroid_cmd(pattern="remfsub$") +async def remor(e): + res = rem_forcesub(e.chat_id) + if not res: + return await e.eor(get_string("fsub_3"), time=5) + await e.eor("Removed ForceSub...") + + +@ultroid_cmd(pattern="checkfsub$") +async def getfsr(e): + res = get_forcesetting(e.chat_id) + if not res: + return await e.eor("ForceSub is Not Active In This Chat !", time=5) + cha = await e.client.get_entity(int(res)) + await e.eor(f"**ForceSub Status** : `Active`\n- **{cha.title}** `({res})`") + + +@in_pattern("fsub( (.*)|$)", owner=True) +async def fcall(e): + match = e.pattern_match.group(1).strip() + spli = match.split("_") + user = await ultroid_bot.get_entity(int(spli[0])) + cl = await ultroid_bot.get_entity(int(spli[1])) + text = f"Hi {inline_mention(user)}, You Need to Join" + text += f" {cl.title} in order to Chat in this Group." + el = ( + f"https://t.me/{cl.username}" + if cl.username + else (await ultroid_bot(ExportChatInviteRequest(cl))).link + ) + + res = [ + await e.builder.article( + title="forcesub", + text=text, + buttons=[ + [Button.url(text=get_string("fsub_4"), url=el)], + [Button.inline(get_string("fsub_5"), data=f"unm_{match}")], + ], + ) + ] + await e.answer(res) + + +@callback(re.compile("unm_(.*)")) +async def diesoon(e): + match = (e.data_match.group(1)).decode("UTF-8") + spli = match.split("_") + if e.sender_id != int(spli[0]): + return await e.answer(get_string("fsub_7"), alert=True) + try: + values = await ultroid_bot(GetParticipantRequest(int(spli[1]), int(spli[0]))) + if isinstance(values.participant, ChannelParticipantLeft) or ( + isinstance(values.participant, ChannelParticipantBanned) and values.left + ): + raise UserNotParticipantError("") + except UserNotParticipantError: + return await e.answer( + "Please Join That Channel !\nThen Click This Button !", alert=True + ) + await ultroid_bot.edit_permissions( + e.chat_id, int(spli[0]), send_messages=True, until_date=None + ) + await e.edit(get_string("fsub_8")) + + +async def force_sub(ult): + if not udB.get_key("FORCESUB"): + return + user = await ult.get_sender() + joinchat = get_forcesetting(ult.chat_id) + if (not joinchat) or (isinstance(user, User) and user.bot): + return + if CACHE.get(ult.chat_id): + if CACHE[ult.chat_id].get(user.id): + CACHE[ult.chat_id].update({user.id: CACHE[ult.chat_id][user.id] + 1}) + else: + CACHE[ult.chat_id].update({user.id: 1}) + else: + CACHE.update({ult.chat_id: {user.id: 1}}) + count = CACHE[ult.chat_id][user.id] + if count == 11: + CACHE[ult.chat_id][user.id] = 1 + return + if count in range(2, 11): + return + try: + await ultroid_bot.get_permissions(int(joinchat), user.id) + return + except UserNotParticipantError: + pass + if isinstance(user, Channel): + try: + await ultroid_bot.edit_permissions( + ult.chat_id, user.id, view_messages=False + ) + return + except BaseException as er: + LOGS.exception(er) + try: + await ultroid_bot.edit_permissions(ult.chat_id, user.id, send_messages=False) + except ChatAdminRequiredError: + return + except Exception as e: + await ult.delete() + LOGS.info(e) + res = await ultroid_bot.inline_query(asst.me.username, f"fsub {user.id}_{joinchat}") + await res[0].click(ult.chat_id, reply_to=ult.id) + + +# Add a handler for chat actions (user join events) +async def force_sub_action(ult): + if not udB.get_key("FORCESUB"): + return + if not (ult.user_joined or ult.user_added): + return + if not get_forcesetting(ult.chat_id): + return + user = await ult.get_user() + if user.bot: + return + joinchat = get_forcesetting(ult.chat_id) + try: + await ultroid_bot(GetParticipantRequest(int(joinchat), user.id)) + except UserNotParticipantError: + await ultroid_bot.edit_permissions( + ult.chat_id, user.id, send_messages=False + ) + res = await ultroid_bot.inline_query( + asst.me.username, f"fsub {user.id}_{joinchat}" + ) + await res[0].click(ult.chat_id, reply_to=ult.action_message.id) + + +if udB.get_key("FORCESUB"): + ultroid_bot.add_handler(force_sub, events.NewMessage(incoming=True)) + ultroid_bot.add_handler(force_sub_action, events.ChatAction()) diff --git a/addons/fun.py b/addons/fun.py new file mode 100644 index 0000000..0e69df7 --- /dev/null +++ b/addons/fun.py @@ -0,0 +1,123 @@ +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +""" +โœ˜ Commands Available + +โ€ข `{i}joke` + To get joke. + +โ€ข `{i}url ` + To get a shorten link of long link. + +โ€ข `{i}phlogo ` + Make a phub based logo. + +โ€ข `{i}decide` + Decide something. + +โ€ข `{i}xo` + Opens tic tac game only where using inline mode is allowed. + +โ€ข `{i}wordi` + Opens word game only where using inline mode is allowed. + +โ€ข `{i}gps ` + Shows the desired place in the map. +""" + +import random, os + +import requests +from bs4 import BeautifulSoup as bs +from pyjokes import get_joke +from telethon.errors import ChatSendMediaForbiddenError +from phlogo import generate + +from . import ultroid_cmd, get_string, HNDLR, async_searcher + + +@ultroid_cmd(pattern="joke$") +async def _(ult): + await ult.eor(get_joke()) + + +@ultroid_cmd(pattern="url ?(.*)") +async def _(event): + input_str = event.pattern_match.group(1) + if not input_str: + await event.eor("`Give some url`") + return + sample_url = "https://da.gd/s?url={}".format(input_str) + response_api = requests.get(sample_url).text + if response_api: + await event.eor( + "**Shortened url**==> {}\n**Given url**==> {}.".format( + response_api, input_str + ), + ) + else: + await event.eor("`Something went wrong. Please try again Later.`") + + +@ultroid_cmd(pattern="decide$") +async def _(event): + hm = await event.eor("`Deciding`") + r = await async_searcher("https://yesno.wtf/api", re_json=True) + try: + await event.reply(r["answer"], file=r["image"]) + await hm.delete() + except ChatSendMediaForbiddenError: + await event.eor(r["answer"]) + + +@ultroid_cmd(pattern="xo$") +async def xo(ult): + xox = await ult.client.inline_query("xobot", "play") + await xox[random.randrange(0, len(xox) - 1)].click( + ult.chat_id, reply_to=ult.reply_to_msg_id, silent=True, hide_via=True + ) + await ult.delete() + + +@ultroid_cmd(pattern="phlogo( (.*)|$)") +async def make_logog(ult): + msg = await ult.eor(get_string("com_1")) + match = ult.pattern_match.group(1).strip() + reply = await ult.get_reply_message() + if not match and (reply and reply.text): + match = reply.text + else: + return await msg.edit(f"`Provide a name to make logo...`") + first, last = "", "" + if len(match.split()) >= 2: + first, last = match.split()[:2] + else: + last = match + logo = generate(first, last) + name = f"{ult.id}.png" + logo.save(name) + await ult.client.send_message( + ult.chat_id, file=name, reply_to=ult.reply_to_msg_id or ult.id + ) + os.remove(name) + await msg.delete() + + +Bot = {"gps":"openmap_bot", "wordi":"wordibot"} + +@ultroid_cmd(pattern="(gps|wordi) (.*)") +async def _map(ult): + cmd = ult.pattern_match.group(1) + get = ult.pattern_match.group(2) + if not get: + return await ult.eor(f"Use this command as `{HNDLR}{cmd} `") + quer = await ult.client.inline_query(Bot[cmd], get) + await quer[0].click( + ult.chat_id, reply_to=ult.reply_to_msg_id, silent=True, hide_via=True + ) + await ult.delete() diff --git a/addons/gdrive.py b/addons/gdrive.py new file mode 100644 index 0000000..bafffbd --- /dev/null +++ b/addons/gdrive.py @@ -0,0 +1,232 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +""" +โœ˜ Commands Available + +โ€ข `{i}gdul ` + Reply to file to upload on Google Drive. + Add file name to upload on Google Drive. + +โ€ข `{i}gdown | ` + Download from Gdrive link or file id. + +โ€ข `{i}gdsearch ` + Search file name on Google Drive and get link. + +โ€ข `{i}gdlist` + List all GDrive files. + +โ€ข `{i}gdfolder` + Link to your Google Drive Folder. + If added then all files will be uploaded in this folder. +""" + +import os +import time + +from telethon.tl.types import Message + +from pyUltroid.fns.gDrive import GDriveManager +from pyUltroid.fns.helper import time_formatter + +from . import ULTConfig, asst, eod, eor, get_string, ultroid_cmd + + +@ultroid_cmd( + pattern="gdown( (.*)|$)", + fullsudo=True, +) +async def gdown(event): + GDrive = GDriveManager() + match = event.pattern_match.group(1).strip() + if not match: + return await eod(event, "`Give file id or Gdrive link to download from!`") + filename = match.split(" | ")[1].strip() if " | " in match else None + eve = await event.eor(get_string("com_1")) + _start = time.time() + status, response = await GDrive._download_file(eve, match, filename) + if not status: + return await eve.edit(response) + await eve.edit( + f"`Downloaded ``{response}`` in {time_formatter((time.time() - _start)*1000)}`" + ) + + +@ultroid_cmd( + pattern="gdlist$", + fullsudo=True, +) +async def files(event): + GDrive = GDriveManager() + if not os.path.exists(GDrive.token_file): + return await event.eor(get_string("gdrive_6").format(asst.me.username)) + eve = await event.eor(get_string("com_1")) + msg = "" + if files := GDrive._list_files: + msg += f"{len(files.keys())} files found in gdrive.\n\n" + for _ in files: + msg += f"> [{files[_]}]({_})\n" + else: + msg += "Nothing in Gdrive" + if len(msg) < 4096: + await eve.edit(msg, link_preview=False) + else: + with open("drive-files.txt", "w") as f: + f.write( + msg.replace("[", "File Name: ") + .replace("](", "\nยป Link: ") + .replace(")\n", "\n\n") + ) + try: + await eve.delete() + except BaseException: + pass + await event.client.send_file( + event.chat_id, + "drive-files.txt", + thumb=ULTConfig.thumb, + reply_to=event, + ) + os.remove("drive-files.txt") + + +@ultroid_cmd( + pattern="gdul( (.*)|$)", + fullsudo=True, +) +async def _(event): + GDrive = GDriveManager() + if not os.path.exists(GDrive.token_file): + return await eod(event, get_string("gdrive_6").format(asst.me.username)) + input_file = event.pattern_match.group(1).strip() or await event.get_reply_message() + if not input_file: + return await eod(event, "`Reply to file or give its location.`") + mone = await event.eor(get_string("com_1")) + if isinstance(input_file, Message): + location = "resources/downloads" + if input_file.photo: + filename = await input_file.download_media(location) + else: + filename = input_file.file.name + if not filename: + filename = str(round(time.time())) + filename = f"{location}/{filename}" + try: + filename, downloaded_in = await event.client.fast_downloader( + file=input_file.media.document, + filename=filename, + show_progress=True, + event=mone, + message=get_string("com_5"), + ) + filename = filename.name + except Exception as e: + return await eor(mone, str(e), time=10) + await mone.edit( + f"`Downloaded to ``{filename}`.`", + ) + else: + filename = input_file.strip() + if not os.path.exists(filename): + return await eod( + mone, + "File Not found in local server. Give me a file path :((", + time=5, + ) + folder_id = None + if os.path.isdir(filename): + files = os.listdir(filename) + if not files: + return await eod( + mone, "`Requested directory is empty. Can't create empty directory.`" + ) + folder_id = GDrive.create_directory(filename) + c = 0 + for files in sorted(files): + file = f"{filename}/{files}" + if not os.path.isdir(file): + try: + await GDrive._upload_file(mone, path=file, folder_id=folder_id) + c += 1 + except Exception as e: + return await mone.edit( + f"Exception occurred while uploading to gDrive {e}" + ) + return await mone.edit( + f"`Uploaded `[{filename}](https://drive.google.com/folderview?id={folder_id})` with {c} files.`" + ) + try: + g_drive_link = await GDrive._upload_file( + mone, + filename, + ) + await mone.edit( + get_string("gdrive_7").format(filename.split("/")[-1], g_drive_link) + ) + except Exception as e: + await mone.edit(f"Exception occurred while uploading to gDrive {e}") + + +@ultroid_cmd( + pattern="gdsearch( (.*)|$)", + fullsudo=True, +) +async def _(event): + GDrive = GDriveManager() + if not os.path.exists(GDrive.token_file): + return await event.eor(get_string("gdrive_6").format(asst.me.username)) + input_str = event.pattern_match.group(1).strip() + if not input_str: + return await event.eor("`Give filename to search on GDrive...`") + eve = await event.eor(f"`Searching for {input_str} in G-Drive...`") + files = GDrive.search(input_str) + msg = "" + if files: + msg += ( + f"{len(files.keys())} files with {input_str} in title found in GDrive.\n\n" + ) + for _ in files: + msg += f"> [{files[_]}]({_})\n" + else: + msg += f"`No files with title {input_str}`" + if len(msg) < 4096: + await eve.eor(msg, link_preview=False) + else: + with open("drive-files.txt", "w") as f: + f.write( + msg.replace("[", "File Name: ") + .replace("](", "\nยป Link: ") + .replace(")\n", "\n\n") + ) + try: + await eve.delete() + except BaseException: + pass + await event.client.send_file( + event.chat_id, + f"{input_str}.txt", + thumb=ULTConfig.thumb, + reply_to=event, + ) + os.remove(f"{input_str}.txt") + + +@ultroid_cmd( + pattern="gdfolder$", + fullsudo=True, +) +async def _(event): + GDrive = GDriveManager() + if not os.path.exists(GDrive.token_file): + return await event.eor(get_string("gdrive_6").format(asst.me.username)) + if GDrive.folder_id: + await event.eor( + "`Your G-Drive Folder link : `\n" + + GDrive._create_folder_link(GDrive.folder_id) + ) + else: + await eod(event, "Set FOLDERID from your Assistant bot's Settings ") diff --git a/addons/giftools.py b/addons/giftools.py new file mode 100644 index 0000000..5c8d0d2 --- /dev/null +++ b/addons/giftools.py @@ -0,0 +1,128 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +""" +โœ˜ Commands Available + +โ€ข`{i}invertgif` + Make Gif Inverted(negative). + +โ€ข`{i}bwgif` + Make Gif black and white + +โ€ข`{i}rvgif` + Reverse a gif + +โ€ข`{i}vtog` + Reply To Video , It will Create Gif + Video to Gif + +โ€ข`{i}gif ` + Send video regarding to query. +""" +import os +import random +import time +from datetime import datetime as dt + +from . import HNDLR, LOGS, bash, downloader, get_string, mediainfo, ultroid_cmd + + +@ultroid_cmd(pattern="(bw|invert)gif$") +async def igif(e): + match = e.pattern_match.group(1).strip() + a = await e.get_reply_message() + if not (a and a.media): + return await e.eor("`Reply To gif only`", time=5) + wut = mediainfo(a.media) + if "gif" not in wut: + return await e.eor("`Reply To Gif Only`", time=5) + xx = await e.eor(get_string("com_1")) + z = await a.download_media() + if match == "bw": + cmd = f'ffmpeg -i "{z}" -vf format=gray ult.gif -y' + else: + cmd = f'ffmpeg -i "{z}" -vf lutyuv="y=negval:u=negval:v=negval" ult.gif -y' + try: + await bash(cmd) + await e.client.send_file(e.chat_id, "ult.gif", supports_streaming=True) + os.remove(z) + os.remove("ult.gif") + await xx.delete() + except Exception as er: + LOGS.info(er) + + +@ultroid_cmd(pattern="rvgif$") +async def reverse_gif(event): + a = await event.get_reply_message() + if not (a and a.media) and "video" not in mediainfo(a.media): + return await event.eor("`Reply To Video only`", time=5) + msg = await event.eor(get_string("com_1")) + file = await a.download_media() + await bash(f'ffmpeg -i "{file}" -vf reverse -af areverse reversed.mp4 -y') + await event.respond("- **Reversed Video/GIF**", file="reversed.mp4") + await msg.delete() + os.remove(file) + os.remove("reversed.mp4") + + +@ultroid_cmd(pattern="gif( (.*)|$)") +async def gifs(ult): + get = ult.pattern_match.group(1).strip() + xx = random.randint(0, 5) + n = 0 + if ";" in get: + try: + n = int(get.split(";")[-1]) + except IndexError: + pass + if not get: + return await ult.eor(f"`{HNDLR}gif `") + m = await ult.eor(get_string("com_2")) + gifs = await ult.client.inline_query("gif", get) + if not n: + await gifs[xx].click( + ult.chat_id, reply_to=ult.reply_to_msg_id, silent=True, hide_via=True + ) + else: + for x in range(n): + await gifs[x].click( + ult.chat_id, reply_to=ult.reply_to_msg_id, silent=True, hide_via=True + ) + await m.delete() + + +@ultroid_cmd(pattern="vtog$") +async def vtogif(e): + a = await e.get_reply_message() + if not (a and a.media): + return await e.eor("`Reply To video only`", time=5) + wut = mediainfo(a.media) + if "video" not in wut: + return await e.eor("`Reply To Video Only`", time=5) + xx = await e.eor(get_string("com_1")) + dur = a.media.document.attributes[0].duration + tt = time.time() + if int(dur) < 120: + z = await a.download_media() + await bash( + f'ffmpeg -i {z} -vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 ult.gif -y' + ) + else: + filename = a.file.name + if not filename: + filename = "video_" + dt.now().isoformat("_", "seconds") + ".mp4" + vid = await downloader(filename, a.media.document, xx, tt, get_string("com_5")) + z = vid.name + await bash( + f'ffmpeg -ss 3 -t 100 -i {z} -vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 ult.gif' + ) + + await e.client.send_file(e.chat_id, "ult.gif", support_stream=True) + os.remove(z) + os.remove("ult.gif") + await xx.delete() diff --git a/addons/glitch.py b/addons/glitch.py new file mode 100644 index 0000000..d147ae4 --- /dev/null +++ b/addons/glitch.py @@ -0,0 +1,42 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +""" +โœ˜ Commands Available - + +โ€ข`{i}glitch ` + gives a glitchy gif. +""" +import os + +from .. import bash, get_string, mediainfo, ultroid_cmd + + +@ultroid_cmd(pattern="glitch$") +async def _(e): + try: + import glitch_me # ignore :pylint + except ModuleNotFoundError: + await bash( + "pip install -e git+https://github.com/1Danish-00/glitch_me.git#egg=glitch_me" + ) + reply = await e.get_reply_message() + if not reply or not reply.media: + return await e.eor(get_string("cvt_3")) + xx = await e.eor(get_string("glitch_1")) + wut = mediainfo(reply.media) + if wut.startswith(("pic", "sticker")): + ok = await reply.download_media() + elif reply.document and reply.document.thumbs: + ok = await reply.download_media(thumb=-1) + else: + return await xx.eor(get_string("com_4")) + cmd = f"glitch_me gif --line_count 200 -f 10 -d 50 '{ok}' ult.gif" + await bash(cmd) + await e.reply(file="ult.gif", force_document=False) + await xx.delete() + os.remove(ok) + os.remove("ult.gif") diff --git a/addons/globaltools.py b/addons/globaltools.py new file mode 100644 index 0000000..ff2231b --- /dev/null +++ b/addons/globaltools.py @@ -0,0 +1,789 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +""" +โœ˜ Commands Available - + +โ€ข `{i}gban ` +โ€ข `{i}ungban` + Ban/Unban Globally. + +โ€ข `{i}gstat ` + Check if user is GBanned. + +โ€ข `{i}listgban` : List all GBanned users. + +โ€ข `{i}gmute` | `{i}ungmute` + Mute/UnMute Globally. + +โ€ข `{i}gkick ` `Globally Kick User` +โ€ข `{i}gcast ` `Globally Send msg in all grps` + +โ€ข `{i}gadmincast ` `Globally broadcast in your admin chats` +โ€ข `{i}gucast ` `Globally send msg in all pm users` + +โ€ข `{i}gblacklist ` + globally promote user where you are admin + - Set whether To promote only in groups/channels/all. + Eg- `gpromote group boss` ~ promotes user in all grps. + `gpromote @username all sar` ~ promote the user in all group & channel +โ€ข `{i}gdemote` - `demote user globally` +""" +import asyncio +import os + +from telethon.errors.rpcerrorlist import ChatAdminRequiredError, FloodWaitError +from telethon.tl.functions.channels import EditAdminRequest +from telethon.tl.functions.contacts import BlockRequest, UnblockRequest +from telethon.tl.types import ChatAdminRights, User + +from pyUltroid.dB import DEVLIST +from pyUltroid.dB.base import KeyManager +from pyUltroid.fns.tools import create_tl_btn, format_btn, get_msg_button + +from . import ( + HNDLR, + LOGS, + NOSPAM_CHAT, + OWNER_NAME, + eod, + eor, + get_string, + inline_mention, + udB, + ultroid_bot, + ultroid_cmd, +) +from ._inline import something + +def list_gbanned(): + return udB.get_key("GBAN") or {} + + +def gban(user, reason): + ok = list_gbanned() + ok.update({int(user): reason or "No Reason. "}) + return udB.set_key("GBAN", ok) + + +def ungban(user): + ok = list_gbanned() + if ok.get(int(user)): + del ok[int(user)] + return udB.set_key("GBAN", ok) + + +def is_gbanned(user): + ok = list_gbanned() + if ok.get(int(user)): + return ok[int(user)] + + +def gmute(user): + ok = list_gmuted() + ok.append(int(user)) + return udB.set_key("GMUTE", ok) + + +def ungmute(user): + ok = list_gmuted() + if user in ok: + ok.remove(int(user)) + return udB.set_key("GMUTE", ok) + + +def is_gmuted(user): + return int(user) in list_gmuted() + + +def list_gmuted(): + return udB.get_key("GMUTE") or [] + + +_gpromote_rights = ChatAdminRights( + add_admins=False, + invite_users=True, + change_info=False, + ban_users=True, + delete_messages=True, + pin_messages=True, +) + +_gdemote_rights = ChatAdminRights( + add_admins=False, + invite_users=False, + change_info=False, + ban_users=False, + delete_messages=False, + pin_messages=False, +) + +keym = KeyManager("GBLACKLISTS", cast=list) + + +@ultroid_cmd(pattern="gpromote( (.*)|$)", fullsudo=True) +async def _(e): + x = e.pattern_match.group(1).strip() + ultroid_bot = e.client + if not x: + return await e.eor(get_string("schdl_2"), time=5) + user = await e.get_reply_message() + if user: + ev = await e.eor("`Promoting Replied User Globally`") + ok = e.text.split() + key = "all" + if len(ok) > 1 and (("group" in ok[1]) or ("channel" in ok[1])): + key = ok[1] + rank = ok[2] if len(ok) > 2 else "AdMin" + c = 0 + user.id = user.peer_id.user_id if e.is_private else user.from_id.user_id + async for x in e.client.iter_dialogs(): + if ( + "group" in key.lower() + and x.is_group + or "group" not in key.lower() + and "channel" in key.lower() + and x.is_channel + ): + try: + await e.client( + EditAdminRequest( + x.id, + user.id, + _gpromote_rights, + rank, + ), + ) + c += 1 + except BaseException: + pass + elif ( + ("group" not in key.lower() or x.is_group) + and ( + "group" in key.lower() + or "channel" not in key.lower() + or x.is_channel + ) + and ( + "group" in key.lower() + or "channel" in key.lower() + or x.is_group + or x.is_channel + ) + ): + try: + await e.client( + EditAdminRequest( + x.id, + user.id, + _gpromote_rights, + rank, + ), + ) + c += 1 + except Exception as er: + LOGS.info(er) + await eor(ev, f"Promoted The Replied Users in Total : {c} {key} chats") + else: + k = e.text.split() + if not k[1]: + return await eor( + e, "`Give someone's username/id or replied to user.", time=5 + ) + user = k[1] + if user.isdigit(): + user = int(user) + try: + name = await e.client.get_entity(user) + except BaseException: + return await e.eor(f"`No User Found Regarding {user}`", time=5) + ev = await e.eor(f"`Promoting {name.first_name} globally.`") + key = "all" + if len(k) > 2 and (("group" in k[2]) or ("channel" in k[2])): + key = k[2] + rank = k[3] if len(k) > 3 else "AdMin" + c = 0 + async for x in e.client.iter_dialogs(): + if ( + "group" in key.lower() + and x.is_group + or "group" not in key.lower() + and "channel" in key.lower() + and x.is_channel + or "group" not in key.lower() + and "channel" not in key.lower() + and (x.is_group or x.is_channel) + ): + try: + await ultroid_bot( + EditAdminRequest( + x.id, + user, + _gpromote_rights, + rank, + ), + ) + c += 1 + except BaseException: + pass + await eor(ev, f"Promoted {name.first_name} in Total : {c} {key} chats.") + + +@ultroid_cmd(pattern="gdemote( (.*)|$)", fullsudo=True) +async def _(e): + x = e.pattern_match.group(1).strip() + ultroid_bot = e.client + if not x: + return await e.eor(get_string("schdl_2"), time=5) + user = await e.get_reply_message() + if user: + user.id = user.peer_id.user_id if e.is_private else user.from_id.user_id + ev = await e.eor("`Demoting Replied User Globally`") + ok = e.text.split() + key = "all" + if len(ok) > 1 and (("group" in ok[1]) or ("channel" in ok[1])): + key = ok[1] + rank = "Not AdMin" + c = 0 + async for x in e.client.iter_dialogs(): + if ( + "group" in key.lower() + and x.is_group + or "group" not in key.lower() + and "channel" in key.lower() + and x.is_channel + or "group" not in key.lower() + and "channel" not in key.lower() + and (x.is_group or x.is_channel) + ): + try: + await ultroid_bot( + EditAdminRequest( + x.id, + user.id, + _gdemote_rights, + rank, + ), + ) + c += 1 + except BaseException: + pass + await eor(ev, f"Demoted The Replied Users in Total : {c} {key} chats") + else: + k = e.text.split() + if not k[1]: + return await eor( + e, "`Give someone's username/id or replied to user.", time=5 + ) + user = k[1] + if user.isdigit(): + user = int(user) + try: + name = await ultroid_bot.get_entity(user) + except BaseException: + return await e.eor(f"`No User Found Regarding {user}`", time=5) + ev = await e.eor(f"`Demoting {name.first_name} globally.`") + key = "all" + if len(k) > 2 and (("group" in k[2]) or ("channel" in k[2])): + key = k[2] + rank = "Not AdMin" + c = 0 + async for x in ultroid_bot.iter_dialogs(): + if ( + "group" in key.lower() + and x.is_group + or "group" not in key.lower() + and "channel" in key.lower() + and x.is_channel + or "group" not in key.lower() + and "channel" not in key.lower() + and (x.is_group or x.is_channel) + ): + try: + await ultroid_bot( + EditAdminRequest( + x.id, + user, + _gdemote_rights, + rank, + ), + ) + c += 1 + except BaseException: + pass + await eor(ev, f"Demoted {name.first_name} in Total : {c} {key} chats.") + + +@ultroid_cmd(pattern="ungban( (.*)|$)", fullsudo=True) +async def _(e): + xx = await e.eor("`UnGbanning...`") + match = e.pattern_match.group(1).strip() + peer = None + if e.reply_to_msg_id: + userid = (await e.get_reply_message()).sender_id + elif match: + try: + userid = int(match) + except ValueError: + userid = match + try: + userid = (await e.client.get_entity(userid)).id + except Exception as er: + return await xx.edit(f"Failed to get User...\nError: {er}") + elif e.is_private: + userid = e.chat_id + else: + return await xx.eor("`Reply to some msg or add their id.`", time=5) + if not is_gbanned(userid): + return await xx.edit("`User/Channel is not Gbanned...`") + try: + if not peer: + peer = await e.client.get_entity(userid) + name = inline_mention(peer) + except BaseException: + userid = int(userid) + name = str(userid) + chats = 0 + if e.client._dialogs: + dialog = e.client._dialogs + else: + dialog = await e.client.get_dialogs() + e.client._dialogs.extend(dialog) + for ggban in dialog: + if ggban.is_group or ggban.is_channel: + try: + await e.client.edit_permissions(ggban.id, userid, view_messages=True) + chats += 1 + except FloodWaitError as fw: + LOGS.info( + f"[FLOOD_WAIT_ERROR] : on Ungban\nSleeping for {fw.seconds+10}" + ) + await asyncio.sleep(fw.seconds + 10) + try: + await e.client.edit_permissions( + ggban.id, userid, view_messages=True + ) + chats += 1 + except BaseException as er: + LOGS.exception(er) + except (ChatAdminRequiredError, ValueError): + pass + except BaseException as er: + LOGS.exception(er) + ungban(userid) + if isinstance(peer, User): + await e.client(UnblockRequest(userid)) + await xx.edit( + f"`Ungbaned` {name} in {chats} `chats.\nRemoved from gbanwatch.`", + ) + + +@ultroid_cmd(pattern="gban( (.*)|$)", fullsudo=True) +async def _(e): + xx = await e.eor("`Gbanning...`") + reason = "" + if e.reply_to_msg_id: + userid = (await e.get_reply_message()).sender_id + try: + reason = e.text.split(" ", maxsplit=1)[1] + except IndexError: + pass + elif e.pattern_match.group(1).strip(): + usr = e.text.split(maxsplit=2)[1] + try: + userid = await e.client.parse_id(usr) + except ValueError: + userid = usr + try: + reason = e.text.split(maxsplit=2)[2] + except IndexError: + pass + elif e.is_private: + userid = e.chat_id + try: + reason = e.text.split(" ", maxsplit=1)[1] + except IndexError: + pass + else: + return await xx.eor("`Reply to some msg or add their id.`", time=5) + user = None + try: + user = await e.client.get_entity(userid) + name = inline_mention(user) + except BaseException: + userid = int(userid) + name = str(userid) + chats = 0 + if userid == ultroid_bot.uid: + return await xx.eor("`I can't gban myself.`", time=3) + elif userid in DEVLIST: + return await xx.eor("`I can't gban my Developers.`", time=3) + elif is_gbanned(userid): + return await eod( + xx, + "`User is already gbanned and added to gbanwatch.`", + time=4, + ) + if e.client._dialogs: + dialog = e.client._dialogs + else: + dialog = await e.client.get_dialogs() + e.client._dialogs.extend(dialog) + for ggban in dialog: + if ggban.is_group or ggban.is_channel: + try: + await e.client.edit_permissions(ggban.id, userid, view_messages=False) + chats += 1 + except FloodWaitError as fw: + LOGS.info( + f"[FLOOD_WAIT_ERROR] : on GBAN Command\nSleeping for {fw.seconds+10}" + ) + await asyncio.sleep(fw.seconds + 10) + try: + await e.client.edit_permissions( + ggban.id, userid, view_messages=False + ) + chats += 1 + except BaseException as er: + LOGS.exception(er) + except (ChatAdminRequiredError, ValueError): + pass + except BaseException as er: + LOGS.exception(er) + gban(userid, reason) + if isinstance(user, User): + await e.client(BlockRequest(userid)) + gb_msg = f"**#Gbanned** {name} `in {chats} chats and added to gbanwatch!`" + if reason: + gb_msg += f"\n**Reason** : {reason}" + await xx.edit(gb_msg) + + +@ultroid_cmd(pattern="g(admin|)cast( (.*)|$)", fullsudo=True) +async def gcast(event): + text, btn, reply = "", None, None + if xx := event.pattern_match.group(2): + msg, btn = get_msg_button(event.text.split(maxsplit=1)[1]) + elif event.is_reply: + reply = await event.get_reply_message() + msg = reply.text + if reply.buttons: + btn = format_btn(reply.buttons) + else: + msg, btn = get_msg_button(msg) + else: + return await eor( + event, "`Give some text to Globally Broadcast or reply a message..`" + ) + + kk = await event.eor("`Globally Broadcasting Msg...`") + er = 0 + done = 0 + err = "" + if event.client._dialogs: + dialog = event.client._dialogs + else: + dialog = await event.client.get_dialogs() + event.client._dialogs.extend(dialog) + for x in dialog: + if x.is_group: + chat = x.entity.id + if ( + not keym.contains(chat) + and int(f"-100{str(chat)}") not in NOSPAM_CHAT + and ( + ( + event.text[2:7] != "admin" + or (x.entity.admin_rights or x.entity.creator) + ) + ) + ): + try: + if btn: + bt = create_tl_btn(btn) + await something( + event, + msg, + reply.media if reply else None, + bt, + chat=chat, + reply=False, + ) + else: + await event.client.send_message( + chat, msg, file=reply.media if reply else None + ) + done += 1 + except FloodWaitError as fw: + await asyncio.sleep(fw.seconds + 10) + try: + if btn: + bt = create_tl_btn(btn) + await something( + event, + msg, + reply.media if reply else None, + bt, + chat=chat, + reply=False, + ) + else: + await event.client.send_message( + chat, msg, file=reply.media if reply else None + ) + done += 1 + except Exception as rr: + err += f"โ€ข {rr}\n" + er += 1 + except BaseException as h: + err += f"โ€ข {str(h)}" + "\n" + er += 1 + text += f"Done in {done} chats, error in {er} chat(s)" + if err != "": + open("gcast-error.log", "w+").write(err) + text += f"\nYou can do `{HNDLR}ul gcast-error.log` to know error report." + await kk.edit(text) + + +@ultroid_cmd(pattern="gucast( (.*)|$)", fullsudo=True) +async def gucast(event): + msg, btn, reply = "", None, None + if xx := event.pattern_match.group(1).strip(): + msg, btn = get_msg_button(event.text.split(maxsplit=1)[1]) + elif event.is_reply: + reply = await event.get_reply_message() + msg = reply.text + if reply.buttons: + btn = format_btn(reply.buttons) + else: + msg, btn = get_msg_button(msg) + else: + return await eor( + event, "`Give some text to Globally Broadcast or reply a message..`" + ) + kk = await event.eor("`Globally Broadcasting Msg...`") + er = 0 + done = 0 + if event.client._dialogs: + dialog = event.client._dialogs + else: + dialog = await event.client.get_dialogs() + event.client._dialogs.extend(dialog) + for x in dialog: + if x.is_user and not x.entity.bot: + chat = x.id + if not keym.contains(chat): + try: + if btn: + bt = create_tl_btn(btn) + await something( + event, + msg, + reply.media if reply else None, + bt, + chat=chat, + reply=False, + ) + else: + await event.client.send_message( + chat, msg, file=reply.media if reply else None + ) + done += 1 + except BaseException: + er += 1 + await kk.edit(f"Done in {done} chats, error in {er} chat(s)") + + +@ultroid_cmd(pattern="gkick( (.*)|$)", fullsudo=True) +async def gkick(e): + xx = await e.eor("`Gkicking...`") + if e.reply_to_msg_id: + userid = (await e.get_reply_message()).sender_id + elif e.pattern_match.group(1).strip(): + userid = await e.client.parse_id(e.pattern_match.group(1).strip()) + elif e.is_private: + userid = e.chat_id + else: + return await xx.edit("`Reply to some msg or add their id.`", time=5) + name = (await e.client.get_entity(userid)).first_name + chats = 0 + if userid == ultroid_bot.uid: + return await xx.eor("`I can't gkick myself.`", time=3) + if userid in DEVLIST: + return await xx.eor("`I can't gkick my Developers.`", time=3) + if e.client._dialogs: + dialog = e.client._dialogs + else: + dialog = await e.client.get_dialogs() + e.client._dialogs.extend(dialog) + for gkick in dialog: + if gkick.is_group or gkick.is_channel: + try: + await e.client.kick_participant(gkick.id, userid) + chats += 1 + except BaseException: + pass + await xx.edit(f"`Gkicked` [{name}](tg://user?id={userid}) `in {chats} chats.`") + + +@ultroid_cmd(pattern="gmute( (.*)|$)", fullsudo=True) +async def _(e): + xx = await e.eor("`Gmuting...`") + if e.reply_to_msg_id: + userid = (await e.get_reply_message()).sender_id + elif e.pattern_match.group(1).strip(): + userid = await e.client.parse_id(e.pattern_match.group(1).strip()) + elif e.is_private: + userid = e.chat_id + else: + return await xx.eor("`Reply to some msg or add their id.`", tome=5, time=5) + name = await e.client.get_entity(userid) + chats = 0 + if userid == ultroid_bot.uid: + return await xx.eor("`I can't gmute myself.`", time=3) + if userid in DEVLIST: + return await xx.eor("`I can't gmute my Developers.`", time=3) + if is_gmuted(userid): + return await xx.eor("`User is already gmuted.`", time=4) + if e.client._dialogs: + dialog = e.client._dialogs + else: + dialog = await e.client.get_dialogs() + e.client._dialogs.extend(dialog) + for onmute in dialog: + if onmute.is_group: + try: + await e.client.edit_permissions(onmute.id, userid, send_messages=False) + chats += 1 + except BaseException: + pass + gmute(userid) + await xx.edit(f"`Gmuted` {inline_mention(name)} `in {chats} chats.`") + + +@ultroid_cmd(pattern="ungmute( (.*)|$)", fullsudo=True) +async def _(e): + xx = await e.eor("`UnGmuting...`") + if e.reply_to_msg_id: + userid = (await e.get_reply_message()).sender_id + elif e.pattern_match.group(1).strip(): + userid = await e.client.parse_id(e.pattern_match.group(1).strip()) + elif e.is_private: + userid = e.chat_id + else: + return await xx.eor("`Reply to some msg or add their id.`", time=5) + name = (await e.client.get_entity(userid)).first_name + chats = 0 + if not is_gmuted(userid): + return await xx.eor("`User is not gmuted.`", time=3) + if e.client._dialogs: + dialog = e.client._dialogs + else: + dialog = await e.client.get_dialogs() + e.client._dialogs.extend(dialog) + for hurr in dialog: + if hurr.is_group: + try: + await e.client.edit_permissions(hurr.id, userid, send_messages=True) + chats += 1 + except BaseException: + pass + ungmute(userid) + await xx.edit(f"`Ungmuted` {inline_mention(name)} `in {chats} chats.`") + + +@ultroid_cmd( + pattern="listgban$", +) +async def list_gengbanned(event): + users = list_gbanned() + x = await event.eor(get_string("com_1")) + msg = "" + if not users: + return await x.edit("`You haven't GBanned anyone!`") + for i in users: + try: + name = await event.client.get_entity(int(i)) + except BaseException: + name = i + msg += f"User: {inline_mention(name, html=True)}\n" + reason = users[i] + msg += f"Reason: {reason}\n\n" if reason is not None else "\n" + gbanned_users = f"List of users GBanned by {OWNER_NAME}:\n\n{msg}" + if len(gbanned_users) > 4096: + with open("gbanned.txt", "w") as f: + f.write( + gbanned_users.replace("", "") + .replace("", "") + .replace("", "") + ) + await x.reply( + file="gbanned.txt", + message=f"List of users GBanned by {inline_mention(ultroid_bot.me)}", + ) + os.remove("gbanned.txt") + await x.delete() + else: + await x.edit(gbanned_users, parse_mode="html") + + +@ultroid_cmd( + pattern="gstat( (.*)|$)", +) +async def gstat_(e): + xx = await e.eor(get_string("com_1")) + if e.is_private: + userid = (await e.get_chat()).id + elif e.reply_to_msg_id: + userid = (await e.get_reply_message()).sender_id + elif e.pattern_match.group(1).strip(): + try: + userid = await e.client.parse_id(e.pattern_match.group(1).strip()) + except Exception as err: + return await xx.eor(f"{err}", time=10) + else: + return await xx.eor("`Reply to some msg or add their id.`", time=5) + name = (await e.client.get_entity(userid)).first_name + msg = f"**{name} is " + is_banned = is_gbanned(userid) + reason = list_gbanned().get(userid) + if is_banned: + msg += "Globally Banned" + msg += f" with reason** `{reason}`" if reason else ".**" + else: + msg += "not Globally Banned.**" + await xx.edit(msg) + + +@ultroid_cmd(pattern="gblacklist$") +async def blacklist_(event): + await gblacker(event, "add") + + +@ultroid_cmd(pattern="ungblacklist$") +async def ungblacker(event): + await gblacker(event, "remove") + + +async def gblacker(event, type_): + try: + chat_id = int(event.text.split(maxsplit=1)[1]) + try: + chat_id = (await event.client.get_entity(chat_id)).id + except Exception as e: + return await event.eor(f"**ERROR**\n`{str(e)}`") + except IndexError: + chat_id = event.chat_id + if type_ == "add": + keym.add(chat_id) + elif type_ == "remove": + keym.remove(chat_id) + await event.eor(f"Global Broadcasts: \n{type_}ed {chat_id}") diff --git a/addons/greetings.py b/addons/greetings.py new file mode 100644 index 0000000..b519f29 --- /dev/null +++ b/addons/greetings.py @@ -0,0 +1,354 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +""" +โœ˜ Commands Available - + +---- Welcomes ---- +โ€ข `{i}setwelcome ` + Set welcome message in the current chat. + +โ€ข `{i}clearwelcome` + Delete the welcome in the current chat. + +โ€ข `{i}getwelcome` + Get the welcome message in the current chat. + +---- GoodByes ---- +โ€ข `{i}setgoodbye ` + Set goodbye message in the current chat. + +โ€ข `{i}cleargoodbye` + Delete the goodbye in the current chat. + +โ€ข `{i}getgoodbye` + Get the goodbye message in the current chat. + +โ€ข `{i}thankmembers on/off` + Send a thank you sticker on hitting a members count of 100*x in your groups. +""" +import os +import asyncio + +from . import upload_file as uf +from telethon.utils import pack_bot_file_id, get_display_name + +from pyUltroid.fns.tools import create_tl_btn, format_btn, get_msg_button +from pyUltroid.dB import stickers +from pyUltroid.fns.helper import inline_mention + +from . import HNDLR, eor, get_string, mediainfo, udB, ultroid_cmd, events, ultroid_bot, something + +# Functions moved from greetings_db.py +def get_stuff(key=None): + return udB.get_key(key) or {} + + +def add_welcome(chat, msg, media, button): + ok = get_stuff("WELCOME") + ok.update({chat: {"welcome": msg, "media": media, "button": button}}) + return udB.set_key("WELCOME", ok) + + +def get_welcome(chat): + ok = get_stuff("WELCOME") + return ok.get(chat) + + +def delete_welcome(chat): + ok = get_stuff("WELCOME") + if ok.get(chat): + ok.pop(chat) + return udB.set_key("WELCOME", ok) + + +def add_goodbye(chat, msg, media, button): + ok = get_stuff("GOODBYE") + ok.update({chat: {"goodbye": msg, "media": media, "button": button}}) + return udB.set_key("GOODBYE", ok) + + +def get_goodbye(chat): + ok = get_stuff("GOODBYE") + return ok.get(chat) + + +def delete_goodbye(chat): + ok = get_stuff("GOODBYE") + if ok.get(chat): + ok.pop(chat) + return udB.set_key("GOODBYE", ok) + + +def add_thanks(chat): + x = get_stuff("THANK_MEMBERS") + x.update({chat: True}) + return udB.set_key("THANK_MEMBERS", x) + + +def remove_thanks(chat): + x = get_stuff("THANK_MEMBERS") + if x.get(chat): + x.pop(chat) + return udB.set_key("THANK_MEMBERS", x) + + +def must_thank(chat): + x = get_stuff("THANK_MEMBERS") + return x.get(chat) + + +Note = "\n\nNote: `{mention}`, `{group}`, `{count}`, `{name}`, `{fullname}`, `{username}`, `{userid}` can be used as formatting parameters.\n\n" + + +@ultroid_cmd(pattern="setwelcome", groups_only=True) +async def setwel(event): + x = await event.eor(get_string("com_1")) + r = await event.get_reply_message() + btn = format_btn(r.buttons) if (r and r.buttons) else None + try: + text = event.text.split(maxsplit=1)[1] + except IndexError: + text = r.text if r else None + if r and r.media: + wut = mediainfo(r.media) + if wut.startswith(("pic", "gif")): + dl = await r.download_media() + m = uf(dl) + os.remove(dl) + elif wut == "video": + if r.media.document.size > 8 * 1000 * 1000: + return await eor(x, get_string("com_4"), time=5) + dl = await r.download_media() + m = uf(dl) + os.remove(dl) + elif wut == "web": + m = None + else: + m = pack_bot_file_id(r.media) + if r.text: + txt = r.text + if not btn: + txt, btn = get_msg_button(r.text) + add_welcome(event.chat_id, txt, m, btn) + else: + add_welcome(event.chat_id, None, m, btn) + await eor(x, get_string("grt_1")) + elif text: + if not btn: + txt, btn = get_msg_button(text) + add_welcome(event.chat_id, txt, None, btn) + await eor(x, get_string("grt_1")) + else: + await eor(x, get_string("grt_3"), time=5) + + +@ultroid_cmd(pattern="clearwelcome$", groups_only=True) +async def clearwel(event): + if not get_welcome(event.chat_id): + return await event.eor(get_string("grt_4"), time=5) + delete_welcome(event.chat_id) + await event.eor(get_string("grt_5"), time=5) + + +@ultroid_cmd(pattern="getwelcome$", groups_only=True) +async def listwel(event): + wel = get_welcome(event.chat_id) + if not wel: + return await event.eor(get_string("grt_4"), time=5) + msgg, med = wel["welcome"], wel["media"] + if wel.get("button"): + btn = create_tl_btn(wel["button"]) + return await something(event, msgg, med, btn) + await event.reply(f"**Welcome Note in this chat**\n\n`{msgg}`", file=med) + await event.delete() + + +@ultroid_cmd(pattern="setgoodbye", groups_only=True) +async def setgb(event): + x = await event.eor(get_string("com_1")) + r = await event.get_reply_message() + btn = format_btn(r.buttons) if (r and r.buttons) else None + try: + text = event.text.split(maxsplit=1)[1] + except IndexError: + text = r.text if r else None + if r and r.media: + wut = mediainfo(r.media) + if wut.startswith(("pic", "gif")): + dl = await r.download_media() + m = uf(dl) + os.remove(dl) + elif wut == "video": + if r.media.document.size > 8 * 1000 * 1000: + return await eor(x, get_string("com_4"), time=5) + dl = await r.download_media() + m = uf(dl) + os.remove(dl) + elif wut == "web": + m = None + else: + m = pack_bot_file_id(r.media) + if r.text: + txt = r.text + if not btn: + txt, btn = get_msg_button(r.text) + add_goodbye(event.chat_id, txt, m, btn) + else: + add_goodbye(event.chat_id, None, m, btn) + await eor(x, "`Goodbye note saved`") + elif text: + if not btn: + txt, btn = get_msg_button(text) + add_goodbye(event.chat_id, txt, None, btn) + await eor(x, "`Goodbye note saved`") + else: + await eor(x, get_string("grt_7"), time=5) + + +@ultroid_cmd(pattern="cleargoodbye$", groups_only=True) +async def clearwgb(event): + if not get_goodbye(event.chat_id): + return await event.eor(get_string("grt_6"), time=5) + delete_goodbye(event.chat_id) + await event.eor("`Goodbye Note Deleted`", time=5) + + +@ultroid_cmd(pattern="getgoodbye$", groups_only=True) +async def listgd(event): + wel = get_goodbye(event.chat_id) + if not wel: + return await event.eor(get_string("grt_6"), time=5) + msgg = wel["goodbye"] + med = wel["media"] + if wel.get("button"): + btn = create_tl_btn(wel["button"]) + return await something(event, msgg, med, btn) + await event.reply(f"**Goodbye Note in this chat**\n\n`{msgg}`", file=med) + await event.delete() + + +@ultroid_cmd(pattern="thankmembers (on|off)", groups_only=True) +async def thank_set(event): + type_ = event.pattern_match.group(1).strip() + if not type_ or type_ == "": + await eor( + event, + f"**Current Chat Settings:**\n**Thanking Members:** `{must_thank(event.chat_id)}`\n\nUse `{HNDLR}thankmembers on` or `{HNDLR}thankmembers off` to toggle current settings!", + ) + return + chat = event.chat_id + if type_.lower() == "on": + add_thanks(chat) + elif type_.lower() == "off": + remove_thanks(chat) + await eor( + event, + f"**Done! Thank you members has been turned** `{type_.lower()}` **for this chat**!", + ) + +# Add handlers for welcome and goodbye messages +async def welcome_handler(ult): + if not (ult.user_joined or ult.user_added): + return + if get_welcome(ult.chat_id): + user = await ult.get_user() + chat = await ult.get_chat() + title = chat.title or "this chat" + count = ( + chat.participants_count + or (await ult.client.get_participants(chat, limit=0)).total + ) + mention = inline_mention(user) + name = user.first_name + fullname = get_display_name(user) + uu = user.username + username = f"@{uu}" if uu else mention + userid = user.id + wel = get_welcome(ult.chat_id) + msgg = wel["welcome"] + med = wel["media"] or None + msg = None + if msgg: + msg = msgg.format( + mention=mention, + group=title, + count=count, + name=name, + fullname=fullname, + username=username, + userid=userid, + ) + if wel.get("button"): + btn = create_tl_btn(wel["button"]) + await something(ult, msg, med, btn) + elif msg: + send = await ult.reply( + msg, + file=med, + ) + await asyncio.sleep(150) + await send.delete() + else: + await ult.reply(file=med) + +async def goodbye_handler(ult): + if not (ult.user_left or ult.user_kicked): + return + if get_goodbye(ult.chat_id): + user = await ult.get_user() + chat = await ult.get_chat() + title = chat.title or "this chat" + count = ( + chat.participants_count + or (await ult.client.get_participants(chat, limit=0)).total + ) + mention = inline_mention(user) + name = user.first_name + fullname = get_display_name(user) + uu = user.username + username = f"@{uu}" if uu else mention + userid = user.id + wel = get_goodbye(ult.chat_id) + msgg = wel["goodbye"] + med = wel["media"] + msg = None + if msgg: + msg = msgg.format( + mention=mention, + group=title, + count=count, + name=name, + fullname=fullname, + username=username, + userid=userid, + ) + if wel.get("button"): + btn = create_tl_btn(wel["button"]) + await something(ult, msg, med, btn) + elif msg: + send = await ult.reply( + msg, + file=med, + ) + await asyncio.sleep(150) + await send.delete() + else: + await ult.reply(file=med) + +# Thank members handler +async def thank_members_handler(ult): + if must_thank(ult.chat_id): + chat_count = (await ult.client.get_participants(ult.chat_id, limit=0)).total + if chat_count % 100 == 0: + stik_id = chat_count / 100 - 1 + sticker = stickers[stik_id] + await ult.respond(file=sticker) + +# Register handlers +ultroid_bot.add_handler(welcome_handler, events.ChatAction()) +ultroid_bot.add_handler(goodbye_handler, events.ChatAction()) +ultroid_bot.add_handler(thank_members_handler, events.ChatAction()) diff --git a/addons/hack.py b/addons/hack.py new file mode 100644 index 0000000..1b0a553 --- /dev/null +++ b/addons/hack.py @@ -0,0 +1,134 @@ +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +""" +โœ˜ Commands Available + +โ€ข `{i}hack` + Do a Prank With Replied user. + +""" + +import asyncio +import random + +from . import * + + +@ultroid_cmd(pattern="hack") +async def _(event): + animation_interval = 0.7 + animation_ttl = range(0, 11) + xx = await event.eor("Installing..") + animation_chars = [ + "`Installing Files To Hacked Private Server...`", + "`Target Selected.`", + "`Installing... 0%\nโ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’ `", + "`Installing... 4%\nโ–ˆโ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’ `", + "`Installing... 8%\nโ–ˆโ–ˆโ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’ `", + "`lnstallig... 20%\nโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’ `", + "`Installing... 36%\nโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’ `", + "`Installing... 52%\nโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’โ–’ `", + "`Installing... 84%\nโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–’โ–’โ–’โ–’ `", + "`Installing... 100%\nโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆInstalledโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ `", + "`Target files Uploading...\n\nDirecting To Remote server to hack..`", + ] + for i in animation_ttl: + await asyncio.sleep(animation_interval) + await xx.edit(animation_chars[i % 11]) + await asyncio.sleep(2) + animation_interval = 0.6 + animation_ttl = range(0, 14) + await xx.edit("`Connecting nd getting combined token from my.telegram.org`") + await asyncio.sleep(1) + animation_chars = [ + "`root@anon:~#` ", + "`root@anon:~# ls`", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~#`", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~# # So Let's Hack it ...`", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~# # So Let's Hack it ...\nroot@anon:~# `", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~# # So Let's Hack it ...\nroot@anon:~# touch setup.py`", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~# # So Let's Hack it ...\nroot@anon:~# touch setup.py\n\nsetup.py deployed ...`", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~# # So Let's Hack it ...\nroot@anon:~# touch setup.py\n\nsetup.py deployed ...\nAuto CMD deployed ...`", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~# # So Let's Hack it ...\nroot@anon:~# touch setup.py\n\nsetup.py deployed ...\nAuto CMD deployed ...\n\nroot@anon:~# trap whoami`", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~# # So Let's Hack it ...\nroot@anon:~# touch setup.py\n\nsetup.py deployed ...\nAuto CMD deployed ...\n\nroot@anon:~# trap whoami\n\nwhoami=user`", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~# # So Let's Hack it ...\nroot@anon:~# touch setup.py\n\nsetup.py deployed ...\nAuto CMD deployed ...\n\nroot@anon:~# trap whoami\n\nwhoami=user\nboost_trap on force ...`", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~# # So Let's Hack it ...\nroot@anon:~# touch setup.py\n\nsetup.py deployed ...\nAuto CMD deployed ...\n\nroot@anon:~# trap whoami\n\nwhoami=user\nboost_trap on force ...\nvictim detected in ghost ...`", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~# # So Let's Hack it ...\nroot@anon:~# touch setup.py\n\nsetup.py deployed ...\nAuto CMD deployed ...\n\nroot@anon:~# trap whoami\n\nwhoami=user\nboost_trap on force ...\nvictim detected in ghost ...\n\nAll Done!`", + "`root@anon:~# ls\n\n usr ghost codes \n\nroot@aono:~# # So Let's Hack it ...\nroot@anon:~# touch setup.py\n\nsetup.py deployed ...\nAuto CMD deployed ...\n\nroot@anon:~# trap whoami\n\nwhoami=user\nboost_trap on force ...\nvictim detected in ghost ...\n\nAll Done!\nInstalling Token!\nToken=`DJ65gulO90P90nlkm65dRfc8I`", + ] + for i in animation_ttl: + await asyncio.sleep(animation_interval) + await xx.edit(animation_chars[i % 14]) + await asyncio.sleep(2) + await xx.edit("`starting telegram hack`") + await asyncio.sleep(1) + # credit to kraken,sawan + await xx.edit( + "`hacking... 0%completed.\nTERMINAL:\nDownloading Bruteforce-Telegram-0.1.tar.gz (1.3) kB`" + ) + await asyncio.sleep(2) + await xx.edit( + " `hacking... 4% completed\n TERMINAL:\nDownloading Bruteforce-Telegram-0.1.tar.gz (9.3 kB)\nCollecting Data Package`" + ) + await asyncio.sleep(1) + await xx.edit( + "`hacking.....6% completed\n TERMINAL:\nDownloading Bruteforce-Telegram-0.1.tar.gz (9.3 kB)\nCollecting Data Packageseeing target account chat\n lding chat tg-bot bruteforce finished`" + ) + await asyncio.sleep(1) + await xx.edit( + "`hacking.....8%completed\n TERMINAL:\nDownloading Bruteforce-Telegram-0.1.tar.gz (9.3 kB)\nCollecting Data Packageseeing target account chat\n lding chat tg-bot bruteforce finished\n creating pdf of chat`" + ) + await asyncio.sleep(1) + await xx.edit( + "`hacking....15%completed\n Terminal:chat history from telegram exporting to private database.\n terminal 874379gvrfghhuu5tlotruhi5rbh installing`" + ) + await asyncio.sleep(1) + await xx.edit( + "`hacking....24%completed\n TERMINAL:\nDownloading Bruteforce-Telegram-0.1.tar.gz (9.3 kB)\nCollecting Data Packageseeing target account chat\n lding chat tg-bot bruteforce finished\nerminal:chat history from telegram exporting to private database.\n terminal 874379gvrfghhuu5tlotruhi5rbh installed\n creting data into pdf`" + ) + await asyncio.sleep(1) + await xx.edit( + "`hacking....32%completed\n looking for use history \n downloading-telegram -id prtggtgf . gfr (12.99 mb)\n collecting data starting imprute attack to user account\n chat history from telegram exporting to private database.\n terminal 874379gvrfghhuu5tlotruhi5rbh installed\n creted data into pdf\nDownload sucessful Bruteforce-Telegram-0.1.tar.gz (1.3)`" + ) + await asyncio.sleep(1) + await xx.edit( + "`hacking....38%completed\n\nDownloading Bruteforce-Telegram-0.1.tar.gz (9.3 kB)\nCollecting Data Package\n Downloading Telegram-Data-Sniffer-7.1.1-py2.py3-none-any.whl (82 kB): finished with status 'done'\nCreated wheel for telegram: filename=Telegram-Data-Sniffer-0.0.1-py3-none-any.whl size=1306 sha256=cb224caad7fe01a6649188c62303cd4697c1869fa12d280570bb6ac6a88e6b7e`" + ) + await asyncio.sleep(1) + await xx.edit( + "`hacking....52%completed\nexterting data from telegram private server\ndone with status 36748hdeg \n checking for more data in device`" + ) + await asyncio.sleep(2) + await xx.edit( + "`hacking....60%completed\nmore data found im target device\npreparing to download data\n process started with status 7y75hsgdt365ege56es \n status changed to up`" + ) + await asyncio.sleep(1) + await xx.edit( + "`hacking....73% completed\n downloading data from device\n process completed with status 884hfhjh\nDownloading-0.1.tar.gz (9.3 kB)\nCollecting Data Packageseeing target\n lding chat tg-bot bruteforce finished\n creating pdf of chat`" + ) + await asyncio.sleep(1) + await xx.edit( + "`hacking...88%completed\nall data from telegram private server downloaded\nterminal download sucessfull--with status jh3233fdg66y yr4vv.irh\n data collected from tg-bot\nTERMINAL:\n Bruteforce-Telegram-0.1.tar.gz (1.3)downloaded`" + ) + await asyncio.sleep(0.5) + await xx.edit( + "`100%\nโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆHACKEDโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ `\n\n\n TERMINAL:\nDownloading Bruteforce-Telegram-0.1.tar.gz (9.3 kB)\nCollecting Data Package\n Downloading Telegram-Data-Sniffer-7.1.1-py2.py3-none-any.whl (82 kB)\nBuilding wheel for Tg-Bruteforcing (setup.py): finished with status 'done'\nCreated wheel for telegram: filename=Telegram-Data-Sniffer-0.0.1-py3-none-any.whl size=1306 sha256=cb224caad7fe01a6649188c62303cd4697c1869fa12d280570bb6ac6a88e6b7e\n Stored in directory: `" + ) + await asyncio.sleep(2) + await xx.edit("`account hacked\n collecting all data\n converting data into pdf`") + await asyncio.sleep(1) + sub = "https://drive.google.com/file/d/" + LINKS = [ + "1JNA0HY1v8ClBDU9PhmyQ-z8KuLgvteT5/view?usp=sharing", + "1HXclQumyRIRy9STTiHcTAHpSMM2mj5ZF/view?usp=sharing", + ] + ME = sub + LINKS[random.randrange(0, len(LINKS))] + MSG = "`pdf created click link below to download data\n\n" + MSG += " Don't worry only i can open this ๐Ÿ˜Ž๐Ÿ˜Ž.. If u don't" + MSG += f" Believe me, try to download` ๐Ÿ™‚\n\n{ME}" + await xx.edit(MSG) diff --git a/addons/howto.py b/addons/howto.py new file mode 100644 index 0000000..8018438 --- /dev/null +++ b/addons/howto.py @@ -0,0 +1,38 @@ +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +""" +โœ˜ Commands Available - + +โ€ข `{i}htg ` + How To Google. + Some peoples don't know how to google so help them ๐Ÿ™ƒ๐Ÿ™‚. + +โ€ข `{i}htd ` + How to duck duck go... +""" + + +from . import ultroid_cmd, async_searcher + + +API = {"g": "lmgtfy.com/?q={}%26iie=1", "d": "lmddgtfy.net/?q={}"} + + +@ultroid_cmd(pattern="ht(g|d)( ?(.*)|$)") +async def _(e): + key = e.pattern_match.group(1) + text = e.pattern_match.group(2) + if not text: + return await e.eor("`Give some text`", time=5) + url = "https://da.gd/s?url=https://" + API[key].format(text.replace(" ", "+")) + response = await async_searcher(url) + if response: + return await e.eor( + "[{}]({})\n`Thank me Later ๐Ÿ™ƒ` ".format(text, response.rstrip()), time=8 + ) + await e.eor("`something is wrong. please try again later.`") diff --git a/addons/imagetools.py b/addons/imagetools.py new file mode 100644 index 0000000..22fd011 --- /dev/null +++ b/addons/imagetools.py @@ -0,0 +1,292 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +""" +โœ˜ Commands Available - + +โ€ข `{i}border ` + To create border around that media.. + Ex - `{i}border 12,22,23` + - `{i}border 12,22,23 ; width (in number)` + +โ€ข `{i}grey ` + To make it black nd white. + +โ€ข `{i}color ` + To make it Colorfull. + +โ€ข `{i}toon ` + To make it toon. + +โ€ข `{i}danger ` + To make it look Danger. + +โ€ข `{i}negative ` + To make negative image. + +โ€ข `{i}blur ` + To make it blurry. + +โ€ข `{i}quad ` + create a Vortex. + +โ€ข `{i}mirror ` + To create mirror pic. + +โ€ข `{i}flip ` + To make it flip. + +โ€ข `{i}sketch ` + To draw its sketch. + +โ€ข `{i}blue ` + just cool. + +โ€ข `{i}csample ` + example : `{i}csample red` + `{i}csample #ffffff` + +โ€ข `{i}pixelator ` + Create a Pixelated Image.. +""" +import os + +from . import LOGS, con + +try: + import cv2 +except ImportError: + LOGS.error(f"{__file__}: OpenCv not Installed.") + +import numpy as np + +try: + from PIL import Image +except ImportError: + Image = None + LOGS.info(f"{__file__}: PIL not Installed.") + +from . import upload_file as upf +from telethon.errors.rpcerrorlist import ( + ChatSendMediaForbiddenError, + MessageDeleteForbiddenError, +) + +from . import ( + Redis, + async_searcher, + download_file, + get_string, + requests, + udB, + ultroid_cmd, +) + + +@ultroid_cmd(pattern="color$") +async def _(event): + reply = await event.get_reply_message() + if not (reply and reply.media): + return await event.eor("`Reply To a Black and White Image`") + xx = await event.eor("`Coloring image ๐ŸŽจ๐Ÿ–Œ๏ธ...`") + image = await reply.download_media() + img = cv2.VideoCapture(image) + ret, frame = img.read() + cv2.imwrite("ult.jpg", frame) + if udB.get_key("DEEP_API"): + key = Redis("DEEP_API") + else: + key = "quickstart-QUdJIGlzIGNvbWluZy4uLi4K" + r = requests.post( + "https://api.deepai.org/api/colorizer", + files={"image": open("ult.jpg", "rb")}, + headers={"api-key": key}, + ) + os.remove("ult.jpg") + os.remove(image) + if "status" in r.json(): + return await event.edit( + r.json()["status"] + "\nGet api nd set `{i}setdb DEEP_API key`" + ) + r_json = r.json()["output_url"] + await event.client.send_file(event.chat_id, r_json, reply_to=reply) + await xx.delete() + + +@ultroid_cmd(pattern="(grey|blur|negative|danger|mirror|quad|sketch|flip|toon)$") +async def ult_tools(event): + match = event.pattern_match.group(1) + ureply = await event.get_reply_message() + if not (ureply and (ureply.media)): + await event.eor(get_string("cvt_3")) + return + ultt = await ureply.download_media() + xx = await event.eor(get_string("com_1")) + if ultt.endswith(".tgs"): + xx = await xx.edit(get_string("sts_9")) + file = await con.convert(ultt, convert_to="png", outname="ult") + ult = cv2.imread(file) + if match == "grey": + ultroid = cv2.cvtColor(ult, cv2.COLOR_BGR2GRAY) + elif match == "blur": + ultroid = cv2.GaussianBlur(ult, (35, 35), 0) + elif match == "negative": + ultroid = cv2.bitwise_not(ult) + elif match == "danger": + dan = cv2.cvtColor(ult, cv2.COLOR_BGR2RGB) + ultroid = cv2.cvtColor(dan, cv2.COLOR_HSV2BGR) + elif match == "mirror": + ish = cv2.flip(ult, 1) + ultroid = cv2.hconcat([ult, ish]) + elif match == "flip": + trn = cv2.flip(ult, 1) + ish = cv2.rotate(trn, cv2.ROTATE_180) + ultroid = cv2.vconcat([ult, ish]) + elif match == "quad": + ult = cv2.imread(file) + roid = cv2.flip(ult, 1) + mici = cv2.hconcat([ult, roid]) + fr = cv2.flip(mici, 1) + trn = cv2.rotate(fr, cv2.ROTATE_180) + ultroid = cv2.vconcat([mici, trn]) + elif match == "sketch": + gray_image = cv2.cvtColor(ult, cv2.COLOR_BGR2GRAY) + inverted_gray_image = 255 - gray_image + blurred_img = cv2.GaussianBlur(inverted_gray_image, (21, 21), 0) + inverted_blurred_img = 255 - blurred_img + ultroid = cv2.divide(gray_image, inverted_blurred_img, scale=256.0) + elif match == "toon": + height, width, _ = ult.shape + samples = np.zeros([height * width, 3], dtype=np.float32) + count = 0 + for x in range(height): + for y in range(width): + samples[count] = ult[x][y] + count += 1 + _, labels, centers = cv2.kmeans( + samples, + 12, + None, + (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10000, 0.0001), + 5, + cv2.KMEANS_PP_CENTERS, + ) + centers = np.uint8(centers) + ish = centers[labels.flatten()] + ultroid = ish.reshape(ult.shape) + cv2.imwrite("ult.jpg", ultroid) + await ureply.reply( + file="ult.jpg", + force_document=False, + ) + await xx.delete() + os.remove("ult.jpg") + os.remove(file) + + +@ultroid_cmd(pattern="csample (.*)") +async def sampl(ult): + if color := ult.pattern_match.group(1).strip(): + img = Image.new("RGB", (200, 100), f"{color}") + img.save("csample.png") + try: + try: + await ult.delete() + await ult.respond(f"Colour Sample for `{color}` !", file="csample.png") + except MessageDeleteForbiddenError: + await ult.reply(f"Colour Sample for `{color}` !", file="csample.png") + except ChatSendMediaForbiddenError: + await ult.eor("Umm! Sending Media is disabled here!") + + else: + await ult.eor("Wrong Color Name/Hex Code specified!") + + +@ultroid_cmd( + pattern="blue$", +) +async def ultd(event): + ureply = await event.get_reply_message() + xx = await event.eor("`...`") + if not (ureply and (ureply.media)): + await xx.edit(get_string("cvt_3")) + return + ultt = await ureply.download_media() + if ultt.endswith(".tgs"): + await xx.edit(get_string("sts_9")) + file = await con.convert(ultt, convert_to="png", outname="ult") + lnk = upf(file) + r = await async_searcher( + f"https://nekobot.xyz/api/imagegen?type=blurpify&image={lnk}", re_json=True + ) + ms = r.get("message") + if not r["success"]: + return await xx.edit(ms) + await download_file(ms, "ult.png") + img = Image.open("ult.png").convert("RGB") + img.save("ult.webp", "webp") + await event.client.send_file( + event.chat_id, + "ult.webp", + force_document=False, + reply_to=event.reply_to_msg_id, + ) + await xx.delete() + os.remove("ult.png") + os.remove("ult.webp") + os.remove(ultt) + + +@ultroid_cmd(pattern="border( (.*)|$)") +async def ok(event): + hm = await event.get_reply_message() + if not (hm and (hm.photo or hm.sticker)): + return await event.eor("`Reply to Sticker or Photo..`") + col = event.pattern_match.group(1).strip() + wh = 20 + if not col: + col = [255, 255, 255] + else: + try: + if ";" in col: + col_ = col.split(";", maxsplit=1) + wh = int(col_[1]) + col = col_[0] + col = [int(col) for col in col.split(",")[:2]] + except ValueError: + return await event.eor("`Not a Valid Input...`") + okla = await hm.download_media() + img1 = cv2.imread(okla) + constant = cv2.copyMakeBorder(img1, wh, wh, wh, wh, cv2.BORDER_CONSTANT, value=col) + cv2.imwrite("output.png", constant) + await event.client.send_file(event.chat.id, "output.png") + os.remove("output.png") + os.remove(okla) + await event.delete() + + +@ultroid_cmd(pattern="pixelator( (.*)|$)") +async def pixelator(event): + reply_message = await event.get_reply_message() + if not (reply_message and (reply_message.photo or reply_message.sticker)): + return await event.eor("`Reply to a photo`") + hw = 50 + try: + hw = int(event.pattern_match.group(1).strip()) + except (ValueError, TypeError): + pass + msg = await event.eor(get_string("com_1")) + image = await reply_message.download_media() + input_ = cv2.imread(image) + height, width = input_.shape[:2] + w, h = (hw, hw) + temp = cv2.resize(input_, (w, h), interpolation=cv2.INTER_LINEAR) + output = cv2.resize(temp, (width, height), interpolation=cv2.INTER_NEAREST) + cv2.imwrite("output.jpg", output) + await msg.respond("โ€ข Pixelated by Ultroid", file="output.jpg") + await msg.delete() + os.remove("output.jpg") + os.remove(image) diff --git a/addons/imdb.py b/addons/imdb.py new file mode 100644 index 0000000..fee6a6b --- /dev/null +++ b/addons/imdb.py @@ -0,0 +1,27 @@ +# Ultroid Userbot +# + +""" +Search movie details from IMDB + +โœ˜ Commands Available +โ€ข `{i}imdb ` +""" + +from . import * + + +@ultroid_cmd(pattern="imdb ?(.*)") +async def imdb(e): + m = await e.eor("`...`") + movie_name = e.pattern_match.group(1) + if not movie_name: + return await eor(m, "`Provide a movie name too`") + try: + mk = await e.client.inline_query("imdbot", movie_name) + await mk[0].click(e.chat_id) + await m.delete() + except IndexError: + return await eor(m, "No Results Found...") + except Exception as er: + return await eor(m, str(er)) \ No newline at end of file diff --git a/addons/inline/__init__.py b/addons/inline/__init__.py new file mode 100644 index 0000000..7aab8e9 --- /dev/null +++ b/addons/inline/__init__.py @@ -0,0 +1 @@ +from .. import * diff --git a/addons/inline/__pycache__/__init__.cpython-312.pyc b/addons/inline/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..bac11af Binary files /dev/null and b/addons/inline/__pycache__/__init__.cpython-312.pyc differ diff --git a/addons/inline/__pycache__/ghfeeds.cpython-312.pyc b/addons/inline/__pycache__/ghfeeds.cpython-312.pyc new file mode 100644 index 0000000..09abb8a Binary files /dev/null and b/addons/inline/__pycache__/ghfeeds.cpython-312.pyc differ diff --git a/addons/inline/__pycache__/imdb.cpython-312.pyc b/addons/inline/__pycache__/imdb.cpython-312.pyc new file mode 100644 index 0000000..082515d Binary files /dev/null and b/addons/inline/__pycache__/imdb.cpython-312.pyc differ diff --git a/addons/inline/__pycache__/koo.cpython-312.pyc b/addons/inline/__pycache__/koo.cpython-312.pyc new file mode 100644 index 0000000..bb7ac70 Binary files /dev/null and b/addons/inline/__pycache__/koo.cpython-312.pyc differ diff --git a/addons/inline/__pycache__/npmsearch.cpython-312.pyc b/addons/inline/__pycache__/npmsearch.cpython-312.pyc new file mode 100644 index 0000000..7f88eeb Binary files /dev/null and b/addons/inline/__pycache__/npmsearch.cpython-312.pyc differ diff --git a/addons/inline/__pycache__/omgubuntu.cpython-312.pyc b/addons/inline/__pycache__/omgubuntu.cpython-312.pyc new file mode 100644 index 0000000..368a069 Binary files /dev/null and b/addons/inline/__pycache__/omgubuntu.cpython-312.pyc differ diff --git a/addons/inline/__pycache__/pypi.cpython-312.pyc b/addons/inline/__pycache__/pypi.cpython-312.pyc new file mode 100644 index 0000000..5175ecf Binary files /dev/null and b/addons/inline/__pycache__/pypi.cpython-312.pyc differ diff --git a/addons/inline/__pycache__/winget.cpython-312.pyc b/addons/inline/__pycache__/winget.cpython-312.pyc new file mode 100644 index 0000000..c8d19d0 Binary files /dev/null and b/addons/inline/__pycache__/winget.cpython-312.pyc differ diff --git a/addons/inline/__pycache__/xdasearch.cpython-312.pyc b/addons/inline/__pycache__/xdasearch.cpython-312.pyc new file mode 100644 index 0000000..47df49a Binary files /dev/null and b/addons/inline/__pycache__/xdasearch.cpython-312.pyc differ diff --git a/addons/inline/ghfeeds.py b/addons/inline/ghfeeds.py new file mode 100644 index 0000000..99565d9 --- /dev/null +++ b/addons/inline/ghfeeds.py @@ -0,0 +1,111 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2022 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + + +from telethon.tl.custom import Button +from . import in_pattern, InlinePlugin, async_searcher, asst +from telethon.tl.types import InputWebDocument + +__doc__ = f""" +โœ˜ Commands Available - +โ€ข `@{asst.username} gh .` + Searches for the Github username and returns the latest feeds. + End your query with a dot (.) to search. +""" + +@in_pattern("gh", owner=True) +async def gh_feeds(ult): + try: + username = ult.text.split(maxsplit=1)[1] + except IndexError: + await ult.answer( + [], + switch_pm="Enter Github Username to see feeds...", + switch_pm_param="start", + ) + return + if not username.endswith("."): + return await ult.answer( + [], switch_pm="End your query with . to search...", switch_pm_param="start" + ) + username = username[:-1] + data = await async_searcher( + f"https://api.github.com/users/{username}/events", re_json=True + ) + if not isinstance(data, list): + msg = "".join(f"{ak}: `{data[ak]}" + "`\n" for ak in list(data.keys())) + return await ult.answer( + [ + await ult.builder.article( + title=data["message"], text=msg, link_preview=False + ) + ], + cache_time=300, + switch_pm="Error!!!", + switch_pm_param="start", + ) + res = [] + res_ids = [] + for cont in data[:50]: + text = f"@{username}" + title = f"@{username}" + extra = None + if cont["type"] == "PushEvent": + text += " pushed in" + title += " pushed in" + dt = cont["payload"]["commits"][-1] + url = "https://github.com/" + dt["url"].split("/repos/")[-1] + extra = f"\n-> message: {dt['message']}" + elif cont["type"] == "IssueCommentEvent": + title += " commented at" + text += " commented at" + url = cont["payload"]["comment"]["html_url"] + elif cont["type"] == "CreateEvent": + title += " created" + text += " created" + url = "https://github.com/" + cont["repo"]["name"] + elif cont["type"] == "PullRequestEvent": + if ( + cont["payload"]["pull_request"].get("user", {}).get("login") + != username.lower() + ): + continue + url = cont["payload"]["pull_request"]["html_url"] + text += " created a pull request in" + title += " created a pull request in" + elif cont["type"] == "ForkEvent": + text += " forked" + title += " forked" + url = cont["payload"]["forkee"]["html_url"] + else: + continue + repo = cont["repo"]["name"] + repo_url = f"https://github.com/{repo}" + title += f" {repo}" + text += f" {repo}" + if extra: + text += extra + thumb = InputWebDocument(cont["actor"]["avatar_url"], 0, "image/jpeg", []) + article = await ult.builder.article( + title=title, + text=text, + url=repo_url, + parse_mode="html", + link_preview=False, + thumb=thumb, + buttons=[ + Button.url("View", url), + Button.switch_inline("Search again", query=ult.text, same_peer=True), + ], + ) + if article.id not in res_ids: + res_ids.append(article.id) + res.append(article) + msg = f"Showing {len(res)} feeds!" if res else "Nothing Found" + await ult.answer(res, cache_time=5000, switch_pm=msg, switch_pm_param="start") + +InlinePlugin.update({"Gษชแด›Hแดœส™ า“แด‡แด‡แด…s": "gh"}) diff --git a/addons/inline/imdb.py b/addons/inline/imdb.py new file mode 100644 index 0000000..e5d28c8 --- /dev/null +++ b/addons/inline/imdb.py @@ -0,0 +1,295 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2024 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +import hashlib +import json +import re + +import requests +from bs4 import BeautifulSoup +from telethon import Button + +try: + from PIL import Image +except ImportError: + Image = None + +from telethon.tl.types import InputWebDocument as wb + +from . import LOGS, callback, in_pattern, udB, async_searcher, asst + +__doc__ = f""" +โœ˜ Commands Available - +โ€ข `@{asst.username} imdb ` + Searches for the movie on IMDb and returns the results. +โ€ข `@{asst.username} imdb y=` + Searches for the movie on IMDb by year and returns the results. +""" + +# Define your OMDB API key +OMDB_API_KEY = udB.get_key("OMDb_API") #OpenMovies Database get free key from http://www.omdbapi.com/ with 1000 dailiy uses +imdbp = "https://graph.org/file/3b45a9ed4868167954300.jpg" + +LIST = {} +hash_to_url = {} + + +def generate_unique_id(url): + hashed_id = hashlib.sha256(url.encode()).hexdigest()[:8] + hash_to_url[hashed_id] = url + return hashed_id + + +def get_original_url(hashed_id): + return hash_to_url.get(hashed_id) + + +async def get_movie_data(search_term, full_plot=False): + if "y=" in search_term: + parts = search_term.split("y=") + if parts: + LOGS.info(f"YEAR_prts: {parts}") + movie_name = parts[0].strip() + if movie_name: + year = parts[1].strip() if len(parts) > 1 else None + if year: + SBY = True + else: + SBY = False + else: + SBY = False + movie_name = search_term + else: + SBY = False + movie_name = search_term + else: + SBY = False + movie_name = search_term + + url = f"http://www.omdbapi.com/?apikey={OMDB_API_KEY}&t={movie_name}" + + if SBY is True: + url += f"&y={year}" + if full_plot is True: + url += "&plot=full" + + data = await async_searcher(url, re_json=True) + if data.get("Response") == "True": + return data + else: + LOGS.info("Error: Unable to fetch movie data") + return None + + +def get_trailer(imdbID): + url = f"https://www.imdb.com/title/{imdbID}/" + headers = {"User-Agent": "Mozilla/5.0"} + + response = requests.get(url, headers=headers) + if response.status_code == 200: + soup = BeautifulSoup(response.content, "html.parser") + script = soup.find("script", type="application/ld+json") + data = json.loads(script.string) + trailer_url = data.get("trailer", {}).get("embedUrl") + if trailer_url: + LOGS.info(f"Trailer URL: {trailer_url}") + return f"{trailer_url}" + else: + LOGS.info("Could not find trailer link") + return None + + else: + LOGS.info("Error: Unable to fetch IMDb page") + return None + + +@in_pattern("imdb", owner=False) +async def inline_imdb_command(event): + try: + movie_name = event.text.split(" ", maxsplit=1)[1] + LOGS.info(f"QUERY\n{movie_name}") + except IndexError: + indexarticle = event.builder.article( + title="Sแด‡แด€ส€แด„สœ Sแดแดแด‡แด›สœษชษดษข", + thumb=wb(imdbp, 0, "image/jpeg", []), + text="**Iแดแด…ส™ Sแด‡แด€ส€แด„สœ**\n\nสแดแดœ แด…ษชแด…ษด'แด› sแด‡แด€ส€แด„สœ แด€ษดสแด›สœษชษดษข", + buttons=[ + Button.switch_inline( + "Sแด‡แด€ส€แด„สœ Aษขแด€ษชษด", + query="imdb ", + same_peer=True, + ), + Button.switch_inline( + "Sแด‡แด€ส€แด„สœ Bส Yแด‡แด€ส€", + query="imdb IF y= 2024 ", + same_peer=True, + ), + ], + ) + await event.answer([indexarticle]) + return + + try: + movie_data = await get_movie_data(movie_name) + if movie_data: + title = movie_data.get("Title", "") + year = movie_data.get("Year", "") + rated = movie_data.get("Rated", "") + released = movie_data.get("Released", "") + runtime = movie_data.get("Runtime", "") + ratings = movie_data.get("Ratings", "") + ratings_str = ", ".join( + [f"{rating['Source']}: `{rating['Value']}`" for rating in ratings] + ) + genre = movie_data.get("Genre", "") + director = movie_data.get("Director", "") + actors = movie_data.get("Actors", "") + plot = movie_data.get("Plot", "") + language = movie_data.get("Language", "") + country = movie_data.get("Country", "") + awards = movie_data.get("Awards", "") + poster_url = movie_data.get("Poster", "") + imdbRating = movie_data.get("imdbRating", "") + imdbVotes = movie_data.get("imdbVotes", "") + BoxOffice = movie_data.get("BoxOffice", "") + imdbID = movie_data.get("imdbID", "") + movie_details = ( + f"**Tษชแด›สŸแด‡:** {title}\n" + f"**Yแด‡แด€ส€:** `{year}`\n" + f"**Rแด€แด›แด‡แด…:** `{rated}`\n" + f"**Rแด‡สŸแด‡แด€sแด‡แด…:** {released}\n" + f"**Rแดœษดแด›ษชแดแด‡:** `{runtime}`\n" + f"**Gแด‡ษดส€แด‡:** {genre}\n" + f"**Dษชส€แด‡แด„แด›แดส€:** {director}\n" + f"**Aแด„แด›แดส€s:** {actors}\n" + f"**PสŸแดแด›:** {plot}\n" + f"**Lแด€ษดษขแดœแด€ษขแด‡:** `{language}`\n" + f"**Cแดแดœษดแด›ส€ส:** {country}\n" + f"**Aแดกแด€ส€แด…s:** {awards}\n" + f"**Rแด€แด›ษชษดษขs:** {ratings_str}\n" + f"**IMDส™ Rแด€แด›ษชษดษข:** `{imdbRating}`\n" + f"**IMDส™ Lษชษดแด‹:** https://www.imdb.com/title/{imdbID}\n" + f"**ษชแดแด…ส™Vแดแด›แด‡s:** `{imdbVotes}`\n" + f"**BแดxOา“า“ษชแด„แด‡:** `{BoxOffice}`" + ) + except Exception as er: + LOGS.info(f"Exception: {er}") + + try: + plot_id = generate_unique_id(movie_details) + except UnboundLocalError: + if " y= " in movie_name: + noresult = movie_name.replace(" y= ", " ") + elif "y= " in movie_name: + noresult = movie_name.replace("y= ", "") + elif "y=" in movie_name: + noresult = movie_name.replace("y=", "") + else: + noresult = movie_name + + return await event.answer( + [ + await event.builder.article( + title="Nแด ส€แด‡sแดœสŸแด›s า“แดแดœษดแด…", + text=f"**IMDส™**\nTส€ส แด€ษดแดแด›สœแด‡ส€ sแด‡แด€ส€แด„สœ", + thumb=wb(imdbp, 0, "image/jpeg", []), + buttons=[ + Button.switch_inline( + "Sแด‡แด€ส€แด„สœ Aษขแด€ษชษด", + query="imdb ", + same_peer=True, + ), + Button.switch_inline( + "Sแด‡แด€ส€แด„สœ Bส Yแด‡แด€ส€", + query=f"imdb {movie_name} y= ", + same_peer=True, + ), + ], + ) + ], + switch_pm=f"{noresult}", + switch_pm_param="start", + ) + except Exception as er: + LOGS.info(f"Exception: {er}") + return + + txt = f"**Tษชแด›สŸแด‡:** {title}\n**Rแด‡สŸแด‡แด€sแด‡แด…:** {released}\n**Cแดแดœษดแด›ส€ส:** {country}" + button = [ + [Button.inline("FแดœสŸสŸ Dแด‡แด›แด€ษชสŸs", data=f"plot_button:{plot_id}")], + [Button.switch_inline("Sแด‡แด€ส€แด„สœ Aษขแด€ษชษด", query="imdb ", same_peer=True)], + ] + + article = await event.builder.article( + type="photo", + text=txt, + title=f"{title}", + include_media=True, + description=f"{released}\nษชแดแด…ส™: {imdbRating}\nLแด€ษดษขแดœแด€ษขแด‡: {language}", + link_preview=False, + thumb=wb(poster_url, 0, "image/jpeg", []), + content=wb(poster_url, 0, "image/jpeg", []), + buttons=button, + ) + LIST.update( + { + plot_id: { + "text": txt, + "buttons": button, + "imdbID": imdbID, + "movie_name": movie_name, + "plot": plot, + } + } + ) + await event.answer([article]) + + +@callback(re.compile("plot_button:(.*)"), owner=False) +async def plot_button_clicked(event): + plot_id = event.data.decode().split(":", 1)[1] + details = get_original_url(plot_id) + plot = LIST[plot_id]["plot"] + imdbID = LIST[plot_id]["imdbID"] + trailer_url = get_trailer(imdbID) + btns = [ + [Button.inline("Back", data=f"imdb_back_button:{plot_id}")], + ] + if trailer_url: + btns.insert(0, [Button.url("Trailer", url=trailer_url)]) + if plot.endswith("..."): + btns.insert( + 0, [Button.inline("Extended Plot", data=f"extended_plot:{plot_id}")] + ) + await event.edit(details, buttons=btns) + + +@callback(re.compile("imdb_back_button:(.*)"), owner=False) +async def back_button_clicked(event): + plot_id = event.data.decode().split(":", 1)[1] + if not LIST.get(plot_id): + return await event.answer("Query Expired! Search again ๐Ÿ”") + text = LIST[plot_id]["text"] + buttons = LIST[plot_id]["buttons"] + await event.edit(text, buttons=buttons) + + +@callback(re.compile("extended_plot:(.*)"), owner=False) +async def extended_plot_button_clicked(event): + plot_id = event.data.decode().split(":", 1)[1] + if not LIST.get(plot_id): + return await event.answer("Query Expired! Search again ๐Ÿ”") + movie_name = LIST[plot_id]["movie_name"] + + ext_plot = await get_movie_data(movie_name, full_plot=True) + fullplot = ext_plot.get("Plot", "") + + if fullplot: + extended_plot = f"**Exแด›แด‡ษดแด…แด‡แด… PสŸแดแด›:** {fullplot}" + btns = [ + [Button.inline("Back", data=f"imdb_back_button:{plot_id}")], + ] + await event.edit(extended_plot, buttons=btns) diff --git a/addons/inline/koo.py b/addons/inline/koo.py new file mode 100644 index 0000000..d8371a3 --- /dev/null +++ b/addons/inline/koo.py @@ -0,0 +1,112 @@ + +# Ultroid - UserBot +# Copyright (C) 2021-2022 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from telethon.tl.types import InputWebDocument as wb +from telethon.tl.custom import Button +from . import in_pattern, InlinePlugin, async_searcher, asst + +__doc__ = f""" +โœ˜ Commands Available - +โ€ข `@{asst.username} koo ` + Searches for the query on Koo and returns the results. +""" + +_koo_ = {} + + +@in_pattern("koo", owner=True) +async def koo_search(ult): + """Search Users on koo with API""" + try: + match = ult.text.split(maxsplit=1)[1].lower() + match_ = match + except IndexError: + await ult.answer( + [], switch_pm="Enter Query to Search..", switch_pm_param="start" + ) + return + if _koo_.get(match): + return await ult.answer( + _koo_[match], switch_pm="โ€ข Koo Search โ€ข", switch_pm_param="start" + ) + res = [] + se_ = None + key_count = None + if " | " in match: + match = match.split(" | ", maxsplit=1) + try: + key_count = int(match[1]) + except ValueError: + pass + match = match[0] + match = match.replace(" ", "+") + req = await async_searcher( + f"https://www.kooapp.com/apiV1/search?query={match}&searchType=EXPLORE", + re_json=True, + ) + if key_count: + try: + se_ = [req["feed"][key_count - 1]] + except KeyError: + pass + if not se_: + se_ = req["feed"] + for count, feed in enumerate(se_[:10]): + if feed["uiItemType"] == "search_profile": + count += 1 + item = feed["items"][0] + profileImage = ( + item["profileImageBaseUrl"] + if item.get("profileImageBaseUrl") + else "https://telegra.ph/file/dc28e69bd7ea2c0f25329.jpg" + ) + extra = await async_searcher( + "https://www.kooapp.com/apiV1/users/handle/" + item["userHandle"], + re_json=True, + ) + img = wb(profileImage, 0, "image/jpeg", []) + text = f"โ€ฃ **Name :** `{item['name']}`" + if extra.get("title"): + text += f"\nโ€ฃ **Title :** `{extra['title']}`" + text += f"\nโ€ฃ **Username :** `@{item['userHandle']}`" + if extra.get("description"): + text += f"\nโ€ฃ **Description :** `{extra['description']}`" + text += f"\nโ€ฃ **Followers :** `{extra['followerCount']}` โ€ฃ **Following :** {extra['followingCount']}" + if extra.get("socialProfile") and extra["socialProfile"].get("website"): + text += f"\nโ€ฃ **Website :** {extra['socialProfile']['website']}" + res.append( + await ult.builder.article( + title=item["name"], + description=item.get("title") or f"@{item['userHandle']}", + type="photo", + content=img, + thumb=img, + include_media=True, + text=text, + buttons=[ + Button.url( + "View", + "https://kooapp.com/profile/" + item["userHandle"], + ), + Button.switch_inline( + "โ€ข Share โ€ข", + query=ult.text if key_count else f"{ult.text} | {count}", + ), + ], + ) + ) + + if not res: + switch = "No Results Found :(" + else: + _koo_.update({match_: res}) + switch = f"Showing {len(res)} Results!" + await ult.answer(res, switch_pm=switch, switch_pm_param="start") + + +InlinePlugin.update({"Kแดแด Sแด‡แด€ส€แด„สœ": "koo @__kumar__amit"}) diff --git a/addons/inline/npmsearch.py b/addons/inline/npmsearch.py new file mode 100644 index 0000000..3996c99 --- /dev/null +++ b/addons/inline/npmsearch.py @@ -0,0 +1,49 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2022 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from telethon.tl.types import InputWebDocument as wb +from telethon.tl.custom import Button +from . import in_pattern, InlinePlugin, async_searcher, asst + +__doc__ = f""" +โœ˜ Commands Available - +โ€ข `@{asst.username} npm ` + Searches for the package on NPM and returns the results. +""" + + +@in_pattern("npm") +async def search_npm(event): + try: + query = event.text.split(maxsplit=1)[1] + except IndexError: + await event.answer([], switch_pm="Enter query to search", switch_pm_param="start" + ) + return + data = await async_searcher(f"https://registry.npmjs.com/-/v1/search?text={query.replace(' ','+')}&size=7", re_json=True) + res = [] + for obj in data["objects"]: + package = obj["package"] + url = package["links"]["npm"] + title = package["name"] + keys = package.get("keywords", []) + text = f"**[{title}]({package['links'].get('homepage', '')})\n{package['description']}**\n" + text += f"**Version:** `{package['version']}`\n" + text += f"**Keywords:** `{','.join(keys)}`" + res.append(await event.builder.article( + title=title, + text=text, + url=url, + link_preview=False, + buttons=[ + Button.url("View", url), + Button.switch_inline("Search again", query=event.text, same_peer=True), + ], + )) + await event.answer(res, switch_pm="NPM Search", switch_pm_param="start") + +InlinePlugin.update({"Npm Search": "npm"}) diff --git a/addons/inline/omgubuntu.py b/addons/inline/omgubuntu.py new file mode 100644 index 0000000..5441575 --- /dev/null +++ b/addons/inline/omgubuntu.py @@ -0,0 +1,72 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2022 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + + +from telethon.tl.custom import Button +from telethon.tl.types import InputWebDocument as wb +from .. import async_searcher, in_pattern, InlinePlugin, asst +from bs4 import BeautifulSoup as bs + +__doc__ = f""" +โœ˜ Commands Available - +โ€ข `@{asst.username} omgu ` + Searches for the query on OMG Ubuntu and returns the results. +""" + +_OMG = {} + +@in_pattern("omgu", owner=True) +async def omgubuntu(ult): + try: + match = ult.text.split(maxsplit=1)[1].lower() + except IndexError: + await ult.answer( + [], switch_pm="Enter Query to search...", switch_pm_param="start" + ) + return + if _OMG.get(match): + return await ult.answer( + _OMG[match], switch_pm="OMG Ubuntu Search :]", switch_pm_param="start" + ) + get_web = "https://www.omgubuntu.co.uk/?s=" + match.replace(" ", "+") + get_ = await async_searcher(get_web, re_content=True) + BSC = bs(get_, "html.parser", from_encoding="utf-8") + res = [] + for cont in BSC.find_all("div", "sbs-layout__item"): + img = cont.find("div", "sbs-layout__image") + url = img.find("a")["href"] + src = img.find("img")["src"] + con = cont.find("div", "sbs-layout__content") + tit = con.find("a", "layout__title-link") + title = tit.text.strip() + desc = con.find("p", "layout__description").text.strip() + text = f"[{title.strip()}]({url})\n\n{desc}" + img = wb(src, 0, "image/jpeg", []) + res.append( + await ult.builder.article( + title=title, + type="photo", + description=desc, + url=url, + text=text, + buttons=Button.switch_inline( + "Search Again", query=ult.text, same_peer=True + ), + include_media=True, + content=img, + thumb=img, + ) + ) + await ult.answer( + res, + switch_pm=f"Showing {len(res)} results!" if res else "No Results Found :[", + switch_pm_param="start", + ) + _OMG[match] = res + + +InlinePlugin.update({"OแดษขUส™แดœษดแด›แดœ": "omgu cutefish"}) diff --git a/addons/inline/pypi.py b/addons/inline/pypi.py new file mode 100644 index 0000000..4b64fb1 --- /dev/null +++ b/addons/inline/pypi.py @@ -0,0 +1,287 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2024 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +from . import * +import hashlib +import inspect +import os +import re +from datetime import datetime +from html import unescape +from random import choice +from re import compile as re_compile +from bs4 import BeautifulSoup as bs + +try: + from markdownify import markdownify as md +except ImportError: + os.system("pip3 install -q markdownify") + from markdownify import markdownify as md + +from telethon import Button +from telethon.tl.alltlobjects import LAYER, tlobjects +from telethon.tl.types import DocumentAttributeAudio as Audio +from telethon.tl.types import InputWebDocument as wb +from telethon.tl.types import MessageEntityTextUrl + +__doc__ = f""" +โœ˜ Commands Available - +โ€ข `@{asst.username} pypi ` + Searches for the package on PyPI and returns the results. +""" + +hash_to_url = {} + + +def generate_unique_id(url): + hashed_id = hashlib.sha256(url.encode()).hexdigest()[:8] + hash_to_url[hashed_id] = url + return hashed_id + + +def get_original_url(hashed_id): + return hash_to_url.get(hashed_id) + + +def clean_desc(description): + # Remove lines starting with ".." + description = re.sub(r"^\.\.", "", description, flags=re.MULTILINE) + # Remove lines starting with "|" + description = re.sub(r"^\|", "", description, flags=re.MULTILINE) + # Remove lines starting with ":" + description = re.sub(r"^:", "", description, flags=re.MULTILINE) + # Remove lines starting with " :" + description = re.sub(r"^ {2}:", "", description, flags=re.MULTILINE) + # Remove lines starting with "/3/" + description = re.sub(r"/\d+/", "", description) + # Remove lines starting with "code-block:: python" + description = re.sub( + r"^\s*code-block::.*$", "", description, flags=re.IGNORECASE | re.MULTILINE + ) + # Remove any remaining leading or trailing whitespace + description = description.strip() + return description + + +PYPI_LIST = {} + + +@in_pattern("pypi") +async def inline_pypi_handler(event): + pypimg = "https://graph.org/file/004c65a44efa1efc85193.jpg" + BASE_URL = "https://pypi.org/pypi/{}/json" + try: + package = event.text.split(" ", maxsplit=1)[1] + except IndexError: + await event.answer( + [ + event.builder.article( + type="photo", + include_media=True, + title="sแด‡แด€ส€แด„สœ แด˜สแด˜ษช", + thumb=wb(pypimg, 0, "image/jpeg", []), + content=wb(pypimg, 0, "image/jpeg", []), + text=f"**แด˜สแด˜ษช sแด‡แด€ส€แด„สœ**\n\nสแดแดœ แด…ษชแด…ษด'แด› sแด‡แด€ส€แด„สœ า“แดส€ แด€ษดสแด›สœษชษดษข.", + buttons=[ + Button.switch_inline( + "sแด‡แด€ส€แด„สœ แด€ษขแด€ษชษด", + query="pypi ", + same_peer=True, + ), + ], + ) + ] + ) + return + + response = await async_searcher(BASE_URL.format(package), re_json=True) + if response is not None and "info" in response: + info = response["info"] + name = info["name"] + url = info["package_url"] + version = info["version"] + summary = info["summary"] + qid = generate_unique_id(name) + txt = f"**แด˜แด€แด„แด‹แด€ษขแด‡:** [{name}]({url}) (`{version}`)\n\n**แด…แด‡แด›แด€ษชสŸs:** `{summary}`" + + offset = txt.find(name) + length = len(name) + url_entity = MessageEntityTextUrl(offset=offset, length=length, url=url) + + # Extract document links from description + document_links = re.findall(r"(https?://\S+)", info["description"]) + + buttons = [ + Button.inline("sสœแดแดก แด…แด‡แด›แด€ษชสŸs", data=f"pypi_details:{qid}"), + Button.inline("แด…แดแด„แดœแดแด‡ษดแด› สŸษชษดแด‹s", data=f"pypi_documents:{qid}"), + ] + + await event.answer( + [ + event.builder.article( + type="photo", + include_media=True, + title="แด˜แด€แด„แด‹แด€ษขแด‡ ษชษดา“แด", + thumb=wb( + "https://graph.org/file/f09380ada91534b2f6687.jpg", + 0, + "image/jpeg", + [], + ), + content=wb( + "https://graph.org/file/f09380ada91534b2f6687.jpg", + 0, + "image/jpeg", + [], + ), + description=f"{name}\n{version}", + text=txt, + buttons=buttons, + ) + ] + ) + + PYPI_LIST.update( + { + qid: { + "info": info, + "name": name, + "url": url, + "version": version, + "summary": summary, + "text": txt, + "document_links": document_links, + "buttons": buttons, + } + } + ) + else: + await event.answer( + [ + event.builder.article( + title="แด˜แด€แด„แด‹แด€ษขแด‡ ษดแดแด› า“แดแดœษดแด…", + thumb=wb(pypimg, 0, "image/jpeg", []), + text=f"**แด˜แด€แด„แด‹แด€ษขแด‡:** `{package}`\n\n**แด…แด‡แด›แด€ษชสŸs:** `ษดแดแด› า“แดแดœษดแด…`", + ) + ] + ) + return + + +@callback(re.compile("pypi_details:(.*)"), owner=False) +async def show_details(event): + qid = event.data.decode().split(":", 1)[1] + if not PYPI_LIST.get(qid): + return await event.answer("Qแดœแด‡ส€ส แด‡xแด˜ษชส€แด‡แด…! Sแด‡แด€ส€แด„สœ แด€ษขแด€ษชษด ๐Ÿ”") + info = PYPI_LIST[qid] + details = info["info"] + + author = details.get("author", "Uษดแด‹ษดแดแดกษด") + author_email = details.get("author_email", "Uษดแด‹ษดแดแดกษด") + classifiers = "\n".join(details.get("classifiers", [])) + description = details.get("description", "N/A") + + formatted_description = md(description) + clean_description = re.sub(r"\*\*|`|\\|_", "", formatted_description) + clean_description = clean_desc(clean_description) + PYPI_LIST[qid]["description"] = clean_description + + text = f"**แด€แดœแด›สœแดส€:** {author}\n" + text += f"**แด€แดœแด›สœแดส€ แด‡แดแด€ษชสŸ:** {author_email}\n" + text += f"**แด„สŸแด€ssษชา“ษชแด‡ส€s:**\n{classifiers}\n" + + if description == "N/A": + buttons = [ + Button.inline("ส™แด€แด„แด‹", data=f"pypi_back_button:{qid}"), + ] + await event.edit(text, buttons=buttons) + else: + buttons = [ + Button.inline("แดแดส€แด‡", data=f"pypi_description_more:{qid}"), + Button.inline("ส™แด€แด„แด‹", data=f"pypi_back_button:{qid}"), + ] + await event.edit(text, buttons=buttons) + + +@callback(re.compile("pypi_documents:(.*)"), owner=True) +async def show_documents(event): + qid = event.data.decode().split(":", 1)[1] + if not PYPI_LIST.get(qid): + return await event.answer("Qแดœแด‡ส€ส แด‡xแด˜ษชส€แด‡แด…! Sแด‡แด€ส€แด„สœ แด€ษขแด€ษชษด ๐Ÿ”") + document_links = PYPI_LIST[qid]["document_links"] + if document_links: + text = "**แด…แดแด„ สŸษชษดแด‹s**\nโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ€ข\n" + text += "\n".join( + [ + f"โ•ฐโžข [{link.split('//')[1].split('/')[0]}]({link})" + for link in document_links + ] + ) + text += "\nโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ€ข" + buttons = [ + Button.inline("ส™แด€แด„แด‹", data=f"pypi_back_button:{qid}"), + ] + await event.edit(text, buttons=buttons) + else: + await event.answer("ษดแด แด…แดแด„แดœแดแด‡ษดแด› สŸษชษดแด‹s า“แดแดœษดแด….") + + +@callback(re.compile("pypi_description_more:(.*)"), owner=True) +async def show_full_description(event): + qid = event.data.decode().split(":", 1)[1] + description = PYPI_LIST[qid].get("description") + if description: + already_defined_text_length = len("แด…แด‡sแด„ส€ษชแด˜แด›ษชแดษด:\nPage X/Y\n") + current_page = 1 + await show_description_with_pagination( + event, qid, description, already_defined_text_length, current_page + ) + + +async def show_description_with_pagination( + event, qid, description, already_defined_text_length, current_page +): + available_length = 1024 - already_defined_text_length + + description_chunks = [ + description[i : i + available_length] + for i in range(0, len(description), available_length) + ] + total_chunks = len(description_chunks) + + text = f"**แด…แด‡sแด„ส€ษชแด˜แด›ษชแดษด:**\n**Pแด€ษขแด‡** `{current_page}`/`{total_chunks}`\n{description_chunks[current_page - 1]}" + buttons = [ + Button.inline("<<", data=f"pypi_description_page:{qid}:{current_page-1}"), + Button.inline("ส™แด€แด„แด‹", data=f"pypi_back_button:{qid}"), + Button.inline(">>", data=f"pypi_description_page:{qid}:{current_page+1}"), + ] + await event.edit(text, buttons=buttons) + + +@callback(re.compile("pypi_description_page:(.*):(\\d+)"), owner=True) +async def handle_description_page(event): + qid, page = event.data.decode().split(":")[1:] + description = PYPI_LIST[qid].get("description") + if description: + already_defined_text_length = len("แด…แด‡sแด„ส€ษชแด˜แด›ษชแดษด:\nPage X/Y\n") + page_number = int(page) + await show_description_with_pagination( + event, + qid, + description, + already_defined_text_length, + current_page=page_number, + ) + + +@callback(re.compile("pypi_back_button:(.*)"), owner=True) +async def back_button_clicked(event): + qid = event.data.decode().split(":", 1)[1] + if not PYPI_LIST.get(qid): + return await event.answer("Qแดœแด‡ส€ส แด‡xแด˜ษชส€แด‡แด…! Sแด‡แด€ส€แด„สœ แด€ษขแด€ษชษด ๐Ÿ”") + text = PYPI_LIST[qid]["text"] + buttons = PYPI_LIST[qid]["buttons"] + await event.edit(text, buttons=buttons) diff --git a/addons/inline/winget.py b/addons/inline/winget.py new file mode 100644 index 0000000..14571a4 --- /dev/null +++ b/addons/inline/winget.py @@ -0,0 +1,49 @@ +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# + +import re +from telethon.tl.types import InputWebDocument as wb +from . import get_string, async_searcher, in_pattern, InlinePlugin, async_searcher, asst + +__doc__ = f""" +โœ˜ Commands Available - +โ€ข `@{asst.username} winget ` + Searches for the query on Winget and returns the results. +""" +from telethon.tl.custom import Button + +@in_pattern("winget", owner=True) +async def search_winget(event): + QUERY = event.text.split(maxsplit=1) + try: + query = QUERY[1] + except IndexError: + await event.answer( + [], switch_pm=get_string("instu_3"), switch_pm_param="start" + ) + return + le = "https://api.winget.run/v2/packages?ensureContains=true&partialMatch=true&take=20&query=" + query.replace(" ", "+") + ct = await async_searcher(le, re_json=True) + out = [] + for on in ct["Packages"]: + data = on["Latest"] + name = data["Name"] + homep = data.get("Homepage") + text = f"> **{name}**\n - {data['Description']}\n\n`winget install {on['Id']}`\n\n**Version:** `{on['Versions'][0]}`\n" + text += "**Tags:**" + " ".join([f"#{_}" for _ in data["Tags"]]) + if homep: + text += f"\n\n{homep}" + out.append( + await event.builder.article( + title=name, description=data["Description"], url=homep, text=text, buttons=Button.switch_inline("Search Again", "winget", same_peer=True) + ) + ) + uppar = "|> Winget Results" if out else "No Results Found :(" + await event.answer(out, switch_pm=uppar, switch_pm_param="start", cache_time=3000) + + +InlinePlugin.update({"Search Winget": "winget telegram"}) diff --git a/addons/inline/xdasearch.py b/addons/inline/xdasearch.py new file mode 100644 index 0000000..42a3bdc --- /dev/null +++ b/addons/inline/xdasearch.py @@ -0,0 +1,66 @@ +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# + +import re +from bs4 import BeautifulSoup as bs +from telethon.tl.types import InputWebDocument as wb +from . import get_string, async_searcher, in_pattern, InlinePlugin, asst + +__doc__ = f""" +โœ˜ Commands Available - + +โ€ข `@{asst.username} xda ` + Searches for the query on XDA Developers and returns the results. +""" + +# Inspired by @FindXDaBot + +@in_pattern("xda", owner=True) +async def xda_dev(event): + QUERY = event.text.split(maxsplit=1) + try: + query = QUERY[1] + except IndexError: + await event.answer( + [], switch_pm=get_string("instu_3"), switch_pm_param="start" + ) + return + le = "https://www.xda-developers.com/search/?q=" + query.replace(" ", "+") + ct = await async_searcher(le, re_content=True) + ml = bs(ct, "html.parser", from_encoding="utf-8") + cards = ml.find_all("div", class_="display-card") + out = [] + for card in cards: + # Title and URL + title_tag = card.find("h5", class_="display-card-title") + a_tag = title_tag.find("a") if title_tag else None + title = a_tag.get("title") or a_tag.text.strip() if a_tag else "No Title" + href = a_tag.get("href") if a_tag else "" + if href and href.startswith("/"): + href = "https://www.xda-developers.com" + href + + # Description + desc_tag = card.find("p", class_="display-card-excerpt") + desc = desc_tag.text.strip() if desc_tag else "" + + # Thumbnail + img_tag = card.find("img") + thumb = img_tag.get("data-img-url") or img_tag.get("src") if img_tag else None + if thumb: + thumb = wb(thumb, 0, "image/jpeg", []) + + text = f"[{title}]({href})" + out.append( + await event.builder.article( + title=title, description=desc, url=href, thumb=thumb, text=text + ) + ) + uppar = "|| XDA Search Results ||" if out else "No Results Found :(" + await event.answer(out, switch_pm=uppar, switch_pm_param="start") + + +InlinePlugin.update({"Search on XDA": "xda telegram"}) \ No newline at end of file diff --git a/addons/inlinefun.py b/addons/inlinefun.py new file mode 100644 index 0000000..abd0f1a --- /dev/null +++ b/addons/inlinefun.py @@ -0,0 +1,136 @@ +# +# Ultroid - UserBot +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +# .tweet made for ultroid + +# .uta ported from Dark-Cobra + +""" +โœ˜ Commands Available - + +โ€ข `{i}uta ` + Inline song search and downloader. + +โ€ข `{i}gglax ` + Create google search sticker with text. + +โ€ข `{i}stic ` + Get random stickers from emoji. + +โ€ข `{i}frog ` + make text stickers. + +โ€ข `{i}tweet ` + make twitter posts. + +โ€ข `{i}quot ` + write quote on animated sticker. +""" + +from random import choice + +from addons.waifu import deEmojify + +from . import ultroid_cmd, get_string + + +@ultroid_cmd(pattern="tweet ?(.*)") +async def tweet(e): + wai = await e.eor() + text = e.pattern_match.group(1) + if not text: + return await wai.edit("`Give me Some Text !`") + try: + results = await e.client.inline_query("twitterstatusbot", text) + await e.reply("New Tweet", file=results[0].document) + await wai.delete() + except Exception as m: + await e.eor(str(m)) + + +@ultroid_cmd(pattern="stic ?(.*)") +async def tweet(e): + if len(e.text) > 5 and e.text[5] != " ": + return + wai = await e.eor(get_string("com_1")) + text = e.pattern_match.group(1) + if not text: + return await wai.edit("`Give me Some Emoji !`") + results = await e.client.inline_query("sticker", text) + num = choice(results) + await e.reply("@sticker", file=num.document) + await wai.delete() + + +@ultroid_cmd(pattern="gglax ?(.*)") +async def gglax_sticker(e): + wai = await e.eor(get_string("com_1")) + text = e.pattern_match.group(1) + if not text: + return await wai.edit("`Give me Some Text !`") + try: + results = await e.client.inline_query("googlaxbot", text) + await e.reply("Googlax", file=results[0].document) + await wai.delete() + except Exception as m: + await e.eor(str(m)) + + +@ultroid_cmd(pattern="frog ?(.*)") +async def honkasays(e): + wai = await e.eor(get_string("com_1")) + text = e.pattern_match.group(1) + if not text: + return await wai.edit("`Give Me Some Text !`") + text = deEmojify(text) + if not text.endswith("."): + text += "." + if len(text) <= 9: + q = 2 + elif len(text) >= 14: + q = 0 + else: + q = 1 + try: + res = await e.client.inline_query("honka_says_bot", text) + await e.reply("Honka", file=res[q].document) + await wai.delete() + except Exception as er: + await wai.edit(str(er)) + + +@ultroid_cmd(pattern="uta ?(.*)") +async def nope(doit): + ok = doit.pattern_match.group(1) + replied = await doit.get_reply_message() + a = await doit.eor(get_string("com_1")) + if ok: + pass + elif replied and replied.message: + ok = replied.message + else: + return await doit.eor( + "`Sir please give some query to search and download it for you..!`", + ) + sticcers = await doit.client.inline_query("Lybot", f"{(deEmojify(ok))}") + await doit.reply(file=sticcers[0].document) + await a.delete() + + +@ultroid_cmd(pattern="quot ?(.*)") +async def quote_(event): + IFUZI = event.pattern_match.group(1) + if "quotly" in event.text: + return + if not IFUZI: + return await event.eor("`Give some text to make Quote..`") + EI_IR = await event.eor(get_string("com_1")) + try: + RE_ZK = await event.client.inline_query("@QuotAfBot", IFUZI) + await event.reply(file=choice(RE_ZK).document) + except Exception as U_TG: + return await EI_IR.edit(str(U_TG)) + await EI_IR.delete() diff --git a/addons/limited.py b/addons/limited.py new file mode 100644 index 0000000..3a5edff --- /dev/null +++ b/addons/limited.py @@ -0,0 +1,32 @@ +# inspired from bin.py which was made by @danish_00 +# written by @senku_ishigamiii/@Uzumaki_Naruto_XD + +""" +โœ˜ Commands Available - + +โ€ข `{i}limited` + Check you are limited or not ! +""" + +from telethon import events +from telethon.errors.rpcerrorlist import YouBlockedUserError + +from . import ultroid_cmd + + +@ultroid_cmd(pattern="limited$") +async def demn(ult): + chat = "@SpamBot" + msg = await ult.eor("Checking If You Are Limited...") + async with ult.client.conversation(chat) as conv: + try: + response = conv.wait_event( + events.NewMessage(incoming=True, from_users=178220800) + ) + await conv.send_message("/start") + response = await response + await ult.client.send_read_acknowledge(chat) + except YouBlockedUserError: + await msg.edit("Boss! Please Unblock @SpamBot ") + return + await msg.edit(f"~ {response.message.message}") diff --git a/addons/locks.py b/addons/locks.py new file mode 100644 index 0000000..edbdc6b --- /dev/null +++ b/addons/locks.py @@ -0,0 +1,39 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +""" +โœ˜ Commands Available - + +โ€ข `{i}lock ` + Lock the Used Setting in Used Group. + +โ€ข `{i}unlock ` + UNLOCK the Used Setting in Used Group. +""" +from telethon.tl.functions.messages import EditChatDefaultBannedRightsRequest + +from pyUltroid.fns.admins import lock_unlock + +from . import ultroid_cmd + + +@ultroid_cmd( + pattern="(un|)lock( (.*)|$)", admins_only=True, manager=True, require="change_info" +) +async def un_lock(e): + mat = e.pattern_match.group(2).strip() + if not mat: + return await e.eor("`Give some Proper Input..`", time=5) + lock = e.pattern_match.group(1) == "" + ml = lock_unlock(mat, lock) + if not ml: + return await e.eor("`Incorrect Input`", time=5) + msg = "Locked" if lock else "Unlocked" + try: + await e.client(EditChatDefaultBannedRightsRequest(e.chat_id, ml)) + except Exception as er: + return await e.eor(str(er)) + await e.eor(f"**{msg}** - `{mat}` ! ") diff --git a/addons/logo.py b/addons/logo.py new file mode 100644 index 0000000..551104f --- /dev/null +++ b/addons/logo.py @@ -0,0 +1,101 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +""" +โœ˜ Commands Available - + +โ€ข `{i}logo ` + Generate a logo of the given Text + Or Reply To image , to write ur text on it. + Or Reply To Font File, To write with that font. + +""" +import glob +import os +import random + +from telethon.tl.types import InputMessagesFilterPhotos + +try: + from PIL import Image +except ImportError: + Image = None +from pyUltroid.fns.misc import unsplashsearch +from pyUltroid.fns.tools import LogoHelper + +from . import OWNER_ID, OWNER_NAME, download_file, get_string, mediainfo, ultroid_cmd + + +@ultroid_cmd(pattern="logo( (.*)|$)") +async def logo_gen(event): + xx = await event.eor(get_string("com_1")) + name = event.pattern_match.group(1).strip() + if not name: + return await xx.eor("`Give a name too!`", time=5) + bg_, font_ = None, None + if event.reply_to_msg_id: + temp = await event.get_reply_message() + if temp.media: + if hasattr(temp.media, "document") and ( + ("font" in temp.file.mime_type) + or (".ttf" in temp.file.name) + or (".otf" in temp.file.name) + ): + font_ = await temp.download_media("resources/fonts/") + elif "pic" in mediainfo(temp.media): + bg_ = await temp.download_media() + if not bg_: + SRCH = [ + "background", + "neon", + "anime", + "art", + "bridges", + "streets", + "computer", + "cyberpunk", + "nature", + "abstract", + "exoplanet", + "magic", + "3d render", + ] + res = await unsplashsearch(random.choice(SRCH), limit=1) + bg_, _ = await download_file(res[0], "resources/downloads/logo.png") + newimg = "resources/downloads/unsplash-temp.jpg" + img_ = Image.open(bg_) + img_.save(newimg) + os.remove(bg_) + bg_ = newimg + + if not font_: + fpath_ = glob.glob("resources/fonts/*") + font_ = random.choice(fpath_) + if len(name) <= 8: + strke = 10 + elif len(name) >= 9: + strke = 5 + else: + strke = 20 + name = LogoHelper.make_logo( + bg_, + name, + font_, + fill="white", + stroke_width=strke, + stroke_fill="black", + ) + await xx.edit("`Done!`") + await event.client.send_file( + event.chat_id, + file=name, + caption=f"Logo by [{OWNER_NAME}](tg://user?id={OWNER_ID})", + force_document=True, + ) + os.remove(name) + await xx.delete() + if os.path.exists(bg_): + os.remove(bg_) diff --git a/addons/memify.py b/addons/memify.py new file mode 100644 index 0000000..82beda2 --- /dev/null +++ b/addons/memify.py @@ -0,0 +1,339 @@ +# Ported Nd Modified For Ultroid +# Ported From DarkCobra (Modified by @ProgrammingError) +# +# Ultroid - UserBot +# Copyright (C) 2020 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +""" +โœ˜ Commands Available - + +โ€ข `{i}mmf ; ` + To create memes as sticker, + for trying different fonts use (.mmf _1)(u can use 1 to 10). + +โ€ข `{i}mms ; ` + To create memes as pic, + for trying different fonts use (.mms _1)(u can use 1 to 10). + +""" + +import asyncio +import os +import textwrap + +import cv2 +from PIL import Image, ImageDraw, ImageFont + +from . import * + + +@ultroid_cmd(pattern="mmf ?(.*)") +async def ultd(event): + ureply = await event.get_reply_message() + msg = event.pattern_match.group(1) + if not (ureply and (ureply.media)): + xx = await event.eor("`Reply to any media`") + return + if not msg: + xx = await event.eor("`Give me something text to write...`") + return + ultt = await ureply.download_media() + if ultt.endswith((".tgs")): + xx = await event.eor("`Ooo Animated Sticker ๐Ÿ‘€...`") + cmd = ["lottie_convert.py", ultt, "ult.png"] + file = "ult.png" + process = await asyncio.create_subprocess_exec( + *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() + stderr.decode().strip() + stdout.decode().strip() + elif ultt.endswith((".webp", ".png")): + xx = await event.eor("`Processing`") + im = Image.open(ultt) + im.save("ult.png", format="PNG", optimize=True) + file = "ult.png" + else: + xx = await event.eor("`Processing`") + img = cv2.VideoCapture(ultt) + heh, lol = img.read() + cv2.imwrite("ult.png", lol) + file = "ult.png" + stick = await draw_meme_text(file, msg) + await event.client.send_file( + event.chat_id, stick, force_document=False, reply_to=event.reply_to_msg_id + ) + await xx.delete() + try: + os.remove(ultt) + os.remove(file) + os.remove(stick) + except BaseException: + pass + + +async def draw_meme_text(image_path, msg): + img = Image.open(image_path) + os.remove(image_path) + i_width, i_height = img.size + if "_" in msg: + text, font = msg.split("_") + else: + text = msg + font = "default" + if ";" in text: + upper_text, lower_text = text.split(";") + else: + upper_text = text + lower_text = "" + draw = ImageDraw.Draw(img) + m_font = ImageFont.truetype( + f"resources/fonts/{font}.ttf", int((70 / 640) * i_width) + ) + current_h, pad = 10, 5 + if upper_text: + for u_text in textwrap.wrap(upper_text, width=15): + bbox = draw.textbbox((0, 0), u_text, font=m_font) + u_width, u_height = bbox[2] - bbox[0], bbox[3] - bbox[1] + draw.text( + xy=(((i_width - u_width) / 2) - 1, int((current_h / 640) * i_width)), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=(((i_width - u_width) / 2) + 1, int((current_h / 640) * i_width)), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=((i_width - u_width) / 2, int(((current_h / 640) * i_width)) - 1), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=(((i_width - u_width) / 2), int(((current_h / 640) * i_width)) + 1), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=((i_width - u_width) / 2, int((current_h / 640) * i_width)), + text=u_text, + font=m_font, + fill=(255, 255, 255), + ) + current_h += u_height + pad + if lower_text: + for l_text in textwrap.wrap(lower_text, width=15): + bbox = draw.textbbox((0, 0), l_text, font=m_font) + u_width, u_height = bbox[2] - bbox[0], bbox[3] - bbox[1] + draw.text( + xy=( + ((i_width - u_width) / 2) - 1, + i_height - u_height - int((80 / 640) * i_width), + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=( + ((i_width - u_width) / 2) + 1, + i_height - u_height - int((80 / 640) * i_width), + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=( + (i_width - u_width) / 2, + (i_height - u_height - int((80 / 640) * i_width)) - 1, + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=( + (i_width - u_width) / 2, + (i_height - u_height - int((80 / 640) * i_width)) + 1, + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=( + (i_width - u_width) / 2, + i_height - u_height - int((80 / 640) * i_width), + ), + text=l_text, + font=m_font, + fill=(255, 255, 255), + ) + current_h += u_height + pad + imag = "ultt.webp" + img.save(imag, "WebP") + return imag + + +@ultroid_cmd(pattern="mms ?(.*)") +async def mms(event): + ureply = await event.get_reply_message() + msg = event.pattern_match.group(1) + if not (ureply and (ureply.media)): + xx = await event.eor("`Reply to any media`") + return + if not msg: + xx = await event.eor("`Give me something text to write ๐Ÿ˜‘`") + return + ultt = await ureply.download_media() + if ultt.endswith((".tgs")): + xx = await event.eor("`Ooo Animated Sticker ๐Ÿ‘€...`") + cmd = ["lottie_convert.py", ultt, "ult.png"] + file = "ult.png" + process = await asyncio.create_subprocess_exec( + *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() + stderr.decode().strip() + stdout.decode().strip() + elif ultt.endswith((".webp", ".png")): + xx = await event.eor("`Processing`") + im = Image.open(ultt) + im.save("ult.png", format="PNG", optimize=True) + file = "ult.png" + else: + xx = await event.eor("`Processing`") + img = cv2.VideoCapture(ultt) + heh, lol = img.read() + cv2.imwrite("ult.png", lol) + file = "ult.png" + pic = await draw_meme(file, msg) + await event.client.send_file( + event.chat_id, pic, force_document=False, reply_to=event.reply_to_msg_id + ) + await xx.delete() + try: + os.remove(ultt) + os.remove(file) + except BaseException: + pass + os.remove(pic) + + +async def draw_meme(image_path, msg): + img = Image.open(image_path) + os.remove(image_path) + i_width, i_height = img.size + if "_" in msg: + text, font = msg.split("_") + else: + text = msg + font = "default" + if ";" in text: + upper_text, lower_text = text.split(";") + else: + upper_text = text + lower_text = "" + draw = ImageDraw.Draw(img) + m_font = ImageFont.truetype( + f"resources/fonts/{font}.ttf", int((70 / 640) * i_width) + ) + current_h, pad = 10, 5 + if upper_text: + for u_text in textwrap.wrap(upper_text, width=15): + bbox = draw.textbbox((0, 0), u_text, font=m_font) + u_width, u_height = bbox[2] - bbox[0], bbox[3] - bbox[1] + draw.text( + xy=(((i_width - u_width) / 2) - 1, int((current_h / 640) * i_width)), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=(((i_width - u_width) / 2) + 1, int((current_h / 640) * i_width)), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=((i_width - u_width) / 2, int(((current_h / 640) * i_width)) - 1), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=(((i_width - u_width) / 2), int(((current_h / 640) * i_width)) + 1), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=((i_width - u_width) / 2, int((current_h / 640) * i_width)), + text=u_text, + font=m_font, + fill=(255, 255, 255), + ) + current_h += u_height + pad + if lower_text: + for l_text in textwrap.wrap(lower_text, width=15): + bbox = draw.textbbox((0, 0), l_text, font=m_font) + u_width, u_height = bbox[2] - bbox[0], bbox[3] - bbox[1] + draw.text( + xy=( + ((i_width - u_width) / 2) - 1, + i_height - u_height - int((20 / 640) * i_width), + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=( + ((i_width - u_width) / 2) + 1, + i_height - u_height - int((20 / 640) * i_width), + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=( + (i_width - u_width) / 2, + (i_height - u_height - int((20 / 640) * i_width)) - 1, + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=( + (i_width - u_width) / 2, + (i_height - u_height - int((20 / 640) * i_width)) + 1, + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=( + (i_width - u_width) / 2, + i_height - u_height - int((20 / 640) * i_width), + ), + text=l_text, + font=m_font, + fill=(255, 255, 255), + ) + current_h += u_height + pad + pics = "ultt.png" + img.save(pics, "png") + return pics \ No newline at end of file diff --git a/addons/misc.py b/addons/misc.py new file mode 100644 index 0000000..e82b670 --- /dev/null +++ b/addons/misc.py @@ -0,0 +1,140 @@ +# Ultroid - UserBot +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +""" +โœ˜ Commands Available - + +โ€ข `{i}eod` + `Get Event of the Today` + +โ€ข `{i}pntrst ` + Download and send pinterest pins + +โ€ข `{i}gadget ` + Gadget Search from Telegram. + +โ€ข `{i}randomuser` + Generate details about a random user. + +โ€ข `{i}ascii ` + Convert replied image into html. +""" + +import os +from datetime import datetime as dt + +from bs4 import BeautifulSoup as bs + +try: + from htmlwebshot import WebShot +except ImportError: + WebShot = None +try: + from img2html.converter import Img2HTMLConverter +except ImportError: + Img2HTMLConverter = None + +from . import async_searcher, get_random_user_data, get_string, re, ultroid_cmd + + +@ultroid_cmd(pattern="eod$") +async def diela(e): + m = await e.eor(get_string("com_1")) + li = "https://daysoftheyear.com" + te = "๐ŸŽŠ **Events of the Day**\n\n" + da = dt.now() + month = da.strftime("%b") + li += f"/days/{month}/" + da.strftime("%F").split("-")[2] + ct = await async_searcher(li, re_content=True) + bt = bs(ct, "html.parser", from_encoding="utf-8") + ml = bt.find_all("a", "js-link-target", href=re.compile("daysoftheyear.com/days")) + for eve in ml[:5]: + te += f'โ€ข [{eve.text}]({eve["href"]})\n' + await m.edit(te, link_preview=False) + + +@ultroid_cmd( + pattern="pntrst( (.*)|$)", +) +async def pinterest(e): + m = e.pattern_match.group(1).strip() + if not m: + return await e.eor("`Give pinterest link.`", time=3) + soup = await async_searcher( + "https://www.expertstool.com/download-pinterest-video/", + data={"url": m}, + post=True, + ) + try: + _soup = bs(soup, "html.parser").find("table").tbody.find_all("tr") + except BaseException: + return await e.eor("`Wrong link or private pin.`", time=5) + file = _soup[1] if len(_soup) > 1 else _soup[0] + file = file.td.a["href"] + await e.client.send_file(e.chat_id, file, caption=f"Pin:- {m}") + + +@ultroid_cmd(pattern="gadget( (.*)|$)") +async def mobs(e): + mat = e.pattern_match.group(1).strip() + if not mat: + await e.eor("Please Give a Mobile Name to look for.") + query = mat.replace(" ", "%20") + jwala = f"https://gadgets.ndtv.com/search?searchtext={query}" + c = await async_searcher(jwala) + b = bs(c, "html.parser", from_encoding="utf-8") + co = b.find_all("div", "rvw-imgbox") + if not co: + return await e.eor("No Results Found!") + bt = await e.eor(get_string("com_1")) + out = "**๐Ÿ“ฑ Mobile / Gadgets Search**\n\n" + li = co[0].find("a") + imu, title = None, li.find("img")["title"] + cont = await async_searcher(li["href"]) + nu = bs(cont, "html.parser", from_encoding="utf-8") + req = nu.find_all("div", "_pdsd") + imu = nu.find_all( + "img", src=re.compile("https://i.gadgets360cdn.com/products/large/") + ) + if imu: + imu = imu[0]["src"].split("?")[0] + "?downsize=*:420&output-quality=80" + out += f"โ˜‘๏ธ **[{title}]({li['href']})**\n\n" + for fp in req: + ty = fp.findNext() + out += f"- **{ty.text}** - `{ty.findNext().text}`\n" + out += "_" + if imu == []: + imu = None + await e.reply(out, file=imu, link_preview=False) + await bt.delete() + + +@ultroid_cmd(pattern="randomuser") +async def _gen_data(event): + x = await event.eor(get_string("com_1")) + msg, pic = await get_random_user_data() + await event.reply(file=pic, message=msg) + await x.delete() + + +@ultroid_cmd( + pattern="ascii( (.*)|$)", +) +async def _(e): + if not Img2HTMLConverter: + return await e.eor("'img2html-converter' not installed!") + if not e.reply_to_msg_id: + return await e.eor(get_string("ascii_1")) + m = await e.eor(get_string("ascii_2")) + img = await (await e.get_reply_message()).download_media() + char = e.pattern_match.group(1).strip() or "โ– " + converter = Img2HTMLConverter(char=char) + html = converter.convert(img) + shot = WebShot(quality=85) + pic = await shot.create_pic_async(html=html) + await m.delete() + await e.reply(file=pic) + os.remove(pic) + os.remove(img) diff --git a/addons/morsecode.py b/addons/morsecode.py new file mode 100644 index 0000000..84139a9 --- /dev/null +++ b/addons/morsecode.py @@ -0,0 +1,38 @@ +# Ultroid - UserBot +# +# This file is a part of < https://github.com/TeamUltroid/UltroidAddons/ > +# PLease read the GNU Affero General Public License in +# . + +""" +โœ˜ Commands Available - + +โ€ข `{i}mencode ` + Encode the given text to Morse Code. + +โ€ข `{i}mdecode ` + Decode the given text from Morse Code. +""" + +from . import async_searcher, ultroid_cmd, get_string + +@ultroid_cmd(pattern="mencode ?(.*)") +async def mencode(event): + msg = await event.eor(get_string("com_1")) + text = event.pattern_match.group(1) + if not text: + return msg.edit("Please give a text!") + base_url = "https://apis.xditya.me/morse/encode?text=" + text + encoded = await async_searcher(base_url, re_content=False) + await msg.edit("**Encoded.**\n\n**Morse Code:** `{}`".format(encoded)) + + +@ultroid_cmd(pattern="mdecode ?(.*)") +async def mencode(event): + msg = await event.eor(get_string("com_1")) + text = event.pattern_match.group(1) + if not text: + return await msg.edit("Please give a text!") + base_url = "https://apis.xditya.me/morse/decode?text=" + text + encoded = await async_searcher(base_url, re_content=False) + await msg.edit("**Decoded.**\n\n**Message:** `{}`".format(encoded)) \ No newline at end of file diff --git a/addons/mute.py b/addons/mute.py new file mode 100644 index 0000000..f183a5b --- /dev/null +++ b/addons/mute.py @@ -0,0 +1,235 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2025 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +""" +โœ˜ Commands Available - + +โ€ข `{i}mute ` + Mute user in current chat. + +โ€ข `{i}unmute ` + Unmute user in current chat. + +โ€ข `{i}dmute ` + Mute user in current chat by deleting msgs. + +โ€ข `{i}undmute ` + Unmute dmuted user in current chat. + +โ€ข `{i}tmute