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
'
+ 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