diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 0000000..08ddfa8 --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,8 @@ +version = 1 + +[[analyzers]] +name = "python" +enabled = true + +[analyzers.meta] +runtime_version = "3.x.x" \ No newline at end of file diff --git a/.env.example b/.env.example deleted file mode 100644 index 25fba40..0000000 --- a/.env.example +++ /dev/null @@ -1,9 +0,0 @@ -TOKEN = [discord bot token] -HYPIXEL = [hypixel api key] -CLIENT_SECRET = [reddit client secret] -CLIENT_ID = [reddit client id] -DISCORD_CLIENT_ID = [the bots client id] -DISCORD_CLIENT_SECRET = [the bots client secret] -ISAPI = [google image search api key] -NASA = [nasa api key] -WEATHER = [open weather map api key] \ No newline at end of file diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..051c106 --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +ignore = E203, W503, E266, F722, F821, E999 +max-line-length=120 +max-complexity=39 \ No newline at end of file diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..195a8ad --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,42 @@ +name: "CodeQL" + +on: + push: + paths: + - "src/**.py" + branches: ["master", "rewrite-the-rewrite"] + pull_request: + branches: ["master", "rewrite-the-rewrite" ] + schedule: + - cron: '38 17 * * 5' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.gitignore b/.gitignore index 179818f..96f00a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,33 @@ -.env -.DS_Store -.breakpoints -*__pycache__ *.db -*.json -log.txt +*.DS_Store +*.env +*.pyc +*__pycache__/ +*venv/ +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST +.cache +.env +config.yaml +*.log +*.so +*.mypy_cache +*logfiles/ +*testing_cog.py +src/scripts/backups +src/scripts/b.sh \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..970cae4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: + - repo: https://github.com/psf/black + rev: 22.3.0 + hooks: + - id: black + args: [--safe] + - repo: https://github.com/pycqa/flake8 + rev: 6.0.0 + hooks: + - id: flake8 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a6735e5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.analysis.typeCheckingMode": "off" +} \ No newline at end of file diff --git a/.vscode/why.code-snippets b/.vscode/why.code-snippets new file mode 100644 index 0000000..b48c9bf --- /dev/null +++ b/.vscode/why.code-snippets @@ -0,0 +1,27 @@ +{ + "newcog": { + "prefix": "newcog", + "body": [ + "import discord", + "from discord.ext import commands\n", + "from core.models import WhyBot", + "from core.helpers.checks import run_bot_checks\n", + "class $1(commands.Cog):", + "\tdef __init__(self, client: WhyBot):", + "\t\tself.client = client", + "\t\tself.cog_check = run_bot_checks\n\n\n", + "def setup(client):", + "\tclient.add_cog($1(client))" + ], + "description": "Creates a Cog" + }, + "slash": { + "prefix": "slash", + "body": [ + "@commands.slash_command()", + "async def $1(self, ctx: discord.ApplicationContext):", + "\tpass" + ], + "description": "Create a new slash command" + } +} \ No newline at end of file diff --git a/COC.md b/COC.md new file mode 100644 index 0000000..06b645c --- /dev/null +++ b/COC.md @@ -0,0 +1,78 @@ + +# Collaborator's Code of Conduct + +## Our commitment + +In the interest of promoting an open and welcoming environment, we +contributors and maintenance workers we are committed to making participation in our +project and our community an experience free from harassment for +all, regardless of age, build, disability, ethnicity, +sexual characteristics, identity and gender expression, level of +experience, education, socio-economic status, nationality, appearance, race, +religion or identity and sexual orientation. + +## Our Standards + +Examples of behaviors that contribute to creating an environment +positive: + +* Use of a welcoming and inclusive language +* Respect different points of view and experiences +* Accept constructive criticism gracefully +* Focus on what's best for the community +* Show empathy towards other community members + +Examples of unacceptable behavior of participants: + +* The use of language or sexualized images and sexual attention or + unwanted advances +* Troll behavior, offensive comments and personal or political attacks +* Harassment in public or private +* Publication of other people's private information, such as a postal address or + electronic, without explicit authorization +* Other behaviors that could reasonably be considered + inappropriate in a professional context + +## Our responsibilities + +Project maintainers are responsible for clarifying the standards of +acceptable behavior and are required to take corrective action +appropriate and fair in response to any case of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, +modify or reject comments, commits, code, changes to wikis, issuee and others +contributions not aligned with this Code of Conduct, or to exclude, +temporarily or permanently, any contributor for +behaviors deemed inappropriate, threatening, offensive or harmful. + +## Purpose + +This Code of Conduct applies both within the spaces of the +project that in public spaces when an individual represents the project +or its community. Examples of representation of a project or community +include the use of an official project email address, publication +through an official account through social media or the function of +designated representative at an online or offline event. The representation of a +project can be further defined and clarified by the project maintainers. + +## Application + +Cases of abusive, harassing or otherwise unacceptable behavior can +be presented by contacting the project leader at +`whybot@fusionsid.xyz` or by DMing `FusionSid#3645`. All complaints will be reviewed and investigated and give +place of an answer deemed necessary and appropriate to the circumstances. The +project team is obliged to maintain confidentiality regarding +the person who reported the case. +Further details on specific implementation policies can be +published separately. + +Project maintainers who do not respect or apply the Code of +Behavior in good faith may suffer temporary repercussions or +permanent as determined by other members at the helm of the project. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[ homepage ]: https://www.contributor-covenant.org diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b771e4f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +FROM python:3.10-slim + +# install packages +RUN apt-get update && apt-get install -y --no-install-recommends \ + git unzip wget fontconfig imagemagick fonts-symbola \ + libmagick++-dev gcc ffmpeg python3-dev && rm -rf /var/lib/apt/lists/* + +# Set work dir as root +WORKDIR / + +# copy all the files from project to container +COPY ./ ./ + +# set work dir as src kinda like cd-ing into it and staying there +WORKDIR /src + +# Configure imagemagick settings +RUN sed -i '/ /etc/ImageMagick-6/policy.xml +RUN sed -i_bak 's/rights="none" pattern="PDF"/rights="read | write" pattern="PDF"/' /etc/ImageMagick-6/policy.xml + +# install verdana font for crab command +RUN wget --progress=dot:giga "https://www.dafontfree.io/download/verdana/?wpdmdl=71901&refresh=6362eb8bd1a9e1667427211&ind=1612703173429&filename=verdana-font-family.zip" -O font.zip +RUN unzip font.zip -d font +RUN cp -r font/* /usr/local/share/fonts + +# reload font cache +RUN fc-cache -f -v + +# Install required packages from requirements.txt +RUN pip install -r requirements.txt --no-cache-dir + +# run the bot :) +CMD ["python3", "main.py"] \ No newline at end of file diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..a6c0fcc --- /dev/null +++ b/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Siddhesh Zantye (FusionSid) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f09c237..0000000 --- a/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2022 Siddhesh Zantye - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is 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. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - 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. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - 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 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. Use with the GNU Affero General Public License. - - 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 Affero 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 special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU 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 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 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 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 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - 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 GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c0da488 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +format: + @python3 -m black ./ + +clean: + @find . | grep -E '(__pycache__|\.pyc|\.pyo$|\.DS_Store|\.mypy_cache)' | xargs rm -rf + @clear + +run: + @python3 src/main.py \ No newline at end of file diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 0000000..b4d7261 --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,35 @@ +# Privacy Policy + +The use of this application ("Bot") in a server requires the collection of some specific user data ("Data"). The Data collected includes, but is not limited to Discord User username values, Discord User ID values, Per command usage statistics, and Discord Guild ID/Name values. +Use of the Bot is considered an agreement to the terms of this Policy. + +## Access to Data + +Access to Data is only permitted to Bot's developers, and only in the scope required for the development, testing, and implementation of features for the Bot. Data is not sold, provided to, or shared with any third party, except when/where required by law or a Terms of Service agreement. You can view the data upon request to [`@FusionSid`](#contact). + +## Storage of Data + +Data is stored in a PostgreSQL database, User data is also cached in Redis databases. The database is secured to prevent external access, however, no guarantee is provided and the Bot owners assume no liability for the unintentional or malicious breach of Data. In the event of unauthorized Data access, users will be notified through the Discord client application. + +## User Rights + +At any time, you have the right to request to view the Data pertaining to your Discord account. You may submit a request through the [Discord Server](https://discord.gg/ryEmgnpKND). You have the right to request the removal of relevant Data. + +## Underage Users + +The use of the Bot is not permitted for minors under the age of 13, or under the age of legal consent in their country. This is in compliance with the [Discord Terms of Service](https://discord.com/terms). No information will be knowingly stored from an underage user. If it is found out that a user is underage we will take all necessary actions to delete the stored data. + +## Questions + +If you have any questions or are concerned about what data might be being stored from your account contact [`@FusionSid`](#contact). For more information check the [Discord Terms Of Service](https://discord.com/terms). + +## Contact + +**Email:** `whybot@fusionsid.xyz` +**Discord:** `FusionSid#3645` + +Alternatively, you can also contact the owner by DMing the discord application + +## Attribution + +This privacy policy was adapted from the freeCodeCamp 100 Days of Code Bot and can be found here: https://github.com/freeCodeCamp/100DaysOfCode-discord-bot/blob/main/PRIVACY.md \ No newline at end of file diff --git a/README.md b/README.md index 8f5c3b3..fd289cb 100644 --- a/README.md +++ b/README.md @@ -2,24 +2,26 @@ [![Contributors][contributors-shield]][contributors-url] [![Forks][forks-shield]][forks-url] -[![Stargazers][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url] +[![Stargazers][stars-shield]][stars-url] [![MIT License][license-shield]][license-url] - -[![CodeFactor](https://www.codefactor.io/repository/github/fusionsid/why-bot/badge)](https://www.codefactor.io/repository/github/fusionsid/why-bot) +[![CodeFactor](https://img.shields.io/codefactor/grade/github/FusionSid/Why-Bot?style=for-the-badge)](https://www.codefactor.io/repository/github/fusionsid/why-bot)
- - Logo + + Logo + ----

Why Bot

-

An open source, multi-purpose discord bot made to enhance your discord experience :)

+

An open source, multi-purpose discord bot made to enhance your discord experience

[Add to your server]

@@ -28,13 +30,9 @@ Report Bug

-
- ----- -

?help - For help

+--- -----
Table of Contents @@ -53,26 +51,6 @@ ## About The Project -Why bot is a multipurpose open-source bot. - -Why has many features such as: - -* Onping messages -* Autoroles / Reactroles and Moderation -* Welcome images -* Fun -* Counting game and Auto calc -* Many Utilities -* Logging -* Tickets -* Music and Custom Vc’s -* Search commands like reddit, google and images -* Image generation -* Leveling System -* and much much more! - -Why bot is an open-source project written in Python using Py-Cord. So you can always check the code or contribute to it. -Also why bot will respond to any DM you do to it. So if you want to talk with someone, Why bot will be there.

(back to top)

@@ -81,10 +59,7 @@ Also why bot will respond to any DM you do to it. So if you want to talk with so ## Built With -This project was made using a fork of `discord.py` called `py-cord`. -I did orginaly use `discord.py` for this project but since its no longed being maintained I switched to `py-cord` -### [Pycord Repo](https://github.com/Pycord-Development/pycord)

(back to top)

@@ -99,7 +74,7 @@ Don't forget to give the project a star :) 1. Fork the Project 2. Create your Feature Branch (`git checkout -b newfeature`) -3. Commit your Changes (`git commit -m 'Add some newfeature'`) +3. Commit your Changes (`git commit -m 'feat: Add newfeature'`) Please try to follow [conventional commit names](https://www.conventionalcommits.org/en/v1.0.0/) 4. Push to the Branch (`git push origin newfeature`) 5. Open a Pull Request @@ -107,34 +82,31 @@ Don't forget to give the project a star :) ## License -Distributed under the MIT License. See `LICENSE` for more information. +Distributed under the MIT License. See [`LICENSE`](/LICENCE) for more information.

(back to top)

## Contact -Siddhesh Zantye - [@Fusion_Sid](https://twitter.com/Fusion_Sid) - whybot@fusionsid.xyz +Siddhesh Zantye - whybot@fusionsid.xyz -Discord Account: `FusionSid#3645` +Discord Account: [`FusionSid#3645`](https://discordapp.com/users/624076054969188363) Join the Why [Discord Server](https://discord.gg/ryEmgnpKND) For help. +You can also directly DM the bot to contact the Why Bot devs

(back to top)

## Acknowledgments -Even though I take take credit for most of the code, I would like to thank everyone who helped with this bot or any other open source projects which I used code from: - -These are the ones I remember: - -* [Knedme's Beat](https://github.com/Knedme/Beat) -* [Auroris Ticketing Bot](https://github.com/ifisq/discord-ticket-system) -* [Voice Master - Custom VC](https://github.com/SamSanai/VoiceMaster-Discord-Bot/blob/master/cogs/voice.py) +Even though I take take credit for most of the code, I would like to thank everyone who helped with this bot or any other open source projects which I used code from.

(back to top)

+## Commands: + [contributors-shield]: https://img.shields.io/github/contributors/FusionSid/Why-Bot.svg?style=for-the-badge @@ -146,136 +118,4 @@ These are the ones I remember: [issues-shield]: https://img.shields.io/github/issues/FusionSid/Why-Bot.svg?style=for-the-badge [issues-url]: https://github.com/FusionSid/Why-Bot/issues [license-shield]: https://img.shields.io/github/license/FusionSid/Why-Bot.svg?style=for-the-badge -[license-url]: https://github.com/FusionSid/Why-Bot/blob/master/LICENSE.txt -[product-screenshot]: images/screenshot.png - - ----- - -## Commands: - ----- - -| Name | Usage | Description | -| ------------- | ------------- | ----------- | -| play | play [name/url] | Play a song -| binarytotext | binarytotext | Convert binary to text -| wh | webhook [id] [text] | Send message through a webhook -| settings | settings | Shows the servers Why Bot settings -| numrn | numrn | Current number for the counting game -| skip | skip | Skips the playing song -| leave | leave | Leave vc -| report | report [message/member/bug] | Report member/message to your server mods and report bugs to me -| rank | rank [@user(optional)] | Shows your rank image -| pause | pause | Pause song -| youtube | youtube [search query] | Searches through youtube for videos -| resume | resume | Resume the paused song -| queue | queue | Show queue -| leaderboard | leaderboard | Shows the leaderboard for your server -| loop | loop | Loop queue/song -| createplaylist | createplaylist [playlist name] | Create a playlist -| currentgames | currentgames | Show current games being played -| info | info [@user] | Returns info on a user -| getuuid | getuuid [ign(optional)] | Returns your minecraft ign -| setign | setign | Sets you minecraft ign -| plugins | plugins [enable/disable] [plugin name] | Enable/Disable Plugins for Why bot -| hystats | hystats [ign(optional)] | Shows a nice pic of your hypixel stats -| plist | plist [playlist name] | Displays all the songs in a playlist -| serverinfo | serverinfo | shows server info -| ping | ping | Shows bot ping -| playlist | playlist [playlist name] | Play a playlist -| botinfo | botinfo | Info about Why bot -| padd | padd [playlist name] [song/songurl] | Add song to a playlist -| bwstats | bwstats [ign(optional) | Shows your bedwars stats -| warnings | warnings [@user] | Displays a users warnings -| createhook | createhook [name] [channel(optional)] | Creates a webhook -| shuffleplaylist | shuffleplaylist [playlist name] | Shuffle a playlist -| goose_mode | goose_mode | This command toggles goose_mode -| pdel | pdel [playlist name] | Delete song from a playlist -| mp3 | mp3 [attach mp3 file] | Play an mp3 file -| tts | tts [text] | Text to speech -| music | music | Small music button dashboard -| claim | claim | Free Coins -| tictactoe | tictactoe | Tic Tac Toe -| hextotext | hextotext [text] | Converts hex to Text -| texttobinary | texttobinary | Convert to binary -| whosplaying | whosplaying [game/activity] | Check whosplaying activity -| setprefix | setprefix [prefix] | Sets the server prefix -| reddit | reddit [subreddit] | Find a random reddit post -| warn | warn [@user] [reason] | Warns a user -| weather | weather [city] | Get weather -| autocalc | autocalc [True/false] | Toggles autocalc for this server -| texttohex | texttohex [text] | Converts Text to hex -| reverse | reverse [text] | Reverses Text -| rock | rock | Rock Image -| dog | dog | Dog Image -| meme | meme | Gets a meme -| cat | cat | Cat Image -| panda | panda | Panda Image -| imagesearch | imagesearch [search query] | Find an image from google -| expand | expand [num] [text] | Expands Text -| fox | fox | Fox Image -| join | join | Bot joins VC -| bird | bird | Bird Image -| tweet | tweet [@person] [message] | Fake tweet image -| ban | ban [@user] | Ban a member -| bug | bug [bug] | Report a bug -| giverole | giverole [@role] [@member] | Give role to a member -| ytcomment | ytcomment [@person] [message] | Fake youtube comment image -| kick | kick [@user] | Kick a member -| apod | apod | Astronomy Picture of the day -| make_vc | make_vc [limit(If none change to None)] [name] | Create a VC -| simp | simp [@member] | Simp card -| lockdown | lockdown [#channel] | Lockdown a channel -| horny | horny [@member] | Horny licence -| unlock | unlock [#channel] | Unlock a channel -| make_channel | make_channel [name] | Create a text channel -| joke | joke | Joke -| overlay | overlay [type] [@member] | Image overlays for you discord profile pic -| avatar | avatar [member] | User avatar -| clear | clear [amount] | Delete messages -| lyrics | lyrics [song name] | Song lyrics command -| nickname | nick [@user] [nickname] | Change a users nick -| wiki | wiki [search] | Wikipedia Search -| newticket | newticket | Creates a ticket -| drunkify | drunkify [text] | Drunkifies Text -| botinvite | botinvite | Invite why to your server -| slowmode | slowmode [seconds] | Set channel to slowmode -| unban | unban [member id] | Unban a banned member -| pin | pin [message id] | Pin a message -| closeticket | closeticket | Close a ticket -| addaccess | addaccess [roleid] | Gives a role access to tickets -| redditimg | redditimg [subreddit] | Find a image from a subreddit -| qrcode | qrcode [url] | Creates a qrcode -| suggest | suggest [suggestion] | Suggest something for Why -| rslowmode | rslowmode | Removes slowmode from a channel -| calculate | calculator | Interactive button calculator -| delaccess | delaccess [roleid] | Removes a role from accessing tickets -| nitro | nitro | Free Coins -| calc | calc [query] | Calculates your query -| addadminrole | addadminrole [roleid] | Adds an admin role for tickets -| unpin | unpin [id] | Unpins a message -| vote | vote | Vote for why bot -| deladminrole | deladminrole [roleid] | Removes an admin role from accessing tickets -| removereactions | removereactions [message id] | Removes reactions from a message -| cuse | cuse [@user(optional)] | How many times have you used Why? -| rps | rps [rock/paper/scissors] | Play a game of rock paper scissors against the bot -| dm | dm [@user] [message] | Bot sends a message on your behalf -| roast | roast | Bot roasts you -| onpinged | onpinged [set/clear(optional)] | Sets your Onpinged message -| sendroast | sendroast [@user] | The bots send a roast to someone on your behalf -| takerole | takerole [@role] [@member] | Remove roles form member -| embed | embed --title test --desc test --channel 123456789 --color blue --timestamp yes --fields 2 | Makes an embed -| 8 ball | 8ball [question] | Asks the 8ball a question -| runcode | runcode [language] [code] | Runs code -| reactrole | reactrole [:emoji:] [@role] [message text] | Creates a reactrole -| invite | invite | Creates a 10 day invite for your discord server. -| say | say [text] | Bot sends text -| yesorno | yesorno [question] | Makes a Yah or Nah poll -| poll | poll [time:seconds] '[title]' [each option followed by a space] | Makes a poll -| autorole | autorole [@role] [all/bot] | Sets the autorole role for the server -| reactemoji | reactemoji [message_id] [word] | React a word to a message -| voice | voice [set/setlimit] | Sets the custom vc for you voice channel -| hack | hack [@user] | Hack someone -| screenshot | screenshot [url] | Screenshot a url -| emojify | emojify [text] | Emojifies Text \ No newline at end of file +[license-url]: https://github.com/FusionSid/Why-Bot/blob/master/LICENSE.txt \ No newline at end of file diff --git a/assets/fonts/Monocraft.otf b/assets/fonts/Monocraft.otf new file mode 100644 index 0000000..631c97a Binary files /dev/null and b/assets/fonts/Monocraft.otf differ diff --git a/assets/fonts/Some_Time_Later.otf b/assets/fonts/Some_Time_Later.otf new file mode 100644 index 0000000..78346ed Binary files /dev/null and b/assets/fonts/Some_Time_Later.otf differ diff --git a/assets/images/heads.png b/assets/images/heads.png new file mode 100644 index 0000000..7a28a76 Binary files /dev/null and b/assets/images/heads.png differ diff --git a/assets/whybotlogo.png b/assets/images/logo.png old mode 100644 new mode 100755 similarity index 100% rename from assets/whybotlogo.png rename to assets/images/logo.png diff --git a/assets/images/selfhosting/advanced.jpg b/assets/images/selfhosting/advanced.jpg new file mode 100644 index 0000000..9c4f214 Binary files /dev/null and b/assets/images/selfhosting/advanced.jpg differ diff --git a/assets/images/selfhosting/bug_alert.jpg b/assets/images/selfhosting/bug_alert.jpg new file mode 100644 index 0000000..0be3073 Binary files /dev/null and b/assets/images/selfhosting/bug_alert.jpg differ diff --git a/assets/images/selfhosting/devmode.jpg b/assets/images/selfhosting/devmode.jpg new file mode 100644 index 0000000..969cc8b Binary files /dev/null and b/assets/images/selfhosting/devmode.jpg differ diff --git a/assets/images/selfhosting/dm1.jpg b/assets/images/selfhosting/dm1.jpg new file mode 100644 index 0000000..7057d37 Binary files /dev/null and b/assets/images/selfhosting/dm1.jpg differ diff --git a/assets/images/selfhosting/dm2.jpg b/assets/images/selfhosting/dm2.jpg new file mode 100644 index 0000000..9841e1b Binary files /dev/null and b/assets/images/selfhosting/dm2.jpg differ diff --git a/assets/images/selfhosting/dm3.jpg b/assets/images/selfhosting/dm3.jpg new file mode 100644 index 0000000..a14090b Binary files /dev/null and b/assets/images/selfhosting/dm3.jpg differ diff --git a/assets/images/selfhosting/join_alert.jpg b/assets/images/selfhosting/join_alert.jpg new file mode 100644 index 0000000..95748a4 Binary files /dev/null and b/assets/images/selfhosting/join_alert.jpg differ diff --git a/assets/images/selfhosting/leave_alert.jpg b/assets/images/selfhosting/leave_alert.jpg new file mode 100644 index 0000000..701b471 Binary files /dev/null and b/assets/images/selfhosting/leave_alert.jpg differ diff --git a/assets/images/selfhosting/online_alert.jpg b/assets/images/selfhosting/online_alert.jpg new file mode 100644 index 0000000..cb7c055 Binary files /dev/null and b/assets/images/selfhosting/online_alert.jpg differ diff --git a/assets/images/selfhosting/rightclickid.jpg b/assets/images/selfhosting/rightclickid.jpg new file mode 100644 index 0000000..bb76532 Binary files /dev/null and b/assets/images/selfhosting/rightclickid.jpg differ diff --git a/assets/images/selfhosting/suggestion_alert.jpg b/assets/images/selfhosting/suggestion_alert.jpg new file mode 100644 index 0000000..c84379a Binary files /dev/null and b/assets/images/selfhosting/suggestion_alert.jpg differ diff --git a/assets/images/selfhosting/user_settings.jpg b/assets/images/selfhosting/user_settings.jpg new file mode 100644 index 0000000..f376598 Binary files /dev/null and b/assets/images/selfhosting/user_settings.jpg differ diff --git a/assets/images/spongebob/bamboo.png b/assets/images/spongebob/bamboo.png new file mode 100644 index 0000000..b8ccf88 Binary files /dev/null and b/assets/images/spongebob/bamboo.png differ diff --git a/assets/images/spongebob/blueseaweed.png b/assets/images/spongebob/blueseaweed.png new file mode 100644 index 0000000..f9cf706 Binary files /dev/null and b/assets/images/spongebob/blueseaweed.png differ diff --git a/assets/images/spongebob/crosshatch.png b/assets/images/spongebob/crosshatch.png new file mode 100644 index 0000000..6ff5590 Binary files /dev/null and b/assets/images/spongebob/crosshatch.png differ diff --git a/assets/images/spongebob/flowers.png b/assets/images/spongebob/flowers.png new file mode 100644 index 0000000..863fea1 Binary files /dev/null and b/assets/images/spongebob/flowers.png differ diff --git a/assets/images/spongebob/greenseaweed.jpg b/assets/images/spongebob/greenseaweed.jpg new file mode 100644 index 0000000..35107fc Binary files /dev/null and b/assets/images/spongebob/greenseaweed.jpg differ diff --git a/assets/images/spongebob/heads.png b/assets/images/spongebob/heads.png new file mode 100644 index 0000000..04cd136 Binary files /dev/null and b/assets/images/spongebob/heads.png differ diff --git a/assets/images/spongebob/purplewood.jpg b/assets/images/spongebob/purplewood.jpg new file mode 100644 index 0000000..12dcb9d Binary files /dev/null and b/assets/images/spongebob/purplewood.jpg differ diff --git a/assets/images/spongebob/sand.png b/assets/images/spongebob/sand.png new file mode 100644 index 0000000..78a24a0 Binary files /dev/null and b/assets/images/spongebob/sand.png differ diff --git a/assets/images/spongebob/seaweed.png b/assets/images/spongebob/seaweed.png new file mode 100644 index 0000000..a375d53 Binary files /dev/null and b/assets/images/spongebob/seaweed.png differ diff --git a/assets/images/spongebob/steel.png b/assets/images/spongebob/steel.png new file mode 100644 index 0000000..ae100d3 Binary files /dev/null and b/assets/images/spongebob/steel.png differ diff --git a/assets/images/spongebob/tiles.png b/assets/images/spongebob/tiles.png new file mode 100644 index 0000000..02dd5b1 Binary files /dev/null and b/assets/images/spongebob/tiles.png differ diff --git a/assets/images/spongebob/title.png b/assets/images/spongebob/title.png new file mode 100644 index 0000000..b53de9c Binary files /dev/null and b/assets/images/spongebob/title.png differ diff --git a/assets/images/spongebob/wood.png b/assets/images/spongebob/wood.png new file mode 100644 index 0000000..03081af Binary files /dev/null and b/assets/images/spongebob/wood.png differ diff --git a/assets/images/tails.png b/assets/images/tails.png new file mode 100644 index 0000000..c800cb7 Binary files /dev/null and b/assets/images/tails.png differ diff --git a/assets/json_files/fun_text.json b/assets/json_files/fun_text.json new file mode 100644 index 0000000..5f020dd --- /dev/null +++ b/assets/json_files/fun_text.json @@ -0,0 +1,4589 @@ +{ + "compliment": [ + "You're that ā€œNothingā€ when people ask me what I'm thinking about.", + "You look great today.", + "You're a smart cookie.", + "I bet you make babies smile.", + "You have impeccable manners.", + "I like your style.", + "You have the best laugh.", + "I appreciate you.", + "You are the most perfect you there is.", + "Our system of inside jokes is so advanced that only you and I get it. And I like that.", + "You're strong.", + "Your perspective is refreshing.", + "You're an awesome friend.", + "You light up the room.", + "You deserve a hug right now.", + "You should be proud of yourself.", + "You're more helpful than you realize.", + "You have a great sense of humor.", + "You've got all the right moves!", + "Is that your picture next to ā€œcharmingā€ in the dictionary?", + "Your kindness is a balm to all who encounter it.", + "You're all that and a super-size bag of chips.", + "On a scale from 1 to 10, you're an 11.", + "You are brave.", + "You're even more beautiful on the inside than you are on the outside.", + "You have the courage of your convictions.", + "Aside from food. You're my favorite.", + "If cartoon bluebirds were real, a bunch of them would be sitting on your shoulders singing right now.", + "You are making a difference.", + "You're like sunshine on a rainy day.", + "You bring out the best in other people.", + "Your ability to recall random factoids at just the right time is impressive.", + "You're a great listener.", + "How is it that you always look great, even in sweatpants?", + "Everything would be better if more people were like you!", + "I bet you sweat glitter.", + "You were cool way before hipsters were cool.", + "That color is perfect on you.", + "Hanging out with you is always a blast.", + "You always know — and say — exactly what I need to hear when I need to hear it.", + "You smell really good.", + "You may dance like no one's watching, but everyone's watching because you're an amazing dancer!", + "Being around you makes everything better!", + "When you say, ā€œI meant to do that,ā€ I totally believe you.", + "When you're not afraid to be yourself is when you're most incredible.", + "Colors seem brighter when you're around.", + "You're more fun than a ball pit filled with candy. (And seriously, what could be more fun than that?)", + "That thing you don't like about yourself is what makes you so interesting.", + "You're wonderful.", + "Everyday is just BLAH when I don't see you For reals! (awesome – you are halfway through the list. You're awesome!)", + "Jokes are funnier when you tell them.", + "You're better than a triple-scoop ice cream cone. With sprinkles.", + "Your bellybutton is kind of adorable.", + "Your hair looks stunning.", + "You're one of a kind!", + "You're inspiring.", + "If you were a box of crayons, you'd be the giant name-brand one with the built-in sharpener.", + "You should be thanked more often. So thank you!!", + "Our community is better because you're in it.", + "Someone is getting through something hard right now because you've got their back.", + "You have the best ideas.", + "You always know how to find that silver lining.", + "Everyone gets knocked down sometimes, but you always get back up and keep going.", + "You're a candle in the darkness.", + "You're a great example to others.", + "Being around you is like being on a happy little vacation.", + "You always know just what to say.", + "You're always learning new things and trying to better yourself, which is awesome.", + "If someone based an Internet meme on you, it would have impeccable grammar.", + "You could survive a Zombie apocalypse.", + "You're more fun than bubble wrap.", + "When you make a mistake, you fix it.", + "Who raised you? They deserve a medal for a job well done.", + "You're great at figuring stuff out.", + "Your voice is magnificent.", + "The people you love are lucky to have you in their lives.", + "You're like a breath of fresh air.", + "You're gorgeous — and that's the least interesting thing about you, too.", + "You're so thoughtful.", + "Your creative potential seems limitless.", + "You're the coolest person I know. And I consider myself bet friends with like all celebrities, so. . . .", + "You're irresistible when you blush.", + "Actions speak louder than words, and yours tell an incredible story.", + "Somehow you make time stop and fly at the same time.", + "When you make up your mind about something, nothing stands in your way.", + "You seem to really know who you are.", + "Any team would be lucky to have you on it.", + "In high school I bet you were voted ā€œmost likely to keep being awesome.ā€", + "I bet you do the crossword puzzle in ink.", + "Babies and small animals probably love you.", + "If you were a scented candle they'd call it Perfectly Imperfect (and it would smell like summer).", + "There's ordinary, and then there's you.", + "You're someone's reason to smile.", + "You're even better than a unicorn, because you're real.", + "How do you keep being so funny and making everyone laugh?", + "You have a good head on your shoulders.", + "Has anyone ever told you that you have great posture?", + "The way you treasure your loved ones is incredible.", + "You're really something special.", + "You're a gift to those around you." + ], + "dares": [ + "Act like a monkey and record a video of it", + "Act like you do not understand human language until your next turn (come up with your own language)", + "Act like your favourite Disney character for the rest of the game", + "Close your eyes and send a blind text to a random person", + "Compose a poem on the spot based on something the group comes up with", + "Everything you say for the next 5 minutes has to rhyme", + "Everything you say for the next 5 minutes must not contain the words: 'but', 'a', 'the', 'or", + "Make a freestyle rap song about each person in the grou", + "Make a poem using the words 'orange' and 'moose'", + "Make a poem using the words 'pineapple' and 'apple'", + "Make a poem using the words 'goose' and 'peanuts'", + "Make up a poem about the colour blue", + "Make up a story about a random person in the group", + "Post 'I love English!' on a social media", + "Record a video of you dancing, but without music", + "Record a video of you playing the air drums to a song of your choice", + "Record a video of you playing the air guitar to a song of your choice", + "Record an impression of your favourite celebrity", + "Record an impression of your favourite animal", + "Record your best evil laugh; as loud as you can", + "Record your best president impression", + "Record yourself saying the alphabet backwards", + "Record yourself singing 'Twinkle Twinkle, Little Star' while beat boxing", + "Record yourself singing the alphabet without moving your mouth", + "Record yourself talking about your favourite food in a russian accent", + "Say 'ya heard meh' after everything you say for the next 5 minutes", + "Say 'you know what am sayin' after everything you say for the next 5 minutes", + "Text someone asking them if they believe in aliens, send a screenshot of the conversation", + "Send an email to one of your teachers, telling them about how your day is going and take a screenshot", + "Send an unsolicited text message to one of your friends, telling them about how your day is going and take a screenshot", + "Send the last photo you took with your phone camera", + "Send the last screenshot you took on your phone", + "Send the most embarrassing photo on your phone", + "Send the oldest selfie on your phone", + "Send a screenshot of your most recent google search history", + "Send a selfie of you making a funny face", + "Set your phone language to Chinese for the next 10 minutes", + "Show the last three people you texted and what the messages said", + "Text your crush and tell them how much you like them", + "Use the letters of the name of another player to describe them (ex. SAM : S = Silly ; A = Attractive ; M = Merry", + "Yell out the first word that comes to your mind, and record it", + "Go on Facebook Live and read the back of a shampoo bottle.", + "Call a 7-Eleven and ask if they're open.", + "Stand in the backyard and yell at the top of your lungs, ā€œNooooo! I was adopted!ā€", + "Go outside in the driveway and do the disco without music.", + "Call a car part store and tell them that you need a part for your Model T.", + "Take a selfie with the toilet and post it online.", + "Sniff everyone's feet and rank them in order of freshest to stinkiest.", + "Call a NY-style pizza place and ask them what the difference is between NY pizza and ā€œrealā€ pizza.", + "Open your front door and loudly sing ā€œHallelujah!ā€", + "Go outside and pretend you're cutting the grass with an invisible mower.", + "Call a pizza place and ask if they use cruelty-free wheat in their dough.", + "Call your mom and tell her you can't find a girlfriend in a very panicked voice.", + "Wear your underwear over your pants for the rest of the game.", + "Call the library and ask if they carry a dictionary that translates British to American.", + "Send a Snapchat of you pretending to cry because you just found out you were adopted.", + "Go on Facebook and write 'How do you spell 'facebook'?' as your status.", + "Sniff the armpit of the person next to you, and describe what it smells like to the entire group.", + "Go outside and try to summon the rain.", + "Sing the 'Star-Spangled Banner' in a British accent.", + "Take a picture of a tampon and post it on Instagram.", + "Call a random number, and when someone picks up, immediately start singing the national anthem.", + "Call Target and ask them if they deliver popcorn.", + "Call McDonald's and ask if they sell Whoppers.", + "Call a pizza shop and ask if you can return a pizza.", + "Call a car dealership and ask if they have any horse buggies in stock.", + "Change your relationship status on Facebook to 'it's complicated.'", + "Call Macy's and tell them you're interested in buying them.", + "Sing instead of speaking for the next two rounds of the game.", + "Call a random number and sing 'Happy Birthday.'", + "Call a Chinese restaurant and ask if they have sushi.", + "Go outside and pick exactly 40 blades of grass with a pair of tweezers.", + "Eat a whole piece of paper.", + "Fill your mouth with water, and each person in the group must tell the funniest joke they know. If you spit up the water, you have to eat a spoonful of dirt.", + "Tie your hands to your ankles for the rest of the game.", + "Suck your big toe.", + "Eat a mouthful of raw pasta.", + "Dump a bunch of LEGOs on the floor and walk over them with your bare feet.", + "Write a letter to your doctor describing an embarrassing rash you have, and post it on Facebook.", + "Go outside and hug a mailbox until at least three passersby have seen you.", + "Only speak in rhymes for the rest of the game.", + "Soak a shirt in water, put it in the fridge for 20 minutes, and then wear it.", + "Trade clothes with the person next to you.", + "Make a silly face and keep it that way until the next round.", + "Kneel for an hour.", + "Let someone wax your back.", + "Drink water straight from a running faucet for a whole minute.", + "For the rest of the game, do not say 'I.'", + "Make up a rap about koalas.", + "Call a stranger and tell them a secret.", + "Allow someone to pour ice down your shirt and pants.", + "Let each person in the group slap you as hard as they can on your butt.", + "Walk down the street in your underwear.", + "Make your ear touch your shoulder for the rest of the game.", + "Run around outside yelling, ā€œI have lice!ā€", + "Stop a car that is going down the street and tell them that their wheels are turning.", + "Open Facebook, go to the account of the first person you see, and Like every post on their wall going back a full year.", + "Pick the nose of the person next to you.", + "Lick a car tire.", + "Lick the bottom of your shoe.", + "Jump into a dumpster.", + "Take a plate of leftovers over to your neighbor, knock on their door, and say, 'Welcome to the neighborhood' as if you've never met them before.", + "Text someone ā€œhey.ā€ Every time they respond, say ā€œhey.ā€ Do this 10 times. For the 11th time, reply with ā€œhi.ā€", + "Dip your finger in the toilet, and then kiss that finger.", + "Make a hand puppet by drawing a face on your hand, and use your hand to say what you want to say.", + "Let everyone look through your search history for two minutes.", + "Coat your hands in food coloring and don't wash them off for 10 minutes.", + "Skype/FaceTime someone and pick your nose during the conversation.", + "Remove your underwear and throw it in the garbage.", + "Lick mayonnaise off of someone's toe.", + "Let a person in the group put a leash on you and walk you down the street.", + "Cry like a baby for one full minute.", + "Call a drug store and ask them which laxative is the most effective. After they answer, ask how many they have" + ], + "facts": [ + "The scientific term for brain freeze is ā€œsphenopalatine ganglioneuralgiaā€.", + "Canadians say ā€œsorryā€ so much that a law was passed in 2009 declaring that an apology can't be used as evidence of admission to guilt.", + "Back when dinosaurs existed, there used to be volcanoes that were erupting on the moon.", + "The only letter that doesn't appear on the periodic table is J.", + "One habit of intelligent humans is being easily annoyed by people around them, but saying nothing in order to avoid a meaningless argument.", + "If a Polar Bear and a Grizzly Bear mate, their offspring is called a ā€œPizzy Bearā€.", + "In 2006, a Coca-Cola employee offered to sell Coca-Cola secrets to Pepsi. Pepsi responded by notifying Coca-Cola.", + "There were two AI chatbots created by Facebook to talk to each other, but they were shut down after they started communicating in a language they made for themselves.", + "Nintendo trademarked the phrase ā€œIt's on like Donkey Kongā€ in 2010.", + "Calling ā€œshotgunā€ when riding in a car comes from the term ā€œshotgun messengerā€.", + "The famous line in Titanic from Leonardo DiCaprio, ā€œI'm king of the world!ā€ was improvised.", + "A single strand of Spaghetti is called a ā€œSpaghettoā€.", + "There is actually a difference between coffins and caskets -", + "coffins are typically tapered and six-sided, while caskets are rectangular.", + "Sunflowers can help clean radioactive soil. Japan is using this to rehabilitate Fukashima. Almost 10,000 packets of sunflower seeds have been sold to the people of the city.", + "To leave a party without telling anyone is called in English, a ā€œFrench Exitā€. In French, it's called a ā€œpartir Ć  l'anglaiseā€, to leave like the English.", + "If you cut down a cactus in Arizona, you be penalized up to 25 years in jail. It is similar to cutting down a protected tree species.", + "The Buddha commonly depicted in statues and pictures is a different person entirely. The real Buddha was actually incredibly skinny because of self-deprivation.", + "In Colorado, USA, there is still an active volcano. It last erupted about the same time as the pyramids were being built in Egypt.", + "The first movie ever to put out a motion-picture soundtrack was Snow White and the Seven Dwarves.", + "If you point your car keys to your head, it increases the remote's signal range.", + "In order to protect themselves from poachers, African Elephants have been evolving without tusks, which unfortunately also hurts their species.", + "The scientific name for Giant Anteater is Myrmecophaga Tridactyla. This means ā€œant eating with three fingersā€.", + "Originally, cigarette filters were made out of cork, the look of which was incorporated into today's pattern.", + "In 1923, a jockey suffered a fatal heart attack but his horse finished and won the race, making him the first and only jockey to win a race after death.", + "At birth, a baby panda is smaller than a mouse.", + "Iceland does not have a railway system.", + "The largest known prime number has 17,425,170 digits. The new prime number is 2 multiplied by itself 57,885,161 times, minus 1.", + "Forrest Fenn, an art dealer and author, hid a treasure chest in the Rocky Mountains worth over 1 million dollars. It still has not been found.", + "The lead singer of The Offspring started attending school to achieve a doctorate in molecular biology while still in the band. He graduated in May 2017.", + "The world's largest grand piano was built by a 15-year-old in New Zealand.", + "In order to keep Nazis away, a Polish doctor faked a typhus outbreak. This strategy staved 8,000 people.", + "After the release of the 1996 film Scream, which involved an anonymous killer calling and murdering his victims, Caller ID usage tripled in the United States.", + "The spiked dog collar was invented by the Ancient Greeks to protect their dogs from wolf attacks.", + "Jack Daniel (the founder of the whiskey) died from kicking a safe. When he kicked it, he broke his toe which got infected. He eventually died from blood poisoning.", + "There is a boss in Metal Gear Solid 3 that can be defeated by not playing the game for a week; or by changing the date.", + "The Roman - Persian wars are the longest in history, lasting over 680 years. They began in 54 BC and ended in 628 AD.", + "Elton John tried to commit suicide once by sticking his head in an oven with the gas on low and windows open. He was found and stopped by his best friend Bernie Taupin.", + "If you translate ā€œJesusā€ from Hebrew to English, the correct translation is ā€œJoshuaā€. The name ā€œJesusā€ comes from translating the name from Hebrew, to Greek, to Latin, to English.", + "Ed Sheeran bought a ticket to LA with no contacts. He was spotted by Jamie Foxx, who offered him the use of his recording studio and a bed in his Hollywood home for six weeks.", + "German Chocolate Cake is named after an American baker by the name of Samuel German.", + "The first service animals were established in Germany during World War I. References to service animals date as far back as the mid-16th Century.", + "An 11-year-old girl proposed the name for Pluto after the Roman god of the Underworld.", + "The voice actor of SpongeBob and the voice actor of Karen, Plankton's computer wife, have been married since 1995.", + "An Italian banker, Gilberto Baschiera is considered a modern-day Robin Hood. Over the course of 7 years, he secretly diverted 1 million euros to poorer clients from the wealthy ones so they could qualify for loans. He made no profit and avoided jail in 2018 due to a plea bargain.", + "Octopuses and squids have beaks. The beak is made of keratin - the same material that a bird's beak, and our fingernails are made of.", + "An estimated 50% of all gold ever mined on Earth came from a single plateau in South Africa: Witwatersrand.", + "75% of the world's diet is produced from just 12 plant and five different animal species.", + "The original Star Wars premiered on just 32 screens across the U.S. in 1977. This was to produce buzz as the release widened to more theaters.", + "The British government coined the slogan, ā€œKeep Calm and Carry onā€ during World War 2 in order to motivate citizens to stay strong.", + "Apple paid a couple $1.7 million dollars for their plot of land, which was only worth $181,700.", + "Tirana, the capital of Albania has a lot of things in common with other European capitals - except one. It's one of two capitals without a McDonalds. The second is Vatican City.", + "Joe Arridy had an IQ of 46 and is known as the ā€œhappiest prisoner on death rowā€. He went into the gas chamber with a smile. It turned out he was innocent.", + "The largest Japanese population outside of Japan stands at 1.6 million people who live in Brazil.", + "IKEA is an acronym which stands for Ingvar Kamprad Elmtaryd Agunnaryd, which is the founder's name, farm where he grew up, and hometown.", + "In 2009, Stephen Hawking held a reception for time travelers, but didn't publicize it until after. This way, only those who could time travel would be able to attend. Nobody else attended.", + "Violin bows are commonly made from horse hair.", + "There are less than 30 ships in the Royal Canadian Navy which is less than most third-world countries.", + "Larry the Cable Guy's real name is Daniel Lawrence Whitney. His notable Southern accent is fake - he was born and raised in the mid-west, not the South.", + "The youngest Pope in history was Pope Benedict IX who was 11 years old at the time of election. He is also the only person to have been the Pope more than once.", + "In Svalbard, a remote Norwegian island, it is illegal to die.", + "Costa Coffee employs Gennaro Pelliccia as a coffee taster, who has had his tongue insured for Ā£10 million since 2009.", + "Johnny Cash took only three voice lessons before his teacher advised him to stop taking lessons and to never deviate from his natural voice.", + "During the Prohibition era, the U.S. Government allowed Whiskey to be sold through pharmacies. As a result, Walgreens grew from 20 retail stores to almost 400.", + "People who post their fitness routine to Facebook are more likely to have psychological problems.", + "Medieval chastity belts are a myth. A great majority of examples now existing were made in the 18th and 19th centuries as jokes.", + "Nowadays, millionaires with just $1 million isn't considered wealthy anymore by most Americans. Now, the typical American sees at least $2.4 million as wealthy.", + "Hanna-Barbera pitched The Flintstones to networks for 8 weeks before it was finally picked up. It became the first ever animated show to air during primetime.", + "In 1325, two Italian city states fought over a bucket which resulted in 2,000 deaths. It started when two soldiers stole a bucket from a well from the city center.", + "There's no period in ā€œDr. Pepperā€. It was removed because the old logo font made it look like ā€œDi: Pepperā€.", + "There is an underwater version of rugby, unsurprisingly called ā€œunderwater rugbyā€.", + "Standing around burns calories. On average, a 150 pound person burns 114 calories per hour while standing and doing nothing.", + "Although GPS is free for the world to use, it costs $2 million per day to operate. The money comes from American tax revenue.", + "In World War II, Germany tried to collapse the British economy by dropping millions of counterfeit bills over London.", + "Playboy has been publishing braille versions of their magazines since 1970, however no pictorial representations are included.", + "When Space Invaders was created, Tomohiro Nishikado left in the lag caused by more invaders on the screen in order to create greater difficulty in the games.", + "The color red doesn't really make bulls angry; they are color-blind.", + "65% of autistic kids are left-handed, and only 10% of people in general are left-handed.", + "Herring fish communicate by using flatulence.", + "Until 2016, the ā€œHappy Birthdayā€ song was not for public use. Meaning, prior to 2016, the song was copyrighted and you had to pay a license to use it.", + "When mice live in the wild, they typically only live for about six months.", + "There is a punctuation mark used to signify irony or sarcasm that looks like a backwards question mark āø®", + "During the cremation process of a 500 pound body, the corpse was so obese that it set the crematorium on fire.", + "Researches have found that flossing your teeth can help your memory. Flossing prevents gum disease, which prevents stiff blood vessels, which cause memory issues.", + "When George Washington died, Napoleon Bonaparte of France gave a personal eulogy and ordered a ten day mourning period for France.", + "The Hobbit has been published in two editions. In the first edition, Gollum willingly bet on his ring in the riddle game.", + "For nearly 60 years, Texas didn't have an official state flag between 1879 & 1933. During that time, the Lone Star flag was the active, but unofficial flag.", + "A wildlife technician, Richard Thomas, took the famous tongue twister, ā€œhow much wood would a woodchuck chuck if a woodchuck could chuck woodā€ and calculated a rough estimate of what the answer would actually be. It came out to be around 700 pounds.", + "Red Solo cups are a common souvenir to bring back from the United States. The novelty comes from the cups being used in many party scenes in movies.", + "Swedish meatballs originated from a recipe King Charles XII brought back from Turkey in the early 1800s.", + "Saint Lucia is the only country in the world named after a woman.", + "Ben & Jerry's has an online flavor graveyard for their 10 discontinued ice cream flavors. Each one has a photo, life span, and epitaph.", + "Scientists discovered sharks that are living in an active underwater volcano. Divers cannot investigate because they would get burns from the acidity and heat.", + "There are times when Pluto is closer to the Sun than Neptune - one of these timelines was from 1979 to 1999.", + "There is a town in Nebraska called Monowi with a population of one. The only resident is a woman who is the Mayor, Bartender and Librarian.", + "The Ethiopian calendar is 7.5 years behind the Gregorian calendar due to the fact that it has 13 months.", + "In 1994, the company who had a patent on GIFs tried to charge a fee for using GIFS. The PNG was invented as an alternative, and the company backed down.", + "China is spending $3 billion dollars to build panda shaped solar farms in order to get more young people interested in renewable energy.", + "While hunting, stoats go crazy jumping, spinning, and twisting to get a rabbits attention. This hypnotizes the rabbit until the stoat gets close enough to attack.", + "The average American child is given $3.70 per tooth that falls out.", + "To properly write adjectives in order, you would list them by amount, value, size, temperature, age, shape, color, origin, and material.", + "The world's first motel is in San Luis Obispo. Built in 1925. When opened, it cost $1.25 for a two-room bungalow with a kitchen and a private adjoining garage.", + "Scotland was one of the few countries able to hold off being conquered by the Romans in the first century A.D.", + "I Will Always Love You was originally written and recorded in 1973 by Dolly Parton. It was written as a farewell to her mentor of seven years.", + "ā€œOpposites attractā€ is a common myth. People are actually attracted to people who look like family members, or those with a similar personality type.", + "Llamas can be used as guards against coyote attacks on sheep herds. Studies have proven that just one guard llama is an effective protector and can even kill the attacking coyotes.", + "The unique smell of rain actually comes from plant oils, bacteria, and ozone.", + "Vanilla flavoring is sometimes made with the urine of beavers.", + "If you heat up a magnet, it will lose its magnetism.", + "The most expensive virtual object is ā€œClub NEVERDIEā€ in the Entropia Universe which is worth $635,000. It was originally bought at $10,000.", + "Cruise ships have morgues that can store up to 10 bodies at once. The average amount of people that die on cruise ships per year is 200.", + "Birds are the closest living relatives of crocodilians, as well as the descendants of extinct dinosaurs with feathers. This makes them the only surviving dinosaurs.", + "Small as they may be, ladybugs have a unique smell that humans are incredibly sensitive to.", + "During WWII, a U.S. naval destroyer won a battle against a Japanese submarine by throwing potatoes at them. The Japanese thought they were grenades.", + "The Marshal Mathers foundation for at-risk and disadvantaged youth, was founded by Eminem.", + "A man with severe OCD and a phobia of germs attempted to commit suicide with a gun to his head. Instead of killing him, the bullet eliminated his mental illness without any other damage.", + "Since 1955, 50% of the population of Niger is consistently under 16 years old. The total current population is 21,600,000.", + "The author of Mary Had a Little Lamb, Sarah Josepha Hale, is most responsible for the creation of Thanksgiving being a national holiday.", + "The oldest unopened bottle of wine was found in a Roman tomb that is over 1,650 years old.", + "Chicken Run is the highest-grossing stop motion animated film, even beating The Nightmare Before Christmas.", + "Nobody knows how the Academy Awards came to be referred to as the Oscars. The earliest mention was 1932, and was made official in 1939.", + "More tornadoes occur in the United Kingdom per square mile than any other country in the world.", + "Owners of personalized license plates in Uganda are facing a tax increase of over 300%, which will raise the tax from $1,498 to $5,992.", + "Popularized by the Shakespeare play, many people think Julius Caesar's last words were ā€œAnd you, Brutus?ā€ In reality, he said ā€œYou too, my child?ā€", + "Times Square was originally called Longacre square until it was renamed in 1904 after The New York Times moved its headquarters to the newly built Times Building.", + "Daniel Craig was an anonymous Storm Trooper in Star Wars: The Force Awakens. Originally, he denied his cameo and claimed he wouldn't bother being an extra in a movie.", + "Queen Elizabeth has a personal net worth of 425 million dollars. That includes the $65 million Sandringham House and $140 million Balmoral Castle.", + "Although there is currently no drug proven to make someone tell the truth, some countries like Russia, Canada, and India use truth serums.", + "Only primates, humans, and opossums have opposable thumbs. Out of these, the opossum is the only one with no thumbnail.", + "One of the World Trade Center's was built to be 1,776 feet tall on purpose to reference the year the Declaration of Independence was signed.", + "The word ā€œkimonoā€, literally means a ā€œthing to wearā€. Ki is ā€œwearā€, and mono is ā€œthingā€.", + "There is a statue of Tesla in Silicon Valley that radiates free Wi-Fi. It was done as an homage to his vision for wireless communication.", + "It snows metal on planet Venus! There are two types that have been found, galena and bismuthinite.", + "Tic Tacs got their name from the sound they make when they are tossed around in their container.", + "Only official members of a federally accepted Native American tribes may legally possess or collect eagle feathers. If a normal citizen has one, it is illegal.", + "By the time they have been retired for 2 years, 78% of former NFL players have gone bankrupt or are under financial stress because of joblessness or divorce.", + "500 seeds of 5 different types of seeds were taken into orbit around the moon, and later planted around the U.S. as well as a few countries. They were called Moon Trees.", + "In order to protest the high tariffs enforced by a U.K. censorship board, a filmmaker sent in a 10 hour ā€œmovieā€ of white paint drying. They had to watch the entire film.", + "The popular LMFAO group who created the viral hit, Party Rock Anthem, is made up of an uncle-nephew duo.", + "50% of apartments in Los Angeles don't come with a fridge. This is legal, as fridges are considered an ā€œamenityā€, and therefore landlords are not required to provide one.", + "Norway has a 25 year statute of limitation on murder. This means if the murder happened more than 25 years ago, they cannot be charged.", + "Several of the facts on Snapple caps have been found to be outdated, incorrect or exaggerated.", + "Both of the drummers from Queen and Duran Duran had the same name - Roger Taylor.", + "There is a company in the U.K. that offers ā€œbeing hungoverā€ as a valid reason for calling off work. They are allotted four hungover days per year.", + "The majority of blind people in the U.S. and the U.K. cannot read braille. Statistically, less than 1% of blind in the U.K., and under 10% in the U.S.", + "It's not just humans who are right or left-handed. Most female cats prefer using their right paw and males are more likely to be left-pawed.", + "Over 290 people have died climbing Mount Everest since 1922. Most deaths occur because of avalanches, and not all bodies have been recovered.", + "There are only two countries in the world that have the color purple in their flags: Nicaragua and Dominica.", + "A bolt of lightning can reach 53,540 degrees Fahrenheit. That's 5 times hotter than the surface of the sun, which is 10,340 degrees Fahrenheit.", + "There is a village in Russia called Tsovkra where every resident can tightrope walk. It is a tradition that dates back over 100 years but no one knows how it started.", + "When Shakira was in second grade, she was rejected for the school choir because her vibrato was too strong. The music teacher told her that she sounded like a goat.", + "Four of the top seven highest grossing films of all time were released in 2015. Avengers: Age of Ultron, Furious 7, Jurassic World and Star Wars: The Force Awakens.", + "Four Nile crocodiles have been found in Florida. They are the second largest crocodile and are more dangerous than the native crocodiles and alligators in Florida.", + "Julius Caesar's only son, Caesarion, was the last Pharaoh of Egypt. Even though Cleopatra swears he is Caesar's son, Caesar never officially acknowledged him.", + "The quietest room in the world in Minnesota is measured in negative decibels - so quiet that you can hear your own heartbeat and your bones moving.", + "ā€œTsundokuā€ is a Japanese word for the habit of buying too many books, letting them pile up in your house, and never reading them.", + "The Guinness World Record for the time longest spend searching for the Loch Ness Monster, is held by Steve Feltham who camped at Loch Ness for 25 years.", + "Brain fibers lose 10% of their total length every decade. They can shrink even more so under acute stress.", + "Chewing gum boosts mental proficiency and is considered a better test aid than caffeine - but nobody knows why.", + "Per capita, the happiest countries in the world also rank highest in terms of consumers of antidepressants.", + "Even though Irish is the official language of Ireland, Polish is more widely spoken.", + "There's a bar in Yukon that serves a ā€œSourtoe cocktailā€. It consists of a shot of whisky with a human toe floating in the glass. An estimated 60,000 people have had it.", + "The Stockholm archipelago has more islands than the Pacific Ocean at around 30,000.", + "Pope Francis has been given many extravagant gifts over the years, and one of them was a Harley-Davidson motorcycle. However, rather than keeping it for his own pleasure and adventures, he sold it off and used the money to benefit homeless people.", + "In Japan, Domino's started testing pizza delivery via reindeer in 2016.", + "The motto on the United Kingdom's Royal Coat of Arms is in French. The motto is ā€œDieu et mon droitā€, which means ā€œGod and my rightā€.", + "The average household income of the top 1% in the United States is $1,260,508 per year.", + "Disney sold the streaming right for the original Star Wars films in 2016 to Turner until 2024. Disney has since decided to start a streaming service and has tried asking for the rights back, but Turner refuses every time.", + "Gaming-related accidents increased by 26.5% during the first 5 months of PokĆ©mon Go being released. This included 2 deaths and $25.5 million in damages.", + "Helen Keller was related to Robert E. Lee. Her paternal grandmother was second cousins with him.", + "During the 1908 Olympics in London, the Russians showed up 12 days late due to the fact that they were using the Julian calendar instead of the Gregorian calendar.", + "Non-violent attempts to escape Mexican prisons are not punished because ā€œit's human nature to want freedomā€.", + "The line, ā€œBorn and raised in South Detroitā€ in Journey's ā€œDon't Stop Believinā€ actually refers to Canada, not Michigan.", + "On one slow news day on April 18th, 1930, a BBC radio announcer blatantly said ā€œthere is no newsā€.", + "If you cut a starfish, it won't bleed - it doesn't have blood! Rather, they circulate nutrients by using seawater in their vascular system.", + "12% of the world's total languages is found in Papua New Guinea, which has over 820 indigenous languages. There are more languages on this island than any other country.", + "The hottest temperature ever recorded in Washington state was at Ice Harbor Dam at 118 °F (47.8 °C) on August 5, 1961.", + "In efforts to undercut the Dreamcast's sales of the upcoming SEGA release, Sony announced the PlayStation 2 and exaggerated its performance capabilities.", + "Nepal has the most mathematical flag in the world. It even has an article in its constitution that details the steps of drawing the flag.", + "Mount Rushmore cost less than one million dollars to construct. It took 14 years to build - from 1927 to 1941, and took 400 workers.", + "Samsung means ā€œthree starsā€ in Korean. This was chosen by the founder because he wanted the company to be powerful and everlasting like stars in the sky.", + "On average, 46.1% of Americans have less than $10,000 in assets when they die.", + "While shedding, geckos will eat their skin in order to prevent predators from finding and eating them more easily.", + "Bees actually have knees. The expression comes from the fact that they store large build ups of pollen in hairy baskets on their knees.", + "Between North and South Korea lies 155 miles of no man's land where hundreds of rare animals species thrive.", + "While watching a Merry-Go-Round from a bench in Griffith Park, Los Angeles, Walt Disney was struck with inspiration for the creation of Disneyland.", + "There is a Scottish tartan designed for Mars exploration. It was officially registered in 2016 to be worn during Mars science, exploration and outreach activities.", + "Santa Claus was issued a pilot's license from the U.S. government in 1927. They also gave him airway maps and promised to keep the runway lights on.", + "When you exercise, the burned fat metabolizes to become carbon dioxide, water, and energy. Meaning: you exhale the fat that you lose.", + "The word ā€œvelociraptorā€ comes from the Latin words ā€œveloxā€ which means swift, and ā€œraptorā€ which means robber. Literally - speedy robber!", + "The largest stadium in the world is the Rungrado 1st of May Stadium in North Korea. It can hold up to 114,000 spectators. It covers 51 acres and is 197 feet tall.", + "Polar bears often hunt walruses by simply charging at a group of them and eating the ones that were crushed or wounded in the mass panic to escape. Direct attacks are rare.", + "The group of spikes at the end of stegosaurid tails are called the ā€œthagomizerā€. They had no distinct name until the term was coined in 1982 by a cartoonist.", + "There is a correlation between pulling an all-nighter and snapping out of depression. This is because the brain gets more active the longer it goes without sleep.", + "Adult cats only meow at humans, not other cats. Kittens meow to their mother but once they get a little older, cats no longer meow to other cats.", + "When shuffling a deck of cards, the number of possible arrangements is approximately 8Ɨ1067. That's more than the number of stars in the observable universe.", + "There is a United Arab Emirates' territory inside an Oman's territory that itself is inside the United Arab Emirates country. It is called Madha village.", + "Disappointment Island is an uninhabited island in New Zealand. Over 65,000 pairs of white-capped albatross live there. In 1868, a steel tanker crashed on the island which killed 68 people, leaving the 15 survivors waiting 18 months to be rescued. In 1907, another ship ended up crashing there and 12 men drowned.", + "During the entire run of Gilligan's Island, it was never revealed if ā€œGilliganā€ was his first or last name.", + "When Jorge Garcia first got the part on LOST as Hurley, he lost a total of 30 pounds in weight before filming started.", + "Videogames have been found to be more effective at battling depression than therapy.", + "Bi-weekly has two different definitions: Twice a week, or once every two weeks.", + "Mona Lisa was stolen from the Louvre in 1911, which drew more visitors to see the empty space than the actual painting.", + "There is an insurance policy issued against alien abduction. Around 50,000 policies have been sold, mainly to residents of the U.S. and England.", + "Volvo invented the three-point seatbelt, then gave the invention away for free. They decided it was too important of an invention to keep to themselves.", + "In 2005, Connecticut was accidentally issued an Emergency Alert to evacuate the entire state. Only about 1% of the people actually tried to leave.", + "An 18-year old with dwarfism played the 8-year-old Grinch in The Grinch (2000). He passed away two years after the film was released.", + "Rebecca Felton was the first woman to ever serve for the United States Senate - but she only served for one day.", + "Websters Dictionary accidentally had a word that didn't exist in it for five years - ā€œDordā€.", + "Amber colored rear turn signals are statistically proven to reduce collisions by about 28%.", + "Roselle, a guide dog, lead her blind owner down 78 flights of stairs during 9/11. The descent took about an hour and they both safely made it out.", + "It is thought by Russians that eating ice cream will keep you warm.", + "Somebody hid an episode of South Park inside Tiger Woods 99 as an Easter egg, causing EA to do a massive recall.", + "Madagascar once was a stomping ground for lemurs which were the size of today's gorillas.", + "Underneath the streets of Beijing, there are over a million people who live in nuclear bunkers.", + "The full name of the famous Chuck E. Cheese's mouse is Charles Entertainment Cheese.", + "The American roulette wheel is different from the European wheel. The American one has 2 green spaces while the European one only has 1. Although on both, if you add up all the numbers on a roulette wheel, you will get 666.", + "The day after Thanksgiving is called ā€œBrown Fridayā€ by plumbers in America because it is their busiest day of the year.", + "The wife of Anthony Perkins, the original Norman Bates, Berry Berenson died as a result of being a passenger on one of the 9/11 planes.", + "The thumbs up sign is believed to have originated from Chinese pilots. It was used to communicate with the ground crew before take-off.", + "In Toy Story 2 in the scene where Buzz gives a speech in front of the American flag, the flag was edited to be a globe for release in non-American countries.", + "The tallest mountain in our solar system, Olympic Mons, is 3 times taller than Mount Everest.", + "Batman and Predator exist in the same fictional universe. Since 1991, they have been featured together in three comic books.", + "A study from Harvard University finds that having no friends can be just as deadly as smoking. Both effect levels of a blood-clotting protein.", + "In South Dakota, you can get a driver's permit at age 14 with parental consent. Once you turn 16, you can get your license.", + "The Backpack Kid's real name is Russell Horning.", + "In 2017, Adidas sold over 1 million pairs of shoes made from recycled ocean plastic, each pair taking 11 plastic bottles to make.", + "The British Pound is the world's oldest currency still in use at 1,200 years old. The pound has been an identity as a symbol of British sovereignty.", + "The world's smallest mammal, a Bumblebee Bat, weights about the same as a U.S. dime. Native to Myanmar and Thailand, these bats are endangered.", + "As of December 2018, The Itchy & Scratchy Show that takes place within The Simpsons, has 107 episodes.", + "Will Smith owed $2.8 Million to the IRS and almost went bankrupt, just before he signed the contract for The Fresh Prince Of Bel-Air.", + "The Great Pyramid of Giza actually has eight sides, rather than four. All of the other pyramids have just four sides.", + "English is not native to the British Isles. It was brought to Britain in the mid 5th to 7th Centuries by German, Danish, and Dutch settlers.", + "When we're born, the only innate fears we have are the fear of falling, and the fear of loud sounds. All other fears are learned.", + "In Korea, there is a breed of dog called a Sapsali which was originally thought to banish ghosts and evil spirits.", + "Qantas Airways once powered an interstate flight on cooking oil. This marked Australia's first commercial flight using sustainably derived biofuel.", + "Berries are simple fruits stemming from one flower. This means that pineapples, bananas, watermelon, pumpkins, and avocados are berries.", + "One horse can have approximately 15 horsepower. Horsepower is about 746 watts. The term was coined in the late 18th Century.", + "The Leaning Tower of Piza is tilted because of the soft soil that it's built on - which has also protected it from at least 4 powerful earthquakes.", + "All the paint on the Eiffel Tower weighs the same as ten elephants. It gets repainted every seven years without closing to the public.", + "All new FBI special agents and intelligence analysts are required to visit the United States Holocaust Memorial Museum.", + "While dinosaurs roamed the earth, they lived on every continent including Antarctica.", + "The blue whale's heart is the size of a VW Beetle and weighs up to 1,000 lbs (453 kg).", + "One of the most widely used symbols for medical assistance is actually the Swiss flag, and not a medical symbol.", + "Until 2007, slavery was legal in Mauritania. Even still, 1-4% of the population is still living as slaves.", + "A man named Ronald MacDonald robbed a Wendy's in 2005.", + "The longest unbroken alliance in world history is between England and Portugal. It has lasted since 1386, and still stands today.", + "Before finally being accepted, J.K. Rowling's original Harry Potter pitch was rejected by 12 publishers.", + "The Japanese term for a Shotgun Wedding is ā€œDekichatta kekkonā€, which literally translates to ā€œoops-we-did-it-marriageā€.", + "Garlic is known to attract leeches.", + "In 1992, a shipping crate containing 28,000 rubber duckies fell overboard. They washed up around the world for the next 20 years.", + "There is a company in Japan that has schools that teach you how to be funny. The first one opened in 1982. About 1,000 students take the course each year.", + "Aeroflot Flight 593 crashed because the pilot let his kids fly the plane, who unknowingly disengaged the autopilot function. The crash killed everyone on board.", + "It takes Uranus 84 years to orbit the Sun once.", + "A 26 sided shape is known as a rhombicuboctahedron.", + "Mob boss Vincent Gigante used to wander around New York in his bath robe to convince the police he was insane and avoid capture.", + "The military has used silly string to detect trip wires in Iraq. Before entering a room, they can squirt it inside. If it hangs in the air, it may have revealed a wire.", + "By applying even pressure on an egg, it is nearly impossible to break the shell by squeezing it.", + "So far, two diseases have successfully been eradicated: smallpox and rinderpest. The last case of smallpox was in 1977, and the last of rinderpest was in 2001.", + "A recently discovered deep sea snail, the Scaly-Food Gastropod, has a shell that is impressively developed. So much so, that the U.S. military is trying to use its design to inspire defensive layers in military armor.", + "There are over 6,000 known species of grass.", + "The total weight of all air on Earth is 11 quintillion pounds.", + "ā€œTime anxietyā€ occurs in a person when they are perpetually afraid of being late, or of others being late.", + "Ant queens can live for up to 30 years.", + "In 2014, Princeton researches determined that Facebook would lose 80% of its users by 2017.", + "Orlando Bloom has swinophobia, which is a fear of pigs!", + "Eight of the ten largest statues in the world are of Buddha's.", + "Meghan Trainor has Nyctophobia, which is a huge fear of the dark.", + "Garrett McNamara holds the record for the largest wave ever surfed, set in 2011 in Nazare, Portugal. The wave was 78 feet tall.", + "The logo on the Red Bull cans are not cattle, but a type of bovine called a ā€œgaurā€.", + "In Bruges, Belgium, there an underground pipeline that runs 2 miles to transfer beer from a brewery to the bottling plant.", + "The collective group of lemurs is called a conspiracy.", + "The first Primark store opened in Dublin in June 1969 under the name Penney's.", + "Ron Swanson's character on Parks and Recreation is based off a real person - a woman who works in a high bureaucratic position who is also anti-government.", + "Captive tigers in the U.S. alone outnumber the amount of wild tigers worldwide.", + "The 1831 London Bridge was sold in 1962 when it needed to be replaced. It was bought by Lake Havasu City, Arizona, to help tourism.", + "Scrappy-Doo, widely considered to be one of the most hated characters in fiction, has not appeared in an animated Scooby-Doo production since 1988 due to audience backlash.", + "The first X-Rated animated movie was Fritz the Cat. It is still the most successful independent animated feature to date.", + "Roughly 33% of cats are not effected by catnip. The euphoric reaction commonly associated with catnip is hereditary.", + "Facebook will track and record nearly everything you do if you browse the web while logged in to your Facebook account.", + "Bubble wrap was originally invented to be wallpaper. The creators tried to make plastic wallpaper with a paper backing, but it came out with plastic backing.", + "The longest Cricket Test match lasted over 12 days between England and South Africa. It only ended because the English team would have missed their boat home.", + "MIT, often cited as one of the world's most prestigious universities, puts almost all of its course materials online for anyone to access for free.", + "Albert Einstein had mastered calculus by the tender age of 15.", + "Madagascar got its name when Marco Polo misspelled it. The name stuck and the island was christened to the name in 1500.", + "Tom Hanks had an asteroid named after him which was called ā€œ12818 tomhanksā€œ.", + "The number of stars on the EU flag doesn't represent anything. 12 stars were chosen as a number with no political association and for a symbol of unity.", + "High heels are nothing new to the world. In the 18th Century, they were fashionable even for children.", + "Elephants think people are cute, the same way people think puppies or kittens are cute.", + "Cats which have blue eyes for the duration of their lives are likely to be deaf.", + "The manager of Guinness started the Guinness Book of World Records when he got annoyed that he couldn't find out what the fastest game bird was ever recorded.", + "Barry Manilow wrote many famous jingles for companies like McDonald's, State Farm and BandAids.", + "One of the smartest bird species is the magpie. They can even recognize themselves in mirrors.", + "The tongue is the only muscle in one's body that is attached from one end.", + "The atomic number of zinc is 30.", + "The tall chef's hat is called a toque.", + "There's very little evidence the TSA has ever stopped a terrorist or found a real bomb. When tested, they failed to find fake weapons and bombs 95% of the time.", + "In the United States, each person owns an average of seven pairs of blue jeans. That's one for every day of the week!", + "Whenever Charles Dickens was away from home, he would always realign the bed he was sleeping in to face Northwards, as he felt that this fostered and unlocked his creativity.", + "The Shawn Mendes EP reached Number 1 on iTunes in 37 minutes.", + "Since 1990 the United States has added more acres of forest than it has lost, with almost 20 million new acres of forest land added in the last 2 decades.", + "There is a liquid that you can breathe in called perfluorohexane. Animals can be submerged in a bath of perfluorohexane without drowning.", + "Feeding curry to a sheep reduces the amount of methane in its farts by up to 40%.", + "During a conversation each speaker's ā€œturnā€ averages 2 seconds, and the pause in between only 200 milliseconds. That figure is nearly universal.", + "Cold showers have more health benefits than hot or warm showers. These include improving circulation, stimulating weight loss, and easing depression.", + "Despite being landlocked, Mongolia has a navy consisting of seven men and one vessel, the ā€œSukhbaatar IIIā€œ, stationed on Lake Khƶvsgƶl.", + "During the Second World War, German tank drivers would drive their vehicles over camel droppings, thinking it would bring them good luck.", + "In Alabama, it is illegal to participate in shooting, hunting, gaming, card playing or racing on Sundays. The fine ranges from $10 to $100.", + "Originally, bumper cars were not supposed to hit each other. Drivers were actually supposed to avoid crashing in spite of chaotic driving. The first company to patent a bumper car was in 1920 named Dodgem.", + "A woman was elected to the House of Representatives four years before women even won the right to vote.", + "In the Netherlands version of Sesame Street, instead of Big Bird, they have a blue bird named Pino. He was later established as Big Bird's cousin.", + "The full Bible has been translated into over 3,000 languages. Among those include fictional languages like Elvish, Klingon and Na'vi.", + "The voice of Stargazer in Mass Effect 3 was done by Buzz Aldrin.", + "The first 4th of July celebration was in 1777.", + "Justin Bieber's first tweet was at 8:27pm on May 11, 2009.", + "Snakes and lizards both molt out of their old skin as they grow. Investigators have come to the conclusion that dinosaurs may have also molted.", + "Zebras have only one toe on each foot.", + "Humans have been performing dentistry since 7000BC, which makes dentists one of the oldest professions.", + "Originally, the PokĆ©mon Vulpix was going to be called ā€œFoxFireā€.", + "The first ever documented feature film was made in Australia in 1906.", + "Ancient Roman surgeons were trained to block out the screams of human pain.", + "William Hung, made famous for his appearance on American Idol singing ā€œShe Bangsā€œ, is a 73rd generation descendant of Confucius.", + "From 1937 to 1953, NBC's Today Show had a chimpanzee co-host named J. Fred Muggs. It is estimated he brought in the network around $100 million.", + "Because snow is composed of ice, it can be classified as a mineral. Water, however, does not fall under the same classification, and is not a mineral.", + "Of the 9,000 Blockbuster stores that existed in the early 1990s, at least 10 Blockbusters are open across the U.S. Seven of those are in Alaska.", + "There is a geocache on the International Space Station placed in 2008. It has since been visited four times by other astronauts.", + "The Vatican had music which was forbidden to be copied and was only played twice per year. It was secret for almost 150 years until a 14-year-old Mozart heard it and transcribed it from memory.", + "Canada eats more macaroni and cheese than any other nation in the world.", + "Octopuses only touch in situations of mating or aggression. Female octopuses sometimes do both, strangling and eating the male after mating.", + "Scotland wanted to replicate the Parthenon bigger and cheaper in 1826. It was never completed and is now nicknamed ā€œScotland's Disgraceā€.", + "A French general gave John Quincy Adams a pet alligator. Adams kept it in one of the White House bathtubs and enjoyed showing it off.", + "In 1866, The United States purchased Alaska from Russia for $7.2 million by writing a check.", + "In the Philippines, you can buy spaghetti at McDonald's, where they also sell a ā€œMcDoā€ piece of chicken.", + "Though most think it's Italian, pepperoni is an American invention. The first use of the word dates back to 1919.", + "The pumpkin is a member of the cucurbit family, which are gourds, such as cucumbers and squashes.", + "Any prime number higher than three, when squared and subtracted by one, will always turn out to be a multiple of 24.", + "When dogs are first born, they are completely blind & cannot hear anything. The first sense which they develop is the sense of touch.", + "Margherita Pizza uses tomato, mozzarella, and basil toppings to represent the Italian national flag. It was originally to honor the Queen of Italy in 1890.", + "Sudan has more pyramids than any country with 255. They outnumber Egyptian pyramids by twice the amount.", + "The money for the Statue of Liberty came from fundraising from auctions, a lottery, and boxing matches in Europe and the U.S. The Statue cost the French about $250,000, which today would be over $5.5 million dollars.", + "Not all hamsters are small. Although some hamsters are as small as 2-4 inches, the largest ones are approximately 13 inches long.", + "The hand and footprints in front of the Chinese Theater tradition started accidentally when silent film actress, Norma Talmadge stepped on wet cement.", + "Snakes can help predict earthquakes. They can sense a coming earthquake from 75 miles away, up to five days before it happens.", + "Soviet Russia needed lighthouses on their uninhabited Northern Coast, so they built automated lighthouses powered by small nuclear reactors.", + "Animal's yawn based on how large their brain is. The bigger the brain, the longer they will yawn.", + "Before Apple bought Siri, it was originally going to be released as an app for Android & Blackberry.", + "Approximately 1 in every 2,000 babies already has a tooth when they are first born.", + "The Young America Township in Minnesota has a population of less than 1,000, but more than 20 zip codes.", + "The United States Department of Agriculture says the official definition of a sandwich is: ā€œat least 35 percent cooked meat and no more than 50 percent breadā€.", + "Nutella was invented during WWII, when an Italian pastry maker mixed hazelnuts into chocolate to extend his chocolate ration.", + "The original name for Xbox was DirectXbox designed to show how Microsoft's Direct X graphics could improve the console market.", + "In Switzerland, it is illegal to own just one guinea pig. This is because guinea pigs are social animals, and they are considered victims of abuse if they are alone.", + "Walter Hunt, a man from America, invented the safety pin back in 1849.", + "The highest recorded fall without a parachute happened in 1972 at 33,333 ft. The victim, Vesna Vulović was in the hospital for 16 months after the fall.", + "A mason in 1700's Jerusalem left his wooden ladder behind after doing some work on a church and now it can't be moved without the agreement & permission of six different Christian leaders.", + "Eminem's mother filed an $11 million defamation lawsuit against him because of his lyrics about her. She settled for $25,000 and $23,354.25 of that went to her lawyer.", + "San Marino is the fifth smallest country in the world (and the third smallest in Europe).", + "A woman faked her entire tragedy and the loss of her husband during the 9/11 attacks and became President of the Support Network in New York.", + "The Postal Service got its name based on the fact that they originally used USPS to send each other music tracks because of conflicting schedules.", + "December 3rd is known as ā€œRoof Over Your Head Dayā€ - a day to be grateful of what we have in life!", + "Alaska is the only state in America that can be typed on one row of a traditional English QWERTY keyboard.", + "Michael Jackson's shiny glove was actually just a modified golf glove.", + "The Lego Group is the world's most powerful brand. There are more Lego minifigures than there are people on Earth.", + "Mr. and Mrs. originated from using the words master and mistress.", + "The only difference between kosher salt and table salt, is the grain size. Kosher salt is smaller, and they both come from underground salt deposits.", + "Invented in 1923, Q-Tips were originally called Baby Gays; then Q-Tip Baby Gays, then finally just Q-Tips. The Q stands for quality.", + "1912 saw the last Olympic gold medals made entirely out of gold.", + "Every 10 years, the human skeleton repairs and renews itself. Essentially, you have different bones now than you did 10 years ago!", + "The first game to be played in space was Starcraft - Daniel Barry took it with him in 1999 on the Space Shuttle mission STS-96.", + "Houston is the most diverse city in the United States. As of 2010, 43.8% is Latino, 25.6% is White, 23.1% is Black, and 6% is Asian.", + "May 29th is ā€œNational Put a Pillow on Your Fridge Dayā€œ. It is celebrated in Europe & USA to bring luck & wealth to the household.", + "When water freezes to ice cubes, it will take up 9% more volume.", + "The little piece of paper sticking out of a Hershey's Kiss is called a niggly wiggly.", + "As a child, Jet Li performed with the Chinese National Wushu Team for President Nixon. Nixon asked him to be his personal bodyguard, but he declined.", + "Eating grapefruit can restrict the effects of 43 different kinds of medications.", + "Dinosaurs would swallow large rocks which stayed in their stomach to help churn and digest food.", + "The word robot comes from the Czech ā€œrobotaā€. This translates into forced labor, or work.", + "During 1943, U.S. officials imposed a short-lived ban on sliced bread as a wartime conservation measure. It lasted less than 3 months.", + "Abraham Lincoln loved cats and once let one eat from the table during a formal White House dinner.", + "The Canadian government isn't sure when Canada became a sovereign state. Its Supreme Court ruled that sovereignty occurred sometime between 1919 and 1931.", + "On April 1st, 2005, NASA pulled an April Fool's prank telling the world that they had found water on Mars.", + "There's a town in the Oklahoma panhandle named ā€œHookerā€ and its slogan is ā€œit's a location, not a vocationā€.", + "Japan is facing a ninja shortage. There is a high demand for ā€œninja showsā€, but it is a dying tradition and companies have trouble time finding properly trained ninjas.", + "New Jersey had 4 Governors in the span of 8 days in early 2002. The shortest term of those was served by John Farmer Jr. for 90 minutes.", + "George Miller - the person who created Mad Max is the same person who created Happy Feet.", + "Surgeons who play video games at least 3 hours a week perform 27% faster and make 37% fewer errors.", + "There were only 9 developers on the team for GoldenEye 007 for Nintendo 64, and only one of them had ever worked on a video game before.", + "When cellophane was invented in 1908, it was originally intended to be used to protect tablecloths from wine spills.", + "A Dutch start-up company have been able to start training wild crows so that they pick up cigarette butts and put them in bins for a peanut as a reward.", + "We feel ā€œhangryā€ because it's harder for us to control our emotions when our brains are low on glucose.", + "The mayor of a historical district in Alaska is a cat named Stubbs. Mayor Stubbs drinks catnip laden water from a wineglass every afternoon, at a local restaurant.", + "Spam mail got its name from the canned meat after a Monty Python skit that made fun of Spam as tasting ā€œhorrible and being ubiquitous and inescapableā€.", + "Humans specifically have eyes that face forward for the purpose of seeing in 3D. On the other hand, vegetarian dinosaurs such as the Triceratops, had eyes that looked out to the side so they could stay vigilant watching for danger as they ate.", + "In the 1980's, the founder of Pringles, Fredric Baur, requested to be buried in a Pringles can. His children honored the request.", + "You can now get a headstone with a QR code. Called ā€œLiving Headstonesā€, they show pages with photos, video biography's, and comments from loved ones.", + "While filming Dumb and Dumber, Jim Carrey had his crown temporarily removed - that chipped tooth is real and a result of a childhood fight.", + "Ginger beer is brewed and fermented with ginger, while ginger ale is merely carbonated water flavored with ginger.", + "During the first live iPhone presentation, Steve Jobs had to frequently switch phones behind his desk. Otherwise, it would run out of RAM and crash.", + "In Mississippi, it is considered a federal offense to Dine-n-Dash on a check that is over $25. In California, it is only a petty theft.", + "The NYPD had a police officer follow Andre the Giant whenever he went out drinking. This was to make sure he didn't get drunk and fall on anyone.", + "In the popular film, The Godfather, the word ā€œmafiaā€ is never said because the actual mafia commanded it.", + "The only window that opens on the presidential car is the driver's window, to pay tolls. It also has no keyholes, and only the Secret Service know how to open the doors.", + "Thomas Edison invented an electric pen in 1876 that was later adapted to become the first tattoo machine in 1891.", + "Animal Planet aired two fake documentaries that ā€œprovedā€ mermaids exist. So many people thought it was real that the U.S. government issued an official statement about it.", + "In 18th Century England, having a pineapple was a symbol of wealth because of high import fees. They would be used as displays instead of being eaten.", + "Marvel's Deadpool issue #27 holds the Guinness World Record for the most comic book characters on one cover.", + "Instead of an agent Bill Murray uses a 1-800 number where you can leave a message for him if you want to cast him for a movie or event.", + "When written down, the word ā€œalmostā€ is the longest word in the English language to have all of its letters in alphabetical order.", + "The black-footed cat is the smallest wild cat, at just 20 inches long.", + "The world's most remote ATM is run by Wells Fargo in the Antarctic. The ATMs serve around 1,200 residents at the U.S. scientific facility.", + "The first Game Boy could run for a staggering 30 hours on only two AA batteries!", + "Hershey's chocolate syrup, Ritz Crackers, DumDums, and Oreos are all suitable for vegans.", + "The sound of a Star Wars lightsaber was created by paring together the sound of an idle film projector and the buzz from an old TV set.", + "Your tonsils can grow back if there was tissue left behind during the removal process. Sometimes it's accidental, other times it's left on purpose.", + "Greenland voted to leave the European Union in 1985 and have not rejoined since.", + "Every second, the human eye moves about 50 times.", + "There's a reply to the saying ā€œLiar, liar, pants on fireā€ - It's ā€œI don't care, I don't care, I can buy another pairā€.", + "Only 2% of the world's population has green eyes.", + "The A.D. and B.C. system was not proposed until 525 A.D. by a monk. However, it was not widely used until the 9th Century.", + "Daniel Radcliffe had the same stunt double for the first six Harry Potter movies - until the double was paralyzed from an accident on set during filming of the 7th movie.", + "ā€œTater Totsā€, a registered trademark, originally failed because people thought the product was too cheap. Popularity rose after the price was raised.", + "Movie theaters make roughly 85% of their profit off concession stands. This is because ticket revenues have to be shared with the movie distributors.", + "By mixing nonoplatelets from carrots and other root vegetables, concrete mixtures can be significantly strengthened, meaning less cement is needed to achieve the same effect.", + "Honey, and items immersed in honey, can be preserved for centuries. The long shelf life is due to an enzyme found in the stomachs of bees.", + "Credit card EMV chip technology has been around since 1986. It was first implemented in France, with Germany following shortly after.", + "The odds of being born on 29th February are 1 in 1461.", + "People who donate blood in Sweden are sent a text message each time their blood saves a life.", + "In the U.S., around ¾ of all houses possess at least 1 jar of peanut butter.", + "30 of the first 31 popes were murdered. Most of them were martyred, but not all causes of their deaths are known.", + "The average U.S. household has 300,000 things, from paper clips to ironing boards. U.S. children make up 3.7% of children on the planet but have 47% of all toys and children's books.", + "The commonly known phrase ā€œto a Tā€, means something that fits just right. Interestingly enough, this expression derives from a similar phrase ā€œto a tittleā€, which is a small mark that is used in spelling norms, like the dots of i's or j's, or an accent mark.", + "Both Motel 6 and Super 8 got their names from the original prices of the rooms. Motel 6 started at $6 in 1962, and Super 8 at $8.88 in 1974.", + "About 25% of all blood from the heart goes into the kidneys.", + "In Israel, it is illegal to bring bears to the beach.", + "Rednex, the band who remixed and popularized Cotton Eye Joe, is not Southern, but Swedish. They all have Southern sounding stage names.", + "BuzzFeed sponsored content costs about $20,000 for five or six ā€œarticlesā€.", + "Comets only reflect 4% of the light that falls on them, the rest is absorbed.", + "The name for the shape of Pringles is called a ā€œHyperbolic Paraboloidā€.", + "Writing down your worries before taking an exam can help combat test anxiety, improve performance and boost your test scores.", + "The highest body count in film history goes to ā€œLord of the Rings: Return of the Kingā€ with 836 on-screen deaths.", + "There is an uninhabited island in the Bahamas known as Pig Beach, which is populated entirely by swimming pigs.", + "While filming Rocky IV, Lundgren hit Sylvester Stallone for real and he ended up in the hospital for nine days.", + "For almost 15 years, Vermont was technically its own country before joining the United States in 1791. It had its own coins and postal service.", + "An adult's kidney weighs about 5 ounces (142 grams) and is the size of a fist.", + "Without saliva, humans are unable to taste food.", + "The German Autobahn has no speed limits because Western Germany saw them as a Nazi relic. This became effective in 1972.", + "If you ate nothing but rabbit meat, you would die from protein poisoning. This would be a mixture of too much protein and an absence of fat in the diet.", + "The loudness of a monkey is relative to the size of its testicles. Researchers found that the smaller the testicles, the louder the monkey.", + "Every Pixar movie contains a reference to the Pixar movie that comes after it.", + "Sour Patch Kids are from the same manufacturer as Swedish Fish. The red Sour Patch Kids are the same candy as Swedish Fish, but with sour sugar.", + "There exists a plankton, Dinoflagellates, which if consumed, reverses your feeling of hot and cold - as well as hallucinations. Symptoms can last from weeks to years.", + "Red pandas have no living relatives - they aren't even related to giant pandas.", + "After Christianity, the largest religious affiliation in the U.S. is Judaism.", + "Ewok Jerky was a popular snack across the Outer Rim in the Star Wars Universe.", + "Italy built an entire courthouse to prosecute the Mafia. They charged 474 members in a trial that lasted from 1986-1992. To date, it was the biggest trial in the world.", + "Doug Engelbart created the very first computer mouse from wood in 1964.", + "The smallest thing ever photographed is the shadow of an atom.", + "Despite Mercury being the closest planet to the Sun, Venus is the warmest planet.", + "There are no visual difference between male and female herons.", + "Cranes are built using cranes.", + "In the popular 2014 movie ā€œGodzillaā€œ, the Godzilla portrayal was only seen for about 8 minutes in the entire film.", + "In every scene of Fight Club, there is a Starbucks coffee cup.", + "It snowed in the Sahara desert for 30 minutes on the 18th February 1979.", + "PewDiePie supported his YouTube channel by selling hot dogs. His persistence paid off when in 2012 his YouTube channel garnered over one million subscribers.", + "Approximately 1,000,000 dogs in the U.S. are named as the heirs of their owners' wills.", + "It takes longer to drown in saltwater than in freshwater. Because of this, around 90% of drownings occur in freshwater.", + "Employees at MillerCoors get three free cases of beer each month, in addition to having access to beer and cider on location after a work day.", + "Steven Spielberg was executive producer of The Animaniacs.", + "Kanye West's song ā€œHey Mamaā€ was dedicated to his mother, who died in 2007 after complications with plastic surgery.", + "More than 90% of survivors of the 9/11 terror attacks delayed evacuation to save their work, shut down computers, change shoes, or visit the bathroom.", + "Queen Elizabeth saved up post-war clothing ration coupons in order to pay for her wedding dress in 1947.", + "Sweden has the most islands in the world, with 221,800 islands.", + "Although dingoes are about the same size as a Springer Spaniel, they are brave enough to target an adult kangaroo when hunting in packs.", + "If you keep smelling something that is not really there, you may be experiencing the earliest symptoms of schizophrenia.", + "A cluster of bananas is called a ā€œhandā€. Along that theme, a single banana is called a ā€œfingerā€.", + "Since the death penalty was restored in 1976, the states of Washington and New Hampshire have returned to hanging as an available method of execution.", + "Even though drugs are officially illegal in the Netherlands, it is legal to be under the influence of any drug to encourage people to seek medical help.", + "The first world leader to create a YouTube channel was the British Prime Minister, Tony Blair who made his account in 2007.", + "Rick Astley has his own brand of beer. It was a collaboration between him and the Danish micro-brewery Mikkeller; it's a red lager with ā€œa hint of gingerā€.", + "The total spend on adult Halloween costumes each year in America is $1.5 billion.", + "When double rainbows occur, the colors of the second arc are always reversed.", + "Bookworms are actual insects that bore holes in books. A major book feeding insect is a paper louse which feeds on microscopic mold in poorly kept books.", + "3 inches of ice can support one person, on foot. 4 inches can support a group of people, single file. 36 inches can support up to 110 tons of weight.", + "January 17th is known as ā€œDitch New Year's Resolution Dayā€.", + "Dinosaurs that could run using just two legs are referred to as bipeds.", + "Russia has the most man-made satellites in orbit with 1,324. There is a total of 2,271 and U.S. is in second place at 658.", + "The most difficult Chinese character requires 62 total strokes to write. The word itself, ā€œbiangā€ holds no meaning.", + "Fruit stickers are edible, though the same with any fruit, washing prior to eating is recommended. The glue used for them is regulated by the FDA.", + "A certain type of box jellyfish's venom will make you feel a strong sense of anxiety and sense of impending doom, and possibly thoughts of suicide. These symptoms can last up to two weeks.", + "The island in Lake Bled is the only natural island in Slovenia.", + "In Switzerland, it is illegal to flush the toilet after 10pm.", + "If sheep have long tails when they are born, the tails are cut off in order to prevent infection.", + "The name for a collective group of rhinoceroses is called a crash.", + "Because of the Electoral College, a presidential candidate can win with only 23% of the popular vote.", + "There was a snail glued to a specimen card in the British Museum mid-1800s. It spent four years glued there before scientists realized it was still alive.", + "August 21st is Senior Citizen's Day - a day to honor the older generation.", + "William Shakespeare had a curse engraved on his tombstone to prevent anyone from moving his bones.", + "Acetaminophen, one of the most popular pain-killers, restrains masculinity and dulls emotions. It had been rejected in 1887 for its side effects, then approved in 1955.", + "The Twenty One Pilots fan base is known as the Skeleton Clique.", + "A man who was hanged for his part in the Guy Fawkes Gunpowder Plot had his skin removed and it was used to bind a book that listed his offenses.", + "Valentine's Day in South Korea is a little different. Only women give gifts, not men.", + "Indonesia consists only of islands - 13,667 total", + "During World War II, the very first bomb dropped on Berlin by the Allies killed the only elephant in the Berlin Zoo", + "People who ride on roller coasters have a higher chance of having a blood clot in the brain", + "The tallest freestanding sculpture in the world is Chief Crazy Horse in South Dakota, USA", + "Marie Curie, the Nobel prize winning scientist who discovered radium, died of radiation poisoning", + "898 tornadoes were recorded to have occurred in the United States in the year 2000.", + "The word Popcorn is derived from the middle English word 'poppe,' which means 'explosive sound'", + "The food that is digested in your stomach is called 'chyme.'", + "Alcohol beverages have all 13 minerals necessary for human life", + "The sentence 'The quick brown fox jumps over the lazy dog.' uses everyletter in the alphabet. (Developed by Western Union to Test telex/twxcommunications)", + "The word housekeeping was invented by Shakespeare", + "The only two days of the year in which there are no professional sportsgames (MLB, NBA, NHL, or NFL) are the day before and the day after theMajorLeague All-Star Game.", + "In the great fire of London in 1666 half of London was burnt down but only 6 people were injured", + "Lack of sleep can affect your immune system and reduce your ability to fight infections", + "All dogs are the descendant of the wolf. These wolves lived in eastern Asia about 15,000 years ago", + "It is not possible to tickle yourself. The cerebellum, a part of the brain, warns the rest of the brain that you are about to tickle yourself. Since your brain knows this, it ignores the resulting sensation", + "Parma ham is only Parma ham if it is made in the Parma region of Italy. The British chain supermarket Asda, made and packaged its own 'Parma ham' and was successfully sued by the real Parma ham people (Parma Ham Trade Association)", + "With winds of 50 miles per hour, The Statue of Liberty sways three inches and the torch sways five inches", + "A famous bullfighter, Lagarijo, killed 4,867 bulls in the 19th century.", + "Police detectives have used snapping turtles to help them locate dead bodies", + "In most advertisements, including newspapers, the time displayed on a watch is 10:10", + "The national sport of Japan is sumo wrestling", + "The early occurrence of a fetus yawning is at eleven weeks after conception", + "In a month, a fingernail grows an eighth of an inch", + "Edward VIII did not officially become the King of England as he abdicated the throne to marry an American divorcee", + "The book 'Little Red Riding Hood' was banned in 1990 by two school districts in California. They did this because in the book there was a picture of a basket that had a bottle of wine in it", + "The reason why golf balls have dimples on them is because it helps in the ball to move a farther distance by reducing drag", + "Americans consume the most peanut butter in the world", + "Celtic warriors sometimes fought their battles naked, their bodies dyed blue from head to toe", + "To make butter more attractive in colour, carrot juice was used by people in the Middle Ages", + "Early hockey games allowed as many as 30 players a side on the ice", + "Most fleas do not live past a year old", + "It takes seven to ten days to make a jelly belly jellybean", + "Some asteroids have other asteroids orbiting them", + "Maine is the only state whose name is just one syllable", + "The male praying mantis cannot copulate while its head is attached to its body. The female initiates sex by ripping the males head off", + "There is enough concrete in the Hoover Dam to pave a two lane highway from San Francisco to New York", + "Americans on the average eat 18 acres of pizza every day", + "Every 238 years, the orbits of Neptune and Pluto change making Neptune at times the farthest planet from the sun", + "There is a certain species of kangaroo that is only 2.5 centimetres long when it is born", + "In a lifetime, the average house cat spends approximately 10,950 hours purring", + "The real name of Toto the dog in 'The Wizard Of Oz' was Terry", + "Stannous fluoride, which is the cavity fighter found in toothpaste is made from recycled tin", + "It takes 12 honeybees to make one teaspoon of honey", + "Thomas Watson, who was the chairman of IBM in 1943 predicted that their would probably only be a world market for five computers.", + "The largest hamburger cooked in the world weighed in at 6,040 pounds", + "The first lighthouse was in Alexandria in 290 B.C", + "Heinz first started making ketchup in 1876 and the recipe has remained the same ever since", + "The largest wedding chapel in Las Vegas is the Viva Las Vegas Chapel, which can seat 100 people", + "The most popular name for a pet in the United States is Max", + "Spiral staircases in medieval castles are running clockwise. This is because all knights used to be right-handed. When the intruding army would climb the stairs they would not be able to use their right hand which was holding the sword because of the difficulties of climbing the stairs. Left-handed knights would have had no troubles, except left-handed people could never become knights because it was assumed that they were descendants of the devil", + "The largest shopping mall in the world is the West Edmonton Mall located in Edmonton, Alberta, Canada", + "The CN Tower located in Toronto, Ontario Canada took a total construction time of 40 months to complete at an original cost of $63 million", + "The 20th president of the United States, James Garfield, was able to write Greek with one hand and Latin with the other at the same time", + "The country of Andorra has a zero percent unemployment rate", + "In Los Angeles, there are fewer people than there are automobiles", + "A woman has approximately 4.5 litres of blood in her body, while men have 5.6 litres", + "In India, pickled ginger, minced mutton and a cottage cheese like substance are popular pizza toppings", + "Oral-B were the first toothbrushes to go to the moon when they were aboard the Apollo 11 mission", + "A maple tree is usually tapped when the tree is at least 45 years old and has a diameter of 12 inches", + "In 1998, a law passed in the U.S. state of Virginia allows drivers to keep their road kill, as long as they report it within 12 hours. updated", + "A language becomes extinct in this world every two weeks", + "An acre of trees can remove about 13 tons of dust and gases every year from the surrounding environment", + "The decomposition point of Olive Oil is 220 degrees Celsius", + "Ten radishes only contain eight calories", + "Annually a thousand people are killed by scorpions in Mexico", + "Every year, 100 million sharks are killed by people", + "Tug of war was an Olympic event from 1900-1920", + "Of all the countries, Brazil has the most plant species, with over 56,000", + "One female mouse can produce up to 100 babies a year", + "Impotence is grounds for divorce in 26 U.S. states", + "Women who are romance novel readers are reported to make love 74% more often with their partners than women who do not read romance novels.", + "The average lifespan of a human taste bud is ten days", + "The monogram 'RR' for Rolls-Royce has never been altered, except for when Sir Henry Royce passed away in 1933. Then it was changed from red to black.", + "People with darker skin will not wrinkle as fast as people with lighter skin", + "Fido means faithful in Latin", + "Pebbles cereal was actually named after the shape of the cereal and not the Pebbles Flintstone character", + "A group of kangaroos is called a mob", + "Cat's urine glows under a blacklight.", + "Every three seconds, a new baby is born", + "More than 260,000 people have been killed by volcanic activity since 1700 AD.", + "The only predator that polar bears have are humans", + "Many insects can carry 50 times their own body weight", + "The last land battle of the U.S. Civil War was fought in Texas", + "Annually 7 million tons of textiles and clothing is thrown out. Out of this, only 12% is used again or recycled", + "A scorpion can have up to 12 eyes", + "A snake charmer in Bangladesh once found 3,500 poisonous cobras and their eggs hidden underneath the floors of two suburban homes", + "The IRS employees tax manual has instructions for collecting taxes after a nuclear war", + "There are approximately fifty Bibles sold each minute across the world", + "The pectin that is found in apples aids in lowering cholesterol levels", + "Post-It Notes, which are adhesive notes, were invented while looking for a way to improve the acrylate adhesive found in tapes", + "Crayola Crayons currently has over 120 different crayon colours", + "Odontophobia is the fear of teeth", + "The width of a tornado can range from less than ten yards to more than a mile.", + "In Johannesburg, the average car will be involved in an accident once every four years.", + "The youngest actress to be nominated as best actress is Keisha Castle-Hughes who was nominated at just 13 years old", + "The Taj Mahal was actually built for use as a tomb", + "According to studies, an average roll of toilet paper lasts about five days in the bathroom", + "Almonds are members of the peach family", + "The oldest known disease in the world is leprosy", + "A fall of 30 feet can be survived my most cats", + "The largest member of the dolphin family are orcas", + "In 1477, the first diamond engagement ring was given to Mary of Burgundy by Archduke Maximillian of Austria", + "The hormone replacement drug 'Premarin' is made from the urine of pregnant horses", + "TWIX Caramel Cookie Bars were first introduced in 1979", + "Nintendo was first establish in 1889 and they started out making special playing cards", + "People over the age of fifty will start to lose their dislike for foods that taste bitter", + "In Kentucky, 50 percent of the people who get married for the first time are teenagers", + "Elephants have been known to learn up to 60 commands", + "On average 1,668 gallons of water are used by each person in the United States daily", + "Copper is the second most used metal in the world.", + "Milton Bradley originally wanted to name the game Twister, Pretzel; but he could not since the name was copyrighted", + "According to studies, men prefer to have white bedrooms and women prefer to have blue bedrooms", + "If someone was to fly once around the surface of the moon, it would be equal to a round trip from New York to London", + "St. Patrick never really drove out any snakes from Ireland. This story was an analogy of how he drove paganism out of Ireland", + "Fat is important for the development of children and normal growth", + "The most common seasonings found in American homes are chili powder, cinnamon, and seasoned salts", + "People who have eaten beetles say that it tastes like apples", + "Montreal was named after a local mountain 'Mont Royal.'", + "Millie the White House dog earned more than 4 times as much as President Bush in 1991. And, rightfully so", + "In an average lifetime, a person will spend 4 years travelling in an automobile and six months waiting at a red light.", + "A small drip from a faucet can waste up to 50 gallons of water daily, which is enough water to run a dishwasher twice on a full cycle", + "Kotex was first manufactured as bandages, during W.W.I", + "The longest Monopoly game ever played was 1,680 hours long, which is seventy straight days", + "The first known contraceptive was crocodile dung, used by Egyptians in 2000 B.C", + "Over 1,600 people in North America have been victims of trunk entrapment (being locked inside of a car trunk)", + "A rhinoceros horn is made of compacted hair", + "In 1992, when EuroDisney first opened in France, the public beat some of the park characters because at the time most people had been against the park being built", + "A jiffy is an actual unit of time for 1/100th of a second. Thus the saying, I will be there in a jiffy.", + "There is a muppet named Kami that appears on the South African version of the T.V. show 'Sesame Street' that is HIV-positive", + "There are approximately one hundred million people in the United States that have a chronic illness", + "The oldest working Post Office in the world is located in the village of Sanquer, located in the Scottish Lowlands. It has been operating since 1712", + "Columbia University is the second largest landowner in New York City, after the Catholic Church", + "Approximately three jars of peanut butter are sold every second", + "In Australia, the average person uses 876 gallons of water daily. In Switzerland they use only 77 gallons of water per person daily", + "Every person has a unique tongue print", + "Hair will fall out faster on a person that is on a crash diet", + "In 1890, there was no sunshine for the whole month of December in Westminster in London.", + "Charles Darwin spent 39 years studying earthworms", + "The Boeing 737 is nicknamed the Fat Albert", + "Florida has twice as many lightning injuries and deaths than any other state", + "Chocolate can be fatal to dogs. Chocolate contains a chemical theobromine, which is poisonous to dogs", + "In China, there is a species of yam that is used to make a dye", + "Annually, approximately 46 millions Cokes, five million pounds of french fries, and seven million hamburgers are consumed at Walt Disney World Resort", + "The Chihuahua Desert is the largest desert in North America, and is over 200,000 square miles", + "Every continent begins and ends in the same letter. eg AfricA, EuropE", + "Baseball games between college teams have been played since the Civil War", + "The real name of actress Whoopi Goldberg is Caryn Elaine Johnson", + "Researches have discovered that eating five or more apples a week is linked to better functioning of the lungs", + "Boeing completed more than 15,000 hours of wind-tunnel testing on the first 747", + "The most popular ethnic food in the United States is Italian food", + "Parts of the Dead Sea Scrolls appeared for sale in the June 1, 1954 issue of the Wall Street Journal", + "If the population of China walked past you in single file, the line would never end because of the rate of reproduction", + "The YKK that you see on zippers stands for Yoshida Kogyo Kabushiki Kaisha which is the name of the founder of the zipper manufacturing company in Japan", + "The theme song of the Harlem Globetrotters is 'Sweet Georgia Brown.'", + "27% of female lottery winners hid their winning ticket in their bras", + "To lose one pound of fat, a person has to burn approximately 3,500 calories", + "In 1969, the American side of Niagara Falls was stopped completely for several months", + "The name for insect poop is frass", + "A can of Pepsi has 41 grams of sugar. This amount to about seven teaspoons of sugar", + "Montreal is actually located on an island", + "There are over 2,000 species of butterflies in the rainforests of South America", + "The world record for the number of body piercings on one individual is 702, which is held by Canadian Brent Moffat", + "Before toilet paper was invented, French royalty wiped their bottoms with fine linen", + "The earliest known example of an organized market for equities dates from Rome, second century B.C", + "There are over 2,000 different species of cactuses", + "Each day 400 gallons of recycled blood are pumped through the kidneys", + "Ten percent of the Russian government's income comes from the sale ofvodka.", + "Apples, not caffeine, are more efficient at waking you up in the morning", + "Bananas were discovered by Alexander the Great in 327 B.C. when he conquered India", + "Levan, which is located in Utah, got its name from 'navel' which is levan spelt backwards. It was named this because it is in the center of Utah", + "Approximately one out of four injuries by athletes involve the wrist and hand", + "Former U.S. President Abraham Lincoln suffered a nervous breakdown in 1836", + "Musk is extracted from the bottom of a civet, and is used as an ingredient to make perfumes.", + "The first human heart transplant happened on December 3, 1967. Unfortunately the patient only lived for eighteen days, succumbing in the end to pneumonia", + "In New York City there are 6,374.6 miles of streets", + "The sound made by the Victoria Falls in Zimbabwe is so loud that it can be heard 40 miles away", + "Ancient Egyptians used to think having facial hair was an indication of personal neglect", + "In Czechhoslovakia, there is a church that has a chandelier made of human bones", + "The largest hotel in the world is the MGM Grand, which has 5,034 rooms and is located in Las Vegas, Nevada", + "The plastic things on the end of shoelaces are called aglets", + "The fleshy bulbs on each side of your nose are called the Alea (AY-lee) singular Ala (AY-luh)", + "Male koalas mark their territory by rubbing their chests on a tree. Male koalas have a dark scent gland in the middle of their chest", + "An octopus has three hearts", + "Roses generally need around 6 hours of sunlight to grow properly.", + "Buttermilk does not contain any butter, but is a cultured milk product which is usually made from fat free milk", + "Pineapples were first called 'anana', which is Caribbean for 'excellent fruit.'", + "Human birth control pills work on gorillas", + "The tallest woman that ever lived was Zeng Jinlian who was 8 feet 2 inches tall of China. Shed died at the age of 17", + "An adult 'Gold Frog' measures to be 9.8 millimeters in body length", + "Each day, anywhere from 35-150 species of life go extinct", + "Alexander Graham Bell, the inventor of the telephone, never telephoned his wife or mother because they were both deaf", + "Alexander the Great made his troops eat onions as he believed it would prove their vitality", + "Bill Russell was the first black head coach of a major league pro sports team", + "In 1945, a seven ounce bathroom cup was the first item Tupperware marketed", + "Central air conditioners use 98% more energy than ceiling fans.", + "The king of hearts is the only king without a mustache", + "Men can read smaller print than women; women can hear better", + "Everyday, U.S. business use enough paper to circle the Earth over 20 times", + "The Welwitschia plant can live up to 1,000 years", + "The dromedary camel can drink as much as 100 litres of water in just 10 minutes", + "According to the American Institute of Stress, job stress approximately costs the U.S. industry over $300 billion dollars per year", + "It takes 72 minutes for the restaurant at the top of the CN Tower to make one revolution", + "Coffee beans were chewed for more than 400 years before the first cup of coffee was brewed", + "All of the Peking ducks in the United States are descendents from three ducks and one drake imported to Long Island, New York in 1873", + "The first British ship to use the SOS distress signal was the Titanic", + "The Spring peeper (a frog) can survive the winter season with 65% of its body water as ice", + "Studies have shown that the scent of Rosemary can help in better mental performance and make individuals feel more alert", + "The search engine Google got its name from the word 'googol,' which refers to the number one with a hundred zeros after it", + "The Goliath beetle is about the size of your fist and can weigh as much as 3-4 ounces", + "If you fart consistently for 6 years and 9 months, enough gas is produced to create explosion that is equal to an atomic bomb", + "Humans have about the same number of hair follicles as a chimpanzee has", + "Studies indicate that listening to music is good for digestion", + "The Chihuahua was named after the Mexican state where they were discovered", + "There are no snakes in New Zealand", + "The most popular grown bulbs are tulips", + "Every day the human stomach produces about 2 liters of hydrochloric acid", + "The country of Bolivia is named after a fighter Simon Bolivar", + "Peanuts are one of the ingredients of dynamite", + "The first state to give the right to women to vote was Wyoming", + "In 1949 UNICEF produced the first charity Christmas card. The picture shown on the card was painted by a seven year old girl", + "Archeologists report that cannabis was most likely the first plant cultivated by humans. Cannabis was used for linen, paper, and garments", + "The garfish has green bones", + "Women who drink more than two cups of coffee a day have a higher chance of developing osteoporosis", + "The banana was officially introduced in 1876 in the U.S. at the Philadelphia Centennial Exhibition. The bananas were wrapped in tinfoil and were sold for 10 cents each", + "A yawn usually lasts for approximately six seconds", + "Thirty-five percent of the people who use personal ads for dating are already married", + "The food that people crave the most is cheese", + "Every day more money is printed for Monopoly than the US Treasury", + "The only animal, besides humans that can get leprosy is the Armadillo", + "In 1894, the carnival made its debut in North America", + "The artist Vincent Van Gogh sliced part of his ear off in madness", + "According to Scientists, vampire bat saliva is the best known medicine for keeping blood from clotting.", + "People from North America prefer pickles with warts, where as Europeans prefer pickles with no warts", + "People that suffer from gum disease are twice as likely to have a stroke or heart attack", + "Close to 50% of the water used in a home originates from the bathroom", + "After the Krakatoa volcano eruption in 1883 in Indonesia, many people reported that, because of the dust, the sunset appeared green and the moon blue. The moon was said to appear blue for almost two years.", + "The country with the highest consumption of chocolate per capita is Switzerland, with 22 pounds per person, per year", + "In China, September 20 is 'Love Your Teeth Day.'", + "Actor Richard Gere was considered to play the role of John McClane in the movie Die Hard. Bruce Willis played the part instead", + "The record for the world?s worst drivers is a toss-up between two candidates: First, a 75-year-old man who received 10 traffic tickets, drove on the wrong side of the road four times, committed four hit-and-run offenses, and caused six accidents, all within 20 minutes on October 15, 1966. Second, a 62-year-old woman who failed her driving test 40 times before passing it in August, 1970 (by that time, she had spent over $700 in lessons, and could no longer afford to buy a car)", + "Tigers have striped skin, not just striped fur", + "Left-handed people are better at sports that require good spatial judgment and fast reaction, compared to right-handed individuals", + "Half of a cup of figs will give you just as much calcium as half a cup of milk", + "A 'hairbreadth away' is 1/48 of an inch", + "In 1281, the Mongol army of Kublai Khan tried to invade Japan but were ravaged by a hurricane that destroyed their fleet", + "Walt Disney was afraid of mice", + "Studies show that couples that smoke during the time of conception have a higher chance of having a girl compared to couples that do not smoke", + "The reason why some people get a cowlick is because the growth of their hair is in a spiral pattern, which causes the hair to either stand straight up, or goes to a certain angle", + "Approximately 50% of Americans admit they have ran a red light", + "In 1755, the first Canadian post office opened in Halifax, Nova Scotia. The fist Deputy Postmaster General was American inventor Benjamin Franklin who was later dismissed for sympathizing with the American revolutionary cause", + "Reno, Nevada is west of Los Angeles, California.", + "On average, 90% of the people that have the disease Lupus are female", + "Unlike other four legged mammals, kangaroos cannot walk backwards", + "The itch from a mosquito bite can be soothed by cutting open a clove of garlic and rubbing it on the bite", + "If you have three quarters, four dimes, and four pennies, you have $1.19. You also have the largest amount of money in coins without being able to make change for a dollar", + "Most American car horns honk in the key of F", + "A superstition in baseball is to never lend your bat to anyone or you will be jinxed", + "Bats always turn left when exiting a cave", + "Penguins can jump as high as 6 feet in the air", + "In 1998, approximately 1.6 billion tree seedlings were planted in the United States. This amounts to about five trees per American", + "There are species of fish that can walk on land in search of water when its water source dries up. Some can survive as long as three days on land such as the snakehead fish", + "Racecar driver Lee Petty once left a pitstop and did a full lap at Nascar with a pit crew member still on the hood", + "The first fashion house to be set up was in 1858 by Charles Worth. He opened his store in Paris with the idea of having pre-made gowns presented on models to his customers", + "St. Patrick explained the Holy Trinity to King Laoghaire, using the shamrock to illustrate the trinity", + "More twins are born in the Western world than in the Eastern world", + "Nine egg yolks have been found in one chicken egg", + "The record for the longest Monopoly game played in a bathtub is ninety-nine hours", + "The flea can jump 350 times its body length, that is like a 6 foot-tall human jumping the length of 7 football fields", + "The 1988 move 'Big' which was directed by Penny Marshall was the first movie by a female director to gross over $100 million domestically", + "When the Galileo Probe entered Jupiter's atmosphere, it was traveling at a speed of 106,000 miles per hour. This is the fastest impact speed ever achieved by a man-made object.", + "In 1972, a gorilla by the name of 'Koko' was taught ASL (American Sign Language) for the deaf. By the year 2000, the gorilla could understand approximately 2,000 English words", + "3000 children die every day in Africa because of malaria", + "The reason why hair turns gray as we age is because the pigment cells in the hair follicle start to die, which is responsible for producing 'melanin' which gives the hair colour", + "Approximately 125 people die in the United States from an anaphylaxis to foods each year", + "The word 'vamp' is used to describe the upper front top of a shoe", + "Construction on the White House began in October of 1792", + "A galactic year is 250 million Earth-years. This is the time it takes for our solar system to make one revolution around the Milky Way Galaxy.", + "Leonardo da Vinci was dyslexic, and he often wrote backwards", + "The male platypus has poisonous spurs on its legs", + "Polar bears can smell seal from 20 miles away", + "Canadians Scott Abbott and Chris Haney invented Trivial Pursuit. They were planning on playing Scrabble and realized that some of the pieces were missing so they came up with the idea of making their own game; Trivial Pursuit", + "On average, there is about three molecules of ozone for every 10 million air molecules.", + "A person uses approximately fifty-seven sheets of toilet paper each day", + "The Barbie doll has more than 80 careers", + "James Buchanan was the only unmarried president of the United States", + "The Stanley Cup originally was only seven and a half inches high", + "In 1991, during an attempted political coup on Russian President Boris Yelstin, food supplies had dwindled down at the parliament buildings so they ordered Pizza Hut to deliver pizzas", + "Some people drink the urine of pregnant women to build up their immune system", + "The five Olympic rings represent the five continents linked together in friendship", + "Ray Kroc bought McDonalds for $2.7 million in 1961 from the McDonald brothers", + "It is possible to lead a cow upstairs but not downstairs", + "Shark cartilage has been used to make artificial skin for human burn victims", + "The first person to die in the electric chair was William Kemmler, an ax murderer from New York on August 6, 1890", + "Finland has 187,888 lakes and 179,584 islands", + "The average adult has approximately six pounds of skin", + "A crocodile can open and close its jaw but cannot move it side to side", + "There are over 1,000,000 swimming pools in Florida, eventhough the ocean is no farther than 80 miles away", + "99% of the blueberries that are produced in the United States are produced in the state of Maine", + "On May 9, 1999 approximately 600,000 gallons of whiskey flowed into the Kentucky River during a fire at Wild Turkey Distillery in Lawrenceburg", + "Thomas Jefferson had three achievements placed on his headstone at his request, 'Here Was Buried Thomas Jefferson/Author Of The Declaration Of American Independence/Of The Statute Of Virginia For Religious Freedom/And Father Of The University of Virginia.? He never mentioned being President of the United States", + "Humans and cows have the same gestation period, which is about nine months", + "In the Victoria era, red tulips were a declaration of love", + "The sport Lacrosse was initially played by Native American Indians. They played the sport to prepare for war", + "It takes a sloth up to six days to digest the food it eats", + "According to Scandinavian traditions, if a boy and girl eat from the same loaf of bread, they are bound to fall in love", + "In 1796, Napoleon was only 26 years old when he took command of the French Army of Italy", + "A bomb dropped by the Allies on Berlin during World War II killed every animal in the Berlin Zoo except the elephant, which escaped and roamed the city. When a Russian commander saw hungry Germans chasing the elephant and trying to kill it, he ordered his troops to protect it and shoot anyone who tried to kill it", + "The expression 'Tying the Knot' comes from an old Roman custom where the brides clothes were tied up all in knots and the groom was supposed to untie the knots", + "Snake is a delicacy in China", + "In 1999, All Nippon Airlines, had one of its jets fully decorated with Pokemon characters from nose to tail on its exterior", + "The Dead Sea has been sinking for last several years", + "Uranus has 27 moons", + "Actress Sally Field was paid $4,000 a week for her role in the TV show The Flying Nun", + "More pollution is emitted from the average home compared to the average car.", + "The snow leopard protects itself from extreme cold when it sleeps by wrapping its 3-foot-long tail around its nose", + "Only 4% of babies are born on their actual due date", + "In the 1940s, the FCC assigned television's Channel 1 to mobile Services(two-way radios in taxicabs, for instance) but did not re-number theotherchannel assignments. That is why your TV set has channels 2 and up, butnochannel 1.", + "A man named Charles Osborne had the hiccups for approximately sixty-nine years", + "There are more Subway restaurants in Canada than there are McDonald restaurants", + "The CN Tower, in Toronto, is the tallest freestanding structure in the world with a height of about 553 metres", + "The term 'the whole 9 yards' came from W.W.II fighter pilots in the South Pacific. When arming their airplanes on the ground, the .50 caliber machine gun ammo belts measured exactly 27 feet, before being loaded into the fuselage. If the pilots fired all their ammo at a target, it got 'the whole 9 yards.'", + "Construction on the Leaning Tower of Pisa began on August 9th, 1173", + "President Lyndon Johnson used to smoke three packs of cigarettes a day", + "The Tibetan name for Mount Everest is Chomolungma", + "The word 'laser' stands for 'Light Amplification by Stimulated Emission by radiation.'", + "In a lifetime, on average a honey bee produces 1/12th of a teaspoon of honey", + "There are 315 species of parrot in the world", + "The TV show Doctor Who, when it was popular, had an audience of 110 million people", + "The cost to build the Empire State Building was $40,948,900", + "A person who smokes a pack of cigarettes a day will on average lose two teeth every ten years", + "Wasps that feed on ferment occasionally get drunk and pass out", + "The largest cereal company in the world is Quaker Oats, located in Cedar Rapids, Iowa, USA", + "The first Olympic games only had one event - a foot race", + "Colonel Sanders traveled over 250,000 miles a year visiting various parts of his Kentucky Fried Chicken Empire", + "Some desert snails have been known to sleep for three to four years", + "Over 80% of the brain is water", + "From the age of thirty, humans gradually begin to shrink in size", + "Jackrabbits can reach a speed of fifty miles per hour and can leap as far as twenty feet", + "There are 40 official jelly belly flavours", + "Early sewing machines were destroyed by mobs or workers who felt their jobs were threatened by automation", + "In 1992, the Antarctic Ozone hole was larger than the continent of North America.", + "Someone gets divorced every ten to thirteen seconds", + "There is a certain type of Hawk Moth caterpillar from Brazil that inflates its thorax, which makes its head look like a head of a snake when it feels it is in danger or alarmed", + "The CIA has made a disk camera that is as big as a quarter. This gadget can take many pictures at a time when the disk is opened.", + "The Sanskrit word for 'war' means 'desire for more cows.'", + "In Hong Kong, delivery times are primarily influenced by traffic conditions on elevators. It often takes drivers longer to travel vertically than horizontally, as access to elevators is so congested during 'high peak' hours. This is due to the volume of people residing in high rises", + "The ancient Greeks had a fascination with the planet Mars. They attributed the planet to Ares, their god of war, because of its red colour", + "The only lizard that has a voice is the Gecko", + "In Israel, religious law forbids picking your nose on Sabbath", + "In twins, there is a great chance that one will be left handed", + "In the 1920's, Q-Tips were invented by Leo Gerstenzang who got the idea after watching his wife clean their baby's ears with cotton stuck onto a toothpick.", + "In the Pacific Islands when people get burns they often use a banana leaf as treatment", + "Acorns were used as a coffee substitute during the American Civil War", + "An airplane mechanic invented Slinky while he was playing with engine parts and realized the possible secondary use for the springs. Barbie was invented by Ruth Handler after watching her daughter play with baby dolls imagining then in grown up roles", + "When the female grasshopper lays eggs, she covers her eggs with a pasty liquid that protects the eggs throughout the winter", + "The longest recorded duration of a total solar eclipse was 7.5 minutes.", + "On average, an American makes three pounds of garbage in a day", + "Even if you eat food standing on your head, the food will still end up in your stomach", + "Only one person in two billion will live to be 116 or older.", + "The most common name in the world is Mohammed", + "Apple seeds are poisonous as they contain a cyanide compound", + "The word breakfast was coined due to the fact that after sleeping for hours, we are 'breaking our fast.'", + "The cardigan was originally made to be a military jacket made of knitted wool", + "The month of December is the most popular month for weddings in the Philippines", + "The deepest cave in the world is the 'Lamprechtsofen-Vogelshacht' cave which can be found in Salzburg, Austria. The cave is 5,354 feet deep", + "The capital of Vermont, Montpelier is the only state capital in the United States that does not have a McDonalds", + "The longest engagement lasted 67 years, and the couple ended up marrying when they were 82 years old", + "Our eyes are always the same size from birth, but our nose and ears. never stop growing", + "Milk and cheese can aid in the reduction of tooth decay", + "On average, a strawberry has 200 seeds on it", + "Coconuts kill more people in the world than sharks do", + "The average person spends two weeks of their life kissing", + "Research has indicated that indoor pollution is 10 times more toxic than outdoor pollution", + "Eating a banana at night can help in falling asleep", + "The stapler was invented in Spring Valley, Minnesota.", + "The first television newscaster was Kolin Hager, who used to broadcast farm and weather reports in 1928", + "Pixie, a Siberian Husky, gave birth to 7 puppies, one of which was bright green", + "Back in 1953, it took 27 hours to make one Marshmallow Peep. Now it takes only six minutes", + "On average, an ear of a corn has 16 rows and approximately 800 kernels", + "The green ring that is formed around the yolk of eggs that have been cooked too long is formed by the chemical reaction from the iron in the yolk and the sulphur in the white part of the egg", + "The silk that is produced by spiders is stronger than steel", + "The first president to have a picture taken was John Quincy Adams", + "Some brands of toothpaste contain glycerin or glycerol, which is also an ingredient in antifreeze", + "1 in 2000 babies are born with a tooth that is already visible", + "It was during World War II that clothes with elastic waists were introduced. This is because the metal used in zippers was badly needed for the war", + "In 1902, the game table tennis was brought to the U.S. from Europe by Parker Brothers", + "Hershey's Kisses are called that because the machine that makes themlooks like it's kissing the conveyor belt.", + "The fat from sheep, which is called tallow can be used to make soap and candles", + "Next to bone marrow, hair is the fastest growing tissue in the human body", + "Sigmund Freud had a morbid fear of ferns", + "When playing competitive darts the player must be 7 feet 9 1/4 inches back from the dartboard. Also the board must be 5 feet 8 inches above the floor", + "In England, the Speaker of the House is not allowed to speak", + "Earthworms have 5 hearts", + "If all the gold sitting in the oceans and seas were mined, every person on this plant would get about 20 kilograms of gold each.", + "To make an espresso 42 coffee beans are needed", + "The oil that is found in poison ivy is called 'urushiol.'", + "Of all the days of the week, the most popular day for people to eat ice cream is Sunday", + "The first museum in Moscow that was set up in 1791 was the Natural History Laboratory at Moscow University. This later was changed to the Zoological Museum", + "A surfer once sued another surfer for 'stealing his wave.' The case was thrown out because the court was unable to put a price on 'pain and suffering' endured by the surfer watching someone else ride 'his' wave", + "Many people in parts of China eat insects. Some common insects are bean worms, scoprions, and locusts", + "The largest dog in the world is the Irish Wolfhound", + "Ernest Vincent Wright wrote a fifty thousand-word novel, 'Gadsby,' without any word containing the letter 'e.'", + "The projection light used for IMAX theaters can be seen from space.", + "The human liver performs over 500 functions", + "Ballroom dancing is a course at Brigham Young University in Utah", + "The word 'maverick' came into use after Samuel Maverick, a Texan, refused to brand his cattle. Eventually any unbranded calf became known as a Maverick", + "Finnish folklore states that when Santa comes to Finland to deliver gifts, he leaves his sleigh behind and rides on a goat named Ukko instead", + "More than $1 billion is spent each year on neck ties in the United States", + "In the 18th century, potatoes were given out as a dessert. They were served in a napkin, salted and hot", + "The only poisonous birds in the world are the three species of Pitohui. The Hooded Pitohui from Papua New Guinea is the most deadliest out of the three", + "Pretzels were originally invented for Christian Lent. The twists of the pretzels are to resemble arms crossed in prayer", + "The American Airlines Center in Dallas has more toilets per capita than any other sports and entertainment venue in the country", + "After 8 months, babies are more likely to get a diaper rash", + "The first modern toothbrush was invented in China. Its bristles came from hogs hair or the mane of a horse that were then put into ivory handles", + "The New Zealand Kiwi bird cannot fly", + "66% of wedding cards are hand delivered by people", + "Heavier lemons produce more, and tastier, juice", + "The leading cause of poisoning for children under the age of six in the home is liquid dish soap", + "The same amount of calories are burned by doing 6 sessions that are 5 minutes each of an activity and doing 1 session of that activity for 30 minutes", + "General William Booth is the founder of the Salvation Army", + "Iguanas can stay under water for up to thirty minutes", + "The fastest flying butterfly is the Monarch, which has been clocked with a speed as high as 17 miles per hour", + "Egyptian pyramid builders used to eat a lot of garlic because they thought it would increase their strength", + "The average office document gets copied 19 times", + "In just the first 56 days of life, the larva of the polyphemus moth eats about 86,000 times its birthweight", + "Every hour one billion cells in the body must be replaced", + "American actor Jack Nicholson, and American singer Bobby Darrin were raised believing their grandmothers were their mothers and their mothers were their older sisters", + "The first Ford cars had Dodge engines", + "The average height of an NBA basketball player is 6 feet 7 inches", + "One in five Americans move homes every year", + "The chocolate chip cookie was invented in 1933", + "The capital of Burkina Faso is Ouagadougou", + "A catfish has about 100,000 taste buds", + "The Liberty Bell was the first mechanical slot machine, which was invented by Charles Fey, a car mechanic in 1895.", + "A Russian man who wore a beard during the time of Peter the Great had to pay a special tax", + "The silkworm moth has lost the ability to fly ever since it has been domesticated", + "The first cheerleaders in the U.S. were men", + "The name Jeep came from the abbreviation used in the army for the 'General Purpose' vehicle, G.P", + "The odds of having quadruplets are 1 in 729,000", + "In 1965, the price for an issue of TV Guide was 15 cents", + "In 1565 In St. Augustine, Florida the first orange trees were planted", + "Nose prints are used to identify dogs, much like humans use fingerprints", + "In the United States, six tubs of Cool Whip, a brand of whipping cream, are sold every second", + "The most popular chocolate bar in the United Kingdom for the last 15 years has been Kit Kat", + "White-Out was invented by Bette Nesmith Graham, who is the mother of Michael Nesmith from the 'The Monkees.'", + "There are over 2,000 different types of cheese in the world", + "The giant squid has the largest eyes in the world", + "Owls swallow their prey whole because they have no teeth. After approximately 12 hours they cough up the feathers, bones, and fur in a shape of a football pellet", + "Historically, a blue ribbon has been awarded for first prize", + "Seventy-one percent of households report they have at least one snorer. Forty-five percent of those surveyed admit they snore, 35% said their partner snores, 12% said their child snores and 9% reported their pet snores", + "The original meaning of the word grocer was referring to a person who traded food in wholesale. These people would usually sell in large quantities, or by the 'gross.'", + "Research indicates that mosquitoes are attracted to people who have recently eaten bananas", + "Actress Michelle Pfeiffer was the first choice to play Clarice Starling in the movie 'Silence of the Lambs.' She turned down the role because she found it too scary", + "The White House has 35 bathrooms, 3 elevators, 132 rooms, and 412 doors in it", + "Due to the deforestation of the forests in North China, over one million tons of sands blows into Beijing from the Gobi desert. It sometimes causes the sky to turn yellow.", + "Cows are able to hear lower and higher frequencies better than human beings", + "Approximately 60% of the water used by households during the summer is used for watering flowers, and lawns", + "The largest diamond that was ever found was 3106 carats.", + "In 1970, Chip maker Intel purchased a pear orchard to build their corporate headquarters on", + "The mating call of a male toadfish, who are underwater, is so loud that it can be heard by humans above water", + "The most popular jelly belly jellybean flavour is buttered popcorn", + "The Nike swoosh was invented by Caroline Davidson back in 1971. She received $35 for making the swoosh. The first shoe with the swoosh was introduced in 1972", + "Slaves under the last emperors of China wore pigtails so they could be picked out quickly", + "A crocodile cannot stick its tongue out", + "Kiwis are the only known bird to have nostrils located at the tip of their beak", + "An adult esophagus can range from 10 to 14 inches in length and is one inch in diameter", + "A squash ball moving at 150 kilometers per hour has the same impact of a .22 bullet", + "Telephonophobia is the fear of telephones", + "The word alligator comes from the Spanish word El Lagarto, which means 'The Lizard.'", + "While still in college, Bill Gates and Paul Allen once built a special purpose machine called 'Traff-O-Data.' It was a machine that would analyze information gathered by traffic monitors. They never found any buyers.", + "The citric acid found in lemon juice is said to be able to dissolve a pearl", + "Robert Southey wrote the story 'Goldilocks and the Three Bears' in 1834", + "The tallest woman in the world is American Sandy Allen who is 7 feet 7 inches", + "American Airlines saved $40,000 in 1987 by eliminating one olive from each salad served in first-class", + "Astronauts get taller when they are in space", + "Only 5 to 10 percent of cheetah cubs make it to adulthood", + "Dentyne gum was invented in 1899 by a druggist from New York named Franklin V. Canning", + "It takes about three hours for food to be broken down in the human stomach", + "When former Texas Governor James Hogg was on his deathbed he made a special request that a pecan tree be planted at the head of his grave instead of a tombstone. The governor passed away on March 2, 1906, which is Texas Independence Day. The pecan tree is now the state tree of Texas", + "In a year, there are 60,000 trampoline injuries that occur in the U.S", + "There is an organization called SCROOGE in Charlottesville, Virginia that stands for Society to Curtail Ridiculous, Outrageous, and Ostentatious Gift Exchanges. This was formed to keep gift giving affordable and simple", + "The first World Series baseball playoffs occurred in 1903", + "Archipelago is the word to describe a large group of islands that are located close together", + "The life expectancy of a garbage disposal is about 5 to 10 years", + "In the original movie '101 Dalmatians,' there are exactly 6,469,952 spots on all 101 Dalmatians as they are shown in 113,760 frames of the film combined", + "The average North American car contains 300 pounds of plastics", + "A person who is a specialist in wine making is called an oenologist", + "You can only smell 1/20th as well as a dog", + "The number one cause of rabies in the United States are bats", + "The music for 'The Star Spangled Banner' comes from a British drinking song named 'Anacreon.'", + "27 percent of U.S. male college students believe life is 'a meaningless existential hell.' (big surprise, eh?)", + "Close to fifty percent of the bacteria in the mouth lives on the surface of our tongue", + "Less than 1% of the women in the world will ever be able to wear a diamond that is the size of a carat or more", + "Ketchup originated in China as a pickled fish sauce called ke-tsiap", + "An ostrich's eye is bigger that it's brain.", + "In Britain, The Red Lion is the most common name for a pub", + "In 1997, the record for the highest skydive by a dog at 4,572 feet was established by a dog named Brutus", + "The majority of burglaries occur during the daytime when people are not home", + "Traditionally, wild cabbage was used as an aphrodisiac", + "Tiger Woods was introduced to golf at nine months of age by his father", + "A person will burn 7 percent more calories if they walk on hard dirt compared to pavement", + "It would take 29 million years for a car travelling 100 miles per hour to reach the nearest star", + "Blue Jays can imitate the calls of hawks", + "There are over three trillion craters on the moon, with some being having a diameter over three feet", + "In India, a 9-year-old girl was 'married' to a stray dog, which tribal custom requires in order to protect a child whose first tooth appears on the upper gum", + "There is now an ATM at McMurdo Station in Antarctica, which has a winter population of two hundred people", + "In Canada, men are three times more likely than women to have seen a doctor in the last year", + "The most expensive spice in the world is saffron", + "In one night, an adult hippopotamus eats approximately 150 pounds of grass", + "The U.S. paid Russia $7.2 million for Alaska in 1867", + "Cows can detect odors up to five miles away", + "There are about 125 million multiples (twins, triplets, etc.) worldwide", + "Arthur Giblin was the inventor of the first 'flushable' toilet", + "Consuming chocolate was once considered a sin during the 16th and 17th century. During that time it was provided in the form of a drink and since drinking wine during lent was a sin, so was drinking chocolate", + "Approximately 40% of the states in the U.S. have severe, or extreme pollution problems", + "Wendel Clark holds the record for the longest span between NHL All-Star appearances, with 13 years (1986-1999)", + "Ancient Egyptians believed that onions would keep evil spirits away", + "Dill seeds are so small that approximately 10,000 dill seeds would be required to make an ounce", + "To make one pound of whole milk cheese, 10 pounds of whole milk is needed", + "If all the insects in the world were put on a scale, they would out weigh all creatures", + "Women smile more than men do", + "A ripe cranberry will bounce. Another name for a cranberry is bounceberry", + "Termites work 24 hours per day -- they do not sleep", + "The Romans used to clean themselves with olive oil since they did not have any soap. They would pour the oil on their bodies, and then use a strigil, which is type of blade, to scrape off any dirt along with the oil", + "The act of stretching and yawning is referred to as pandiculation", + "In the 1960 movie 'Psycho' by Alfred Hitchcock, chocolate syrup was used to show the blood in the shower scene", + "Carolyn Shoemaker, famous astronomer, has discovered 32 comets and approximately 300 asteroids", + "The longest fangs of a snake are found on the Gaboon Viper (Bitis gabonica), and can reach over 2 inches in length", + "Once a human reaches the age of 35, he/she will start losing approximately 7,000 brain cells a day. The cells will never be replaced", + "The only king without a moustache in a deck of cards is the king of hearts", + "Approximately 100,000 people get married in Las Vegas each year", + "Amish people do not believe in the use of aerosal air fresheners", + "Coca-cola used to use the slogan 'Good to the last drop,' in 1908. This slogan was later used by Maxwell House", + "The blind cavefish is born with eyes, but they fall off as the fish grows", + "In ancient Egypt, Priests plucked EVERY hair from their bodies including their eyebrows and eyelashes", + "The Indian election in 1984 was the largest election of any country. Over 379,000,000 voters were eligible to vote at over 480,000 polling stations", + "A single chocolate chip gives enough energy to a human being to walk 150 feet", + "There are 54 bones in your hands including the wrists", + "The name for Oz in 'The Wizard of Oz' was thought up when the creator, Frank Baum, looked at his filing cabinet and saw A-N, and O-Z, hence 'Oz.'", + "The town of Churchill, Manitoba, located in Canada, is known as the 'Polar Bear Capital of the World'", + "Amtrak is the combination of the words 'American' and 'Track'", + "On average, an American relocates 11 times in their life", + "Fires onland generally move faster uphill than downhill", + "The cartoon character Popeye was actually based on a real person named Frank 'Rocky' Fiegel who was a tough guy who was quite similar to Popeye physically", + "Frisbee got its name from William Russel Frisbee, who was a pie baker. He used to sell his pies in a thin tin pan, which had Frisbee written on it. When Walter Frederick Morrison thought of the idea of making saucer like disks to play catch, he visited the campus of Yale and noticed people there were using the pie pan to play catch so he therefore renamed his invention to Frisbee", + "Some arthritis medications contain gold salts, which is used as an anti-inflammatory", + "Lemon juice can aid in reducing the swelling caused by insect bites", + "LSD is made from lysergic acid, which is found in ergot, a type of fungus", + "DC-10, the name of an airplane stands for 'Douglas Commercial.'", + "In approximately 18 months, the papaya tree can grow to be 20 feet tall", + "Parrots cannot eat chocolate because it is poisonous to their body", + "Americans are responsible for generating roughly 20% percent of the garbage in the world", + "Termites are roasted and eaten like popcorn in South Africa", + "The official state tree of Illinois is The White Oak", + "It takes 3,000 cows to supply the NFL with enough leather for a year'ssupply of footballs.", + "In 1971, the postal code was introduced in Ottawa, Ontario", + "The tridacna clam can grow up to four feet long and weigh up to 500 pounds", + "The state that has the most diners in the world is New Jersey, which is referred to as the 'Diner Capital of the World.'", + "Approximately 1-2 calorie are burned a minute while watching T.V", + "The first recipe for a lasagna type dish was found to be from a British cookbook in the 14th century. Therefore, Italians were not the first ones to come up with the popular dish as believed", + "Only one person in two billion will live to be 116 or older", + "If an identical twin grows up without having a certain tooth, the other twin will most likely also grow up with that tooth missing", + "Albert Einstein was offered the presidency of Israel in 1952, but he declined", + "According to legend, tea originated in China when tea leaves accidentally blew into a pot of boiling water", + "When you sneeze, all your bodily functions momentarily stop, including your heart", + "Kite flying is a professional sport in Thailand", + "Urine from men?s public urinals was sold as a commodity in Ancient Rome. It was used as a dye and for making clothes hard", + "All 50 states are listed across the top of the Lincoln Memorial on the back of the $5.00 bill", + "1 out of 350,000 Americans get electrocuted in their life", + "New Mexico is known as the 'Land of Enchantment.'", + "In 1890, Scott Paper produced the first toilet paper to be available on a roll", + "An elephant in the wild can eat anywhere from 100 - 1000 pounds of vegetation in a 16 hour period", + "Some of the other names that were thought of for the dwarfs in the Disney movie 'Snow White' were Awful, Dirty, Shifty, Hotsy, and Jumpy", + "In the U.S., over 35 million people have used some sort of illegal drug in the last year", + "96% of candles that are purchased are by women", + "The oldest bird on record was Cocky, a cockatoo, who died in the London Zoo at the age of 82", + "A cow averages 40,000 jaw movements a day", + "The reason the soft drink Dr Pepper is called that is because the inventor Wade Morrison named it after Dr. Charles Pepper who had given him his first job", + "Annually 17 tons of gold is used to make wedding rings in the United States", + "Sex acts like a natural antihistamine, in can clear up a stuffy nose", + "Women on average live seven years longer than men do", + "A British term for slot machine is 'fruit machine' or 'one-armed bandit.'", + "Most dust particles in your house are made from dead skin", + "Even though the rose does not bear any fruit, the rose hips have more Vitamin C than most vegetables and fruits", + "In 1946 Danon Yogurt were the first to add fruit to commercially produced yogurt in U.S", + "The first domain name ever registered was Symbolics.com on March 15, 1985.", + "American Airlines saved $40,000 in 1987 by eliminating one olive from each salad served in first class", + "The first flavour of a cheese ball was called 'Cheddy Blue.'", + "Over 500 million gallons of Kool-Aid drink are consumed each year", + "The waste produced by one chicken in its lifetime can supply enough electricity to run a 100-watt bulb for five hours", + "Hydrogen solid is the most dense substance in the world, at 70.6g/cc", + "Missouri has been to most NCAA tournaments than any other college without reaching the final four", + "The hump of a camel can weigh up to 35 kilograms", + "Lake Malawi has the largest number of fish species in the world", + "The country of Fiji is made up of 332 islands", + "The first Labor Day holiday was celebrated on Tuesday, September 5, 1882, in New York City", + "The word 'sophomore' means 'sophisticated moron.'", + "Jim Bristoe, an American, invented a 30-foot-long, 2-ton pumpkin cannon that can fire pumpkins up to five miles.", + "There are about 61,300 pizza restaurants in the United States of America", + "To tell if a egg is fully cooked or raw, just spin it. If the egg wobbles then it is still raw, and if it easily spins it is fully cooked", + "Used in art the word 'sfumato' refers to the subtle blending of an outline by gradually blending one tone into another", + "There is a species of bird, Antpitta avis canis Ridgley, that barks like a dog", + "The flu pandemic of 1918 killed over 20 million people", + "Approximately 20% of Americans have a passport", + "The Nobel prize was first awarded in 1901", + "The reason why milk is white is because it contains a protein called Casein, which is white. Milk also contains fat, which is also white", + "After twenty-seven years, Betty Rubble made her debut as a Flintstones Vitamin in 1996", + "When telephone companies first began hiring telephone operators, they chose teenage boys for the job. They switched to women because the teenage boys were wrestling instead of working and pulling pranks on callers", + "In a lifetime, the heart pumps about one million barrels of blood", + "The Sears Tower in Chicago contains enough steel to build 50,000 automobiles", + "The first words that Thomas A. Edison spoke into the phonograph were, 'Mary had a little lamb.'", + "There are 400 species of bacteria in the human colon", + "On average, 100 people choke to death on ballpoint pens every year.", + "In the 20th century, over three million people have died from earthquakes", + "It is impossible to sneeze with your eyes open", + "People still cut the cheese shortly after death", + "In ancient Egypt, the only person who was allowed to wear cotton was the High Priest", + "Blueberries have more antioxidents than any other fruit or vegetables", + "The Mount Horeb Mustard Museum which is located in Wisconsin has the biggest collection of prepared mustards. They have approximately 4,000 different jars and tubes from all over the world", + "Bananas trees are not really trees. They are considered to be giant herb plants", + "Sponge Candy was invented in Buffalo, NY", + "When the volcano Krakatoa off the Java islands exploded in 1883, it was so loud that it woke some people up in South Australia", + "Pearls are rarely found in North American oysters", + "The average cocoon contains about 300-400 metres of silk", + "Lake Nicaragua boasts the only fresh-water sharks in the entire world", + "Boxing champion Gene Tunney taught Shakespeare at Yale University", + "The most popular pickle is the Dill pickle", + "Diabetes is the fourth leading cause of death in the U.S., accounting for about 180,000 deaths per year", + "Every year approximately 3,000 people choke to death", + "Cow is a Japanese brand of shaving foam", + "The fastest running bird is the Ostrich, which has been clocked at 97.5 kilometres per hour", + "When the divorce rate goes up in the United States, toy makers report that the sale of toys also rise", + "Q-Tip Cotton Swabs were originally called Baby Gays", + "The Pacific island of Tonga once issued a stamp that was banana shaped", + "A mole can dig a tunnel three hundred feet long in a single night", + "The glue on Israeli postage stamps is certified kosher", + "The only commercial aircraft that is able to break the sound barrier is the Concorde.", + "U.S. Postal Service processes 38 million address changes each year", + "In 1984, Ronald Reagan declared the month of July to be 'National Ice Cream Month.'", + "The small intestine in the human body is about 2 inches around, and 22 feet long", + "In 1905, the first pizzeria in the U.S. opened in New York City", + "In the early nineteenth century some advertisements claimed that riding the carousel was good for the circulation of blood", + "For the blockbuster movie 'The Terminator,' O.J. Simpson was considered to play the role of the Terminator, but producers did not choose him as they thought he would not be taken seriously", + "The actor who played the T-1000 in Terminator 2 (Robert Patrick) and the lead singer of Filter are brothers", + "The sole purpose of a drone bee is to mate with the queen bee", + "There are more plastic flamingos in America than real ones", + "Walt Disney had a fear of mice", + "Cleopatra married two of her brothers", + "In an average lifetime, people spend four years traveling in cars and six months waiting for red light to turn green", + "At one time, pumpkins were recommended for removing freckles", + "In just one drop of liquid, 50 million bacteria can be present", + "The Montreal Canadians hockey team has won the most Stanley Cups with 24", + "Nylon is a man-made fibre that is made from coal and petroleum", + "When the First Lady, Eleanor Roosevelt, received an alarming number of threatening letters, soon after her husband became President at the height of the Depression, the Secret Service insisted that she carry a pistol in her purse", + "Swiss engineer George de Mestral, who got the idea after noticing burrs were sticking to his pants after his regular walks through the woods, invented Velcro.", + "Bees can communicate with other bees by dancing. Their dance can alert other bees as to which direction and the distance nectar and pollen is located", + "The steepest street in the world is Baldwin Street located in Dunedin, New Zealand. It has an incline of 38%", + "One billion pounds of pasta would need approximately 2,021,452,000 gallons of water to cook it. This is equivalent to 75,000 Olympic-size swimming pools", + "Nutmeg is extremely poisonous if injected intravenously", + "The average Super Bowl party has 18 people", + "An ant can detect a movement through 5 centimeters of earth", + "One out of 200 women is colorblind", + "On average, the rainfall across the Amazon is 7 feet annually.", + "Passion fruits have a tranquilizing effect on the body", + "It is physically impossible for pigs to look up into the sky", + "A snail can sleep for 3 years", + "'Dreamt' is the only English word that ends in the letters 'mt'", + "There is a large brass statue of Winnie-the-Pooh in Lima, Peru", + "In 1982, Larry Walters tied 24 weather balloons to his lawn chair in Los Angeles and climbed to an altitude of 16,000 feet", + "Centuries ago in India, a person could get their nose chopped off for breaking the law", + "The sound of E.T. walking was made by someone squishing her hands in Jello", + "The total number of steps in the Eiffel Tower are 1665", + "The amount of blood a female mosquito drinks per serving is five millionths of a liter", + "An adult porcupine has approximately 30,000 quills on its body, which are replaced every year", + "The name for Ivory Soap was inspired by a verse from the Bible. Harley Proctor got though of the name when the minister read from Psalms 45:8, 'All thy garments smell of myrrh and aloes and cassia, out of the ivory palaces whereby they have made thee glad.'", + "A survey done by Clairol 10 years ago came up with 46% of men stating that it was okay to color their hair. Now 66% of men admit to coloring their hair", + "The name of the squiggly line '~' is called a tilde", + "In 1747, the first American mention of the Christmas tree occurred. However, it was a not a tree but instead a pyramid made out of wood and decorated with apples and evergreen boughs", + "There are more Barbie dolls in Italy than there are Canadians in Canada", + "The study of ants is called Myrmecology", + "In the United States, you are more likely to be killed by a bee sting than a shark attack", + "Coca-Cola was the first soft drink to be consumed in outer space", + "Frozen food can be just as nutritious as fresh food", + "Fourteen people die each day from asthma in the United States", + "The average amount of time spent kissing for a person in a lifetime is 20,160 minutes", + "The human body has approximately 37,000 miles of capillaries", + "The most expensive shoes in the world are ruby slippers located in Harrods in London, which cost $1.6 million, has a full time security guard. The shoes are made from platinum thread and has 642 rubies in them. It took over 700 hours to produce the shoe", + "Only 55% of all Americans know that the sun is a star", + "The city of Denver was originally chosen to host the 1976 Winter Olympics, but had to withdraw because Colorado voters rejected to finance it", + "The longest bout of sneezing recorded was by Donna Griffith. It began in January 13 1981 and continued until September 16 1983 and lasted for 978 days", + "The Taj Mahal, located in Agra, India, was actually built for use as a tomb by Mogul ruler Shah Jehan for his wife, Arjuman Banu Begum", + "In 1943, the July issue of 'Transportation Magazine' had an article entitled '1943 Guide to Hiring Women.'", + "Spotted skunks do handstands before they spray", + "A dime has 118 ridges around the edge", + "Scientists with high-speed cameras have discovered that rain drops are not tear shaped but rather look like hamburger buns.", + "Ancient Egyptian women used to wear perfume cones made of wax that would melt in the heat letting out a nice fragrance", + "Mardi Gras means 'Fat Tuesday.' This is the festival that New Orleans, Louisiana is famous for having every year", + "There was once a fish caught in Delaware Bay with a watch still ticking inside", + "In Singapore, it is illegal to sell or own chewing gum", + "During the female orgasm, endorphines are released, which are powerful painkillers. So headaches are in fact a bad excuse not to have sex", + "Donkeys kill more people annually than plane crashes", + "The nut 'filbert' got its name from St. Philbert which is celebrated on August 22nd, which is also when the nut matures", + "Every time Beethoven sat down to write music, he poured ice water over his head", + "Some species of dolphin sleep with one eye open", + "The chewing gum Juicy Fruit has 10 calories. This is approximately the same as a bite of whole wheat bread", + "In ancient Egypt, doctors used jolts from the electric catfish to reduce the pain of arthritis", + "In order to scare away predators, Giant petrels, a type of seabird, throw up all over the intruder", + "Elvis Presley used to be a truck driver before he started singing", + "The average cow produces about 2,305 gallons of milk each year", + "Former U.S. president Ronald Reagan worked as a lifeguard in his youth at a beach near Dixon, Illinois and saved over 77 lives", + "In 2000, there were 1,579,566 drug arrests in the United States. Of those, close to half were for marijuana", + "Just like fingerprints, every cats nose pad is different", + "Popeye is 34 years old, weighs 158 lbs, and is 5 feet 6 inches tall", + "Researchers have shot footage of Orcas (killer whales) attacking and killing great white sharks", + "The three most valuable brand names on earth: Marlboro, Coca Cola, and Budweiser, in that order", + "One bushel of wheat can make enough sandwiches that you could eat three sandwiches a day for over six months", + "In 1992, approximately 750 deaths occurred in the United States due to workplace violence", + "In the movie 'Babe', the piglet was played by over 30 different piglets they outgrew the part so quickly during the production of the film", + "Research indicates that people prefer the colour blue for their casual clothing", + "The leading cause of deaths for children between the ages of 1 and 4 are motor vehicle crashes", + "The first toilet being flushed in a motion picture was in the movie 'Psycho.'", + "The human brain has about 100,000,000,000 (100 billion) neurons", + "One acre of wheat can produce enough bread to feed a family of four people for about ten years", + "Queen Victoria used marijuana, to help relieve menstrual cramp pain", + "On a ship a toilet is called a head", + "About 1 in 5,000 North Atlantic lobsters are born bright blue", + "Approximately 10.5 gallons of water is used in a dishwasher. Washing the dishes by hand can use up to 20 gallons of water", + "The thing that hangs from the top of the beak of a turkey is called the snood", + "Ticks can be as small as a grain of rice and grow to be as big as a marble", + "An American chews an average of 300 sticks of gum in a year", + "Most cows give more milk when they listen to music", + "Giant flying foxes, which are a type of bat, that live in Indonesia have wingspans of nearly six feet", + "Heavier lemons contain more and tastier juice", + "Since 1950, over 230 million eggs of Silly Putty have been sold", + "Oral-B is a combination of oral hygiene and the letter B, which stands for the word better", + "Frank Wathernam was the last prisoner to leave Alcatraz prison on March 21, 1963", + "A blink lasts approximately 0.3 seconds", + "In 1903 Mary Anderson invented the windshield wipers", + "Both Thomas Jefferson and Jimmy Carter, U.S. presidents, were peanut farmers at one time", + "A cow releases about 125 gallons of gas per day", + "The Apollo 17 crew were the last men on the moon", + "Spartacus led the revolt of the Roman slaves and gladiators in 73 B.C", + "The Mexican version of the Tooth Fairy is known as the Tooth Mouse, which takes the tooth and leaves treasures in its place", + "In a day the blue whale calf drinks approximately 130 gallons of milk", + "Right-handed people live, on average, nine years longer than left-handed people do", + "All racehorses in the U.S. celebrate their birthday on January 1st", + "A person would have to drink more than 12 cups of hot cocoa to equal the amount of caffeine found in one cup of coffee", + "The oldest documented footwear found was a 8,000 year-old sandal found in a cave located in Missouri, USA", + "Broccoli was first introduced into France during the royal marriage of Catherine de Medici to Henry II of France", + "By federal law, for a noodle to actually be a noodle it must have 5.5 percent egg solids in it, otherwise it cannot be called a noodle", + "The first female guest host of Saturday Night Live was actress Candace Bergen", + "The human heart beast roughly 35 million times a year", + "People that use mobile phones are 2.5 time more likely to develop cancer in areas of the brain that are adjacent to the ear they use to talk on the mobile phone", + "Turkeys can have heart attacks. When the Air Force was conducting test runs and breaking the sound barrier, fields of turkeys dropped dead because of heart attacks", + "The act of sneezing is referred to as sternutation", + "The average medium size piano has about 230 strings", + "A study revealed that men that were born with a low birth weight were less likely to get married", + "It takes about 63,000 trees to make the newsprint for the average Sunday edition of The New York Times", + "On average, you would need 12.5 gallons of milk to make one gallon of ice cream", + "A leech can gorge itself up to a maximum of five times its body weight", + "There are places in Saskatchewan called Elbow, Eyebrow, and Drinkwater", + "A butterfly has to have a body temperature greater than 86 degrees to be able to fly", + "The beeswax that is produced by Honey bees comes from eight paired glands that are located on the underside of their stomach", + "People in low-income homes spend 50% more time playing video games than people in high-income homes", + "Lighthouse keepers were nicknamed 'wickies' because they tended the lamps wick", + "Taco Bell serves over 35 million consumers each week in the USA", + "During the Roman times, people used urine, called lotium in Latin, as a hair product", + "There are approximately 90 people that have been frozen after their death.", + "The smallest will ever written was 3.8 cm in diameter. It had 40 words written on it and was signed by two witnesses", + "The length of a human esophagus is 25 centimeters", + "In 1942 the Jello company introduced Cola flavored jello, which only lasted a year", + "Males account for 60% of toy injuries that occur in the U.S", + "The company 'Sony' was originally called 'Totsuken.' They felt the name 'Sony' would be easier to pronounce. The name was invented by a cross between the name 'sonus' and 'sonny.' The name sound and sonic are derived. Sonny was used to represent a young man or boy, which would show a energetic young company", + "The Red Cross is called the Red Crescent in Arab countries", + "The Olympics were originally held for the Greek god Zeus", + "The oldest inhabited house in Scotland is the Traquair Castle. The castle has had 27 kings as visitors", + "Four billion pounds of watermelon were grown in the United States in 1999", + "Wham-O manufactured twenty-thousand hula-hoops a day at the peak of hula-hoop popularity in 1958", + "JELL-O was declared The 'Official State Snack' of Utah in January 2001", + "Elvis Presley was obsessed with brushing his teeth", + "When the Statue of Liberty was moved from France to the United States, 214 crates were used to transport it. The Statue was also reduced to 350 pieces", + "There are approximately 2000 thunderstorms that are active at the same time which results in 100 lightning flashes a second.", + "In-vitro babies are born in Australia more than any other country in the world", + "Uranus? winter and summer seasons last the equivalent of 21 Earth years", + "More people die from eating sharks then from being eaten by them. This is due to a poison in shark meat", + "The murder rate in the United States is about four times greater than in Japan. In Japan, no private citizen can buy a handgun legally", + "The rarest chocolate bar in the world is the Porcelana bar. There are only 20,000 of these bars produced a year, and they sell for $90 per pound", + "The reason why locusts swarm are because when they are in groups, a 'hot-spot' behind their hind legs is stimulated, which in turn causes their destructive nature. A large swarm of locusts can eat eighty thousand tons of corn in a day", + "There are an equivalent number of cows and people in Friesland, Netherlands", + "Centipedes always have an uneven pairs of walking legs", + "A chicken once had its head cut off and survived for over eighteen months, headless", + "The largest diamond found in the United States was a 40.23 carat white diamond. It was found in 1924 and nicknamed the 'Uncle Sam.'", + "Following directions off the Internet and chemicals obtained from a mail order company, a team of U.S. scientists created an identical copy of the polio virus.", + "Every day, the Hubble telescope transmits enough data to fit 10,000 standard computer disks", + "The average number of people that go to a party for the Super Bowl is 17", + "The amount of Kit Kat chocolate bars that are made at the York factory every 15 minutes are enough to outstack the Eiffel Tower", + "The skin of a shark is made up of 'tiny teeth' which are called dermal denticles", + "The strongest gust of wind was recorded at the Mount Washington Observatory on April 12th, 1934, and measured 231 miles per hour.", + "The company Chanel claims that every 30 seconds, somewhere in the world, a bottle of Chanel No 5 is sold", + "In their lifetime, house cats spend approximately 10,950 hours purring", + "The reason why bubbles are round is because this is the most efficient shape that the soap film can take for the amount of air trapped inside", + "It is very common for babies in New Zealand to sleep on sheepskins. This is to help them gain weight faster, and retain their body heat", + "From 1526 to 1707, the first six Mogul emperors of India ruled in unbroken succession from father to son", + "A one kilogram packet of sugar will have about 5 million grains of sugar", + "Bats emit ultrasonic sounds to communicate with each other", + "Rats can survive up to 14 days without any food", + "Canola oil is actually rapeseed oil but the name was changed in Canada for marketing reasons", + "Three consecutive strikes in bowling is called a turkey", + "In a year, about 90 million jars of Skippy Peanut Butter are sold. This works out to three jars sold every second", + "In a lifetime, an average man will shave 20,000 times", + "The Pentagon has 284 restrooms", + "From 1967-1976, the town of Tororo located in Uganda had thunder 251 out of the 365 days in a year for those years.", + "Children grow faster in the springtime than any other season during the year", + "Another name for licorice is 'Sweet Wood' or 'Spanish Juice.'", + "The reason the Animal Crackers box is designed with a string handle is because when the popular circus theme was introduced in 1902 they thought it would also be a good idea to package them with a string as a Christmas novelty so they could be hung from Christmas trees", + "Sheep can detect other sheep faces like humans do. They can remember up to 50 sheep faces", + "The loudest insect in the world is the male cicadas, which are like crickets. When they rub their abdomens, the sound made can be heard from 1300 feet", + "Each year 96 billion pounds of food is wasted in the U.S", + "In the past 60 years, the groundhog has only predicted the weather correctly 28% of the time. The rushing back and forth from burrows is believed to indicate sexual activity, not shadow seeking", + "Pretzel snacks have been around for over 1300 years. A European monk invented the snack using used leftover bread dough", + "Sharks are capable of surviving on average six weeks without eating. The record observed in an aquarium is fifteen months by a species of shark known as the 'swell shark.'", + "The destruction of the Berlin Wall began when private citizens began to demolish entire sections of the Wall without interference from government officials on November 9, 1989", + "Most American women have their first baby when they are 24.3 years old", + "Frogs do not need to drink water as they absorb the water through their skin", + "A group of larks is called an exaltation", + "The Kool Aid Man used to be known as 'Pitcher Man' when he was first introduced in 1975", + "Wheel of Fortune star Vanna White holds the record for putting her hands together approximately 140,000 times to clap", + "Men sweat more than women. This is because women can better regulate the amount of water they lose", + "Research has indicated that approximately eleven minutes are cut off the life of an average male smoker from each cigarette smoked", + "The triangular shape that Toblerone chocolates are packaged in, is protected by law", + "In 1945, the first 'floating ice cream parlor' was built for sailors in the western Pacific. This 'floating ice cream parlour' could produce ten gallons of ice cream every seven seconds", + "The formula for Coca-cola has never been patented", + "The average day is actually 23 hours, 56 minutes and 4.09 seconds. We have a leap year every four years to make up for this shortfall", + "Before its name was changed, the African Penguin used be called the Jackass Penguin because of its donkey-like braying call", + "During the high feeding season, it has been estimated that an adult blue whale can eat up to 40 million krill in one day. (Krill are shrimp like creatures)", + "Pound for pound, leopards are said to be seven times stronger than humans", + "One average, men spend 60 hours a year shaving", + "Botanically a rhubarb is a vegetable. It was changed to a fruit in 1947 by a U.S. Custom Court", + "Sawney Beane, his wife, 8 sons, 6 daughters, and 32 grandchildren were a family of cannibals that lived in the caves near Galloway, Scotland in the early 17th Century. Although the total number is not known, it is believed they claimed over 50 victims per year. The entire family was taken by an army detachment to Edinburgh and executed, apparently without trial", + "The movie that grossed the most money that was adapted from a T.V. cartoon is Scooby-Doo", + "There are five million scent receptors located in a human beings nose", + "When Coca-Cola was invented, American tourists that visited Spain were surprised to see that Coke was three times as expensive as a glass of brandy", + "Some silkworms can spin cocoons that contain more than two miles of silk", + "The mother of famous astronomer Johannes Kepler was accused of being a witch", + "At one time, Pumpkins were recommended for the removal of freckles and curing snake bites", + "The Eisenhower interstate system requires that one mile in every five must be straight. These straight sections are usable as airstrips in times of war or other emergencies", + "Approximately 200 pets are buried in a pet cemetery out of the thousands of pets that die each day", + "The average Hostess Twinkie is 68 percent air as measured by volume according to university researchers", + "In Haiti, only 1 out of every 200 people own a car. This is ironic considering approximately 33% of the country's budget on import is spent on equipment for fuel and transportation.", + "Every U.S. bill regardless of denomination costs just 4 cents to make", + "About 30% of American admit to talking to their dogs or leaving messages on their answering machines for their dogs while they are away", + "A duck's quack doesn't echo, and no one knows why", + "Caterpillar means 'hairy cat' in Old French", + "The ostrich has two toes on each feet which gives it greater speed", + "On September 3, 1970, a hailstone was found in Coffeyville, Kansas that was eight inches in diameter and weighed 1.67 pounds.", + "Honorificabilitudinitatibus is the longest English word that consists strictly of alternating consonants and vowels", + "It can take up to a month for a rattlesnake to re-supply its venom", + "Close to 3 billion movie tickets are sold in India every year", + "The word racecar and kayak are the same whether they are read left to right or right to left", + "There were approximately 2,228 people on board the Titanic when it sank. Of this, only 706 people survived", + "An elephant can live up to the age of seventy, or in some cases even more", + "The giant squid has the largest eyes in the world", + "Elephants can't jump. Every other mammal can.", + "The name 'cranberry' comes from German and Dutch settlers. The berry was intially called 'crane berry.' The reason it was called this was because when the flowers bloom, the petals of the flowers twist backwards and look very much like the head of a crane. Eventually the name was shortened down to be 'cranberry.'", + "In New Mexico, over eleven thousand people have visited a tortilla chip that appeared to have the face of Jesus Christ burned into it", + "On average, he ratio of yellow kernels to white kernals in a bag of popcorn is 9:1", + "The first toilet stall in a public washroom is the least likely to be used. It is also the cleanest", + "In 1955, only 330 Volkswagen Beetle's were sold at a price of $1800 each in the United States.", + "Printed on the tablet being held by the Statue of Liberty is July IV, MDCCLXXVI", + "The country of Fiji is made up of 332 islands", + "Orville Wright, a pilot, was involved in the first aircraft accident. His passenger, a Frenchman, was killed.", + "The first company to mass produce teddy bears was the Ideal Toy Company", + "Princess Anne from the British royal family competed in the 1976 Summer Olympics", + "Brazil produces the most oranges in the world", + "Average life span of a major league baseball: 7 pitches.", + "The eyeball of a human weighs approximately 28 grams", + "A human head remains conscious for about 15 to 20 seconds after it is been decapitated", + "Witchcraft means 'Craft of the Wise Ones.'", + "500,000 kids in the US live in same sex households", + "In July 1874, a swarm of Rocky Mountain locusts flew over Nebraska covering an area estimated at 198,600 square miles. It is estimated that the swarm contained about 12.5 trillion insects. These insects became extinct thirty years later", + "Tropical rainforests cover about 7% of the Earth and receive over 80 inches of rain every year", + "The feet have approximately 250,000 sweat glands", + "Approximately 7.5% of all office documents get lost", + "The desert tortoise can live without having to drink any water. It extracts the water it needs from the vegetation it eats", + "There were 13 couples celebrating their honeymoon on the Titanic", + "A cat has 32 muscles in each ear", + "There are 336 dimples on a regulation golf ball", + "Elvis Presley had a twin brother named Jesse Garon Presley who died at birth", + "Robert Wadlow is the tallest man recorded in history. He grew to be eight feet and eleven inches and weighed 490 pounds when he died", + "Bank robber John Dillinger played professional baseball", + "Research indicates that plants grow healthier when they are stroked.", + "France is known as the perfume capital of the world", + "According to psychologists, the shoe and the foot are the most common sources of sexual fetishism in Western society", + "Constipation is caused when too much water is absorbed in the large intestine and the feces become dry", + "One ton of grapes can produce 720 bottles of wine", + "Eating about twenty tart cherries a day could reduce inflammatory pain and headache pain", + "In 2001, the five most valuable brand names in order were Coca-Cola, Microsoft, IBM, GE, and Nokia", + "Milk chocolate was invented in Switzerland by David Peter in 1876", + "In November 1999, two women were killed by a lightning bolt. The underwire located in their bras acted as a electrical conductors, and when the lightning bolt hit the bra they left burn marks on their chest", + "Basketball was invented by Canadian James Naismith in 1891", + "Over 100,000 birds and sea animals are killed every year due to plastic garbage", + "The big toe is the foot reflexology pressure point for the head", + "85% of weddings are held in a synagogue or church", + "The sport of surfing originated in Hawaii", + "It is possible to lead a cow upstairs...but not downstairs", + "Before soccer referees started using whistles in 1878, they used to rely on waving a handkerchief", + "Tobacco kills more Americans each year than alcohol, cocaine, crack, heroin, homicide, suicide, car accidents, fire and AIDS combined", + "The best time for a person to buy shoes is in the afternoon. This is because the foot tends to swell a bit around this time", + "Dead cells in the body ultimately go to the kidneys for excretion", + "Americans, on average, spend 18% of his or her income on transportation as compared to only 13% spent on food", + "There are some species of snails that are venomous. Their venom can be fatal to humans", + "The first box of Crayola that was ever sold had the same eight colours that are sold in the box today consisting of red, blue, yellow, green, violet, orange, black and brown. The box was sold for a nickel in 1903", + "A turtle can breathe through its butt.", + "The rarest coffee in the world is Kopi Luwak, which is found in Indonesia. It cost about $300 a pound", + "The average America online user spends 70 minutes day online", + "People of Ancient China believed that swinging your arms could cure a headache", + "In 1938, Cliquot Club ginger ale was the first soft drink to be canned", + "The largest apple pie ever baked was forty by twenty three feet", + "Roughly 44% of junk mail is thrown away unopened", + "Catfish have tastebuds located on their whiskers", + "The laundry detergent Tide, has a market share of about forty percent market", + "A Canadian, Troy Hurtubise, spent $100,000 and almost went bankrupt building a RoboCop style suit so that he could withstand a bear attack", + "The 3 most valuable brand names on earth: Marlboro, Coca-Cola, andBudweiser, in that order.", + "The only two days of the year in which there are no professional sports games (MLB, NBA, NHL, or NFL) are the day before and the day after the Major League all-stars Game", + "In the United States, 8.5 million cosmetic surgical and non-surgical procedures were done in the year 2001", + "The Bible has been translated into Klingon.", + "Ian Fleming named his character 'James Bond' after real-life ornithologist and author", + "Most dinosaurs walked on their toes.", + "On December 17 1991, the Cleveland Cavaliers beat the Miami Heat 148-80, the largest margin of victory in an NBA game", + "There are mirrors on the moon. Astronauts left them so that laser beams could be bounced off of them from Earth. These beams help give us the distance to the moon give or take a few metres.", + "The U.S. army packs Tabasco pepper sauce in every ration kit that they give to soldiers", + "The trunk of an elephant can hold up to two gallons of water", + "Every year, an igloo hotel is built in Sweden that has the capacity to sleep 100 people", + "During the holiday season, approximately $220 million worth of Poinsettias are sold", + "A newborn kangaroo weighs approximately 0.03 ounces and is small enough to fit in a teaspoon", + "When Scott Paper Co. first started manufacturing toilet paper they did not put their name on the product because of embarrassment", + "The most senior crayon maker Emerson Moser retired after making 1.4 billion crayons for Crayola. It was then that he revealed that he was actually colorblind", + "The highest point in Pennsylvania is lower than the lowest point inColorado.", + "Scientists have determined that having guilty feelings may actually damage your immune system", + "'Go.' is the shortest complete sentence in the English language", + "There are more than 250,000 rivers in the United States, which amounts to 3.5 million miles of rivers", + "Forty-one percent of women apply body and hand moisturizer at least three times a day", + "Pretzel that have no salt on them are called 'baldies.'", + "The 1912 Olympics was the last Olympics that gave out gold medals that were made entirely out of gold", + "Monopoly is the best-selling board game in the world", + "There was a book written fourteen years before the sinking of the Titanic happened titled 'Futility' by Morgan Robertson. This book was remarkably similar to the tragedy that happened to the Titanic in 1912", + "One ounce of chocolate has about 20 mg of caffeine in it", + "A giraffe can go longer without water than a camel", + "Vikings, after killing their enemies, used their skulls as drinking vessels", + "Studies have shown that classical music helps cows produce more milk", + "Two out of five people end up marrying their first love", + "The Hawaiian alphabet only has 12 letters", + "The name 'Muppet' was coined by Jim Henson. The word was made from a combination of the word 'marionette' and 'puppet.'", + "In the Sahara Desert, there is a town named Tidikelt, which did not receive a drop of rain for ten years", + "The Christmas season begins after sunset on December 24th and lasts until January 5th. This is also known as the Twelve Days of Christmas", + "Mosquitoes are attracted to the color blue more than any other color", + "In Italy, Santa Claus is known by the name Babbo Natale", + "Two objects have struck the earth with enough force to destroy a whole city. Each object, one in 1908 and again in 1947, struck regions of Siberia. Not one human being was hurt either time", + "When blue whales are first born, they gain as much as 200 pounds a day while they are calves", + "Families who do turn off the television during meals tend to eat healthier. This was regardless of family income, or education", + "About 25 percent of all the energy consumed in the US is from natural gas", + "American novelist Mark Twain was the first known author to submit a typed manuscript", + "If you fart consistently for 6 years and 9 months, enough gas is produced to create the energy of an atomic bomb", + "Canada is the only country not to win a gold medal in the Summer Olympic games while hosting the event", + "The sound made by the toadfish when mating underwater is so loud that it can be heard by humans on the shore", + "In America, approximately 20% of children between the ages of 2 - 7 have televisions in their rooms", + "Traveling by air is the safest means of transportation.", + "In 1996, toy company Mattel released a 'Harley Davidson' Barbie. This dolls distinctive feature is a birth mark on her face that changes position with every new release of the doll", + "The most common injury caused by cosmetics is to the eye by a mascara wand", + "There have been close to 200 coups and counter-coups in the country of Bolivia", + "On average, pigs live for about 15 years", + "Roughly 42% of people in the United Kingdom snore", + "No NFL team which plays its home games in a domed stadium has ever won a Superbowl", + "Beluga whales which are also called 'white whales' are not born white. They are born grey in color, and by the age of six become completely white", + "Tiger Woods is the first athlete to has been named 'Sportsman of the Year' by magazine Sports Illustrated two times", + "The eight most popular foods to cause food allergies are: milk, eggs, wheat, peanuts, soy, tree nuts, fish, and shellfish", + "Club Direct, a travel insurance company in Britain, provides insurance plans for protection from falling coconuts", + "There are some bananas that are red instead of yellow", + "Only one out of every three people wash their hands when leaving a public bathroom", + "570 gallons of paint would be needed to paint the outside of the White House", + "Baby robins eat 14 feet of earthworms every day", + "Every three seconds a baby is born somewhere in the world", + "The total mileage driven by all U-Haul trucks in a year is enough to move a person from the Earth to the moon five times a day for an entire year", + "The Eisenhower interstate system requires that one mile in every fivemust be straight. These straight sections are usable as airstrips intimes ofwar or other emergencies.", + "Pluto was discovered on February 10, 1930 by Clyde Tombaugh", + "Termites have been around for over 250 million years", + "The average person changes their career every 13 years", + "The New York Yankees have appeared in the World Series a league leading 38 times and won 26 titles", + "Approximately 18 billion disposable diapers end up in landfills each year. These diapers can takes as long as 500 years to finally decompose", + "Over 4.5 billion sticks have Trident gum have been chewed. If the stick of gum were laid out end to end they could circle the globe approximately 1.8 times", + "Oak trees can live 200 or more years", + "The brain of an ant has about 250,000 brain cells", + "About 26 per cent of all indoor water used by households in Sydney, Australia are for laundry", + "A rainbow can occur only when the sun is 40 degrees or less above the horizon", + "If you spray an antiseptic spray on a polar bear, its fur will turn purple", + "Over $7 billion a year is spent on chocolates by consumers", + "During World War II, Russians used dogs strapped with explosives to blow up German tanks. They trained the dogs to associate the tanks with food and ended up destroying about 25 German tanks using this method", + "Butterflies taste with their feet", + "St. Louis, Missouri was the first U.S. city to host the summer Olympics in 1904", + "The phrase 'rule of thumb' is derived from an old English law whichstated that you couldn't beat your wife with anything wider than yourthumb.", + "Every year Alaska has about 5,000 earthquakes, 1,000 of which measure above 3.5 on the Richter scale", + "A fetus develops fingerprints at eighteen weeks", + "It takes about a half a gallon of water to cook macaroni, and about a gallon to clean the pot", + "The cornea is the only living tissue in the human body that does not contain any blood vessels", + "In the U.S. peanuts account for 66% of all snack nuts", + "There are approximately 7,000 feathers on an eagle", + "Sharks can sense a drop of blood from a mile away", + "As a defense mechanism, the North American Opossum closes its eyes and becomes totally limp. Basically it plays dead", + "The longest town name in the world has 167 letters", + "A sneeze zooms out of your mouth at over 600 m.p.h", + "A cesium atom in an atomic clock that beats over nine billion times a second.", + "The mythical Scottish town of Brigadoon appears for one day every one hundred years", + "Kermit the frog delivered the commencement address at Southampton College located in the state of New York in 1996", + "In World War II, the German submarine U-120 was sunk by a malfunctioning toilet", + "The phrase 'Often a bridesmaid, but never a bride,' actually originates from an advertisement for Listerine mouthwash from 1924", + "Over 50% of lottery players go back to work after winning the jackpot", + "The largest cultivated crop in the United States is corn", + "Walt Disney holds the record for the most Oscar nominations with sixty-four", + "On average, Americans eat one hundred acres of pizza a day. This amounts to about three hundred fifty slices per second", + "As an iceberg melts, it makes a fizzing sound because of the compressed air bubbles popping in the ice", + "The Arctic Ocean covers an area of about 14,056,000 sq miles", + "The first known contraceptive was crocodile dung, used by Egyptians in 2000 B.C", + "Most toilets flush in E flat", + "Bile produced by the liver is responsible for making your feces a brownish, green colour", + "At one time the group 'Grateful Dead' were called 'The Warlocks.'", + "Bats can detect food up to 18 feet away and what type of insect the food may be using their sense of echolocation", + "At the equator the Earth spins at about 1,038 miles per hour", + "People whose mouth has a narrow roof are more likely to snore. This is because they have less oxygen going through their nose", + "In one day, a human sheds 10 billion skin flakes. This amounts to approximately two kilograms in a year", + "On average, an American home has 3-10 gallons of hazardous materials", + "On average, 35 meters of hair fibre is produced on the adult scalp", + "Dalmatian puppies do not have any spots on them when they are born. They actually develop them as they get older", + "Male goats will pee on each other in order to attract mates", + "A dog by the name of Laika was launched into space aboard the Russian spacecraft Sputnik 2 in 1957", + "In 2002, dogs have killed more people in the U.S. than the Great White shark has killed in the past 100 years", + "The study of twins is known as gemellology", + "On an American one-dollar bill, there is an owl in the upper right-hand corner of the '1' encased in the 'shield' and a spider hidden in the front upper right-hand corner", + "During one seven year period, Thomas Edison obtained approximately three hundred patents. In is whole life he obtained over one thousand patents.", + "When Black Jack Ketchum was hung back in 1901 in Clayton New Mexico, the noose actually ended up taking his head off. The head had to be sewn back on so Black Jack could be buried properly", + "Every 40,000 children are killed by fires", + "The highest recorded speed of a sneeze is 165 km per hour", + "In 1985, a pregnant women was falsely accused of shoplifting a basketball", + "In every episode of Seinfeld there is a Superman somewhere", + "The adult electric eel can produce a five hundred volt shock, which is enough to stun a horse", + "When the are in danger, kangaroos will beat the ground loudly with their hind feet", + "To manufacture a new car approximately 148,000 liters of water is needed.", + "In 410 A.D. Alaric the Visigoth demanded that Rome give him three thousand pounds of pepper as ransom", + "Actress Jamie Lee Curtis invented a special diaper for babies that has a pocket", + "Honeybees use the sun as a compass which helps them navigate", + "An average driver spends approximately 2 hours and 14 minutes kissing in their car in a lifetime", + "In gangster slang, a boxing match that is fixed is called a 'barney.'", + "In 75% of American households, women manage the money and pay the bills", + "In a year, an average person uses the toilet 2500 times a year", + "A honey bee has four wings", + "The Bank of America was originally called the Bank of Italy until the founder, Amedeo Giannini, changed the name in 1930", + "Other than London, Liverpool is the most filmed British city, and was used to film more than 140 films in 2002", + "The body of the average baby is 75% water", + "A Chinese Scientist discovered that the Earth is round during the Han Dynasty by measuring the sun and moon's path in the sky. He recorded this fact down in the imperial records but went unnoticed until it was unearthed recently but Chinese archaeologists.", + "Each year, Americans throw away 25 trillion Styrofoam cups", + "There are more than 2,000 different varieties of cheese in the world", + "On average, Guinness sells 7 million glasses of beer a day", + "An artist from Chicago named Dwight Kalb created a statue of Madonna made out of 180 pounds of ham", + "Reports from owners of cats and dogs indicate that 21% of dogs and 7% of cats snore", + "The scarlet tanager, a songbird native to Illinois, can eat as many as 2,100 gypsy-moth caterpillars in one hour", + "To make one raindrop of water, it takes approximately a million cloud droplets", + "At 120 miles per hour, a Formula One car generates so much downforce that it can drive upside down on the roof of a tunnel", + "The smallest bone in the human body is the stapes bone which is located in the ear", + "India used to be the richest country in the world until the British invasion in the early 17th Century", + "The first owner of the Marlboro Company died of lung cancer", + "Some African tribes refer to themselves as 'motherhoods' instead of families", + "Between 1902 and 1907, the same tiger killed 434 people in India", + "The word vaccine comes from the Latin word 'vacca,' which means cow. This name was chosen beacause the first vaccination was derived from cowpox which was given to a boy", + "James Bond is also known as Mr. Kiss-Kiss-Bang-Bang", + "A snail can crawl across a razor blade without getting injured. This is possible because they excrete a slime that protects them", + "Behram, an Indian thug, holds the record for most murders by a single individual. He strangled 931 people between 1790-1840 with a piece of yellow and white cloth, called a ruhmal. The most murders by a woman are 612, by Countess Erzsebet Bathory of Hungary", + "Approximately 97.35618329% of all statistics are made up", + "The largest spider ever was the Megarachne which had a diameter of 50 cm. The fossil was found in Argentina", + "In Russia, when flowers are given for a romantic occasions, flowers are given in odds numbers as even number of flowers is given at funerals only", + "Next to man, the porpoise is the most intelligent creature on earth", + "The hippopotamus has the capability to remain underwater for as long as twenty-five minutes", + "The Australian box-jellyfish has eight eyes", + "In 1916, an elephant was tried and hung for murder in Erwin, Tennessee", + "A sheep, a duck and a rooster were the first passengers in a hot air balloon", + "The liquid inside young coconuts can be used as substitute for blood plasma", + "In the UK, one third of accidental deaths that happen occur in the home", + "After the U.S Civil War, about 33%-50% of all U.S. paper currency in circulation was counterfeit", + "Michael Jordan makes more money from Nike annually than all of the Nike factory workers in Malaysia combined", + "The Hawaiian alphabet only has 12 letters", + "Tycho Brahe, a 16th century astronomer, lost his nose in a duel with one of his students over a mathematical computation. He wore a silver replacement nose for the rest of his life", + "Termites do more damage in the U.S. ever year than all the fires, storms and earthquakes combined. They do an average of $750 million in damage annually", + "Burger King restaurants serve over 400 million ounces of orange juice annually", + "Each year the Pentagon estimates their computer network is hacked about 250,000 times annually", + "The first president to ride in an airplane was Franklin Roosevelt", + "The airplane Buddy Holly died in was the 'American Pie.' (Thus the name of the Don McLean song.)", + "A tree in metropolitan area will survive for approximately eight years", + "The only flying saucer launch pad in the world is located in St. Paul, Alberta, Canada", + "The sex of a baby crocodile is determined by the temperature in the nest and how deeply the eggs are buried", + "Polar bears are left handed", + "Food can only be tasted if it is mixed with saliva", + "Walter Hunt patented the safety pin in 1849. He later sold the patent rights for only $400.", + "The coliseum in Rome was used regularly for about 400 years", + "Children laugh about 400 times a day, while adults laugh on average only 15 times a day", + "The first formal rules for playing the sport of baseball required the winning team to score 21 runs", + "The University of Plymouth was the first university to offer a degree in surfing", + "Retail sales for soft drinks in the United States in 2001 were more than sixty billion dollars", + "Hens will produce larger eggs as they grow older", + "In Quebec, Canada, an old law states that margarine must be a different colour than butter", + "In the United States, about 33% of land is covered by forests", + "Lemons contain more sugar than strawberries", + "Shridhar Chillal from India is known to have the record for the longest fingernails in the world, which were each at least three feet long", + "In 1905, Chapman and Skinner in San Francisco invented the first portable electric vacuum.", + "Minimum wage was 0.25 per hour when it was first enacted in 1938", + "The conjunctiva is a membrane that covers the human eye", + "In 1785, the city of Paris removed bones from cemeteries to ease the overflow of dead people. They took these bones and stacked them in tunnels now known as the Catacombs. You can visit these tunnel attractions and work your way along long corridors, which are stacked with skulls and bones", + "It is estimated that over fifty-four million people died in World War II, which was the bloodiest war in history", + "Arabic numerals were not invented by Arabs, but were invented in India by the Hindus", + "Each year in America there are about 300,000 deaths that can be attributed to obesity", + "The first ever 'World Summit on Toilets' was held in Singapore in November 2001", + "Humphrey Bogart was related to Princess Diana", + "Researchers have developed odourless socks. The sock fabric is made by attaching molecules that contain chlorine called halamines to textile fibers", + "Alexandre Gustave Eiffel, the man who designed the Eiffel Tower, also designed the inner structure of the Statue of Liberty in New York Harbour", + "In the 1985 Boise, Idaho mayoral election, there were four write-in votes for Mr. Potato Head", + "MS-DOS was originally calle QDOS and was bought of the author by Microsoft for a small fee. The rest is history", + "Marilyn Monroe had six toes", + "The Roman emperor Commodus was at one time going to change the name of Rome to Colonia Commodiana", + "The state of Alaska has almost twice as many caribou as people", + "Another way to say 'every 9 years' is Novennial", + "In the spring of 1975, a baby in Detroit fell 14 stories and landed on Joseph Figlock, who was walking below. A few years later it happened again. Figlock and both babies survived", + "Close to fifty percent of Internet shoppers spend over five hours a week online", + "Los Angeles is the most polluted city in the USA", + "For people that are lactose intolerant, chocolate aids in helping milk digest easier", + "Using recycled aluminum cans and making news cans out of them saves 75% energy compared to making it from new material.", + "In a year, Americans eat approximately 20 billion pickles", + "Althaiophobia is the fear of marshmallows", + "There are are roughly 100 million single adults living in the USA", + "In the year 2000, there were approximately 11,000 injuries that were treated in a hospital in the U.S. that resulted from fireworks", + "Brazil is the largest producers of oranges in the world", + "4% of an apples is made up of minerals and vitamins, and over 80% is made up of water", + "From all the oxygen that a human breathes, twenty percent goes to the brain", + "In 1902, the coat hanger was invented Albert Parkhouse who was frustrated at the lack of hooks available to hang up his coat at work. His company thought it was a good idea and patented the invention and unfortunately, Parkhouse never received any money for his idea", + "If a statue in the park of a person on a horse has both front legs in the air, the person died in battle; if the horse has one front leg in the air, the person died as a result of wounds received in battle; if the horse has all four legs on the ground, the person died of natural causes", + "The longest game of Monopoly played underwater is 45 days", + "In WWII, when allied armies reached the Rhine River the first thing men did was pee in it. This was pretty universal from the lowest private to Winston Churchill (who made a big show of it). Gen. Patton had himself photographed in the act", + "Peaches were once known as Persian apples", + "Dustin Phillips of the U.S. has the record for ketchup drinking. He drank a 14-ounce bottle of tomato ketchup through a ? inch straw in 33 seconds on September 23, 1999", + "Ninety-five percent of tropical fish sold in North America originate from Florida", + "The blackberry bush is also called the 'bramble.'", + "The city of Tokyo was originally called Edo", + "The sun shrinks five feet every hour", + "There have been 191 coops in Bolivia since it became a sovereign country in 1825", + "During World War II, Kit Kat was unavailable due to milk shortages, so the chocolate bar was made without milk", + "The first TV commercial advertisement was by the Bulova Watch company on July 1, 1941. The watch company paid $9.00 for an announcement that was 10 seconds long", + "Rubber bands last longer when refrigerated", + "A common custom in Spain is to eat one grape for each of the last 12 seconds of every year for good luck", + "Bill Gates began programming computers at age 13", + "Tobacco contains over 50 chemicals that can cause cancer", + "Sailors once thought that wearing a gold earring would improve their eyesight", + "The smallest bird in the world is the bee hummingbird. The bird is 2.24 inches long", + "A species of earthworm, 'Megascolides australis,' in Australia can grow up to fifteen feet in length", + "Hannibal, who was a soldier, had only one eye after getting a disease while attacking Rome", + "The full name of the Titanic ship is R.M.S. Titanic, which stands for Royal Mail Steamship", + "Electronic companies sell five times as many big-screen TVs during Super Bowl Week", + "Everyday approximately 35 meters of hair fibre is produced on the scalp of an adult", + "A U.S. company came out with a toilet night-light that sends out a green warning beacon when the seat is up", + "A little under one quarter of the people in the world are vegetarians", + "There are approximately 1300 species of scorpion but only 25 of them are deadly", + "An egg shell can have up to 17,000 tiny pores on its surface", + "A lifespan of an eyelash is approximately 150 days", + "66% of home based businesses are owned by women", + "There are approximately 60 muscles in the face", + "In 1924, Kleenex tissues were originally designed as a cold cream remover", + "A women from Berlin Germany has had 3,110 gallstones taken out of her gall bladder", + "Every second, 8000 Coca-Cola Company products are consumed in the world", + "If all the strawberries produced in California annually were put side by side, they would wrap around the Earth fifteen times", + "Devon, England has about 33,000 miles of hedgerows, more hedgerows than any other country", + "'Bookkeeper' is the only word in English language with three consecutive double letters", + "An average home creates more pollution than does the average car", + "Four out of five brides in the U.S. have a job", + "75-90% of primary physician visits are due to stress", + "The reason why the Canadian Arctic is called the 'Land of the Midnight Sun' is because during the summer many communities have light 24 hours of the day. Many people have to cover their windows with tin foil to keep the light out when they sleep", + "On a Canadian two dollar bill, the flag flying over the Parliament building is an American flag", + "Teenage cosmetic surgeries nearly doubled in the USA between 1996 and 1998", + "A rocket-like device can be traced back to Ancient Greece when a flying steam-powered pigeon was built out of wood.", + "The Cincinnati Reds are the oldest professional baseball team", + "In 1871, horse cars were introduced. It was simply a car that was pulled over a track by a horse.", + "High Priests in ancient Egypt were the only ones who were allowed to wear garments made from cotton", + "Kellogg?s started selling their most famous product, Corn Flakes, in 1906", + "Chocolate was used as medicine during the 18th century. It was believed that chocolate could cure a stomach ache", + "Halifax, Nova Scotia, Canada has the largest bar per capita than anywhere else in the world", + "The Eiffel Tower was the tallest structure in the world before the construction of the Empire State Building in 1930", + "The first American celebration of St. Patricks Day was at Boston in 1737", + "The name of the popular sports drink Gatorade was named for the University of Florida Gators where it was developed", + "The largest employer in Central Florida is Walt Disney World. There are approximately 50,000 people working there", + "In his youth, United States president George W. Bush used to play for the Midland (Texas) Central Little League. He played the position of the catcher", + "The number of births that occur in India each year is higher than the entire population of Australia", + "Bobby Carpenter was the first American player to score 50 goals in a season", + "The word, tattoo originated from the Tahitain word 'tattau' which means 'to mark.'", + "There was no punctuation until the 15th century", + "All babies are colour blind when they are born", + "Rocky Mountain spotted fever is a disease caused by ticks", + "There are approximately 9,000 taste buds on the tongue", + "A fetus starts to develop fingerprints at the age of eight weeks", + "The reason why your nose gets runny when you are crying is because the tears from the eyes drain into the nose", + "Gorillas can catch human colds and other illnesses", + "On October 15, 1794, the first silver dollar coins were released to be circulated to the public", + "In one day, the Tootsie Roll Industry makes over 16 million lollipops", + "In many of the milk ads that are shown, a mix of thinner and white paint is used instead of milk", + "Baskin Robbins once made ketchup ice cream. This was the only vegetable flavoured ice cream produced. However, they discontinued it since they thought it would not sell well", + "In an year, an average person makes 1,140 phone calls", + "The strike note of the Liberty Bell in Philadelphia, Pennsylvania is e-flat", + "A Connecticut Toy maker, Herobuilders, sells action figures of President George W. Bush, Islamic militant Osama bin Laden, New York Mayor Rudolph Giuliani and British Prime Minister Tony Blair, which are all major figures tied to the September 11, 2001 WTC attacks", + "Majority of brides plan their wedding for approximately 7 to 12 months", + "The word assassination was invented by William Shakespeare", + "Benjamin Franklin invented the rocking chair.", + "Persia changed its name to Iran in 1935", + "In the wild, the poinsettia flower can reach a height of 12 feet, and have leaves that are eight inches across.", + "Construction workers hard hats were first invented and used in the building of the Hoover Dam in 1933.", + "A study indicates that smokers are likely to die on average six and a half years earlier than non-smokers", + "On a Canadian two dollar bill, the flag flying over the Parliament building is an American flag", + "TYPEWRITER, is one of the longest words that can be made using the letters only one row of the keyboard", + "Bill Gates donated close to $100 million to fight AIDS in India. As a percent of his total wealth, this would be comparable to him donating ten cents if he only had $60", + "In the U.S. there is, on average, three sex change operations per day", + "The American Airlines Sports Center, in Dallas, has most toilets per capita than any other sports and entertainment venue in the USA", + "In 1999, a three headed turtle was discovered by Lin Chi-Fa in his pond in Southern Taiwan", + "Approximately one out of every 55 women from Canada give birth in their car on the way to the hospital or clinic", + "The first United States president to visit China was Richard Nixon", + "The most popular show amongst baby boomers is Star Trek.", + "The first jet engine was invented by Frank Whittleof of England in 1930.", + "In a day, an elephant can drink 80 gallons of water", + "The term 'the whole 9 yards' came from WWII fighter pilots in thePacific. When arming their airplanes on the ground, the .50 calibermachine gunammo belts measured exactly 27 feet, before being loaded into thefuselage. If the pilots fired all their ammo at a target, it got 'the whole 9yards.'", + "In 1948 and 1950 the oldest ears of popping corn were discovered. They were located in the Bat Cave of west central New Mexico. They ranged in size from smaller than a penny to approximately two inches, and were about 4,000 years old", + "The only real person to be a Pez head was Betsy Ross", + "It cost the soft drink industry $100 million a year for thefts committed involving vending machines", + "The only two days of the year in which there are no professional sports games (MLB, NBA, NHL, or NFL) are the day before and the day after the Major League All-Star Game", + "Watermelon is considered a good gift to give a host in Japan and China", + "The planet Venus spins opposite to the other planets in the solar system", + "During a typical human life span, the human heart will beat approximately 2.5 billion times", + "Frog-eating bats identify edible frogs from poisonous ones by listening to the mating calls of male frogs. Frogs counter this by hiding and using short, difficult to locate calls", + "Dieting can cause bad breath since less saliva is produced which leads to dry mouth", + "At lift off, US space shuttles weight about 4.5 million pounds.", + "Average life span of a major league baseball: 7 pitches", + "When an orange is shown in any of the 'Godfather' movies, this means that someone is about to die or a close call is to occur.", + "Doctors in Canada use an adhesive similar to Krazy Glue instead of stitches, lowering the possibility of bacterial infection and minimizing scarring", + "Soil that is heated by geysers are now making it possible to produce bananas in Iceland", + "Chameleon is derived from the Greek, meaning 'little lion.'", + "The name Aspirin was invented from 'A' in acetyl chloride. The 'spir' comes from spiraea ulmaria which is the plant that they got the salicylic acid from, and the 'in' was used because back then it was popular to end the name of medicines with 'in.'", + "About 30% of Canadians rely on getting their water from the ground for their domestic use", + "The eye of a human can distinguish 500 shades of the gray", + "There is a town in Norway called 'Hell'", + "Most heart attacks occur between the hours of 8 and 9 am", + "No one in Greece has memorized all 158 verses", + "A goldfish has a memory span of three seconds", + "May babies are on average 200 grams heavier than babies born in other months", + "Gloucestershire airport in England used to blast Tina Turner songs on its runways to scare birds away", + "The first German car to be built solely outside of Germany is the BMW Z3.", + "In China, fish is eaten more than three times what it is in the United States", + "The United Parcel Service shipped the killer whale Keiko (star of Disney movie 'Free Willy') from Mexico City to Newport, Oregon in 1998", + "Hippos can live up to 40 years in the wild", + "The windiest place in the world is Mount Washington, New Hampshire, USA. The highest wind was on April 12, 1934 when it reached 231 mph.", + "The first restaurant to open in Hollywood was the Musso & Frank Grill in 1919", + "Corn Flakes were invented after Will Keith Kellogg and his brother Dr. John Harvey Kellogg set about developing a nutritious cereal for the patients of a health resort in 1890", + "The three wealthiest families in the world have more assets than the combined wealth of the forty-eight poorest nations", + "Huge Moore, the inventor of Dixie cups got the idea for the name from a neighboring factory, the Dixie Doll Company", + "The turkey was once nominated to be the official bird of the United States", + "In 1958 the United States sent three mice into space named, Mia, Laska and Benji", + "On average, Americans spend five times more of their time in their cars than they do on vacation", + "Natural gas does not have any odor. In order to detect a gas leak, some gas companies add a chemical that smells similar like rotten eggs.", + "Spiders have claws at the ends of their legs", + "Warner Brothers Corset Company created the bra cup sizing system, which is now used universally used by manufacturers", + "In the U.S. 7 out of 10 homes use candles", + "Daytime dramas are called Soap Operas because they were originally used to advertise soap powder. In America in the early days of TV, advertisers would write stories around the use of their soap powder", + "It takes approximately 12 years for Jupiter to orbit the sun", + "98% of houses in the United States have at least one television set", + "Reserves from the Irish army were used as extras in the movie 'Braveheart.'", + "80% of people that are on weight loss programs exercise on average three times a week", + "The first Valentine candy box was invented by Richard Cadbury in the 1800's.", + "An olive tree can live up to 1500 years", + "96% of people put the peanut butter on first when making a peanut butter and jelly sandwich", + "Scientists have actually performed brain surgery on cockroaches", + "Everyday, more money is printed for Monopoly than the U.S. Treasury", + "The Atlantic Ocean is saltier than the Pacific Ocean", + "By donating just one pint of blood, four lives can be saved", + "The biggest hamburger that was served was 8,266 pounds. It was made at the Burger Fest in Seymour, Wisconsin", + "The electric chair was invented by a dentist", + "Pilgrims did not eat potatoes for Thanksgiving as they thought they were poisonous", + "On average, a hen lays 300 eggs per year", + "BluBlocker sunglasses were developed with lenses that were used in the NASA space program for American astronauts", + "Pearls melt in vinegar", + "There are an estimated 2,500 collisions between birds and planes each year in the US", + "Richard Millhouse Nixon was the first US president whose name contains all the letters from the word 'criminal.' The second? William Jefferson Clinton", + "A chicken is 75% water", + "If you keep a Goldfish in the dark room, it will eventually turn white", + "During his lifetime, artist Vincent Van Gogh only sold one of his paintings (The Red Vineyard)", + "Coffee has about five times the amount of caffeine as a can of Coke", + "It takes the Hubble telescope about 97 minutes to complete an orbit of the Earth. On average, the Hubble uses the equivilent amount of energy as 30 household lightbulbs to complete an orbit.", + "Approximately 850 peanuts make a 18 oz jar of peanut butter", + "The age of a saguaro cactus is calculated by its height", + "The most expensive animated movie is 'Prince of Egypt', which cost $70 million to make", + "Koalas sleep up to 19 hours a day", + "The oldest roller coaster in the world is the Leap-The-Dips roller coaster located in Lakemont Park in Pennsylvania. The roller coaster was built in 1902", + "There are three golf balls sitting on the moon", + "You are more likely to be killed by a champagne cork than by a poisonous spider", + "The longest recorded flight of a chicken is thirteen seconds.", + "When Heinz ketchup leaves the bottle, it travels at a rate of 25 milesper year.", + "The story of Mulan had been told in China for almost 1,500 years before Disney decided to make it into an animated movie", + "The name Santa Claus came from Saint Nicholas who was a bishop in the town of Myra, and was known to be very nice to children", + "Microsoft made $16,005 in revenue in its first year of operation", + "At the 1960 Winter Olympic Games, Walt Disney was head of the committee that organized the opening day ceremonies", + "No other animal gives us more by-products than the hog. These by-products include pig suede, buttons, glass, paint brushes, crayons, chalk, and insulation to name a few", + "The ruby red slippers in the movie 'The Wizard of Oz' were sold off at an auction for $660,000", + "40 percent of the almonds in the world are used by manufacturers of chocolate", + "It takes eight and a half minutes for light to get from the sun to earth.", + "Olives, which grow on trees, were first cultivated 5,000 years ago in Syria", + "A person infected with the SARS virus, has a 95-98% chance of recovery", + "When explorers first arrived in Venezuela, they were reminded of Venice. They named the country 'Little Venice', which translated into Spanish is Venezuela", + "Mr. Rogers is an ordained minister", + "Between 12%-15% of the population is left-handed", + "When a polar bear cub is born, it can not see or hear. It takes approximately a month for the cub to start to see and hear", + "Girls have more tastebud than boys", + "Dandelion root can be roasted and ground as a coffee substitute", + "Platypuses mate in the water", + "Julie Nixon, daughter of Richard Nixon married David Eisenhower, grandson of Dwight Eisenhower", + "The microwave oven was invented after a researcher walked by a radar tube and a chocolate bar melted in his pocket.", + "The candlefish is so oily that it was once burned for fuel", + "A flea can jump 150 times its size. That is the same as a person able to jump up 1,000 feet in the air", + "Morihei Ueshiba, founder of Aikido, once pinned an opponent using only a single finger", + "The game of squash originated in the United Kingdom. It came about after a few boys, who were waiting for their turn to play racquets, knocked a ball around in a confined area adjoining the racquets court", + "Coca-Cola was originally green", + "When opossums are playing 'possum, they are not 'playing.' Theyactually pass out from sheer terror.", + "You burn more calories sleeping than you do watching television", + "Lady Bugs really are not bugs. They are actually beetles and their correct name is The Ladybird Beetle", + "The name Reebok was named after the African Gazelle", + "The average human scalp has 100,000 hairs", + "New Jersey has a spoon museum that has over 5,400 spoons from across the world", + "Hydrogen is the most common atom in the universe", + "There is a town named Dildo in the province of Newfoundland, Canada", + "For every human in the world there are one million ants", + "The word 'nerd' was first coined by Dr. Suess in the book 'If I Ran to the Zoo.'", + "The postage rate for a letter in 1693 was determined through how much light could pass through the letter. The postage rate would be more expensive if less light went through, and this process was called candling", + "The Sears Tower located in Chicago, Illinois is made up 76,000 tons of steel", + "Mel Blanc (the voice of Bugs Bunny) was allergic to carrots", + "There are more recreational golfers per capita in Canada than any other country in the world", + "Hitler and Napolean both had only one testical", + "The largest bill U.S. bill made is for $100,000", + "Playwright Shakespeare was only 18 years old when he married Ann Hathaway, who was 26 years old at the time", + "Wherever a person is standing in the state of Michigan in the United States, they are within 85 miles of one of the Great Lakes", + "It only takes a male horse 14 seconds to copulate", + "The driest place on earth is Calama, in the Atacama Desert in Chile", + "After being picked an orange cannot ripen", + "The stomach of an adult can hold 1.5 liters of material", + "On average, a whole chicken from the grocery store weighs 3 pounds 12 ounces", + "A galactic year is 250 million Earth-years. This is the time it takes for our solar system to make one revolution around the Milky Way Galaxy.", + "Singer Paula Abdul used to be a cheerleader for the Los Angeles Lakers", + "Fossilized bird droppings are one of the chief exports of Nauru, an island nation in the Western Pacific", + "Water expands 9% when it is frozen", + "The first toilet tank ever seen on television was on Leave it to Beaver", + "The origin of apples traces back to the Middle East over 4,000 years ago", + "The colours yellow, red, and orange are used in fast food restaraunts because those are the colours that stimulate hunger", + "99% of pumpkins that are sold are sold for decoration", + "On Sunday, December 7, 1941 at 7:55 AM, the attack on Pearl Harbor commenced", + "If all the cars from the U.S. were taken and lined up from bumper to bumper, there would be enough cars to go to the moon from earth and back.", + "The elephant is the national animal of Thailand", + "A Canadian Tour company offers a two-day course in igloo building", + "Dairy cows can produce 20 to 35 gallons of saliva a day", + "A duck's quack doesn't echo, and no one knows why.", + "When the Pez mint dispenser was first introduced it was meant to replace the activity of smoking", + "Gardening is said to be one of the best exercises for maintaining healthy bones", + "The speed of sound must be exceeded to produce a sonic boom.", + "A B-25 bomber airplane crashed into the 79th floor of the Empire State Building on July 28, 1945", + "Over 600,000 people died as a result of the Spanish influenza epidemic", + "From all the vegetables, beets contain the most sugar", + "Smelling bananas can help a person lose weight", + "In Miami, Florida, roosting vultures have taken to snatching poodles from rooftop patios", + "Only female mosquitoes bite humans. Male mosquitoes live on natural liquids from plants and other resources", + "An estimated 690 million people live in Africa", + "The difference between horns and antlers is that horns never stop growing and antlers shed and grow every year", + "African Baobab tree's circumference can reach 180 feet. If the trunk is hollow, 20 people would be able to fit inside of it.", + "On average, the Pentagon uses 666 rolls of toilet paper in one day", + "The USS Abraham Lincoln has five gymnasiums on the ship and a basketball league with 22 teams", + "Found in Argentina, the ornate horned frog can eat an entire mouse with one swallow", + "Great Britain was the first country to issue postage stamps in 1840", + "During World War II, there was not enough sugar in the U.S. to make candy as it was sent to the troops overseas. At this time, popcorn was consumed three times more than its usual amount", + "The most famous movie theatre is the 'Chinese Theatre' located in Los Angeles, USA", + "In the U.S., the sport that causes the most injuries among the age group of 15-24 is basketball", + "Oscar Wilde and his friends came up the with the word 'dude.' It came from the words 'duds' and 'attitude.'", + "By weight, the sun is 70% hydrogen, 28% helium, 1.5% carbon, nitrogen, and oxygen, and 0.5% all other elements.", + "Melba toast is named after an Australian opera singer Dame Nellie Melba", + "Astronomers once believed a planet named Vulcan existed between Mercury and the Sun", + "Talc was used by cavemen 15,000 years ago as an ingredient added to make paint", + "Ants do not sleep", + "The only married couple to fly together in space were Jan Davis and Mark Lee, who flew aboard the Endeavor space shuttle from Sept 12-20, 1992", + "Valentina Tereshkova was the first woman to enter space. She spent three days in space and completed forty-eight orbits of Earth", + "More than 3000 years ago children played with circular hoops made with grape vines. This toy was swung around the waist. Years later this toy was made by company called Wham-O and the Hula-Hoop was invented in 1958", + "The first subway system in America was built in Boston, Massachusetts in 1897.", + "Owls swallow their prey whole because they have no teeth. After approximately 12 hours they cough up the feathers, bones, and fur in a shape of a football pellet", + "Sharks have survived on earth for about 400 million years", + "Ukrainian monk, Dionysius Exiguus, created the modern day Christian calendar", + "The revenue that is generated from gambling is more than the revenue that comes from movies, cruise ships, recorded music, theme parks, and spectator sports combined", + "Every 30 seconds a house fire doubles in size", + "An alligator has about 80 teeth in its mouth at one time. An alligator can go through 3,000 teeth in a lifetime", + "A starfish can turn its stomach inside out", + "The story of Rudolph the Red-Nosed Reindeer was written in 1939 for a store promotion by an advertising employee of the department store Montgomery Ward", + "Actor Charlie Chaplin made 81 movies over a career that spanned 50 years", + "The music group Simply Red got its name from band member Mick Hucknall, who has red hair", + "Every three minutes a woman is diagnosed with breast cancer", + "The lifespan of a rhinoceros is generally 50 years", + "41% of women apply body or hand moisturizer a minimum three times a day", + "The largest ice cream sundae was made with 4,667 gallons of ice cream, was 12 feet high and had 7000 pounds of toppings on it. This was made in Anaheim, California in 1985", + "The name of the award given to honor the best sites on the Internet is called 'The Webby Award.'", + "The United States Mint once considered producing donut-shaped coins.", + "A Hungarian named Ladislo Biro invented the first ballpoint pen in 1938.", + "Adolf Hitler loved chocolate cake", + "Many years ago, a fish was caught that was 33 inches long and seemed to be heavier than it should. When they cut the fish, fishermen found a full of bottle of ale inside it", + "George Washington grew hemp in his garden", + "In New York City, approximately 1,600 people are bitten by other humans annually", + "In 1980, Saddam Hussein received a key to the city of Detroit", + "The song 'Strawberry Fields Forever' sung by the Beatles refers to an orphanage located in Liverpool", + "False Bay, on the southern tip of Africa and close to Cape Town, South Africa, is a breeding ground for great white sharks, which feed off the thousands of seals in the bay. However, it is the only area in the known world in which these sharks are known to breach - they attack the seals by coming up vertically, often leaping clear of the water with their prey in their mouths", + "In 1952, the first TV toy commercial aired. It was for Mr. Potato Head", + "In the United States, approximately seven billion pounds of chocolate and candy are manufactured each year", + "Mules have one horse and one donkey for a parent", + "On average, 350 squirts are needed from milking a cow to make a gallon of milk", + "On average, a baby in the United States will eat fifteen pounds of cereal in their first year of life", + "The name Hasbro was invented by the name of the founders: HASsenfeld BROthers", + "Percentage of Africa that is wilderness: 28%. Percentage of North America that is wilderness: 38%", + "Due to sugar shortages to make candy during World War II, movie theatre owners turned to popcorn, which is now the best selling snack at movie theatres today", + "A group of whales is called a pod or gam", + "Even though a polar bears fur looks white it is actually colourless and is made with hollow tubes. The reason the bear looks white is because the rough inner surface of the tubes make light scatter and reflect at many different angles which gives the white appearance", + "There is a type of coffin made that can be used as a wine rack or picnic table before its final use", + "One gallon of used motor oil can ruin approximately one million gallons of fresh water", + "After the Eiffel Tower was built, one person was killed during the installation of the lifts. No one was killed during the actual construction of the tower", + "Approximately 87% of dog owners say that when they watch T.V. their dog curls up beside them or at their feet", + "Bernd Eilts, a German artist, turns dried cow manure into wall clocks and small sculptures. He is now expanding his business to include cow dung wrist watches", + "People who meet their calcium need reduce their risk of developing kidney stones", + "In every episode of Seinfeld there is a Superman somewhere.", + "The human heart weighs less than a pound", + "The first Life Saver flavour, which was peppermint, was invented in 1912 and it was called Pep-O-Mint", + "The average life span of a peasant during the medieval ages was 25 years", + "Before 1859, baseball umpires were seated in padded chairs behind home plate", + "On February 10, 1964 the first self-adhesive stamps were issued", + "The ocean sunfish can produce thirty million eggs at once", + "The highest point in France is Mont Blanc, located in the Alps.", + "The American Automobile association was formed in 1905 for the sole purpose of warning motorists of police speed traps", + "Oak trees do not produce acorns until they are fifty years of age or older", + "In England, a cigarette is referred to as a fag", + "If you have three quarters, four dimes, and four pennies, you have $1.19. You also have the largest amount of money in coins without being able to make change for a dollar", + "In 1980, there was only one country in the world with no telephones - Bhutan", + "Moscow was founded in 1147 by Yury Dolgoruky", + "In 1976, fourteen banks merged to form a bank credit card called 'Mastercharge.' This was later renamed to what is now know as 'Mastercard.'", + "American Airlines saved $40,000 in 1987 by eliminating 1 olive from each salad served in first-class", + "There are over 500 different types of bananas", + "In 1848, the first American pasta factory opened in Brooklyn, New York. The name of the man that opened it was Antoine Zerega", + "Albert Einstein was cremated and his ashes were spread over a river located in New Jersey", + "Your right lung takes in more air than your left one does", + "Men are four times more likely to be struck by lightning than women", + "The longest acceptance speech in the history of the Oscars was by Greer Garson in 1942. She received an Oscar for Best Actress for the movie Mrs. Miniver, and her speech was five minutes and 30 seconds long", + "Only one person in two billion will live to be 116 or older", + "Octopi change colours when they become frightened. Normally they are a brownish colour, but can change to green or blue when fear sets in", + "A 'gelotologist' is a person who studies laughter", + "A 13-year-old boy in India produced winged beetles in his urine after hatching the eggs in his body", + "Fat is important for the development of children and normal growth", + "The state of California has more 7-Eleven stores than any other state. There are approximately 1,200 stores", + "The deepest underwater penguin dive is 1,772 feet by an Emperor Penguin", + "38% of Americans eat breakfast everyday", + "There are over 100 styles of BluBlocker sunglasses available on the market", + "When a predator is chasing an impala, a type of antelope, it runs in a zig zag formation jumping as high as three metres", + "David Rice Atchinson was President of the United States for exactly one day. This happened due to a glitch in American law at the time. new", + "To make one glass of orange juice, 50 glasses of water are needed to grow enough oranges to make the juice", + "Penguins can jump as high as 6 feet in the air", + "Tomatina is the legendary Spanish tomato-throwing festival held in Bunol, Spain", + "Pigs have no sweat glands, which is why they stay in water or mud to keep cool", + "There is no element on Mendeleev's (the current) periodic table of elements abbreviated, either partially, or fully, with the letter J.", + "One of the Bond girls in the James Bond movie, 'For Your Eyes Only,' used to be a man", + "The microwave was invented after a researcher walked by a radar tube and a chocolate bar melted in his pocket", + "In February 1878, the first telephone book was published in New Haven, Connecticut. The book was one page long and had fifty names in it.", + "Only President to win a Pulitzer: John F. Kennedy for 'Profiles in Courage'", + "Roman emperors ate flamingo tongues which were considered a delicacy. Also parrotfish livers, and pheasant brains were feasted on", + "In 2002, the most popular car color in North America was silver.", + "Forty percent of Americans iron their clothes while wearing their underwear or being completely naked", + "About 85% or product warning labels on household products are inadequate", + "The largest volcano known is on Mars: Olympus Mons, 370 miles wide and 79,000 feet high, is almost three times higher than Mount Everest", + "Mexican jumping beans jump because of moth larvae inside the bean", + "Women blink nearly twice as much as men", + "Dentists have recommended that a toothbrush be kept at least 6 feet away from a toilet to avoid airborne particles resulting from the flush", + "There are 691 drinking fountains in the Pentagon", + "Author Robert May considered the names of Reginald and Rollo before he settled on 'Rudolph, the red-nosed reindeer.'", + "A study concludes that kids who snore do poorly in school", + "When snakes are born with two heads, they fight each other for food", + "Chocolate maker Cadbury uses more than sixty thousand tonnes of cocoa each year, in the United Kingdom alone", + "There are more pigs than humans in Denmark", + "A human eyeball weighs an ounce", + "The 'Mexican Hat Dance' is the official dance of Mexico", + "The first open heart surgery was performed by Dr. Daniel Hall Williams in 1893", + "The DNA of humans is closer to a rat than a cat", + "Peladophobia refers to the fear of bald people", + "One Neptune year lasts 165 Earth years", + "In the U.S., 75% of the pencils sold are painted yellow", + "A person that is struck by lightning has a greater chance of developing motor neurone disease", + "In Canada, the $1 and $2 come in the form of coins. The $1 is nicknamed a 'loonie' because it contains a loon on it and the $2 is nicknamed the 'twonie' because it is the equivalent of two 'loonies.'", + "Singer Michael Jackson owns the rights to the South Carolina State anthem", + "On average a business document is copied 19 times", + "Pigeons can see ultraviolet lights", + "The list of ingredients that make up lipstick include fish scales", + "If a statue in the park of a person on a horse has both front legs inthe air, the person died in battle; if the horse has one front leg inthe air,the person died as a result of wounds received in battle; if the horsehasall four legs on the ground, the person died of natural causes.", + "There are two credit cards for every person in the United States", + "An average person consumes the equivalent of 26 gallons of milk a year, including almost 28 pounds of cheese", + "65% of the candy that is produced in an year is consumed by American adults 18 years and older", + "The little circles of paper that are cut out after a paper has been punched by a hole puncher are called 'chad.'", + "Teflon is the most slipperiest substance in the world", + "The lifespan of a firefly is about seven days. During these days, they are busy trying to find a mate", + "A crocodile can run up to a speed of 11 miles per hour", + "Fried spiders taste like nuts", + "In one day, adult lungs move about 10,000 litres of air", + "Cows do not have any upper front teeth. Instead they have a thick pad on the top jaw", + "On average, Americans move to a new place eleven times in their lifetime", + "Energy is being wasted if a toaster is left plugged in after use", + "The Golden Gate Bridge was first opened in 1937", + "No word in the English language rhymes with month, orange,silver or purple", + "In an episode of the 1962 cartoon The Jestons, Jane Jetson is talking through a video phone. The phone number of The Jetsons was VENUS-1234", + "Trees that are near street lights do not shed their leaves as fast as a tree that is in the country", + "No president of the United States was an only child", + "7-Eleven is the largest retail chain in the world. Click Here For More Details", + "A Singapore singing group by the name of 'The Oriental Singers,' sang non-stop for 74 hours and five minutes", + "Every 25 miles a car produces one pound of pollution.", + "There was a molasses flood in Boston on January 15, 1919 that killed 21 people and injured 150 people", + "There are over one billion people that are actively involved in rice growth", + "On April 4, 1974, John Massis of Belgium pulled two New York Long Island railroad passenger cars totaling 80 tons with a thick rope, with a small bit attached, using only his teeth", + "The oldest major soft drink in America is Dr. Pepper, which originated in Waco, Texas in 1885", + "Hockey pucks were originally made from frozen cow dung", + "In L.A., U.S.A., a man may legally beat his wife with a leather strap, as long as it is less than 2 inches wide", + "The Arctic Tern, which is a small bird, can fly a round trip from the Arctic to the Antarctic and back. This can be as long as twenty thousand miles per year. This is the longest migration for a bird", + "The word 'America' comes from the European explorer 'Amerigo Vespucci", + "Over 100 million birds die annually by crashing into glass windows in the United States", + "Thomas Edison, the inventor of the light bulb was afraid of the dark", + "In 1933, Mickey Mouse is believed to have received 800,000 fan letters", + "Male rabbits are called 'bucks,' females are 'does.'", + "It took eleven years to built the Taj Mahal, (1632-1643)", + "Mangos are known throughout the world as the 'King of Fruits.'", + "The temperature of milk when it is coming out of a cow is about 36 degrees celsius", + "Wild Flamingos are pink because they consume vast quantities of algae and brine shrimp", + "The word 'diastema' is the word for having a gap between your teeth", + "While digging, an Armadillo can hold its breath for up to six minutes", + "Of all the restaurants that are opened, 90% of them fail in the first year. Of the remaining ones that survive, 90% of those fail in the second year", + "The sun is approximately 75% hydrogen, 25% helium by mass", + "When a porcupine is born, its quills are soft and mostly white, but harden within hours", + "Since 1978, at least 37 people have died as a result of shaking vending machines, in an attempt to get free merchandise. More than 100 people have been injured", + "The first drug to be sold in the form of a tablet is Aspirin", + "The original name for butterfly was flutterby", + "There are approximately 100,000 miles of blood vessels in the human body", + "Great White sharks have about 3,000 teeth", + "Former U.S. President Franklin Pierce was arrested during his term as President for running over an old lady with his horse, but the charges were later dropped", + "The world record for time without sleep is 264 hours (11 days) by Randy Gardner in 1965", + "The giant squid is the largest animal without a backbone", + "There is enough water in American swimming pools to cover the whole city of San Francisco seven feet deep", + "215 jeans can be made with one bale of cotton", + "Hawaiian alphabet has 12 letters", + "Pineapples were first introduced into Europe by Christopher Columbus", + "When a women is pregnant, her senses are all heightened", + "The Food and Drug Administration says the most common injury from cosmetics comes from scratching the eye with a mascara wand", + "It has been medically been proven that laughter is an effective pain killer", + "Cataloupes are named after the gardens of Cantaloupe, Italy where some belive this melon was first grown", + "In the 13th century, quality standards for paste were assigned by the Pope", + "In a year, the average person walks four miles to make his or her bed", + "The first television broadcast of the Oscars took place in 1953, hosted by Bob Hope on NBC", + "Most snakes have six rows of teeth", + "NASDAQ stand for, 'National Association of Securities Dealers Automated Quotations.'", + "An average women has 17 square feet of skin. When a women is in her ninth month of pregnancy she has 18.5 square feet of skin", + "The average American eats 35,000 cookies in his/her lifetime", + "The greatest snowfall ever in a single storm was 189 inches at the Mount Shasta Ski Bowl in February, 1959.", + "French astronomer Adrien Auzout had once considered building a telescope that was 1,000 feet long in the 1600s. He thought the magnification would be so great, he would see animals on the moon", + "The first email was sent out by Ray Tomlinson in 1971", + "The city of Chicago has the only post office in the world where you can drive your car through", + "The most overdue book in the world was borrowed from Sidney Sussex College in Cambridge, England and was returned 288 years later", + "The average person can live about a month without eating any food, but can only live about a week without water", + "There are about 34,000 species of spiders", + "In 1968, Abbie Hoffman played with a yo-yo while testifying before a congressional committee. He was found in contempt", + "The biggest candy eaters are the Dutch, who average 65 pounds of candy per person in a year", + "55,700 people in the US are injured by jewelry each year", + "The first owner of the Marlboro Company, Wayne McLaren, died of lung cancer", + "American President Calvin Coolidge (1923-1929) used to like Vaseline being rubbed on his head while he ate breakfast in bed", + "No word in the English language rhymes with month, orange, silver, and purple", + "There was a 19th century Native American tribal chief who went under the name, 'Not Able to Fornicate.'", + "98% of brown bears in the United States are in Alaska", + "In 1657, the first chocolate house was opened in London, England", + "The Xerox company was initially called the Haloid Company", + "On average, Americans spend 33% of their life sleeping", + "One out of every five births in the United States are delivered by Cesarean section", + "In 1980, a Las Vegas hospital suspended workers for betting on when patients would die", + "Studies indicate that surgeons who listen to music while they operate improve in their performance", + "Thomas Edison was afraid of the dark", + "Thailand used to be called Siam", + "A sneeze can travel as fast as one hundred miles per hour", + "The Pentagon cost $49,600,000 to build in 1941", + "Desert snails can stay in their shell for up to three years", + "In the 19th century, it was common practice for a Japanese woman to dye their teeth black. They believed that this enhanced sex appeal and maintained healthier teeth", + "The revolving door was invented in 1888, by Theophilus Van Kannel.", + "The Lion King is the top grossing Disney movie of all-time with domestic gross intake of $312 million", + "The state that grows the most cranberries is Wisconsin. More than 300 million pounds of cranberries are grown in Wisconsin", + "The smallest frog is the 'Brazilian baby frog', which is smaller than a dime", + "The idea of Christmas cards was invented by Englishman Henry Cole in 1843", + "Air is passed through the nose at a speed of 100 miles per hour when a person sneezes", + "A baby octopus is about the size of a flea when it is born", + "Britons eat over 22,000 tonnes of french fries a week", + "People from the United States eat the most chicken per person than anywhere else in the world", + "Sanskrit is considered as the mother of all higher languages. This is because it is the most precise, and therefore suitable language for computer software", + "The origins of the soldier term 'G.I.' is an abbreviation for 'Government Issue,' which was stamped on all government kits supplied to recruits in the US Army during World War II", + "Watermelons are a popular gift to bring to a host in China or Japan", + "Beethoven used to take hay baths to remedy the swelling he used to get in his legs", + "On average Americans spend 18% of their income on transportation", + "Of all the words Dr. Seuss made up in his storybooks, only one has stuck in the English vocabulary: grinch, which is refers to a killjoy -- and it took more than 20 years", + "Montreal has an underground city, which has over 2,000 shops and 26 kilometres of walkways. This is the largest underground network for any city", + "The official state mammal of Texas is the armadillo", + "The word Himalayas means the 'home of snow.'", + "A man filed a lawsuit against his doctor because he survived longer than what the doctor had predicted", + "One of the reasons marijuana is illegal today because cotton growers in the 30s lobbied against hemp farmers -- they saw it as competition. It is not chemically addictive as is nicotine, alcohol, or caffeine", + "It took approximately 2.5 million blocks to build the Pyramid of Giza, which is one of the Great Pyramids", + "The platypus uses its bill to find animals that it feeds on. Its bill can sense the tiny electric fields that their preys emit", + "Central Park located in New York has 125 drinking fountains", + "The largest school in the world is City Montessori School in India and has over 25,000 students in grade levels ranging from kindergarten to college", + "Washing machines use anywhere from 40 to 200 litres of water per load", + "Australia has had stamps that actually look like gems. In 1995 and 1996 they used a special technology to make the stamps look like diamonds and opals.", + "There are only four words in the English language which end in", + "Hummingbirds are the only animal that can fly backwards", + "In only eight minutes, the Space Shuttle can accelerate to a speed of 27,000 kilometres per hour.", + "In a study conducted regarding toilet paper usage, Americans are said to use the most toilet paper per trip to the bathroom, which was seven sheets of toilet paper per trip", + "German cockroaches can survive for up to one month without food and two weeks without water", + "George Washington had teeth made out of hippopotamus ivory", + "Americans write approximately 50 billion checks a year making it the second most frequent payment method used after cash", + "The cruise liner, Queen Elizabeth II, moves only six inches for each gallon of diesel that it burns", + "The amount of aluminum that Americans throw out in three months is enough to rebuild all American commercial planes", + "Humans breathe in and out approximately one litre of air in ten seconds", + "The shortest war in history was between Zanzibar and England in 1896. Zanzibar surrendered after 38 minutes", + "British scientists have found evidence that heart attacks increase significantly for people who watch soccer penalty shoot-outs", + "Fine-grained volcanic ash can be found as an ingredient in some toothpaste", + "The most disliked vegetable by Americans is Brussels sprouts", + "The first ice hotel was built in Swedish Lapland", + "A myrmecologist studies ants", + "Rice is thrown at weddings as a symbol of fertility", + "More than 90% of shark attack victims survive", + "The average temperature on Earth is 15 degrees celcius.", + "To produce a dozen eggs, a hen has to eat about four pounds of feed", + "Pollsters say that 40 percent of dog and cat owners carry pictures of the pets in their wallets", + "When Coca-Cola began to be sold in China, they used characters that would sound like 'Coca-Cola' when spoken. Unfortunately, what they turned out to mean was 'Bite the wax tadpole'. It did not sell well", + "In the United States, 80% of households have oatmeal in their kitchen", + "A cockroach can change directions up to 25 times in a second", + "In 1825, the first toilet was installed in the White House", + "Cricket chirping can tell the temperature outside. Counting how many times a cricket chirps in 15 seconds and then adding 40 to that number will approximately tell you what the temperature is in Fahrenheit", + "Every year, 50,000,000 automobiles are produced in the world.", + "Pikeville, Kentucky consumes the most Pepsi per capita then any other American city", + "After the death of the genius, Albert Einstein, his brain was removed by a pathologist and put in a jar for future study", + "Cubic Zirconia is 55% heavier than real diamonds.", + "The fastest speed a raindrop had reached when falling is seven miles per hour.", + "Battle Creek, Michigan is referred to as the 'Cereal Bowl of America.' The city produces the most breakfast cereals than any other city in the world", + "The stringy thing that is seen in egg whites is called 'chalazae.'", + "The city of Argentia which is located on Newfoundland's southwest coast, is Canada's most fog-bound community. It has 206 days of fog each year.", + "Seven percent of a humans body wieght is made up of blood", + "Most snails are hermaphrodites, meaning they have both female and male reproductive organs", + "Dogs can be trained to detect an upcoming epileptic seizure", + "American women, on average, spend 55 minutes per day getting showered, dressed, and groomed", + "Germany produces more than 5,000 varieties of beer and has about 1,300 breweries in country", + "In some parts of the Atacama Desert it has never rained", + "In 1681, the last dodo bird died", + "Strawberries are a member of the rose family", + "It is common in Israel and Egypt to eat watermelon with feta cheese", + "The most popular recipient of Valentine cards are school teachers", + "The average stay for a prisoner on Alcatraz, when it was used as a prison, was five years", + "Mickey Mouse is known as 'Topolino' in Italy", + "Bird droppings are the chief export of Nauru, an island nation in the Western Pacific", + "About twenty-five percent of the population sneeze when they are exposed to light", + "In Japan, tipping at restaurants is not a norm. However, some restaurants might add a 5 - 10 % service charge to the bill", + "The first automobile racetrack in America was the 'Indianapolis Motor Speedway,' which had 3 million cobblestones.", + "A cow has four compartments in its stomach", + "The most popular place to burn candles in the house is the living room", + "In Japan, by the time man reaches the age of 60, he is commemorated with a special ceremony. This ceremony features the man wearing a red kimono, which denotes that he no longer has the responsibilities of being a mature adult", + "Men are four times more likely to be struck by lightning than women", + "The all-time most nominated Grammy artist with 77 nominations is Quincy Jones", + "In 1845, inventor Thomas Adams started the world's first chewing gum factory.", + "In 1693, the postage rate of a letter was determined by how much light went through the letter. The less the light went through the letter the more expensive the rate would be. This technique was referred to as candling", + "Polar bears can smell seals who are 20 miles away", + "Hair is made from the same substance as fingernails", + "Polar bears have been known to swim more than 60 miles without resting", + "Before 1883, the three-cent U.S. stamp was also used for advertising. The advertisment was located on the back of the stamp for various products", + "The largest ketchup bottle in the world is a 170 feet tall and is located in Collinsville, Illinois, USA. It was built in 1949 by the W.E. Caldwell Company as a water tower", + "Bo Jackson set a Monday Night Football record by rushing for 222 yards in one game against the Seattle Seahawks, including a 91-yard TD run", + "There are approximately 75,000,000 horses in the world", + "The fins of the Spiny Dogfish Shark are sometimes used as sandpaper for wood products", + "The Super Bowl is so popular that it is the number on at-home party event of the year", + "There was a time in Japan where a wife being left handed was a ground for divorce", + "The United States produces enough plastic film annually to cover the entire state of Texas", + "It is possible to get high by licking a toad. The Cane Toad produces a toxin called bufotenine to ward off predators. When licked, this toxin acts as a hallucinogen", + "Stinging insects kills approximately 25 people annually in the U.S", + "The first commercial microwave oven was called the '1161 Radarange' and was the size of a refrigerator", + "Chances of a women getting breast cancer are increased by excesseive use of alcohol", + "A common name for pincurls is also spitcurls because woman sometimes wet their hair with their saliva before curling it", + "The first hot air balloon flight traveled for 5.5 miles over Paris and lasted for 23 minutes", + "Birds do not sweat, as they do not have sweat glands", + "Former U.S. President Ronald Reagan once wore a Nazi uniform while acting in a film during his Hollywood days. The name of the movie was 'Desperate Journey,' which was filmed in 1942", + "The town of Olney, Illinois celebrates a 'Squirrel Day' festival to honour the 200 albino squirrels that live in the town. The festival includes a squirrel blessing by a priest", + "In the United States there are about three million honey producing colonies", + "Adult earwigs can float in water for up to 24 hours", + "January is named for the Roman god Janus. Janus was a temple god who could look forward and backward at the same time", + "Pepper was sold as individual grains during the Elizabethan times. The guards at the London docks had to sew up their pockets so they would not steal any of the pepper", + "On average, the life span of an American dollar bill is eighteen months", + "Shakespeare invented the word 'assassination' and 'bump.'", + "A diet high in fat is said to impede memory", + "There are more than 1,000 chemicals in a cup of coffee. Of these, only 26 have been tested, and half-caused cancer in rats", + "Pound for pound, hamburgers cost more than new cars.", + "Every year approximately 2,500 left-handed people are killed by using object or machinery designed for right-handed people", + "The heart of an adult giraffe weighs on average 26 pounds", + "Two million red blood cells die every second", + "The first American astronaut in space was Alan B. Shepard Jr", + "The world record for donut eating is held by John Haight, who ate 29 donuts (52 ounces) in a little over six minutes", + "Hundreds of years ago, only the wealthy people used to wear underwear", + "Buffalo wings, got their name because the spicy chicken wings originated in Buffalo, New York", + "The board game Scrabble was originally called 'Criss Cross Words' by inventor Alfred Butts", + "Men are more likely to be colorblind than women. About one of out of 12 men are colorblind", + "Edwin Armstrong invented FM radio in 1933. The first men to use FM radio to communicate with Earth from the moon's surface were named Edwin Aldrin and Neil Armstrong.", + "The MGM lion, whose name was Leo, lived in Memphis until his death", + "The Bible was written by over 40 authors over a period of 1500 years", + "The range of a medieval long-bow is 220 yards", + "Studies indicate that weightlifters working out in blue gyms can handle heavier weights", + "African heart-nosed bats can have such a keen sense of sound that they can hear the footsteps of a beetle walking on sand from six feet away", + "Many of the stars that were in the Poltergeist Trilogy had strange deaths", + "The sap of a banana plant leaves serious stains on hands and clothes that is extremely hard to remove", + "In Ireland, a prime minister is a called a Taoiseach", + "American President John Tyler had 15 children", + "Even though red roses looks the same, there are over 900 different types of red roses", + "People in France own more pets in the world per person than any other country", + "In a five card poker game there are 2,598,960 possible hands", + "The most common pear world-wide is the Bartlett. It is bell-shaped, sweet and soft with a light green colour", + "All of the clocks in the movie Pulp Fiction are stuck on 4:20", + "The average person falls asleep in about 12 to 14 minutes", + "Eighty percent of the Vanilla Beans which are used to make ice cream is grown in Madagascar", + "Annually, British people eat more than 15 pounds of beans", + "The largest known hailstone to have fallen was in Germany in 1925, which weighed close to four and a half pounds.", + "The Kraft company produces enough Cool Whip, a brand of whipping cream, in one year to fill the entire Grand Canyon", + "Karate actually originated in India, but was developed further in China", + "A seagull can drink salt water because it has special glands that filter out the salt", + "Elvis had a twin brother named Jesse Garon, who died at birth", + "95% of the entire lemon crop produced in the U.S. is from California and Arizona", + "There are more than 3000 documented caves located in the state of Tennessee", + "Soy crayons have been invented to replace wax crayons and one acre of soybeans can produce over 80,000 crayons", + "People drank gold powder mixed in with water in medieval Europe to relieve pain from sore limbs", + "Basketball great Wilt Chamberlain never fouled out of a game", + "Twit is the name given for a pregnant goldfish", + "A ear trumpet was used before the hearing aid was invented by people who had difficulty hearing", + "In 1982, a cactus in Phoenix, Arizona killed a man. David Grundman fired two shotgun blasts at a giant saguaro cactus that ended up falling on top of him", + "There are 293 steps to the top of the Leaning Tower of Pisa", + "The most popular American city for Kool-Aid sales is St. Louis, Missouri", + "A human embryo is smaller than a grain of rice at four weeks old", + "In 1923, the first neon sign was introduced in the U.S. Two neon signs were sold to a Packard car dealership for $24,000 which read, 'Packard.'", + "Silk was developed in China were it was kept a secret for more than two thousands years. Anyone found trying to smuggle silkworm eggs or cocoons out of the country was immediately put to death", + "Ivory bar soap floating was a mistake. They had been mixing the soap formula causing excess air bubbles that made it float. Customers wrote and told how much they loved that it floated, and it has floated ever since", + "Sir John Harington, the godson of Queen Elizabeth I, was the inventor of the toilet", + "The Popsicle was invented by 11 year-old Frank Epperson in 1905. He left his drink outside with a stir stick in it and he noticed that it had frozen. He applied for a patent in 1923 and named it 'Epsicle.' The name was later changed to Popsicle", + "In the U.S., approximately 46% of the chicken that is eaten by people comes from restaurants or other food outlets", + "An earthquake on Dec. 16, 1811 caused parts of the Mississippi River to flow backwards", + "The highest point in Pennsylvania is lower than the lowest point in Colorado", + "There are only four words in the English language which end in'-dous': tremendous, horrendous, stupendous, and hazardous", + "One pound of maple syrup can make eight pounds of candy or sugar", + "Uranus is the only planet that rotates on its side", + "The peanut is not a nut, it is actually a legume", + "Approximately 71% of American chocolate eaters prefer to eat milk chocolate", + "A colony of bees have to fly almost fifty-five thousand miles and tap two million flowers to make one pound of honey", + "Popped popcorn should be stored in the freezer or refrigerator as this way it can stay crunchy for up to three weeks", + "An office desk has 400 times more bacteria than a toilet", + "In Czechoslovakia, there is a church that has a chandelier made out of human bones", + "The first fruit eaten on the moon was a peach", + "The name 'Lego' came from the Danish word LEg Godt, which means 'play well.'", + "Female and male black bears cannot tolerate being around each other except when they breed", + "In 1998, Ten Speed Press publishing company published a book, 'The Eat A Bug Cookbook' by David George Gordon that contains over 33 bug recipes", + "Twelve men have landed on and explored the moon", + "The football team Green Bay Packers comes from a meat packing company called Acme Packing", + "Unlike a frog a toad cannot jump", + "The average weight of a newborn baby is 7 lbs. 6 oz. For a triplet baby it is 3 lbs. 12 oz", + "Baseball was the first sport to be pictured on the cover of Sports Illustrated", + "A group of tigers is called a streak", + "Hippos drink as much as 250 litres of water in any given 24 hour period", + "850 peanuts are needed to make an 18 oz. jar of peanut butter", + "The Beatles have sold more records than anyone else with over a billion worldwide", + "After the 'Popeye' comic strip was launched in 1931, spinach consumption went up by thirty-three percent in the United States", + "The largest type of penguin is the Emperor Penguin which can stand to be almost 3.5 feet tall and weigh more than 90 pounds", + "Over 175 million cubic yards of earth was removed for the creation of the Panama Canal", + "7-Eleven was the first convenience store to have television advertising. The animated commercial ran in 1949 and had a singing rooster and owl", + "A honey bee strokes its wings about 11,500 times a minute", + "On average men spend 51 minutes a day grooming themselves", + "Sharks are so powerful that their bite can generate a force of up to 18 tons per square inch", + "The designated instrument for the city of Detroit is the accordion", + "Percentage of Americans who have visited Disneyland/Disney World: 70%", + "Venus is the only planet that rotates clockwise", + "A sheep, a duck, and a rooster were the first passengers in a hot air balloon", + "On average, Americans spend $1300 on utility bills annually", + "The New York Stock Exchange started out as a coffee house", + "There are approximately 2,700 different species of mosquitoes", + "Colonel Sanders original white suit was auctioned off for $80,000 in February 2002", + "EPCOT stands for 'Experimental Prototype City Of Tomorrow.'", + "The reason why tattoos do not vanish even though we shed our skin is because the dye is injected deeper into the dermis part of the skin. It is only the epidermis that we shed", + "Over 170,000 Indians from 210 tribes live in the Brazilian Amazon Rainforest", + "Cashew nuts contain oil in the shell that is very irritating to the skin", + "A caterpillar grows roughly 27,000 times its size when it first emerges as an egg", + "Lightning strikes the Empire States Building about seven times a year", + "Snake venom is ninety percent protein", + "More than three thousand people work on research in Antarctica each year", + "The first product that the toy company Mattel came out with was picture frames", + "Scientists have discovered a way to make biodegradable plastic from plants by using genetic engineering.", + "The Earth orbits the Sun at a speed of about 108,000 km per hour", + "In 1897, Bayer, who is the makers of Aspirin, once marketed the drug heroin", + "In Australia, a common 'Boxing day' activity is surfing", + "In 1986, a drunk fan got into a pace car at Talladega Superspeedway, and started joyriding on the track in front of a nationwide audience. The police cruisers who had to chase him around the track were not amused when they caught and arrested him", + "Mary Hart, the co-anchor of Entertainment Tonight, has each of her legs insured for one million dollars", + "The two top toys in 1950 were Silly Putty which sold for $1, and Crayola crayons which sold for 50 cents", + "The King Cobra has enough venom in its bite that it can kill up to 13 adults", + "Earl Dean developed the bottle design for Coca-Cola", + "The word 'super' to a beekeeper refers to the hive box where the honey is stored", + "Only 23% of New Zealand families have more than two children", + "Deliberately infecting people with malaria has been used to cure different viral infections. The high fever will strengthen the immune system and fight the virus. In recent times this has been considered as a treatment to HIV", + "By partially filling saucers with vinegar and distributing the saucers around a room, you can eliminate odors", + "The only animals that are capable of turning their heads 180 degrees are from the genus Galago, such as the Tarsier", + "In the United States birds and planes collided more than 22,000 times between the years of 1990 and 1998", + "The average number of pillowcases washed a day at the MGM Grand Hotel in Las Vegas is 15,000", + "Over 40 million Americans have chronic bad breath", + "There are 158 verses in the Greek National Anthem", + "A regulation baseball has exactly 108 stitches", + "In a study that was done by the University of Chicago in 1907, it was concluded that the easiest color to spot is yellow. This is why John Hertz, who is the founder of the Yellow Cab Company picked cabs to be yellow", + "On average it takes a shark seven days to replace a tooth", + "The biggest religious building in the world is a Hindu Temple, Angkor Wat, located in Cambodia. It was built at the end of the 11th century", + "In Britain, there are approximately 50,000 pubs with 17,000 different names", + "Snails eat with a rasping mouth called a 'radula,' which has thousands of teeth", + "A dragonfly has a lifespan of for to seven weeks", + "Chewing on gum while cutting onions can help a person from producing tears", + "The majority of American models are skinnier than 98% of American women", + "The first jigsaw puzzle was created by map maker John Splisbury who mounted one of his maps on a sheet of hardwood. He proceeded to cut around the borders of each country use a fine saw", + "Owen Falls Lake is the largest man-made lake in the world", + "The patent number of the telephone is 174465.", + "On average, 81% of Americans 16 years of age and older watch television at least once in a day", + "If a raisin is dropped into a glass of champagne it will bounce up and down in the glass", + "Reptiles do not perspire, and do not have any oil glands", + "Eating chocolate three times a month helps people live longer as opposed to people who overeat chocolate or do not eat chocolate at all", + "There is a doggy disco held in Italy every year where owners can dance with their dogs", + "The average number of guests that attend a wedding in the U.S. is 189", + "In ancient Egypt, the dung beetle symbolized eternal rebirth and the Sun God Khepri", + "If a cockroach breaks a leg it can grow another one", + "Budweiser beer is named after a town in Czechoslovakia", + "Each day the sun causes about one trillion tons of water to evaporate", + "Actress Debra Winger helped to perform the voice of E.T. in the movie ?E.T. The Extra Terrestrial (1982).'", + "The only 15 letter word that can be spelled without repeating a letteris uncopyrightable.", + "All penguins live south of the equator", + "There are more than 640 muscles in the human body", + "Airports that are at higher altitudes require a longer airstrip due to lower air density", + "Every second, two Barbie dolls are sold somewhere in the world", + "The Ice Cream cone was invented in the summer of 1904 by Charles Menches. It made its debut one year later at the St. Louis World Fair", + "The rhinoceros beetle can carry up to 850 times its weight on its back. This is equivalent to a person carrying over 50 mini-vans on their back", + "The game rugby was originated at Rugby school located in England in 1823. This happened when William Webb Ellis, while playing soccer, picked the ball up in his hands and started running with it", + "The first episode of the popular television sitcom Happy Days was titled 'All The Way.'", + "Scientists have discovered that the longer the ring finger is in boys the less chance they have of having a heart attack", + "When the body is resting, it takes in about 10 litres of air every minute", + "The smallest bone in the human body is the stirrup bone, which is located in the ear", + "George Washington had to borrow money to go to his own inauguration", + "There have been over fifty million Mr. Potato Heads sold since it came out in 1952", + "All of Chrysler's PT Cruisers are built in Mexico.", + "Movies approximately make five times more from video sales than ticket sales", + "There has only been 193,000 metric tonnes of gold discovered to date.", + "The word Lethologica describes the state of not remembering the word you want to say", + "In 1958, the United States Coast Guard off western Greenland measured the tallest known iceberg at five hundred and fifty feet", + "In every episode of Seinfeld there is a Superman somewhere", + "Baby donkeys or baby mules are also known as 'Foals.'", + "When Sony introduced the walkman, it had a variety of different names in different countries. It was called 'Soundabout' in the U.S., 'Stowaway' in the U.K., and 'Freestyle' in Australia", + "In Canada, the most productive day of the workweek is Tuesday", + "The first toilet ever seen on television was on 'Leave It To Beaver'", + "Author Dr. Seuss wrote the book 'Green Eggs and Ham' because the editor made him a bet that he could not write a book, which contained less than fifty words", + "Armadillos have four babies at a time, and they are always the same sex", + "The tallest mammal in the world is the giraffe", + "Obsessive nose picking is referred to as rhinotillexomania", + "The spider used in the 2002 movie Spider-Man was a Steatoda spider, not a black widow. The spider was given anaesthesia, and was then painted blue and red", + "The human heart creates enough pressure when it pumps out to the body to squirt blood 30 feet", + "In ancient Greece, throwing an apple to a girl was a way to propose for marriage.. If the girl caught it, that would mean she accepts", + "Donkeys can live between 30 to 50 years in captivity", + "In the United States, approximately 25,000 eye injuries occur that result in the person becoming totally blind", + "Australian Graham Barker extracted his own belly button fluff every day for 18 years acquiring a record-breaking amount of fluff. He hopes to accumulate enough fluff to stuff a pillow", + "Every year, surgical tools are left in approximately 1,500 patients in the USA. Fatter patients are more prone to having a surgical tool left inside of them due to the additional amount of space in their bodies", + "Medical reports show that about 18% of the population are prone to sleepwalking", + "In Ancient Egypt, cats were often buried with their masters, or in a special cemetary for cats", + "In Ontario, an average household income of a golfer is greater than $60,000", + "The Danish company Lego, which began in 1932, first manufactured ironing boards, and stepladders", + "Mice will nurse babies that are not their own", + "Saturday is the most popular day for people to eat out in the United States", + "Singer Alice Cooper once had a live chicken thrown at him during a concert in Toronto. He threw the chicken back at the crowd and all the publicity surrounding the incident skyrocketed his singing career", + "Between 1997-2002, there was an increase of 228% in cosmetic procedures in the United States", + "Elmer Smith was the first player to hit a grand slam in a World Series", + "Ancient Egyptians used the spice Thyme to help preserve mummies", + "On average, an American family sends and receives 28 Christmas cards each year", + "In Belgium, 94% of children under the age of fourteen own LEGO products", + "In ancient Rome, lead poisoning was a prevalent disease amongst the wealthy because of their extensive use of lead. Many Roman women died because of lead poisoning caused by the use of make-up that contained lead", + "The famous jewelry store Tiffany & Co. was established on September 18, 1837 in New York City. The amount of sales that were made the first day were $4.98", + "Depending on the size, it can take an oyster anywhere from few months to many years to form a pearl", + "In one minute, the heart of a giraffe can pump 160 gallons of blood", + "Each day Americans 15 and over spend an average of 55 minutes of driving", + "Storks were a symbol of fertility in Europe and were considered to bring good luck", + "The largest pig on record was a Poland-China hog named Big Bill, who weighed 2,552 lbs", + "The parents of Albert Einstein were worried that he was mentally slow because it took him a long time to learn how to speak", + "A honey bee has four wings", + "Badminton originates from a sport in India called 'poona.'", + "In October 1973, Swedish sweet maker Roland Ohisson of Falkenberg was buried in a coffin made of nothing but chocolate", + "More than 100 professional cyclists participate in the Tour de France every year and the race is over 3,200 kilometers", + "In the United States, turkeys are mostly raised in California", + "The only state to grow pineapples is Hawaii", + "There are more than 50 different types of pumpkins. Some of them have names such as Munchkin, Funny Face, and Spooktacular", + "In order to mate, a male deep sea anglerfish will bite a female when he finds her. The male will never let go and will eventually merge his body into the female and spend the rest of his life inside the female mate. The males internal organs will disappear apart from the testes that are needed to for breeding", + "Tacoma Narrows Bridge which was located in Washington was nicknamed 'Galloping Gertie' because of the unusual way it twisted and swayed with even with the slightest winds when people would drive on it. The bridge collapsed on November 7, 1940, fortunately no humans died, except for a dog", + "At least 7% of all health care costs in the United States are attributed to smoking", + "On September 7, 1997, the first flight of the F-22a occurred.", + "There are five years in a quinquennium", + "The average life span of a mosquito is two weeks", + "Eating 11 pounds of apples will make you gain one pound of weight", + "Slugs have 4 noses", + "The shark cornea has been used in eye surgery, since its cornea is similar to a human cornea", + "Singer Billy Joel tried to commit suicide when he was 21 by drinking furniture polish", + "The largest fish in the world is the whale shark. It can weigh several tons and grow to more than fifty feet in length", + "The fastest bird in the world is the Peregrine Falcon, which can reach speeds in excess of two hundred miles per hour", + "The world camel population is close to 19 million", + "The word 'Denim' comes from the French phrase 'serge de Nimes' which is a fabric made in a town located in southern France", + "February 17th, 1930, was the first flight by a cow in an airplane. The milk that was produced by the cow during the flight was put into containers and parachuted over the city of St. Louis", + "The most popular gift that teachers receive in the United States from their students is chocolate", + "The Bible has been translated into Klingon", + "Thirty-five percent of the people who use personal ads for dating arealready married.", + "Stewardesses is one of the longest words typed with only the left hand", + "If a lobster loses an eye or a claw it can usually grow a new one", + "The name for the sign '&' which represents the world 'and' is ampersand", + "The Eiffel Tower weight is approximately 9441 tons", + "The first translation of the Bible into English was in 1382 A.D., by John Wycliff", + "The meaning of Siberia is 'sleeping land.'", + "Ancient Romans at one time used human urine as an ingredient in their toothpaste", + "Billie Jean by Michael Jackson was the first video to air on MTV by a black artist", + "Vatican City is the smallest country in the world, with a population of 1000 and a size 108.7 acres", + "Eyebrow hair lasts between 3-5 months before it sheds", + "An elephant cannot jump", + "Scientists say that babies that are breastfed are more likely to be slimmer as adults than those that are not breastfed", + "The reason why the Mexican sombrero hat is so wide is to provide shade for the entire body", + "Amazingly, goalies in the National Hockey League played without masks until the year 1959", + "According to research, the most productive workday is Tuesday and the least productive is Friday", + "The makers of Crayola produce over 2 billion crayons in a year", + "The human face is made up of 14 bones", + "Mangos are valued for their reputation as an aphrodisiac in countries such as Guatemala and India", + "There are over 9 million beef and dairy cattle in New Zealand", + "The coconut is the largest seed in the world", + "Basketball superstar Wilt Chamberlain holds 56 NBA records", + "In 1990, the largest watermelon ever grown was 262 pounds, by Bill Carson of Tennesse, USA", + "Some toothpastes contain antifreeze", + "During the Gold Rush in 1849, some people paid as much as $100 for a simple glass of water", + "The only English place that has a name that ends with an exclamation mark is 'Westward Ho!'", + "In Cleveland, Ohio, it's illegal to catch mice without a huntinglicense.", + "There are at least six universal facial expressions. They are: happiness, sadness, disgust, fear, anger and surprise", + "In the movie 'The Exorcist' the vomit that (Regan) Linda Blair hurls at Father Damien Karras is thick pea soup", + "The Library of Congress, located in Washington D.C., is the largest library in the world", + "There is a substance in the skin of the African clawed frog that helps in fighting infection", + "The Sea of Tranquility on the moon is deeper than the highest mountain on Earth", + "The most popular vacation destinations for Americans in 1956 was Niagara Falls", + "In 1967, the first successful heart transplant was performed in Cape Town, South Africa", + "There are an average of 178 sesame seeds on a McDonald's Big Mac bun.", + "In one day, a queen bee can lay up to 1500 eggs in one day", + "'Pomology' is the science of growing an apple", + "The total number of episodes for the sitcom 'I Love Lucy' was 180", + "The town with the most stop signs per capita than any other in the US: LaConner, Washington", + "There is a Harley-Davidson that was designed as an exact replica of a hamburger", + "The blue whale is the loudest animal on the earth. Its whistle can reach up to 188 decibels", + "Five Jell-O flavors that flopped: celery, coffee, cola, apple, andchocolate.", + "Mosquitoes prefer children over adults", + "Sharks are capable of going at least 6 weeks without eating; the record observed in an aquarium is 15 months by a species of shark known as the swell sharks)", + "When baby sharks are born, they swim away from their mothers right away and are on there own. In fact, their mothers might see them as prey", + "Soaking beans for twelve hours in water before they are cooked can reduce flatulence caused by beans", + "The vegetable that is eaten most by Americans is potatoes. On average, a person eats about 140 pounds of potatoes annually", + "In the dry valleys region of Antarctica, it has not rained in 2 million years according to scientists", + "Keeping your car tuned up is a good way to save on gas. A car that is tuned up is 9% more efficient on gasoline.", + "The venom of the king cobra is so deadly that one bite can kill twenty people or one elephant", + "There is a law in the state of Idaho that does not permit one citizen to give another citizen a box of candy that is heavier than 50 pounds", + "The kidneys filter over 400 gallons of blood each day", + "Another word to refer to old age is senectitude", + "The most popular meal ordered at restaurants in the U.S. is fried chicken", + "Kit Kat chocolate bar was introduced to the market in 1935", + "The average human head weighs about eight pounds", + "Fear of clowns is called coulrophobia", + "The average person is about a quarter of an inch taller at night", + "Common pesticides such as roach, termite and flea insecticide can be found in the bodies of majority of Americans", + "Isaac Asimov is the only author to have a book in every Dewey-decimal category", + "There is no tipping in Iceland", + "The founder of JC Penny had the middle name of Cash", + "Eating parsley after eating an onion can help in getting rid of onion breath", + "Gorilla gorilla gorilla is the scientific name for the animal gorilla", + "In the U.S. the most common excuse made to get out of paying a ticket is to say they missed the sign", + "On average, it is estimated that females injure themselves ten time more than males do while playing sports", + "A house cat spends 70% of its time sleeping", + "The first hair dryer was a vacuum cleaner that was used for drying hair", + "Whale eyes are the size of a grapefruit", + "Painting a house yellow or having a yellow trim helps in selling a house faster", + "On August 21st, 1911, someone stole the Mona Lisa, the most famous painting in the world, from the Louvre Museum. It was recovered two years later", + "The Great Wall stretches for about 4,500 miles across North China", + "Frosted Flakes mascot 'Tony the Tiger' has a wife, son (Tony Jr.) and daughter (Antoinette) that were used in early advertising commercials", + "The iron disulfide (Pyrite) is considered 'fool's gold' because it looks very similar to gold.", + "Nutmeg is extremely poisonous if injected intraveinously.", + "Smiling releases endorphins in the body, which makes people feel better", + "A dentist from Buffalo New York named Alfred P. Southwick invented the electric chair.", + "ABBA got their name by taking the first letter from each of their names (Agnetha, Bjorn, Benny, Anni-frid.)", + "The space between your eyebrows is called the Glabella", + "Grapes are grown around the world more than any other fruit", + "The best selling game in history for coin-operated machines is Pac-Man", + "Google receives more than 200 million search queries a day, more than half of which come from outside the United States. Peak traffic hours to google.com are between 6 a.m. and noon PST, when more than 2,000 search queries are answered a second", + "There is a Hamburger hall of fame in Seymour, Wisconsin", + "The Olympic torch weighs about 3 pounds", + "The famous Christmas song 'Jingle Bells' was written for a Thanksgiving program in 1857 by James Pierpont. At the time, the song was called 'The One-Horse Open Sleigh.'", + "The average bank teller loses about $250 every year", + "Initially golf balls were made out of wood. After that they were made out of leather which was stuffed with feathers", + "Russian I.M. Chisov survived a 21,980 plunge out of a plane with no parachute. He landed on the steep side of a snow-covered mountain with only a fractured pelvis and slight concussion", + "About 70 percent of Americans who go to college do it just to make more money", + "The oldest actor to win a Best Actor Oscar is Henry Fonda. He was 76 when he won it", + "Babies who wear disposable diapers are five times more likely to develop diaper rash than those that wear cotton diapers", + "The first lighthouse built in the USA was in Boston, MA in 1716", + "The Snickers chocolate bar was invented in 1930", + "When Nylons first went on sale in the United States in 1940, four million pairs were sold in only a few days", + "On average, a man spends about five months of his life shaving", + "Oprah Winfrey was the first black woman to anchor a newscast in Nashville at WTVF-TV", + "Ever year, Americans spend close to $25 billion on beer", + "In 1888, an Egyptian peasant discovered an estimated three hundred thousand mummified cats in Beni Hassan, Egypt. Of the cats that were not stolen once, the find was made public, the remaining mummified cats were shipped to Great Britain to be used as agricultural fertilizer", + "The smallest stamp in the world was issued in 1863 by the Columbian state of Bolivar and measured 9.5 x 8mm", + "There are over 1,800 known species of fleas", + "Ivory soap slogan '99-44/100% Pure' was cleverly invented by Harley Proctor who with the help of chemists determined that Ivory soap was only 56/100 pure. Proctor simply subtracted 56 from 100 and came up with '99-44/100% Pure'", + "The longest word in the English language is 1909 letters long and it refers to a distinct part of DNA", + "Polar bears are left-handed", + "Brain damage will only occur if a fever goes above 107.6 degrees farenheit", + "It requires 63 feet of wire to make a Slinky toy", + "A female ferret can die if she goes into heat and cannot find a mate", + "Casey Kasem is the voice of Shaggy on the cartoon show 'Scooby-Doo.' Casey Kasem, being a strict vegetarian, also requested that Shaggy follow the same diet on the show", + "In the U.S., the milk production per dairy cow is approximately 12,000 pounds", + "In America, 38% of doctors are Indians", + "There are approximately 100 million acts of sexual intercourse each day", + "People with allergies can lower allergy reactions by laughing", + "Some farmers in Japan have learned to grow their watermelons into a square shape. They did this to conserve shelf space", + "The heaviest United States President was William Howard Taft who weighed 332 pounds", + "Tasmania is said to have the cleanest air in the world", + "Studies show that divorced women have more trouble starting new relationships than divorced men", + "Did you know that there are coffee flavored PEZ?", + "An individual coral animal is called a polyp", + "There are dolphins that live in the Amazon River that are the colour pink", + "Navel oranges got their name because the bottom of this type of orange resembles a belly button or navel", + "Tohru Iwatani, the inventor of the video game Pac-Man, came up with the idea when he saw a pizza with a slice missing at a dinner party", + "One tree can filter up to sixty pounds of pollutants from the air each year.", + "In America, one out of every two marriages ends up in divorce", + "A chance of a woman having twins is increased after the age of 35. About 1 in 27 women will give birth to twins after this age. After 50 the chances of having twins is 1 in 9", + "The ant can lift 50 times its own weight, can pull 30 times its own weight, and always falls over on its right side when intoxicated", + "The Hollywood sign was first erected in 1923. It was first erected as 'Hollywoodland.'", + "During his entire life, Vincent Van Gogh sold exactly one painting, Red Vineyard at Arles", + "The first commercial chewing gum was sold in 1848 by John B. Curtis, who also made the gum. He called the gum 'State of Maine Pure Spruce Gum.'", + "Touching and stroking a plant will aid in it growing healthy", + "Average age of top GM executives in 1994: 49.8 years. Average age ofthe Rolling Stones: 50.6.", + "Business.com is currently the most expensive domain name sold for $7.5 million", + "31% of employees skip lunch entirely", + "Olive oil can help in lowering cholesterol levels and decreasing the risk of heart complications", + "Ho-Ho-Kus, a small town in New Jersey, is the only town in the United States of America that has two dashes in its name", + "Romans, in the third century, believed that the lemon was an antidote for all poisons", + "During the Easter season, 600 million Marshmallow Peeps are bought my Americans. The Marshmallow Peep is the most popular Easter candy besides chocolate", + "In the Great Fire of London in 1666, only six people were killed", + "During World War II, the 2nd Polish Corps had a brown bear named Wojtek, who helped move boxes of ammunition during the battle of Monte Cassino", + "In the United States, every year about 15 people die from dog bites", + "Sheep can survive for up to two weeks buried in snow drifts.Click Here For More Details", + "The Crayola colour flesh was changed to peach in 1962 because of the fact that people have many different skin colours", + "'Kemo Sabe' means 'soggy shrub' in Navajo", + "7 out of 10 people believe in life after death", + "Clans of long ago that wanted to get rid of their unwanted people without killing them would burn their houses down - hence the expression 'to get fired.'", + "The largest coral reef in the world is the Great Barrier Reef located in Australia. The reef is approximately 2023 kilometers long", + "On a bottle of brandy VSOP stand for 'Very Special Old Pale.'", + "The word 'dexterity', to do with skill is related to the right hand. The opposite of the word 'deter' is 'sinister', to do with evil, it is related to the left hand", + "Male sea horses are the ones that get impregnated rather than the females. Males have a pouch on their belly that provides incubation for the female?s eggs and they can have up to 1500 babies at one time", + "NASA scientists have concluded that the state of California is moving north and will collide with the state of Alaska in roughly 150 million years", + "The best selling chocolate syrup in the world is Hershey", + "The word Karate means, 'empty hand.'", + "There are some types of chocolates that are actually good for the arteries and heart", + "Intelligent people have more zinc and copper in their hair", + "The speed at which honey bees fly is at 15 miles per hour", + "The average life expectancy of an ant is about 90 days", + "The cruise liner, Queen Elizabeth II, moves only six inches for each gallon of diesel that it burns", + "In France, it is illegal for a person to kiss another on railways", + "The appliance that uses the most use of water in the home is the toilet", + "The first movie to ever cost $100 million to make is Terminator 2: Judgment Day in 1991", + "Adolf Hitler wanted to be an architect, but he failed the entrance exam at the architectural school in Vienna", + "In the first century, people used to drink goats milk to sweeten their breath", + "The first product that Sony came out with was the rice cooker", + "In the United States, ice cream is sold the most on a Sunday", + "Over the course of one year, a coffee tree only produces about 1.5 pounds of coffee", + "The substance that gives red wine and dark beer its color is said to have a positive effects on cholesterol and blood pressure", + "Tripolini pasta was named for the Italian conquest of Tripoli in Libya", + "The six official languages of the United Nations are Arabic, Chinese, English, French, Russian, and Spanish", + "In 2002, the most popular boat name in the U.S. was Liberty", + "One out of 20 people have an extra rib", + "44% of kids watch television before they go to sleep", + "In 1865, the U.S. Secret Service was first established for the specific purpose to combat the counterfeiting of money", + "Istanbul, Turkey is the only city in the world located on two continents", + "In 1967, the IMAX film system was invented by Canadian Ivan Grame Ferguson to premier at Expo 67.", + "Approximately 40% of the U.S. paper currency in circulation was counterfeit by the end of the Civil War", + "Every three days a human stomach gets a new lining", + "In 1873, Colgate made a toothpaste that was available in a jar", + "The Kodiak, which is native to Alaska, is the largest bear and can measure up to eight feet and weigh as much as 1,700 pounds", + "The three best-known western names in China: Jesus Christ, Richard Nixon, and Elvis Presley", + "Mars is the home of Olympus Mons, the largest known volcano in our solar system", + "The Gastric Flu can cause projectile vomiting", + "The second best selling game of all time is Jenga. Jenga is a Swahili word, meaning 'to build.'", + "Cinderella is known as Rashin Coatie in Scotland, Zezolla in Italy, and Yeh-hsien in China", + "The name Wendy was made up for the book 'Peter Pan.'", + "The fur of the binturong, also known as the 'Asian Bear Cat,' smells like popcorn. The scent is believed to come from a gland located near the tail", + "In 1894 the first big Coke sign was found on the side of a building located in Cartersville, Georgia, and still exists today", + "The longest distance a deepwater lobster has been recorded to travel is 225 miles", + "Orcas (killer whales), when traveling in groups, breathe in unison", + "The Great Pyramids used to be as white as snow because they were encased in a bright limestone that has worn off over the years", + "NASCAR stands for National Association for Stock Car Auto Racing", + "Percentage of American men who say they would marry the same woman if they had it to do all over again: 80%", + "Paul Hunn holds the record for the loudest burp, which was 118.1 decibels, which is as loud as a chainsaw", + "A monkey was once tried and convicted for smoking a cigarette in South Bend, Indiana", + "There are six million parts in the Boeing 747-400.", + "The first TONKA truck was made in 1947", + "In the U.S., over one million gallons of cosmetics, drinks, and lotions are sold that contain aloe in them per year", + "Sugar Bear (the mascot for Golden Crisps cereal) was born in 1963", + "The Tonle Sap River in Cambodia flows north for almost half the year and then south for the rest of the year", + "Japanese research has concluded that moderate drinking can boost IQ levels", + "For more than 3,000 years, Carpenter ants have been used to close wounds in India, Asia and South America", + "Baskin Robbins plain vanilla ice cream is the number one selling flavour and accounts for a quarter of their sales", + "Elizabeth Taylor has appeared on the cover of Life magazine more than anyone else", + "The typical lead pencil can draw a line that is thirty five miles long", + "The word 'toy' comes from an old English word that means 'tool.'", + "Smokers are twice as likely to develop lower back pain than non-smokers", + "Humans are born with 300 bones in their body, however when a person reaches adulthood they only have 206 bones. This occurs because many of them join together to make a single bone", + "The reason why hair turns gray as we age is because the pigment cells in the hair follicle start to die, which is responsible for producing 'melanin' which gives the hair colour", + "In 1960 there were 16,067 gambling slots in Nevada. By 1999, this number rose to 205,726 slots which would be one slot for every 10 people residing there", + "It takes the Hubble telescope about 97 minutes to complete an orbit of the Earth. On average, the Hubble uses the equivilent amount of energy as 30 household light bulbs to complete an orbit.", + "The two factories of the Jelly Belly Candy Company produces approximately 100,000 pounds of jelly beans a day. this amounts to about 1,250,000 jelly beans an hour", + "Pucks hit by hockey sticks have reached speeds of up to 150 miles per hour", + "The 'naked recreation and travel' industry has grown by 233% in the past decade", + "The Planters Peanut Company mascot, Mr. Peanut, was created during a contest for schoolchildren in 1916", + "Most lipstick contains fish scales", + "The sentence 'the quick brown fox jumps over the lazy dog' uses every letter in the english language", + "The expression cooked 'al dente' means 'to the tooth.' What this means is that the pasta should be somewhat firm, and offer some resistance to the tooth, but should also be tender", + "Of married couples, 70% of men and 60% of women have cheated on their spouse", + "No piece of paper can be folded in half more than 7 times", + "More people are killed by donkeys annually than are killed in plane crashes", + "The first couple to be shown on a sitcom sleeping in the same bed was 'Mary Kay and Johnny.'", + "Asthma affects one in fifteen children under the age of eighteen", + "A one ounce milk chocolate bar has 6 mg of caffeine", + "Throughout the South, peanuts were known as 'Monkey Nuts,' and 'Goober peas,' before the civil war", + "Scallops have approximately 100 eyes around the edge of its shell", + "In 1810, Peter Durand invented the tin can for preserving food", + "The fear of peanut butter sticking to the roof of the mouth is called Arachibutyrophobia", + "Men in their early twenties shave an average of four times a week", + "Colour is not an indicator for the taste or ripeness in cranberries", + "Each year there are approximately 20 billion coconuts produced worldwide", + "A chicken with red earlobes will produce brown eggs, and a chicken with white earlobes will produce white eggs", + "Not all polar bears hibernate; only pregnant females polar bears do", + "There is a restaurant in Stockholm that only offers all-garlic products. They even have a garlic cheesecake", + "Serving ice cream on cherry pie was once illegal in Kansas", + "Superman The Escape rollercoaster, located in California at Six Flags Magic Mountain, goes from 0 to 100 miles per hour in only 7 seconds", + "Five thousandths of a millimeter is the tolerance of accuracy at the LEGO mould factories", + "2.5 cans of Spam are consumed every second in the United States", + "In 1836, Mexican General Santa Anna held an elaborate state funeral for his amputated leg. updated", + "A meteor has only destroyed one satellite, which was the European Space Agency's Olympus in 1993.", + "The Koala bear is not really a bear, but is really related to the kangaroo and the wombat.", + "One gallon of pure maple syrup weighs 11 pounds", + "Instead of a Birthday Cake, many Russian children are given a Birthday Pie", + "The largest employer in the world is the Indian railway system in India, employing over 1.6 million people", + "The word 'comet' comes from the Greek word 'kometes' meaning long hair and referring to the tail", + "The average price for a major league baseball game in 2004 is $19.82", + "The hydra, which is related to the jellyfish, can grow its body back in a couple of days if it is cut in half", + "The deepest mine in the world is the East Rand mine, which goes to a depth of about 3,585 metres", + "Native Indians have been known to paint their doors blue, which they believe keeps the bad spirits out", + "Before air conditioning was invented, white cotton slipcovers were put on furniture to keep the air cool.", + "It would take about fourteen and half million notes of currency to build a mile high stack", + "Chinese Crested dogs can get acne", + "It costs about 3 cents to make a $1 bill in the United States", + "Colgate faced a big obstacle marketing toothpaste in Spanish speaking countries. Colgate translates into the command 'go hang yourself.'", + "The cross bow was invented by the Chinese and records of its usage goes back to as far as the Three Kingdom Period (220 a.d.-280 a.d.).", + "It is estimated that by the end of 2000, there has been 142,600 tonnes of gold mined in the world", + "One-third pound stalk of broccoli contains more vitamin C than 204 apples", + "The Flintstones cartoon was the first thirty-minute cartoon to be aired during prime time", + "The abbreviation Xmas for the word Christmas is of Greek origin. Since the word for Christ in the Greek language is Xristos, which starts with the letter 'X,' they started putting the X in place of Christ and came up with the short form for the word Christmas", + "Dipsomania refers to an insatiable craving for alcoholic beverages", + "China has more English speakers than the United States", + "Pitcher Darold Knowles once pitched all seven games of one World Series", + "In a day, kids in the U.S. that are between the ages of 2 - 8 spend 28 minutes of their time coloring", + "The Ancient Greek women made a type of cheek blush by painting their cheeks with herbal pastes which was made out of crushed berries and seeds", + "Herbert Hoover, who was the 31st president of the United Stated, turned over all the Federal salary checks he received to charity during the 47 years he was in government", + "Macadamia nuts are not sold in their shells because it takes 300 pounds per square inch of pressure to break the shell", + "Japan has approximately 200 volcanoes and is home to 10% of the active volcanoes in the world", + "Before 1928, yo-yos used to be called bandalores in the United States", + "The only South East Asian country that has never been colonized by a Western Power is Thailand", + "In 1631, two London bible printers accidentally left the word 'not' out of the seventh commandment, which then read, 'Thou shalt commit adultery.' This legendary book is now known as the 'Wicked Bible.'", + "The shortest war in history was between Zanzibar an England in 1896. Zanzibar surrendered after 38 minutes", + "Irish Wolfhound dogs have a short lifespan and live about 7-8 years", + "When Queen Elizabeth I of England died she owned over 3,000 gowns", + "Female alligators lay about 40 eggs that hatch in 60 - 70 days", + "The nickname for a Japanese businessmen is 'Salarymen.'", + "Emus cannot walk backwards", + "The external tank on space shuttles is not painted. It is the only part of the shuttle that is lost after launch, so it is not necessary to worry about metal corrosion.", + "The most popular Twizzler candy flavour is strawberry", + "Thirty percent of all bingo players are under the age of 35", + "Infants spend more time dreaming than adults do", + "The famous Casanova (Giacomo Casanova) was a librarian for many years before he died", + "The only species of turtle that lives in the open ocean is the sea turtle", + "Toronto was the first city in the world with a computerized traffic signal system", + "Seniors who drink a cup of coffee before a memory test score higher than those who drink a cup of decaffeinated coffee", + "Venus is the only planet that rotates clockwise", + "Some octopuses have been known to eat their arms off when they are exposed to stressful situations", + "On average, 749 pounds of paper products is used by an American individual annually", + "The skeleton of a spider is located on the outside of the body. The name for this is exoskelton", + "Incas used to create pots in the shape of peanuts that were highly prized", + "The letter J does not appear anywhere on the periodic table of the elements", + "Over 200 varieties of watermelons are grown in the U.S", + "The most dangerous job in the United States is that of a fisherman, followed by logging and then an airline pilot", + "The words 'abstemioius,' and 'facetious' both have all the five vowels in them in order", + "French soldiers during World War I had the nickname 'poilu' which translates to 'hairy one.'", + "Former U.S. President William Taft converted the White House stable into a four car garage in 1909", + "People living on the east coast prefer creamy peanut butter, while people living on the west coast prefer chunky peanut butter", + "Some snails live on branches in trees", + "Tomato ketchup is a good conditioner for the hair. It also helps get the greenish tinge that some blonde haired people get after swimming in water with chlorine in it", + "The youngest pope was 11 years old", + "Did you know you share your birthday with at least 9 other million people in the world", + "Soldiers disease is a term for morphine addiction. The Civil War produced over 400,000 morphine addicts", + "The longest chapter in the Bible is Psalm 119, which is 176 verses", + "During the First World War, cigarettes were handed out to soldiers along with their rations", + "The longest freshwater shoreline in the world is located in the state of Michigan", + "There are bananas called 'Red banana' that are maroon to dark purple when ripe", + "Franklin Pierce was the first U.S. President to have a Christmas tree in the White House", + "The USA bought Alaska from Russia for 2 cents an acre", + "Walt Disney had originally suggested using the name Mortimer Mouse instead of Mickey Mouse", + "The length of brink of the Canadian 'Horseshoe' Falls located in Niagara Falls, Ontario, Canada is 2600 feet", + "The smile is the most frequently used facial expression. A smile can use anywhere from a pair of 5 to 53 facial muscles", + "The right lung of a human is larger than the left one. This is because of the space and placement of the heart", + "Native Americans used to use pumpkin seeds for medicine", + "The pound key (#) on the keyboard is called an octothorpe", + "The chemical name for caffeine is 1,3,7-trimethylzantihine", + "Corals take a long time to grow. Some corals only grow one centimeter in one year", + "Walmart-mart sells more apparel a year than all the other competing department stores combined", + "Canada has more inland waters and lakes than any other country in the world", + "Ramses II, a pharaoh of Egypt died in 1225 B.C. At the time of his death, he had fathered 96 sons and 60 daughters", + "The word 'lethologica' describes the state of not being able to remember the word you want", + "Since its introduction in February 1935, more than two hundred million Monopoly board games have been sold worldwide", + "Women are twice as likely to be diagnosed with depression than men in the United States", + "The smallest man ever was Gul Mohammed (1957-1997) of India, who measured 1 feet, 10? inches", + "500,000 tons of dog excrement are dumped annually on the streets of Paris", + "Hydrogen gas is the least dense substance in the world, at 0.08988g/cc", + "In Belgium, there is a museum just for strawberries", + "The Simpsons is the longest running prime-time animated series on television history", + "The more a person struggles to get out of quicksand the faster they will sink. Staying still, and being calm will actually make the body float in the quicksand because the body is less dense than the quicksand is", + "On average, a Canadian girl owns seven Barbie dolls, whereas an American girl owns eight", + "A piece of French toast that was partially eaten by Justin Timberlake sold on eBay", + "The Olympic was the sister ship of the Titanic, and she provided twenty-five years of service", + "Atari had to bury millions of unsold 'E.T.' game cartridges in a New Mexico desert landfill in 1982", + "The biggest disco ball in the world has a diameter of 2.41 meters and 137.89 kilograms. It also has 6,900 mirror squares on it", + "The national anthem of Greece has 158 verses", + "An average city dog lives approximately three years longer than an average country dog", + "On average, falling asleep while driving results in 550 accidents per day in the United States", + "Scatologists are experts who study feces. (aka. crap, dung, dookie, dumps, feces, excrement, etc.)", + "Pumpkins contain potassium and vitamin A", + "The greatest mountain range is the Mid-Ocean Ridge, extending 64,374 km from the Arctic Ocean to the Atlantic Ocean", + "Pepsi got its name from the ingredient pepsin, which is said to aid in digestion, however, it is not known", + "The spray WD-40 got its name because there were forty attempts needed before the creation of the 'water displacing' substance", + "Contrary to popular beliefs, chocolate does not cause acne", + "Detroit, Michigan has more registered bowlers than any other city in the USA", + "The fastest moving land snake is the Black Mamba, which can move up to 7 miles per hour", + "Annually Americans eat 45 million turkeys at Thanksgiving", + "Rabbits can live up to ten years", + "The average life span of a single red blood cell is 120 days", + "Over 250 million Slinky toys have been sold since its debut in 1946", + "In 1961, Italian artist Piero Manzoni packed his feces in cans, signed and mounted them, and then sold them as art", + "The last thing Elvis Presley ate before he died was four scoops of ice cream and 6 chocolate chip cookies", + "Some Chinese chopsticks contain gold as on of their materials", + "The chances of making two holes-in-one in a round of golf are one in 67 million", + "The watch was invented by Peter Henlein of Nuremberg in 1510.", + "In North America there are approximately 618 roller coasters", + "The concept of Boxing Day, which is on December 26th, was to give boxes of food and clothing to the poor. It is now viewed in some countries as a time to get merchandise from stores at reduced prices", + "Crayola is a French word that means 'Oily chalk.'", + "Every year, Burger King restaurants prepare over 950,000 pounds of bacon for their breakfast customers", + "Isaac Newton used to be a member of parliament", + "Dumbest Dog: Afghan hound", + "At just four years old Mozart was able to learn a piece of music in half an hour", + "It would take twenty new mid-size cars to generate the same amount of pollution that a mid-size 1960's car did.", + "The honey badger can withstand hundreds of bee stings that would otherwise kill another animal", + "There are 500,000 detectable earthquakes in the world each year", + "Black pepper is the most popular spice in the world", + "In Greece, the climate is so warm that many of the cinemas do not even have roofs", + "The word 'moose' comes from the native Algonquian Indian word meaning 'twig eater.'", + "All 50 states are listed across the top of the Lincoln Memorial on the back of the $5 bill", + "An armadillo can walk under water", + "There are over one hundred billion galaxies with each galaxy having billions of stars", + "The Uape Indians, who live in the Amazon, mix the ashes of their recently cremated relatives with alcohol, then all members of the family drink the mix with fond memories of the deceased", + "The word 'diamond' comes from the Greek word 'adamas,' which means 'unconquerable.'", + "On average, a typical dairy cow lies down and stands up about 14 times a day", + "For the movie 'Tootsie' actor Dustin Hoffman thought of the title. His mother used to call him that as a child", + "The world record for rocking non-stop in a rocking chair is 480 hours held by Dennis Easterling, of Atlanta, Georgia", + "The Sears Tower consists of nine framed tubes, which connects nine skyscrapers as one building", + "The first subway system in America was built in Boston, Massachusetts in 1897", + "There are approximately 45 billion fat cells in an average adult", + "To make one pound of butter, 29 cups of milk are needed", + "The dot that appears over the letter 'i' is called a tittle", + "The San Francisco Cable cars are the only mobile National Monuments", + "Close to 73% of girls in Bangladesh are married by age 18", + "Nerve impulses for muscle position travel at a speed of up to 390 feet per second", + "In the summer of 1858, the smell of the sewage in the Thames River in London was so bad that the Members of Parliament had to leave from the chamber of the House of Commons. This was a result of two million people dumping all their sewage into the river", + "One out of five people that eat ice cream binge on ice cream in the middle of the night. The person is usually between 18 - 24 years old", + "The Basenji dog is the only dog that is not able to bark", + "There is a dog museum in St. Louis, Missouri", + "The tip of a bullwhip moves so fast that it breaks the sound barrier. The crack of the whip is actually a tiny sonic boom.", + "There is a city called Smackover located in Arkansas", + "An average person laughs about 15 times a day", + "The labels for Crayola crayons come in 18 different colors", + "The temperature of milk when it leaves the body of a cow is 101 degrees Fahrenheit. The milk is then quickly chilled and stored at a temperature of 40 degrees Fahrenheit", + "In Spain, it is common to pour chocolate milk or cafe au lait on cereal for breakfast", + "Over 50% of the wedding in the U.S. occur in the afternoon", + "Before the fur trade had started in Canada, it was estimated that there were over 6 million beavers", + "Eating dandelions can make you urinate more", + "Enough paper is recycled in the USA every day, that a 15 mile long train of boxcars could be filled up with paper.", + "The palms of your hands and the soles of your feet cannot tan, or grow hair", + "At the White House, president John Adams was said to be the first to display fireworks there", + "About 10% of the 100,000 thunderstorms that occur in the USA every year are classified as severe.", + "Sylvia Plath was a famous poet who killed herself at age thirty-one by sticking her head into a gas oven", + "A baseball will go farther in hot temperature than in cold temperature", + "A rabbit is not able to vomit", + "The aorta, which is largest artery located in the body, is about the diameter of a garden hose", + "Niagara Falls actually stopped flowing back in 1848 for about 20 hours because there was ice that was blocking the Niagara River", + "The world's first underground was the London Underground in1863. It has 275 stations and 253 miles of track.", + "The first American president to deliver a speech over the radio was Warren G. Harding", + "There are more than 40 million Americans that have 'chronic halitosis,' which is bad breath that never goes away", + "The human body makes anywhere from 1 to 3 pints of saliva every 24 hours", + "Cheetahs are the fastest land animal and can reach speeds up to 72mph", + "The purpose of tonsils is to destroy foreign substances that are swallowed or breathed in", + "The country with the highest consumption of candy at 29.5 pounds annually per person is Denmark", + "One of the most dangerous insect in the world is the common housefly. They carry and transmit more diseases than any other animal in the world", + "Every day, over 1,300 babies are born prematurely in the USA", + "The sun is approximately 149 million kilometres from the earth", + "The Great White Shark can grow to be more than twenty feet long and can weigh approximately 4,000 pounds", + "In 1832, in Paisley, Scotland the first municipal water filtration works was opened", + "The only popcorn museum in the world is lcoated in Marion, Ohio, USA", + "Any animal that has skin hair or fur can get dandruff, but in animals it is called 'dander.'", + "The average ice berg weighs 20,000,000 tons", + "In 1819, the USA purchased Florida from Spain for the cancellation of a $5 million debt", + "In a lifetime, the average driver will honk 15,250 times", + "Jewelers Tiffany & Co., based in New York, are responsible for making the Super Bowl trophy", + "Skippy Peanut Butter is sold more in the world than any other peanut butter", + "The highest bridge in the world is located in the Himalyan mountains. It was built by the Indian Army, in 1982, and is about 5,600 metres above sea level", + "In 1893, the first mosque in the United States was built", + "Apples are part of the rose family", + "Every eleven minutes in the U.S., a woman dies of breast cancer", + "Mr. Butts invented the game SCRABBLE. The game was originally called 'Criss Cross Words.'", + "Actress Meryl Streep holds the record for the most Oscar nominated actress, with a record of 13 nominations", + "In a year approximately 900 million trees are cut down to make the raw materials needed for American pulp mills and paper", + "Enamel is hardest substance in the human body", + "A leech has 32 brains", + "Thomas Edison designed a helicopter that would work with gunpowder. It ended up blowing up and also blew up his factory.", + "In the late 1800's, washing machines and butter churners were sometimes powered by dogs walking on treadmills.", + "In China, people eat a bar of chocolate for every 1,000 chocolate bars eaten by the British", + "Leonardo Da Vinci invented the scissors", + "Clarence Crane the inventor of 'Crane's Peppermint Life Savers' sold his rights to the popular candy for less than three thousand dollars.", + "Namco, who are the manufacturers of Pac Man the video game, has estimated that the original arcade game has been played over 10 billion times by individuals", + "In Ivrea, Italy, thousands of citizens celebrate the beginning of Lent by throwing oranges at one another", + "The sloth moves so slowly that green algae grows in the grooves of their hair", + "In the world, the United States and France have the most pet dogs. Approximately one out of every three families has a pet dog. Switzerland and Germany are the lowest only having one dog per every ten families", + "There are 50% more males that are left handed compared to females", + "Armadillos breed in July, but get pregnant in November after delaying implantation. This allows the young to be born during the spring when there is an abundance of food", + "Carbon monoxide can kill a person in less than 15 minutes", + "The Nobel Peace prize was first awarded in 1901 to Jean Henry Dunant, who was the founder of the Swiss Red Cross", + "In August 1999, Lori Lynn Lomeli set a record by spinning 82 Hula Hoops at the same time for three full revolutions", + "The wheelbarrow was invented by the Chinese.", + "The colour blue has a calming effect. It causes the brain to release calming hormones", + "Over 20 million BluBlocker sunglasses have been sold since its debut in 1986. They now come in over 100 different styles", + "Crabs have very small hairs on their claws and other parts of their body to help detect water currents and vibrations", + "In 1962, the first Wal-Mart opened up in Rogers, Arkansas", + "Duracell, the battery-maker, built parts of its new international headquarters using materials from its own waste", + "Vampire bat saliva has been responsible for many advances in research into stroke recovery", + "During the making of the the movie 'Fight Club,' actor Brad Pitt chipped his tooth. However, he did not get his tooth capped until after the movie was done filming as he thought it would look better chipped for his character", + "Clans of long ago that wanted to get rid of their unwanted peoplewithout killing them used to burn their houses down - hence theexpression 'toget fired.'", + "In ancient Rome, it was considered a sign of leadership to be born with a crooked nose", + "The word 'checkmate' in chess comes from the Persian phrase 'Shah-Mat,' which means the king is dead", + "If you yelled for 8 years, 7 months and 6 days, you would have produced enough sound energy to heat one cup of coffee", + "A common drink for Tibetans is Butter Tea which is made out of butter, salt, and brick tea", + "Bourbon was first made by a Baptist minsister from Bourbon County in Kentucky in 1789. That is where it got its name", + "The Pentagon, in Arlington, Virginia, has twice as many bathrooms as is necessary. When it was built in the 1940s, the state of Virginia still had segregation laws requiring separate toilet facilities for blacks and whites", + "McDonald restaurants serve food and drink to an amazing 43 million customers on a daily basis", + "The game Monopoly was once very popular in Cuba; however, Fidel Castro ordered that all games be destroyed", + "Nearly half of all Americans suffer from symptoms of burnout", + "During the era of Louis XIV, women used lemons to redden their lips", + "70% of the poor people in the world are female", + "The thickness of the Arctic ice sheet is on average 10 feet. There are some areas that are thick as 65 feet", + "The adult human body requires about 88 pounds of oxygen daily", + "The biggest pumpkin the world weighs 1,337.6 pounds", + "The highest consumption of Pizza occurs during Super Bowl week", + "During World War II, condoms were used to cover rifle barrels from being damaged by salt water as the soldiers swam to shore", + "Approximately 55% of movies released are Rated R", + "The Roman emperor Domitian took great pleasure in being secluded in his room for hours and catching flies and stabbing them with pens", + "Tarantulas can live up to 30 years", + "On average redheads have 90,000 hairs. People with black hair have about 110,000 hairs", + "More than half the time spent in United States courts is cases that involve automobiles", + "One barrel of petroleum holds 42 gallons", + "The smoke that is produced by a fire kills more people than a burn does because of carbon monoxide and other dangerous gases", + "The Saguaro Cactus, found in South-western United States does not grow branches until it is 75 years old.", + "In Belgium, 172,000 tons of chocolate are produced in a year", + "The word Nike comes from Greek Mythology. Nike is the goddess of victory and was often depicted as a small winged figure whom the goddess Athene carried", + "The biggest bug in the world is the Goliath Beetle which can weigh up to 3.5 ounces and be 4.5 inches long", + "Leaving the water running while brushing your teeth can waste four gallons of water in a minute", + "Steve Fletcher holds the record for the largest gum wrapper collection. His collection has 5300 gum wrappers from all across the world", + "There was once a country called Prussia. After World War II, it was divided among Poland, Germany, and the USSR", + "The word Spain means 'the land of rabbits.'", + "In 1936, the first practical helicopter was invented. It was the German Focke-Wulf Fw 61.", + "The word tulip comes from the Turkish word for turban", + "Psychokinesis refers to the ability of moving objects through psychic power", + "The fat that comes from sheep, which is called tallow, can also be used to produce soap and candles", + "In Britain, one out of every four potatoes is eaten in the form of french fries", + "In the movie Psycho by Alfred Hitchcock, chocolate syrup was used for blood in the shower scene", + "The movie 'Chicken Run' made in 2,000 had the most plasticine used in an animated movie. They used 2,380 kg of plasticine for the movie", + "During WWII, because a lot of players were called to duty, the Pittsburgh Steelers and Philadelphia Eagles combined to become The Steagles", + "The titan arum flower is the largest flower in the world and gives off a horrible odor that smells like rotting flesh when it blooms", + "Every day, the average person swallows about a quart of snot", + "When the only queen ant dies, so does the entire colony, because no new workers are born", + "The Dutch people are known to be the tallest people in Europe", + "The average American kid will eat approximately 1.500 peanut butter sandwiches by high school graduation", + "Goats do not have upper front teeth", + "It has been suggested that shepherds are responsible for inventing the game golf. It is said that they used to use their staffs to hit the stones", + "There are about 6,800 languages in the world", + "Studies have shown that by putting on slow background music it can make a person eat food at a slower rate", + "By walking an extra 20 minutes every day, an average person will burn off seven pounds of body fat in an year", + "Ironically, when doctors in Los Angeles, California went on strike in 1976, the daily number of deaths in the city dropped 18%", + "Octopus and squid are thought to be the most intelligent of all invertebrates", + "On average, a beaver can cut down two hundred trees a year.", + "The name of the first menthol cigarette in the United States was 'Spud.'", + "A world record 328 pound ovarian cyst was removed from a woman in Galveston, Texas, in 1905. updated", + "The fastest shark is the 'Shortfin Mako,' which can swim as fast as sixty miles per hour", + "The flatulation from domesticated cows produce about 30% of the methane on this planet", + "Ironically, watermelons, which are 92% water, originated from the Kalahari Desert in Africa", + "The first tattoo machine was invented by Samuel O'Reilly. He did this by using equipment that Thomas Edison used to engrave hard surfaces.", + "In a lifetime, an average human produces 10,000 gallons of saliva", + "A slug has four noses", + "Chili Powder was invented in the 19th century in the American Southwest", + "The sea cucumber spills its internal organs out as a defense mechanism", + "Approximately 25,000 workers died during the building of the Panama Canal and approximately 20,000 of them contracted malaria and yellow fever", + "Braces were first invented by Pierre Fauchard in 1728. The braces were made by a flat strip of metal, which was connected to the teeth by thread.", + "Marilyn Monroe had six toes", + "There is a town in Texas called Ding Dong. In 1990, the population was only twenty-two people", + "The total volume of mail that went through the Canadian postal system in 1950 was 1,362,310,155 items", + "The highest toll paid by a ship to cross the Panama Canal was by the Crown Princess on May 2, 1993 in the amount of $141,349.97 U.S. funds", + "The name of the famous snack 'Twinkies' was invented by seeing a billboard in St. Louis, that said 'Twinkle Toe Shoes.'", + "The word 'Nazi' is actually an abbreviation for Nationalsozialistische Deutsche Arbeiterpartei, which refers to the National Socialist German Workers Party", + "The unique characteristics of Barbie dolls in Japan are that they have their lips closed with no teeth showing", + "The Coca Cola company offers more than 300 different beverages", + "Neptune was the first planet in our solar system to be discovered by mathematics", + "Five percent of the people who use personal ads for dating are already married", + "Camel is considered unclean meat in the Bible", + "Soldier Field is the oldest field in the NFL", + "In the U.S., over one million gallons of cosmetics, drinks, and lotions are sold that contain aloe in them per year", + "The name Jeep came from the abbreviation used in the army for the'General Purpose' vehicle, G.P.", + "Eating eight strawberries will provide you with more Vitamin C than an orange", + "The first toilet ever seen on television was on 'Leave It To Beaver'.", + "Mosquitoes have teeth", + "To be born on Sunday was considered a sign of great sin during the Puritan times", + "The citrus soda '7 UP' was created in 1929. The original name of the popular drink was 'Bib-Label Lithiated Lemon-Lime Soda', but it got changed to '7 UP.'", + "The average four year-old child asks over four hundred questions a day", + "Prosopagnosia refers to the inability to identify people by their faces. In severe cased prosopagnosia a person may not be able to identify themselves in a mirror", + "On November 29, 2000, Pope John Paul II was named an 'Honorary Harlem Globetrotter.'", + "An adult sheep can eat between 1 to 4 kg of food per day", + "In 1888, Hollywood was founded by Harvey and Daeida Wilcox, who named the city after their summer home in Chicago", + "Blood is such a good stain that Native Americans used it for paint", + "In 1876, the first microphone was invented by Emile Berliner.", + "'I am.' is the second shortest complete sentence in the English language", + "On average, a person will spend about five years eating during their lifetime", + "Each king in a deck of playing cards represents a great king from history. Spades - King David, Clubs - Alexander the Great, Hearts - Charlemagne, Diamonds - Julius Caesar", + "Many cancer patients that are treated with chemotherapy lose their hair. For some when the hair grows back, it can grow back a different colour, or be curly or straight", + "A volcano has enough power to shoot ash as high as 50 km into the atmosphere", + "The longest hiccups on record was by an American pig farmer whose hiccups persisted from 1922 to 1987", + "Coupons were introduced in 1894 when Asa Candler bought the Coca-Cola formula for $2,300 and gave people coupons that he had written out to receive a free glass of coke", + "Panthers are known as black leopards, as they are the same species of leopard. If looked at closely, black spots can be seen on a panther", + "Approximately 25% of all scald burns to children are from hot tap water and is associated with more deaths than with any other liquid", + "In London, during rush hour traffic moves on average at 13 kilometres an hour", + "Tomatoes and cucumbers are fruits", + "In the United States, approximately 50 million people fish per year", + "Cattle can produce up to 180 litres of saliva in one day", + "Dolphins hear by having sound waves transmit through their skull to their inner ear region", + "Teflon was accidently discovered by scientist Dr. Roy Plunkett while he was conducting a coolant gas experiment in 1938", + "The risk of cardiovascular disease is twice as high in women that snore regularly compared to women who do not snore. updated", + "Close to 80% of people who watch the Super Bowl on television, only do so to view the commercials", + "The first theatre to show motion pictures was the Nickelodeon on June 19, 1905 in Pittsburgh, Pennsylvania. It was opened by Harry Davis on Smithfield Street", + "The White House has a movie theater, swimming pool, bowling lane, jogging track, and a tennis court", + "About two hundred years before the birth of Christ, the Druids used mistletoe to celebrate that winter was approaching", + "Cats have over one hundred vocal sounds, dogs only have about ten", + "A butterfly can see the colors red, green, and yellow", + "In the game of Monopoly, the most landed on properties are B&O Railroad, Illinois Avenue, and 'Go.'", + "The airplane Buddy Holly died in was the 'American Pie.' (Thus the nameof the Don McLean song.)", + "Lions cannot roar until they reach the age of two.", + "A baby kangaroo is called a joey", + "Montreal is the second largest French speaking city after Paris", + "There were 43,687 toilet related accidents in the United States in 1996", + "In Albania, nodding your head means 'no' and shaking your head means 'yes.'", + "Ringo Starr appeared in a Japanese advertisement for apple sauce. Ironically his name means 'apple sauce' in Japanese", + "The average US worker toils for two hours and 47 minutes of each working day just to pay income tax. Indeed, the average American pays more in taxes than for food, clothing and shelter put together", + "There is cyanide in apple pips", + "True spiders always have organs for spinning silk known as spinnerets", + "Great Britain has the highest consumption of ice cream than any other European nation", + "Every continent has a city called Rome", + "The movie 'Cleopatra' cost $44 million to make in 1963. The same movie would now cost $300 million to make taking inflation into account", + "A species of dolphin is born naturally blind in the Indus and Ganges rivers in South Asia. These dolphins have a highly sophisticated sonar system and swim on only one side of their body", + "Kermit the Frog was named after Kermit Scott, a childhood friend of creator Jim Henson, who became a professor of philosophy at Purdue University", + "Weatherman Willard Scott was the first Ronald McDonald", + "Aztec emperor Montezuma had a nephew, Cuitlahac, whose name meant 'plenty of excrement.'", + "Hang On Sloopy is the official rock song of Ohio.", + "Actor Sylvester Stallone once had a job as a lion cage cleaner", + "Play-Doh was introduced in 1956 by Hasbro Inc. The only color availabe was an off white, and it came in one size which a one and a half pound can", + "The USSR launched the world's first artificial satellite, Sputnik 1, in 1957.", + "An oyster can change its gender", + "From all the states, Montana has the most different species of animals", + "The actual smallest sovereign entity in the world is the Sovereign Military Order of Malta (S.M.O.M.). It is located in the city of Rome, Italy, has an area of two tennis courts, and as of 2001 has a population of 80, 20 less people than the Vatican. It is a sovereign entity under international law, just as the Vatican is", + "Queen Elizabeth I always wore a necklace with a little perfume bottle attached everywhere she went", + "A group of people that are hired to clap at a performance are called a claque", + "The tallest tree recorded is located in Humboldt Redwoods State Park, California. It is a coast redwood and has been measured at 117 metres high", + "In 1926, a waiter in Budapest committed suicide. He left his suicide note in the form of a crossword and the police had to get help from the public to solve it", + "Anti-American demonstrators protesting in Bangladesh after the September 11, 2001 terrorist attacks carried posters of Osama bin Laden sitting alongside Bert, a beloved Sesame Street Muppet character", + "Polar bear livers contain so much Vitamin A that it can be fatal if eaten by a human", + "Leather skin does not have any smell. The leather smell that you sense is actually derived from the materials used in the tanning process", + "Finland is also known as 'the land of of the thousand lakes,' because of the over 188,000 lakes found in this country", + "In an year, an average American kid eats 46 slices of pizza", + "In Las Vegas, casinos do not have any clocks", + "Bubble gum contains rubber", + "When Kleenex was first introduced to the market in 1924, it was marketed as a make up or cold cream remover", + "In the year 1900, for a women to be a telephone operator she had to be between the ages of 17 and 26 and not be married", + "The first spacecraft to visit the planet Venus was Mariner 2 in 1962.", + "Humans and dolphins are the only species that have sex for pleasure", + "Babies that are exposed to cats and dogs in their first year of life have a lower chance of developing allergies when they grow older", + "Urophobia is the fear of urine or urinating", + "In 1949, forecasting the relentless march of science, Popular Mechanics said 'Computers in the future may weigh no more than 1.5 tons.'", + "Water that is safe to drink is referred to as POTABLE", + "Actor John Ritter was the voice of Clifford, from 'Clifford The Big Red Dog.'", + "Chocolate accounts for less than two percent of the fat in the American diet", + "The band Duran Duran got their name from an astronaut in the 1968 Jane Fonda movie 'Barbarella.'", + "The accent that Mike Myers used for the character Shrek came from the accent that his mother would use when she was telling him bedtime stories when he was a child", + "In the Netherlands, there are special traffic lanes for bicycles. There are approximately 17,000 kms of cycle lanes with special bicycle traffic lights.", + "It was believed by Ancient Hindus that the world was a sphere and rested on the back of four elephants, which stood on a turtle", + "Alexander the Great and Julius Caesar were both epileptic", + "Every year, more than one million miles of Twizzlers licorice is made", + "A penguin swims at a speed of approximately 15 miles per hour", + "The word Thailand means 'land of the free.'", + "A rose imprint that was fossilized in a slate was discovered in Florisant, Colorado, which is said to be thirty-five million years old", + "U.S. bills are 2.61 inches wide, 6.14 inches long, and are .0043 inches thick and weigh 1 gram", + "The highest mountain in the Western Hemisphere is Mount Aconcagua in Argentina. It rises 22,834 feet above sea level", + "The Barn Owls hearing is so highly developed that they can hunt for their prey in total darkness", + "The average number of bridesmaids at a wedding is four", + "Average number of people airborne over the US any given hour: 61,000", + "In 1876, Maria Spelterina was the first woman to ever cross Niagara Falls on a high wire", + "On April 6, 1925, the first in-flight movie was shown. It was a silent film and appeared on a Deutsche Luft Hansa flight", + "The temperature of lightning bolts is sometimes hotter than the surface of the sun.", + "When Burger King introduced the Whopper Sandwich in 1957, it cost only thirty-seven cents", + "Chopsticks originated from China approximately 4,000 years ago", + "The favorite honeymoon place is Hawaii", + "The 1960 Summer Olympics were the first Olympics to be aired on television by CBS", + "The Canadian holiday Boxing Day got its name from the custom of giving. Servants were given boxes which had money hidden inside them from their employers. The servants would have to break the box into pieces to get the money", + "In proportion, if Jupiter were a basketball, then the sun would be the size of the Louisiana Super Dome", + "The Toronto Maple Leafs used to be called the Toronto Arenas, then the St. Patricks and finally the Maple Leafs", + "A dime has 118 ridges around the edge", + "Next to Warsaw, Chicago has the largest Polish population in the world", + "In 1391, China began producing toliet paper for use by its Emperors", + "In the United States, the most frequent month for a tornado to occur is in May.", + "There are some ice creams that are 75% air", + "In the United States, lightning hits the ground 40 million times a year.", + "A mother hen turns her egg approximately 50 times in a day. This is so the yolk does not stick to the shell", + "The reason why flamingos are pink is because they eat shrimp which have a red pigment", + "Totally Hair Barbie is the best selling Barbie of all time. It sold over ten million units", + "Jellyfish have been on Earth for over 650 million years. This is before sharks and dinosaurs", + "Although white wine can be produced from both red and white grapes, red wine can only be created from red grapes", + "Shirley Temple was considered to play the role of Dorothy in 'The Wizard of Oz.'", + "Babies that wear disposable diapers are five times more likely to get a diaper rash than babies wearing a cotton diaper", + "One million cloud droplets are needed to make enough water to produce one raindrop.", + "In the world, the Netherlands has the highest concentration of museums in the world. Just in Amsterdam alone there are 42 museums", + "Amongst pre-schoolers, Caillou is the fastest-ever-growing television show and is seen in close to 97% of U.S. households", + "Rice flour was used to strengthen some of the bricks that make up the Great Wall of China", + "Research has indicated that a tie that is on too tight can increase the risk of glaucoma in men", + "Each year all of the Hostess bakeries combined bake 500 million Twinkies a year. (A twinkie is a sponge cake with a creamy filling.)", + "Charlie Chaplin once lost a contest for a Charlie Chaplin look a like", + "Pluto is the only planet in our solar system that has not been visited by a spacecraft", + "The worlds tallest free fall rollercoaster is The Giant Drop located in Australia. The drops is 120 meters which is equivalent to a 39 storey building", + "Stalks of sugar cane can reach up to 30 feet", + "The markings that are found on dice are called 'pips.'", + "Joseph Gayetty is credited for inventing toilet paper in 1857. Unfortunately, his invention failed and did not catch on until ten years later", + "A newly hatched fish is called a 'fry.'", + "The music band UB40 got its name from an unemployment form in England", + "The Olympic Flame was introduced in 1928 in Amsterdam", + "The YKK on the zipper of your Levis stands for Yoshida Kogyo Kabushibibaisha, the worlds largest zipper manufacturer", + "Armadillos can be housebroken", + "The Eisenhower interstate system requires that one-mile in every five must be straight. These straight sections are usable as airstrips in times of war or other emergencies", + "The material to build the Taj Mahal was brought in from various parts of India by a fleet of 1000 elephants", + "Medical research has found substances in mistletoe that can slow down tumor growth", + "In the USA, 32% of employees eat lunch and work at the same time", + "In Alabama, it is against the law to wear a fake mustache that could cause laughter in the church", + "In the United States, more than 4.2 million couples live together that are not married", + "Bill Gates house was partially designed using a Macintosh computer. new", + "The male howler monkey of Central and South America is the noisiest land animal, which can be heard clearly from a distance of ten miles away", + "Nerve cells can travel as fast as 120 metres per second", + "It is said that grapefruit got its name because it grows like grapes in clusters. One cluster can have up to 25 grapefruits", + "Abdul Kassam Ismael, Grand Vizier of Persia in the tenth century, carried his library with him wherever he went. Four hundred camels carried the 117,000 volumes", + "An average adult produces about half a litre of flatulent gas per day, resulting in an average of about fourteen occurrences of flatulence a day", + "Maine is the toothpick capital of the world", + "Peanut butter is an effective way to to remove chewing gum from hair or clothes", + "The longest kiss on record lasted 30 hours and 45 minutes. Dror Orpaz and Carmit Tsubara recorded it on April 5, 1999 at a kissing contest held in Tel Aviv, Israel", + "Polar bears are excellent swimmers. They have been known to swim more than 60 miles without a rest", + "The most expensive perfume in the world is Parfum VI, which was made by Arthur Burnham. A 4 inch bottle which is covered with diamonds and 24-carat gold costs $71,380", + "If Wal-Mart was classified as a country, it would be the 24th most productive country in the world", + "Cimeti?re du P?re Lachaise located in Paris is the most visited cemetery in the world. The cemetery opened in 1805 and has over one million people buried there, including rock star Jim Morrison", + "In Australia, a dust-devil is called a 'willy-willy", + "The Leaning Tower of Pisa is 58.36 metres above the ground", + "Americans write approximately 50 billion checks a year making it the second most frequent payment method used after cash", + "The name 'Snickers' for the popular candy bar was named after a horse that the Mars family owned", + "The #1 peanut producing state is Georgia", + "Keeping Warm With an Axe, is the title of a real how-to book. Click Here For More Details", + "An artificial Christmas tree last up to six years in a home", + "Women are four times more likely to have foot problems than men", + "In 1783, the hot air balloon was invented in France.", + "There was an army general during the Liberia Civil War who used to lead his army into battle naked. His nickname was 'General Butt Naked.' Joshua Milton Blahyi (his real name) is now an evangelical preacher in Monrovia", + "There are no two zebras who have stripes that are exactly the same", + "The Angel Falls in Venezuela were named after an American pilot, Jimmy Angel, whose plane got stuck on top of the mountain while searching for gold", + "Lake Ontario was originally named Lake St. Louis", + "Actor John Travolta was offered the role of Billy Flynn many times for the movie 'Chicago.' Richard Gere ended up playing the role", + "The Canadian province of New Brunswick had a bloodless war with the US state of Maine in 1839", + "There are more than 2,400 flea species in the world", + "Ninety-nine percent of pumpkins sold in the United States are for the sole purpose of decoration", + "David McConnell started the California Perfume Company (CPC) in 1886. Today the company is known as Avon, which he named after his favorite playwright William Shakespeare, and Stratford on Avon", + "Americans did not commonly use forks until after the Civil War", + "Chicago has the largest cookie factory, where Nabisco made over 4.6 billion 'Oreo' cookies in 1997", + "In 1963, Mister Rogers was ordained as a Presbyterian minister", + "There was a post office on the Russian space station Mir. Visiting cosmonauts would use unique postal 'markers' to stamp envelopes and other items as having flown aboard the Mir space station", + "In one day, 230 marriage licenses are issued in Las Vegas", + "Every second there are 418 Kit Kat fingers eaten in the world", + "The Great Comet of 1843 had a tail that was over 300 kilometres long.", + "The dumbest dog in the world is the Afghan Hounds", + "There are no blossoms on the branches of a fig tree, instead it is inside the fruit", + "The largest chicken egg ever laid weighed a pound and had a double yolk and shell", + "Billiards used to be so popular at one time that cigarette cards were issued featuring players", + "Chewing gum has rubber as an ingredient", + "An orca whale can hold its breath for up to 15 minutes", + "Alexander the Great was an epileptic", + "Wood frogs can be frozen solid and then thawed, and continue living. They use the glucose in their body to protect their vital organs while they are in a frozen state", + "Canadians eat more Kraft Dinner (Macaroni and Cheese) per capita than any other country in the world", + "In a day, a mature oak tree can draw approximately 50 gallons of water", + "The reason why bubble gum is pink is because the inventor only had pink colouring left. Ever since then, the colour of bubble gum has been predominantly pink", + "Emilio Marco Palma was the first person born in Antarctica in 1978", + "A top freestyle swimmer achieves a speed of only 4 miles per hour. Fish, in contrast, have been clocked at 68 mph", + "Every single hamster in the United States today comes from a single litter captured in Syria in 1930", + "Research on pigs led to the development of CAT scans.", + "The Hundred Years War lasted for 116 years", + "Some dolphins can swim up to 40 kilometers an hour", + "In the last 30 years, only seven people have been killed by a polar bear in Canada", + "The longest U.S. highway is Route 20, which is over 3,365 miles", + "The largest LEGO castle that was ever built was built with 400,000 LEGO bricks and was 4.45 m x 5.22 m", + "In the U.S. there are approximately 65.8 million cats", + "One of the steepest main streets in Canada is located in Saint John, New Brunswick. Over a distance of two blocks the street rises about 80 feet", + "Avery Laser Labels are named after company founder R. Stanton Avery", + "The highest point in Pennsylvania is lower than the lowest point in Colorado", + "On September 9, 1950 dubbed laughter was used for the first time on television. It was used for the sitcom 'The Hank McCune Show.'", + "A violin actually contains 70 separate pieces of wood", + "The human heart can create enough pressure that it could squirt blood at a distance of thirty feet", + "One out of four American households own a cat", + "Queen Lydia Liliuokalani was the last reigning monarch of the Hawaiian Islands. She was also the only Queen the United States ever had", + "Every day 2,700 people die of heart disease", + "There are 10 million bacteria at the place where you rest your hands at a desk", + "The quills of a porcupine are soft when they are born", + "An average American child watches approximately 28 hours of television in one week", + "Quality standards for pasta were set in the 13th century by the Pope", + "The A.A. Milne character of Winnie the Pooh made his animated film debut in 1966 in Winnie the Pooh and the Honey Tree", + "People have the tendency to chew the food on the side that they most often use their hand", + "Antarctica is the only land on our planet that is not owned by any country", + "The Pentagon, in Arlington, Virginia, has twice as many bathrooms as isnecessary. When it was built in the 1940s, the ste of Virginia stillhad segregation laws requiring separate toilet facilities for blacks andwhites.", + "The Lemon shark grows about 24,000 new teeth a year. A new set of teeth grow approximately every 14 days", + "One billion seconds is about 32 years", + "An average American eats approximately 60 hot dogs per year", + "Iceland consumes more Coca-Cola per capita than any other nation", + "The water displacement product, WD-40, can be found in 80% of American homes", + "Dexter is the smallest type of cow. This cow was bred to be a small size for household living", + "As part of the original design, the names of 72 French scientists and other famous people is imprinted on the sides of the Eiffel tower", + "The first domain name ever registered was Symbolics.com", + "Thirty to 40 gallons of sugar maple sap must be boiled down to make just one gallon of maple syrup", + "The most frequent season for most suicides to occur is in the spring. The winter months have the lowest number of suicides", + "A seven year old boy was the first person to survive the Horeshoe Falls (Niagara Falls) in just a life jacket", + "The longest punt return for a touchdown was 103 yards", + "The most popular Hot Wheels vehicle sold is the Corvette", + "A giraffe is able to clean its ears with its own tongue", + "The slowest growing finger nail is on the thumb nail and the fastest growing is the finger nail on the middle finger", + "Flu shots only work about 70% of the time", + "People of Salt Lake City eat the most lime-flavoured gelatin Jell-O in the United States", + "In a survey conducted in 2000 by Kimberly-Clark, it was found that men prefer to fold their toilet paper, and women like to wad it", + "On average, a person has two million sweat glands", + "France, Switzerland, United Kingdom, Greece, and Australia have always been in the modern Olympics since it began in 1896", + "The longer white infants from low-income families are breast-fed, the less likely they will be overweight as young children, researchers said on Monday", + "111,111,111 x 111,111,111 = 12,345,678,987,654,321", + "The Boston University Bridge (on Commonwealth Avenue, Boston, Massachusetts) is one of the few places in the world where a boat can sail under a train driving under a car driving under an airplane", + "The most popular treat for Halloween trick-or-treaters are candy bars with Snickers being the most popular", + "Corned beef got its name because this beef was preserved with pellets of salt that were the size of corn kernels, which was also referred to as 'corns' of salt", + "The Canandian province of New Brunswick had a bloodless war with the US state of Maine in 1839.", + "In 1908, the first machine to make lollipops opened for business in New Haven, Connecticut", + "In 1976, a Los Angeles secretary named Jannene Swift officially married a fifty pound rock. More than twenty people witnessed the ceremony", + "The most diners per capita in the world are located in the U.S. state New Jersey", + "In Denmark, people eat about 36 pounds of candy a year. The highest consumption of candy of any country", + "John Van Wormer invented paper milk cartons after dropping a bottle of milk one morning. The bottle broke spilling the milk everywhere. That annoyance was enough for Van Wormer to come up with the idea.", + "The long fibres that are found in bananas are excellent in making paper. The long fibres that are found in the banana plant can make the banana fibre paper approximately 3000 times stronger than regular paper", + "The state of Tennessee was known as Franklin before 1796", + "Over 90% of poison exposures occur in homes", + "Honolulu, Hawaii boasts the only royal palace in the United States of America", + "Seven asteroids were especially named for the Challenger astronauts who were killed in the 1986 failed launch of the space shuttle", + "Americans consumed more than twenty billion hot dogs in 2000", + "The production of toilet paper in China began in 1391, which was used for the Emperors", + "The reason firehouses have circular stairways is from the days of yorewhen the engines were pulled by horses. The horses were stabled on theground floor and figured out how to walk up straight staircases.", + "The cigarette lighter was invented before the match.", + "It would take approximately twenty-four trees that are on average six to eight inches in diameter to produce one ton of newsprint for the Sunday edition of the New York Times", + "Every year, kids in North America spend close to half a billion dollars on chewing gum", + "The tuatara lizard of New Zealand has three eyes, two in the center of its head and one on the top of its head", + "The world population of chickens is about equal to the number of people", + "The largest number of children born to one woman, who was a Russian peasant is 69", + "In a lifetime, an average driver will release approximately 912 pints of wind inside a car", + "The loss of eyelashes is referred to as madarosis", + "Approximately 75% of human poop is made of water", + "The popular chocolate bar 'Three Musketeers' got its name because when it was first introduced in 1932 there were three individual bars. The flavours were strawberry, chocolate, and vanilla", + "Every photograph of the first American atomic bomb detonation was taken by Harold Edgerton", + "Heinz Catsup leaving the bottle travels at 25 miles per year", + "In 1864, A Quebec farmer found a frog inside a hailstone", + "Actor Sylvester Stallone once had a job as a lion cage cleaner", + "The first time there was an instance where they had a separate toilet for women and men was in 1739 at a ball in Paris", + "In the marriage ceremony of the Ancient Inca Indians of Peru, the couple was considered officially wed when they took off their sandals and handed them to each other", + "Maine is the only state whose name is just one syllable", + "Some birds have been know to put ants into their feathers because the ants squirt formic acid, which kills parasites", + "On average, 42,000 balls are used and 650 matches are played at the annual Wimbledon tennis tournament", + "The WD in WD-40 stands for Water Displacer", + "Lachanophobia is the fear of vegetables", + "Lake Baikal, in Siberia, is the deepest lake in the world", + "In the Middle Ages, peacocks and swans were sometimes served at Christmas dinners", + "India has the most post offices in the world", + "Women take three times longer than men when using the toilet", + "In America, approximately 25% of kids aged 6-14 have a magaznie subscription", + "Canada has more donut shops per capita than the United States", + "In 1886, Coca-cola was first served at a pharmacy in Atlanta, Georgia for only five cents a glass. A pharmacist named John Pemberton created the formula for Coca-cola", + "75% of all raisins eaten by people in the United States are eaten at breakfast", + "Whale oil was used in some car transmissions until 1973", + "Flamingos are able to fly at a speed of approximately 55 kilometers an hour. In one night they can travel about 600 km", + "1 out of every 4 kids in the USA is overweight", + "'Kemo Sabe' means 'soggy shrub' in Navajo", + "In 1903, there were originally only eight Crayola crayons in a box and they sold for five cents", + "Men are able to read fine print better than women can", + "On average, 150 couples get married in Las Vegas each day", + "Spiders usually have eight eyes, but still they cannot see that well", + "One ragweed plant can release as many as a million grains of pollen in one day", + "Women hearts beat faster than men", + "The Central African raffia palm is known to have the longest leaves. The leaves can measure up to 82.5 feet long.", + "Due to the shortages of lead and metals during World War II, toothpaste was packaged in plastic tubes and have been ever since", + "It is estimated that 93% of American children will go out trick or treating for Halloween", + "In humans, the epidermal layer of skin, which consists of many layers of skin regenerates every 27 days", + "A group of crows is called a murder", + "Ellen Macarthur, yachtswoman, had a total of 891 naps in 94 days that were each 36 minutes long while on her Vendee Round the Globe yacht race", + "Davao City, located at the Southern state of Philippines, is the largest city in the world in terms of area", + "Castaways Travel, a Houston-area travel agency, offers an all-nude flight to Cancun Mexico. Once the plane reaches cruising altitude, you are allowed to take off all your clothes and roam about the cabin", + "People generally read 25% slower from a computer screen compared to paper", + "Certain female species of spiders such as the Australian crab spider, sacrifice their bodies as a food source for their offspring", + "One grape vine produce can produce about 20 to 30 glasses of wine", + "The TV show 'Saturday Night Live' made its debut on October 11, 1975", + "In a pack of Skittles candy, there is an equal 20% distribution of each flavour", + "The names of the two stone lions in front of the New York Public Library are Patience and Fortitude. They were named by then-mayor Fiorello LaGuardia", + "The Hubble telescope is so powerful that it is like pointing a beam of light at a dime that is two hundred miles away.", + "The word 'umbrella' is derived from the Latin root word 'umbra', which means shade or shadow", + "An ear of corn consists of 80% water", + "Leonardo Da Vinci never signed or dated his most famous painting, the Mona Lisa", + "On average people fear spiders more than they do death", + "Every day, over five billion gallons of water are flushed down toilets in the United States", + "In one trip, a honey bee visits about 75 flowers", + "Barney, the famous dinosaur that entertains kids is from Dallas", + "Jupiter is the fastest rotating planet, which can complete one revolution in less than ten hours", + "A chicken loses its feathers when it becomes stressed", + "Nutmeg is extremely poisonous if injected intravenously", + "The first Tupperware item marketed was the seven-ounce bathroom cup in 1945", + "Sharks are immune to cancer", + "Manicuring the nails has been done by people for more than 4,000 years", + "Approximately 1 billion stamps are produced in Australia annually", + "The study of the iris of the eye is called iridology", + "Back in 1919, the Russian transplant pioneer Serge Voronoff made headlines by grafting monkey testicles onto human males.", + "The word Cotton originates from the Arabic word 'Qutun.'", + "In 1946, the New York Yankees became the first baseball team to travel by plane", + "Mummy powder was once thought to be a cure for all remedies. English men used to carry the powder with them in a tiny bag wherever they went", + "By recycling just one glass bottle, the amount of energy that is being saved is enough to light a 100 watt bulb for four hours", + "Slinkys were invented by an airplane mechanic; he was playing with engine parts and realized the possible secondary use of one of the springs", + "The most popular brand of raisins is Sunmaid", + "Estuarine crocodiles are the biggest of all 26 species of the crocodilian family", + "Alaska got its name from the Aluet word 'Alyeska' which means 'The Great Land.'", + "The Mall of America, located in Bloomington, Minnesota is so big that it can hold 24,336 school buses", + "Every second, 630 steel cans are recycled", + "The word witch comes from the word 'wicca' which translates to the 'wise one.'", + "If you have three quarters, four dimes, and four pennies, you have$1.19. You also have the largest amount of money in coins without beingable tomake change for a dollar.", + "In the United States, poisoning is the fourth leading cause of death among children", + "Surveys indicate that the number one reason people play BINGO is for leisure", + "In 1916, Charlie Chaplin was making $10,000 a week, making him the highest paid actor of his time", + "Annually, an Australian eats 15 kg of bananas, which comes out to 27 meters of bananas", + "The largest stamp was issued by China and measured 210 x 65 mm", + "Nazi leader Adolf Hitler had only one testicle", + "It's possible to lead a cow upstairs...but not downstairs.", + "The reason firehouses have circular stairways is from the days when the engines were pulled by horses. The horses were stabled on the ground floor and figured out how to walk up straight staircases", + "The best selling Crayola crayon box is the set of 24 crayons", + "People that smoke have 10 times as many wrinkles as a person that does not smoke", + "Thomas Edison was afraid of the dark. (Hence, the light bulb?)", + "The name of the character that is behind bars in the Monopoly board game is Jake the Jailbird", + "In Colorado, there are about 83,000 dairy cows", + "Just by recycling one aluminum can, enough energy would be saved to have a TV run for three hours.", + "The first telephone call from the White House was from Rutherford Hayes to Alexander Graham Bell", + "Turtles can breathe through their butts", + "A pregnant goldfish is called a twit", + "The name Wendy was made up for the book 'Peter Pan.'", + "A glockenspiel is a musical instrument that is like a xylophone. It has a series of metal bars and is played with two hammers", + "Teenage suicide is the second cause of death in the state of Wisconsin", + "Diamonds were first discovered in the riverbeds of the Golconda region of India over 4,000 years ago.", + "French artist, Michel Vienkot, uses cow dung as paint when he creates his pictures", + "Canada is an Indian word meaning 'village' or 'settlement.'", + "There are 122 pebbles per square inch on a Spalding basketball", + "The seventeenth president of the United States, Andrew Johnson did not know how to read until he was 17 years old", + "The fastest growing tissue in the human body is hair", + "Bhutan issued a stamp in 1973 that looked like a record and actually would play the Bhutanese national anthem if placed on a record player", + "Asparagus comes in three colors: green, white and purple", + "Only two people signed the Declaration of Independence on July 4th, JohnHancock and Charles Thomson. Most of the rest signed on August 2, butthe last signature wasn't added until 5 years later.", + "A cubic yard of air weighs about 2 pounds at sea level.", + "Pancakes are served for breakfast, lunch and dinner in Australia", + "A lion feeds once every three to four days", + "A honey bee has four wings", + "Chedder cheese is the best selling cheese in the USA", + "In the movie 'The Matrix Reloaded' a 17 minute battle scene cost over $40 million to produce", + "According to research, Los Angeles highways are so congested that the average commuter sits in traffic for 82 hours a year", + "Over one million Pet Rocks were sold in 1975, makine Gary Dahl, of Los Gatos, California, a millionaire. He got the idea while joking with friends about his pet that was easy to take care of, which was a rock", + "First novel ever written on a typewriter: Tom Sawyer", + "Because metal was scarce; the Oscars given out during World War II were made of plaster", + "The head of a jellyfish is called the 'Bell.'", + "The game Monopoly has been played by approximately 500 million people in the world, and the game is available in 26 languages", + "The dragonfly has not changed over the last 300 million years", + "In 1983, a Japanese artist, Tadahiko Ogawa, made a copy of the Mona Lisa completely out of ordinary toast", + "Average number of days a West German goes without washing his underwear: 7", + "Cotton crops can be sprayed up to 40 times a year making it the most chemical-intensive crop in the world", + "The 'Star Spangled Banner' did not become a national anthem until 1931. It was designated by an Act of Congress", + "Every year in the U.S., there are 178,000 new cases of lung cancer", + "On average, the American household consumes six pounds of peanut butter annually", + "A housefly can only ingest liquid material. They regurgitate their food to liquify the food that they are going to eat", + "Bugs Bunny was originally called 'Happy Rabbit.'", + "In 1685, New France used playing cards as currency because of the shortage of coins", + "Sharks have upper and lower eyelids, but they do not blink", + "The size of a red blood cell is 708 microns. This is equivalent to one millionth of a meter", + "In 1657, the first chocolate house was opened in London, England. The cost of chocolate was about 13 shillings per pound and was a drink that only the elite enjoyed", + "In the movie 'Gandhi' 300,000 extras appeared in the funeral scene. Of the 300,000, approximately 100,000 received a small fee, and the other 200,000 did it for free", + "O.J. Simpson had a severe case of rickets and wore leg braces when he was a child", + "The majority of cats do not have any eyelashes", + "Reindeer like to eat bananas", + "A barnacle has the largest penis of any other animal in relation to its size", + "The hottest chili in the world is the Tezpur chili pepper", + "Over half the textile fibers that are used in the world are cotton", + "You can send a postcard from Hell. There is a small town located in the Cayman Islands called 'Hell.' They even have a post office", + "The property (ID, Facts) VALUES on the Monopoly game board are the same today as they were in 1935", + "The song 'Happy Birthday' brings in about $2 million in licensing revenue to Warner Communications who hold the copyright to the song", + "In 10 minutes, a hurricane releases more energy than all the world'snuclear weapons combined.", + "Ed Cox from San Francisco invented the pot scrubbing S.O.S. pads in 1917. His wife came up with the name, which stands for 'Save Our Saucepans.'", + "The name Wendy was made up for the book 'Peter Pan.'", + "Another word for hiccups is 'singultus.'", + "Better wine can be produced by the soil being of poor quality. This is because the vines have to 'work' harder", + "A white tiger can only be born when both parents carry the gene for white colouring", + "Lipogram refers to writing that does not have certain letter or letters", + "In October 1986, Pepsi paid close to $840 million to Nabisco for the Kentucky Fried Chicken empire", + "Edinburgh has more booksellers per head of population than any other city in Britain", + "The most common rock on Earth is basalt", + "The papaya tree is known as 'the medicinal tree' in some cultures because it?s seeds and leaves have been used as ingredients in different medicines", + "A cat has 32 muscles in each ear", + "In one gram of soil, about ten million bacteria live in it", + "When Alexander Graham Bell invented the telephone back in 1876, only six phones were sold in the first month.", + "Americans eat approximately 20 pounds of pasta per person each year", + "Dolphins sleep with one eye open", + "Minnows have teeth located on a bone in their throat", + "The 20th president of the United States James Garfield could write Greek with one hand and Latin with the other at the same time", + "Japan uses the most energy per year than any other country.", + "Over 436,000 U.S. Troops were exposed to depleted uranium during the first Gulf war", + "Approximately two gallons of water are used to brush your teeth", + "Two-thirds of Canadians live in Quebec and Ontario", + "The first television show to show any portion of a toilet was on 'Leave it to Beaver.' After fighting for ten weeks to show the toilet, CBS would only allow the producers to show the toilet tank, and not the whole toilet", + "Texas is the only state that is allowed to fly its flag at the same height as the U.S. flag", + "German immigrant, Louis Prang was the first to bring Christmas cards to America", + "In total, Americans eat more than 45 billion sandwiches each year, while sales of custom-made sandwiches are rising 15 percent per year", + "Ninety percent of the population has an innie belly button", + "The fear of Halloween is called Samhainophobia", + "In France, the Big Dipper is called the 'casserole.'", + "The first African-American to receive a Nobel Peace Prize was Ralph J. Bunche in 1950", + "Dolphins can swim and sleep at the same time", + "Of all the golfers in Canada, 71.4% golfers are male, 28.6% are female", + "Research indicates that babies who suck on pacifiers are more prone to ear aches", + "In 1917, Margaret Sanger was jailed for one month for establishing the first birth control clinic", + "Iguanas can recognize their human handlers and greet them differently, compared with strangers", + "Being lactose intolerant can cause chronic flatulence", + "Some of the titles that were considered for the hit T.V. show 'Friends' were Six Of One, Across the Hall, and Insomnia Cafe", + "The only bone fully grown at birth is located in the ear", + "The incidents of immune system diseases has increased over 200% in the last five years", + "'dous':tremendous, horrendous, stupendous, and hazardous", + "Bananas contain a natural chemical which can make a person happy. This same chemical is found in Prozac", + "The only desert in Canada is located in Osoyoos, British Columbia", + "The city of Seoul has been the capital city of Korea for more than 600 years", + "Romans used to believe that walnuts could cure head ailments during the Renaissance, since their shape was similar to that of a brain", + "There are coffee flavored PEZ", + "Half of the 42 U.S. Presidents are of Irish descent", + "The General Lee cars used in the popular show The Dukes of Hazards were 1969 Dodge Chargers", + "The word 'limelight' that is used in theatre to refer to the performers on the stage originated because before electricity was available lime was burned in a lamp, which created a white light that was directed at the performers", + "Many hamsters only blink one eye at a time", + "In Kentucky, it is illegal to carry ice cream in your back pocket", + "In 1988, the largest ice cream sundae in history was made. It was made in Edmonton, Alberta, Canada, and weighed in at over 24 tons", + "Sports Illustrated has the largest sports magazine circulation", + "There are some hospitals in Shanghai that have issued a rule that a nurse must wear lipstick while on duty", + "Thirteen percent of the human population reside in deserts", + "There are more chickens than people in the world", + "In 1958, the Crayola crayon color 'Prussian Blue' was changed to 'Midnight Blue' by the request of teachers as kids could not relate to Prussian history", + "Americans on average use about 580 pounds of paper per year per person", + "Wild turkeys can run at speeds of up to 25 miles per hour", + "Annually, fires that occur at home kill more Americans than all natural disasters combined", + "The only sound Seahorses make is a small clicking or popping sound during feeding or courtship", + "The Baltimore Orioles opened the 1988 baseball season by losing the first 21 games and 107 altogether for the entire season", + "Border collies are the most intelligent breed of dog", + "Tug of War was an Olympic event between 1900 and 1920", + "There are 293 ways to make change for a dollar", + "Coca-Cola used to contain cocaine when it was initially introduced", + "All the Krispy Kreme donut stores collectively could make a doughnut stack as high as the Empire State Building in only 2 minutes", + "Pepsi originally contained pepsin, (the same stuff in pepcid AC) thus the name", + "The first letter Vanna White ever turned on the game show Wheel of Fortune was the letter 'T.'", + "Harley Proctor got the idea to name the soap 'Ivory' while he was listening to a bible reading at a church in 1879", + "The average American drinks 400 glasses of milk in a year", + "Canada beat Denmark 47-0 at the 1949 world hockey championships. new", + "111,111,111 x 111,111,111 = 12,345,678,987,654,321", + "Each Jelly Belly jelly belly bean has 4 calories", + "The longest one-syllable word in the English language is 'screeched.'", + "Only 55 percent of all Americans know that the sun is a star", + "Kissing can aid in reducing tooth decay. This is because the extra saliva helps in keeping the mouth clean", + "There is a place called Hell, Michigan. It is about 50 miles from Detroit, Michigan", + "In 1929, the Coca-Cola slogan was 'The Pause That Refreshes.'", + "Bamboo plants can grow up to 36 inches in a day.", + "Since the United Nations was founded in 1945, there have been 140 wars", + "Goat meat contains up to 45 percent less saturated fat than chicken meat", + "Chef Boyardee is actually a real person. His real name is Hector Boiardi and he was born in northern Italy in 1898", + "Bill Bowerman, the co-founder of the shoe company Nike, got his first shoe idea after staring at a waffle iron. This gave him the idea of using squared spikes to make the shoes lighter", + "In 1989, the space shuttle Discovery carried 32 fertilized chicken eggs into orbit", + "The most recycled product in the world is the automobile.", + "Before the 17th century, carrots used to be the colour purple", + "William Taft who was the U.S. president between 1909-1913 once got stuck in the White House bathtub", + "If all the Oreo cookies ever sold were stacked on top of one another, they would be as high as 13.3 million Sears Towers", + "Ancient Egyptians kissed with their noses instead of with their lips", + "Krispy Kreme make five million doughnut a day", + "The only real person to be a Pez head was Betsy Ross", + "There were no red colored M&Ms from 1976 to 1987", + "In 1681, the last dodo bird died", + "There are over 600 different pasta shapes", + "Some people start to sneeze if they are exposed to sunlight or have a light shined into their eye", + "In 1989, twenty-three people were hired in Jacksonville Florida just to flush toilets so the pipes would not freeze", + "Lake Baikal is the oldest freshwater lake on Earth, having formed between 20 and 25 million years ago", + "From 1939 to 1942, there was a undersea post office in the Bahamas", + "An owl has three eyelids", + "Instead of a birthday cake, many children in Russia are given a birthday pie", + "Atlantic salmon can jump as high as 4.5 meters out of the water", + "Although the outsides of a bone are hard, they are generally light and soft inside. They are about 75% water", + "Each nostril of a human being register smell in a different way. Smells that are made from the right nostril are more pleasant than the left. However, smells can be detected more accurately when made by the left nostril", + "Children who are breast fed tend to have an IQ seven points higher than children who are not", + "Each king in a deck of playing cards represents a great king fromhistory. Spades - King David; Clubs - Alexander the Great; Hearts -Charlemagne;and Diamonds - Julius Caesar.", + "There are no ants in Iceland, Antarctica and Greenland", + "In the United States, approximately 135 million cars travel every day on the streets, roads, and interstates.", + "A salmon with two mouths, two sets of teeth and two tongues was caught by Bob Bateman of Canada", + "There is a 'cemetery town' in California named Colma. Concerns about the public health, crime, and the need for space forced the city of San Francisco to outlaw burials in 1902. The city of Colma, which is five miles south of San Francisco, was established to bury the dead. The ratio of dead to living people is 750 to 1", + "Dueling is legal in Paraguay as long as both parties are registered blood donors", + "In Belgium, there is a museum that is just for strawberries", + "The most reproduced image in the world is Mickey Mouse, which can be found on over 7,500 different items", + "On average a person passes gas 14 times a day", + "Vasaloppet, which is located in Sweden is the oldest, longest, and the biggest cross-country ski race in the world. Every year, 14,000 people compete in the race", + "The clown fish has the ability to change its sex. If a breeding female dies, the male fish will change its sex and mate with another male", + "Bats sleep during the day and feed at night. The place that bats sleep in is called the 'roost.'", + "The puma and the leopard are the highest jumping mammals. They are able to reach a height of 16.5 feet", + "The chances of getting a cavity is higher if candy is eaten slowly throughout the day compared to eating it all at once and then brushing your teeth", + "The Main Library at Indiana University sinks over an inch every year because when it was built, engineers failed to take into account the weight of all the books that would occupy the building", + "On average, a car driver will swear or blashpheme 32,025 times in their lifetime while driving", + "The average ear grows 0.01 inches in length every year", + "The first penny candy to be wrapped in America was the Tootsie Roll in 1896", + "Buckingham Palace has over six hundred rooms", + "Male owls weigh less and are smaller than female owls", + "Hypnotism is banned by public schools in San Diego", + "The city of Las Vegas has the most hotel rooms in the world", + "Cows drink anywhere from 25-50 gallons of water each day", + "In the United Kingdom, three million people play bingo every year", + "Humans are the only primates that don't have pigment in the palms oftheir hands.", + "Every square inch of the human body has about 19,000,000 skin cells", + "In 1994, 7-Eleven coined the term 'brain freeze.' The word was developed to explain the feeling people get when drinking a Slurpee.", + "A swordfish can live as long as 25 years and weigh up to 1,200 pounds", + "Due to eating habits in the USA, one in three children born in the year 2000 have a chance of getting type II diabetes", + "The world's termites outweigh the world's humans 10 to 1.", + "Each honeycomb in Honeycomb cereal has seven holes", + "The Nile river is 6,690 kilometers long", + "By law, information collected in a U.S. census must remain confidential for seventy-two years", + "Eighty percent of 10 year old girls in the USA go on a diet", + "97% of all paper money in the US contains traces of cocaine", + "The name 'Grey Poupon' used for mustard comes from two people: Maurice Grey and Auguste Poupon. Grey was the inventor of a machine that mass produced fine textured mustard, and Poupon was an already established maker of mustard. In 1886 the Grey-Poupon firm was formed", + "The Faberge, 'Winter Egg' was sold in 1994 for $5.6 million. This is the most expensive decorative egg that has ever been sold", + "The name 'Tonka' trucks was named after Lake Minnetonka located in Minnesota. Tonka means 'great' in Sioux", + "18% of an Americans income is spent on transportation", + "Feb 1865 and Feb 1999 are the only months in recorded history not to have a full moon", + "Turkeys have a wingspan of approximately 4.5 feet", + "The first music video ever played on MTV Europe was by Dire Straits, 'Money For Nothing.'", + "Arnold Palmer was the first player to win $1 million on the PGA Tour", + "Three years after a person quits smoking, there chance of having a heart attack is the same as someone who has never smoked before", + "In a year, the average Americans eats approximately 18 pounds of Turkey", + "The household wrench was invented by boxing heavyweight champion Jack Johnson in 1922", + "Squids move through the ocean using a jet of water forced out of the body by a siphon", + "Back in 1796, dimes were called dismes", + "Pilgrims did not eat with forks. They only used spoons, knives and their fingers", + "The average human has about 20 square feet of skin weighing about 6 pounds", + "Mass murderer Charles Manson recorded an album titled 'Lie.'", + "On average, each American consumes approximately two hundred and sixty pounds of meat in a year", + "The term 'The Big Apple' was coined by touring jazz musicians of the 1930s who used the slang expression 'apple' for any town or city. Therefore, to play New York City is to play the big time - The Big Apple", + "An apple, potato, and onion all taste the same if you eat them with your nose plugged. They all taste sweet", + "The United States has paved enough roads to circle the Earth over 150 times.", + "Decaffeinated coffee is not 100% caffeine free. When coffee is being decaffeinated, 2% of the caffeine still remains in it", + "Sales of antacids increase by as much as 20% the day after the Superbowl", + "Automobile building is the largest manufacturing industry in the world.", + "A shark is the only fish that can blink with both eyes", + "Polar bears can eat as much as ten percent of their body weight in less than one hour", + "Marlboro was the first cigarette company to market a cigarette that had a red filter called 'beauty tip.' This was done to hide the lipstick marks left on the filter from women smokers", + "In Japan, the number four is considered to be unlucky because the Japanese word for four sounds very similar to the word death", + "The life expectancy of a $100 bill is nine years", + "The words moron, imbecile, and idiot are not interchangable. The one with the highest level of intelligence is a moron, followed, by an imbecile, and then idiot", + "Anise is the scent on the artificial rabbit that is used in greyhound races", + "The first known American novelist to hand in a manuscript that was typed was Mark Twain. His typewriter was a Remington No.1, which was invented by Christopher Sholes and Carlos Glidden.", + "The city of Nottingham in England was the first city to have Braille signs (signs for the blind) in its shopping malls for the blind", + "The only woman that has appeared on a U.S. paper currency is Martha Washington", + "The name of the Taco Bell dog is Gidget", + "Each year approximately half of billions dollars is spent on bubble gum by the kids in North America", + "Hair and fingernails are made from the same substance, keratin", + "The average human eats 8 spiders in their lifetime at night", + "The number one cause of blindness in adults in the United States is diabetes", + "India has a Bill of Rights for cows", + "A drink used to be made by the Aztecs for the gods which had the ingredients of ground cocoa mixed in with spices and corn", + "In America, the most common mental illness is Anxiety Disorders", + "The stage were the television sitcom 'Friends' is shot on is said to be haunted", + "Gases that build up in your large intestine cause flatulence. It usually takes about 30 to 45 minutes for these gases to pass through your system", + "The largest earthworm on record was found in South Africa and measured 22 feet", + "Whooping cranes are born with blue eyes that change to bright gold by the time they six months old", + "Belize is the only country in the world with a jaguar preserve", + "Tomatos were once referred to as 'love apples.' This is because their was a superstition that people would fall in love by eating them", + "The American Kestrel hawk weighs only four ounces", + "A 27 year old heir to a sausage empire was handed a ticket for 116,000 pounds for driving at 80 km/hr in a 40 km/hr zone. This is because the speeding tickets in Finland are based on how much money a person makes", + "In China, pancakes are generally served as side dishes. They are stuffed with meat, bean sprouts, and other vegetables", + "93% of all greeting cards are purchased by women", + "Paper money is not made from wood pulp but from cotton. This means that it will not disintegrate as fast if it is put in the laundry", + "The most deadly fires that occur in the home happen between 6pm and 10pm", + "There are over 200 parts in a typical telephone", + "There is an automobile model called Stutz Bearcat.", + "If you were standing on Mercury, the Sun would appear 2.5 times larger than it appears from Earth", + "The water inside of a coconut is identical to human blood plasma. Many lives in third world countries have been saved from coconut water fed through an IV", + "The least likely day to eat out in the United States is Monday", + "Little Miss Muffet was a girl from the 16th century whose name was really Patience", + "In 1978, the World Water Speed record was made by Ken Warby from Australia. His average speed was 317.6 mph, and his jet-powered hydroplane was 27 feet long called 'Spirit of Australia.' Warby built the boat himself in his back yard", + "Americans collectively eat one hundred pounds of chocolate every second", + "People who studies laughter are called a 'gelotologists.'", + "Adolf Hitler was one of the people that was responsible in the creation of the Volkswagen Beetle. He came up with the idea of producing a car that was cheap enough for the average German working man to afford.", + "Chameleons can move their eyes independently. One eye can be looking forward and one eye backward at the same time", + "Bruce Lee was so fast, that they actually had to slow a film down so you could see his moves. That is the opposite of the norm", + "Over 90% of diseases are caused or complicated by stress", + "In 1953, racecar driver Tim Flock raced at Nascar with a monkey in the seat beside him", + "Taipan snakes have 50 times more toxic than a cobra snake", + "Influenza caused over twenty-one million deaths in 1918", + "English sailors were referred to as 'limeys' because sailors added lime juice to their diet to combat scurvy", + "Ukrainian people celebrate Christmas on January 7th, which is the Orthodox Christmas Day", + "Gorillas are considered apes, not monkeys. The way to distinguish between an ape and a monkey is that apes do not have tails", + "Early Romans used to use porcupine quills as toothpicks", + "The longest human beard on record is 17.5 feet, held by Hans N. Langseth who was born in Norway in 1846", + "Honey is used sometimes for antifreeze mixtures and in the center of golf balls", + "The size of a raindrop is around 0.5 mm - 2.5 mm, and they fall from the sky on average 21 feet per second.", + "In the United States, the first cookbook was published in 1796 and it contained a recipes for watermelon rind pickles", + "The word 'walkman' was included in the Oxford English Dictionary in 1986", + "A headache and inflammatory pain can be reduced by eating 20 tart cherries", + "There is an area located off the south-eastern Atlantic coast of the United States called the 'Bermuda Triangle.' It is known for a high rate of unexplained losses of ships, small boats, and aircraft, which has led some people to believe that this triangle has supernatural powers", + "State with the highest percentage of people who walk to work: Alaska", + "Some toothpastes and deodorants contain the same chemicals found in antifreeze", + "The Shroud of Turin is the single most studied artifact in human history", + "Smartest dogs: 1) Scottish border collie; 2) Poodle; 3) Golden retriever", + "The sperm count of an average American male compared to thirty years ago is down thirty percent", + "Humpback whales are capable of living up to 95 years", + "The 1912, a wrestling match in Stockholm between Finn Alfred Asikainen and Russian Martin Klein lasted more than 11 hours. Klein eventually won, but was to tired to participate in the championship match", + "Manitoulin Island is the largest island in a fresh water lake. It is located in Canadian Lake Superior", + "Cost of raising a medium-size dog to the age of eleven: $6,400", + "The Chinese politician Mao Zedong refused to ever brush his teeth and instead just washed his mouth with tea", + "The Super Bowl is broadcast to over 182 countries in the world", + "Banging your head against a wall uses 150 calories an hour", + "In 1884, Dr. Hervey D. Thatcher invented the milk bottle.", + "Some Ribbon worm will eat themselves if they cannot find food. This type of worm can still survive after eating up to 95% of its body weight", + "The only 15 letter word that can be spelled without repeating a letter is 'uncopyrightable.'", + "Singer Chaka Khan came out with a line of chocolates called 'Chakalates.'", + "In a day 34,000 children die every day from causes that are related to poverty and hunger" + ], + "roasts": [ + "I'd like to roast you, but it looks like God already did.", + "You look like someone set your face on fire and then put it out with a hammer.", + "The only thing attracted to you is gravity", + "You're not good looking enough to be a model, but you're not smart enough to be anything else", + "If you'd like to know what sexual position produces the ugliest babies, you should ask your mother.", + "Can you speak a little louder? I can't hear you over the sound of how stupid you are.", + "Why are you even talking to me? So your self esteem can match your IQ?", + "I'm not insulting you, I'm describing you.", + "If you hide your big nose and shut your big mouth, people will think you are attractive and well-spoken.", + "I guess God's just making anybody these days.", + "You're so ugly, when your mom dropped you off at school she got a fine for littering.", + "Some babies were dropped on their heads but you were clearly thrown at a wall.", + "They say opposites attract. If that's so, you will meet someone who is good-looking, intelligent, and cultured.", + "I didn't hear you. I'm busy ignoring an annoying person.", + "I don't know what your problem is, but I'll bet it's hard to pronounce.", + "Please excuse me while I transfer you to someone who speaks Fucktard.", + "It must take a lot of flexibility to fit your foot in your mouth and your head up your ass at the same time.", + "I don't have the time nor the crayons to explain things to you", + "I'd love to keep chatting with you, but I'd rather have AIDS", + "I bet you swim with a t shirt on", + "You have all the charm and charisma of a burning orphanage", + "Your face is so oily that I'm surprised America hasn't invaded yet.", + "If you were any dumber, someone would need to water you twice a week", + "If you were on fire and I had a cup of my own piss, I'd drink it", + "Do you still love nature, despite what it did to you?", + "The thing I dislike most about your face is that I can see it.", + "If B.S. was music, you'd be an orchestra.", + "You look like a before picture.", + "I've heard farts more intelligent than you.", + "You have a perfect face for radio.", + "They say that a million monkeys on a million typewriters will eventually produce the collected works of Shakespeare. If that theory is correct, I believe you will one day say something intelligent.", + "If you want to lose ten pounds of ugly fat, may I suggest you start with cutting off your head.", + "You look like somebody stepped on a goldfish.", + "I thought the trash got picked up last night, what are you still doing here?", + "Looking the way you do must save a lot of money on halloween.", + "I'd love to continue talking with you but my favorite commercial is on tv", + "I'd love to keep chatting with you, but right now I have to do literally anything else.", + "Did you get a bowl of soup with that haircut?", + "If you don't like what I say about you, it would be a good idea to improve yourself.", + "Does being that ugly require a license?", + "You could throw a rock at the ground and miss", + "There's no one in this world like you. Or at least I hope so.", + "You look like a man, and you need to lose some weight.", + "Did you cancel your barbecue? Because your grill is messed up", + "Some people make millions. You make memes.", + "Did you forget to wipe or is that your natural scent?", + "I missed you this week, but my aim is improving.", + "I'm surprised you've made it this far without being eaten.", + "Your body looks like your head is inflating a water balloon.", + "Your mother was a hamster.", + "How do you make an idiot wait?", + "If balls were dynamite, you wouldn't have enough to kill a fish.", + "I'd like to roast you, but I'm too busy judging your choices.", + "You are the worst part of everybody's day.", + "If your face were scrambled it would improve your looks.", + "I hope you don't feel the way you look.", + "In the book of Who's Who, you are listed as What's That?", + "It's surprising to me that a pig's bladder on a stick has gotten so far in life.", + "Sorry. I'm on the toilet and I can only deal with one shit at a time.", + "If you fell into a river it would be unfortunate, but if anyone pulled you out it would be a disaster.", + "You are the discount version of whatever celebrity you look like.", + "When you go to the dentist, he needs anaesthetic.", + "You suck dick for bus fare and then walk home.", + "The fact that you are still alive is evidence that natural disasters are poorly distributed.", + "You are so dumb you can't fart and chew gum at the same time.", + "I was going to give you a nasty look, but I see you already have one.", + "Me think'st thou are a general offence and every man should beat thee.", + "I don't try to explain myself to idiots like you. I'm not the Fucktard Whisperer.", + "Your mom circulates like a public key, servicing more requests than HTTP.", + "Your mom is so fat and dumb, the only reason she opened her email is because she heard it contained spam.", + "Your mom is so fat, she has to iron her pants in the driveway.", + "Your face invites a slap.", + "The only way you could get laid is if you crawled up a chicken's ass and waited.", + "When I look at you, I wish I could meet you again for the first time… and walk past.", + "You are the sun in my life… now get 93 million miles away from me.", + "You have such a beautiful face… But let's put a bag over that personality.", + "There is someone out there for everyone. For you, it's a therapist.", + "I would smack you, but I'm against animal abuse.", + "If I wanted to kill myself, I would simply jump from your ego to your IQ.", + "I can't wait to spend my whole life without you.", + "Whoever told you to be yourself, gave you a bad advice.", + "I didn't mean to offend you… but it was a huge plus.", + "I don't hate you, but if you were drowning, I would give you a high five.", + "If I throw a stick, will you leave me too?", + "Sorry I can't think of an insult dumb enough for you to understand.", + "I don't know what makes you so stupid, but it works.", + "Whatever doesn't kill you, disappoints me.", + "It is hilarious how you are trying to fit your entire vocabulary into one sentence.", + "I like the way you comb your hair, so horns don't show up.", + "Have a nice day… somewhere else.", + "I would call you an idiot, but it would be an insult for stupid people.", + "I told my therapist about you; she didn't believe me.", + "Did you know your incubator had tinted windows? That explains a lot.", + "The last time I saw something like you, it was behind metal grids.", + "If I had a dollar every time you shut up, I would give it back as a thank you.", + "You were so happy for the negativity of your Covid test, we didn't want to spoil the happiness by telling you it was IQ test.", + "Honey, only thing bothering me is placed between your ears.", + "Only thing that is pleasing about our relationship is that you are no longer in it.", + "Every time I have a stick in my hand, you look like a pinata.", + "You are like a software update. every time I see you, I immediately think ā€œnot nowā€.", + "When I look at you, I think to myself where have you been my whole life? Can you go back there?", + "You are the reason why there are instructions on shampoo bottles.", + "I think you just need a high five… in the face… with a chair.", + "When I listen to you, I think you really going to go far. I hope you stay there.", + "I look at you and think what a waste of two billion years of the evolution.", + "It would be a great day If you used a glue stick instead of Chapstick.", + "Yes, I'm fully vaccinated, but I will still not hang out with you.", + "When I see you coming, I get pre annoyed. I'm just giving myself a head start.", + "You are the reason why God is not talking to us anymore.", + "You can't imagine how much happiness you can bring… by leaving the room.", + "I know you don't like me, that says a lot. You need to acquire a better taste.", + "It's all about balance… you start talking, I stop listening.", + "Are you talking to me? I thought you only talk behind my back.", + "I'm sorry… did my back hurt your knife?", + "Everyone is allowed to act stupid once, but you… you are abusing that privilege.", + "Cry me a river, then drown yourself in it.", + "Ola soy Dora. Can you help me find where we asked?", + "Somewhere tree is producing oxygen for you. I'm sorry for it.", + "Earth is full. Go home.", + "Everyone has purpose in this life, yours is to become an organ donor.", + "I am jealous of people who didn't meet you.", + "Why are you rolling your eyes? Are you looking for your brain?", + "You didn't change since last time I saw you. You should.", + "What is wrong with you? Have you had too many drugs in mental hospital today?", + "It is better to shut your mouth and make people think you are stupid than open it and remove all doubt.", + "Hurting you is the least thing I want to do… but it's still in the list.", + "Oh, sorry, did the middle of my sentence interrupted the beginning of yours?", + "Let me tell you. If I don't answer you the first time, what makes you think the next 25 will work?", + "I am not ignoring you; I am just giving you a time to understand what you just said.", + "Every time I think you can't get any dumber, you are proving me wrong.", + "Where is your off button?", + "Is part 2 of your argument coming out soon or is that it?", + "You're not simply a drama queen. You're the whole royal family.", + "You hear that? It's the sound of me not caring.", + "All mistakes are fixable, yet you aren't.", + "I'd tell you to blow your brains out, but I'm pretty certain there's nothing there.", + "I don't want to rain on your parade. I want a typhoon.", + "Are you at a loss for words, or did you exhaust your entire vocabulary? ", + "I gave out all my trophies a while ago, but here's a participation award.", + "When God made you, you must have been on the bottom of his ā€œto-doā€ list.", + "You're the reason the divorce rate is so high.", + "A glowstick has a brighter future than you. Lasts longer in bed, too.", + "You can be anything you want…except good looking.", + "I know I make stupid choices, but you're the worst of all my choices", + "Taking a picture of you would put a virus on my phone", + "You deserve to be loved… from a distance", + "God wanted to spice the earth with jokes, and he made your kind", + "Remember, if anyone says you're beautiful, it's all lies", + "The good books say to make good friends, but I think I made a mistake ", + "You make me increase the amount of caffeine I take daily", + "You do realize we tolerate you." + ], + "truth": [ + "Are you a hard-working student?", + "Are you into any sports?", + "Are you scared of any animals?", + "Are you scared of dying? Why?", + "Are you scared of ghosts?", + "Are you still a virgin?", + "Can you lick your elbow?", + "Can you see yourself being married to the creepiest kid a your school someday?", + "Can you speak a different language?", + "Can you touch your tongue to your nose?", + "Can you use a pogo stick?", + "Could you go a week without junk food?", + "Could you go two months without talking to your friends?", + "Describe the weirdest dream you've ever had?", + "Describe the weirdest dream you've ever had?", + "Describe your most recent dream that you recall.", + "Describe your most recent romantic encounter.", + "Describe your worst kiss ever.", + "Do you believe in love at all?", + "Do you believe in love at first sight?", + "Do you ever talk to yourself in the mirror?", + "Do you have a hidden talent? What is it?", + "Do you have a job? If so, what is your favourite thing about it?", + "Do you have an imaginary friend?", + "Do you have any phobias?", + "Do you have any unusual talents?", + "Do you know how to cook?", + "Do you know how to dance?", + "Do you like doing chores?", + "Do you like to exercise?", + "Do you message people during your classes?", + "Do you prefer apple or android?", + "Do you sing in the shower? What song did you sing the last time?", + "Do you think you will marry your bf/gf? If not, why not?", + "Explain to the person you like least in this group why you like them the least.", + "Have you been in any fights while in school?", + "Have you ever been kissed yet? If so, who was your best kiss?", + "Have you ever bitten a toenail?", + "Have you ever blamed a fart on an animal?", + "Have you ever blamed something that you have done on one of your siblings?", + "Have you ever cheated on a test?", + "Have you ever cheated or been cheated on?", + "Have you ever climbed a tree?", + "Have you ever crapped your pants since you were a child?", + "Have you ever eaten food that you've dropped on the ground? If so, how long was it on the ground?", + "Have you ever fallen asleep during a class?", + "Have you ever had a crush on a teacher?", + "Have you ever had a crush on someone that your best friend has dated?", + "Have you ever had someone write an assignment or a work for you?", + "Have you ever kissed an animal?", + "Have you ever let someone take the blame for something you did?", + "Have you ever lied to your best friend?", + "Have you ever lied to your parents about if you were in classes or not?", + "Have you ever lied to your parents about what you were doing after school?", + "Have you ever peed in a pool?", + "Have you ever picked your nose in public?", + "Have you ever posted something on the internet/social media that you regret?", + "Have you ever pulled a prank on one of your teachers?", + "Have you ever received a love letter?", + "Have you ever ridden the bus without paying the fare?", + "Have you ever stolen something of value worth more than $10?", + "Have you ever stolen something?", + "Have you ever taken money from your roommate?", + "Have you ever taken money that didn't belong to you?", + "Have you ever thrown a party at your house?", + "Have you ever told a lie during a game of Truth or Dare? What was it and why?", + "Have you ever told one of your best friend's secrets, even if you said you wouldn't?", + "Have you ever used someone else's password?", + "Have you ever watched an adult film without your parents knowing?", + "Have you ever worn the same clothes for more than three days?", + "How do you feel about end pieces of a loaf of bread?", + "How do you feel about social media?", + "How far would you go to land the guy or girl of your dreams?", + "How many boyfriends (or girlfriends) have you had?", + "How many days could you go without your partner?", + "How many kids would you like to have?", + "How many siblings do you have?", + "How many times have you skipped class for no reason?", + "How old were you when your parents sat you down for 'the talk' and what did they say (or not say) about 'the birds and the bees'?", + "How soon did you realize that you were in love with your partner?", + "How soon did/do you want start a family?", + "How was your first kiss?", + "If there was no such thing as money, what would you do with your life?", + "If you could be a superhero; what would your power be?", + "If you could be any animal, which one would you be?", + "If you could be any dinosaur; which would it be?", + "If you could be any super villain; who would you be?", + "If you could change one thing on your body, what would it be?", + "If you could dye your hair any colour, what colour would you pick?", + "If you could erase one past experience, what would it be?", + "If you could go anywhere in the world, where would you go?", + "If you could make one wish right this second, what would it be?", + "If you could only hear one song for the rest of your life, what would it be?", + "If you could own your own business one day, what would it be?", + "If you could own your own business one day; what would it be?", + "If you could switch lives with any celebrity for a day, who would it be?", + "If you could take away one bad thing in the world, what would it be?", + "If you could, what would you change about your life?", + "If you had never met your partner, where do you think you would be?", + "If you had the choice to live on your own right now, would you do it?", + "If you have ever cheated, why did you do it?", + "If you suddenly had a million dollars; what would you do with all of your money?", + "If you were a billionaire, what would you spend your time doing?", + "If you were rescuing everyone here from a burning building, but you had to leave one behind, who would it be?", + "If you were to be stuck on a deserted island, which friend would you want with you?", + "If you were to be trapped on an island for 3 days, what would you take with you?", + "Of the people in this group, who are you the most jealous of?", + "Of the people in this group, who do you disagree with most frequently?", + "Of the people in this group, who do you most want to switch lives with and why?", + "Of the people in this group, who would you feel most comfortable with naked?", + "Talk about about the last time someone unexpectedly walked in on you while you were naked.", + "Talk about about your most awkward date.", + "Talk about the best vacation you've ever been on?", + "Tell us about your most embarrassing vomit story.", + "Tell us about your your first kiss.", + "What are the three qualities you feel are most important in a friend?", + "What are you most self-conscious about?", + "What are your must have future wife/husband qualities.", + "What are your three favourite colours, and why?", + "What do most of your friends think about you that is totally untrue?", + "What do you daydream about the most?", + "What do you do when you are alone at home?", + "What do you like most and least about your own appearance?", + "What do you like most and least about your personality?", + "What do you really hope your parents never find out about you?", + "What do you think is your biggest fear?", + "What do you think is your biggest flaw?", + "What do you think is your biggest insecurity?", + "What do you think is your biggest strength?", + "What do you think is your biggest weakness?", + "What is one embarrassing fact about you?", + "What is one lie your partner/best friend tells everyone else, but you know the truth?", + "What is one misconception about you?", + "What is one thing that you've always wanted to do?", + "What is one thing you wish people knew about you?", + "What is something childish that you still do?", + "What is something that people think you would never do but you have?", + "What is something that you have never told anyone?", + "What is something you're glad your parents don't know about you?", + "What is something you've done to try to be 'cooler'?", + "What is the biggest lie you've ever told without getting caught?", + "What is the biggest romantic fail you've ever experienced?", + "What is the biggest secret you've ever kept from your parents?", + "What is the craziest thing that you have ever done without your parents knowing?", + "What is the craziest thing you've ever done to attract a crush?", + "What is the cruelest thing you've ever done to a friend?", + "What is the first thing you'd do if you woke up one day and you were the opposite sex?", + "What is the longest that you have ever been without taking a shower?", + "What is the longest time that you think you could go without the internet?", + "What is the longest time that you think you could go without your smartphone?", + "What is the longest time you have ever been grounded?", + "What is the longest you have gone without sleep?", + "What is the meanest thing you've ever said to someone?", + "What is the most annoying thing that your siblings do?", + "What is the most awkward experience you've had with a crush?", + "What is the most childish thing you still do?", + "What is the most disgusting habit of yours?", + "What is the most disgusting thing you've ever done?", + "What is the most embarrassing moment in your life?", + "What is the most embarrassing nickname you have ever had?", + "What is the most embarrassing photo you have on your phone?", + "What is the most embarrassing thing in your web browser history (Assume you've never had Incognito mode on.)?", + "What is the most embarrassing thing you have put up on social media?", + "What is the most embarrassing thing your parents have caught you doing?", + "What is the most evilest thing you've ever done?", + "What is the most expensive thing you have stolen?", + "What is the most expensive thing you own?", + "What is the most flirtatious thing you've ever done?", + "What is the most useless piece of knowledge you know?", + "What is the silliest thing you have an emotional attachment to?", + "What is the stupidest thing that you've done in front of a crowd?", + "What is the stupidest thing you have ever done?", + "What is the weirdest thing that your parents have caught you doing?", + "What is the weirdest thing you've done when you were alone?", + "What is the weirdest thing you've ever done in front of the mirror?", + "What is your best talent?", + "What is your biggest fantasy?", + "What is your biggest fear in a relationship?", + "What is your biggest pet peeve?", + "What is your biggest regret?", + "What is your biggest turn off in a partner?", + "What is your deepest darkest fear?", + "What is your dream job?", + "What is your dream wedding?", + "What is your favourite TV show?", + "What is your favourite animal?", + "What is your favourite game to play?", + "What is your favourite holiday and why?", + "What is your favourite kind of clothing?", + "What is your favourite movie?", + "What is your favourite place to go out to eat?", + "What is your favourite school subject?", + "What is your favourite season of the year?", + "What is your favourite song that is out right now?", + "What is your favourite song?", + "What is your favourite sport?", + "What is your favourite sports team?", + "What is your favourite thing about your partner?", + "What is your favourite thing to do after school?", + "What is your favourite thing to do on the weekends?", + "What is your favourite vegetable?", + "What is your favourite way of getting attention from the opposite sex?", + "What is your guilty pleasure?", + "What is your partner's most annoying habit?", + "What is your real GPA?", + "What is your shoe size?", + "What is your weirdest habit?", + "What was the best day of your life?", + "What was the biggest mistake of your life?", + "What was the cruelest joke you ever played on someone?", + "What was the last lie that you told your best friend?", + "What was the last thing you ate?", + "What was the most awkward romantic encounter you have had?", + "What was the strangest dream you have ever had?", + "What was the worst encounter you had with a police officer?", + "What was your childhood ambition?", + "What was your childhood nickname?", + "What was your first impression of your partner?", + "What was your funniest first date ever?", + "What was your partner's childhood nickname?", + "What was your scariest nightmare?", + "What would you do if you were invisible for a day?", + "What would you do if you were the opposite sex for a month?", + "What would you do if your parents left the house to you for a whole week?", + "When did you learn how to ride a bike?", + "When was the last time you cried?", + "When was the last time you picked your nose without a tissue?", + "When was the last time you shit yourself?", + "When was the last time you were flat-out rejected and how did you handle it?", + "When was the last time you wet the bed?", + "When was the most inappropriate time you farted?", + "Where is the strangest place you have peed?", + "Where was your first kiss?", + "Which celebrity would you like to a date?", + "Who do you admire the most?", + "Who do you think you act more like, your father or your mother?", + "Who have you loved but they didn't love you back?", + "Who here knows something about you that you wouldn't want revealed?", + "Who is most important to you?", + "Who is the best roommate that you have ever had?", + "Who is the most annoying person you know?", + "Who is the person you most regret kissing?", + "Who is the person you're in love with?", + "Who is the worst teacher you have ever had, why?", + "Who is the worst teacher you have ever had; why?", + "Who is your best friend?", + "Who is your best looking teacher that you have ever had?", + "Who is your crush at school?", + "Who is your favourite Disney character?", + "Who is your favourite friend?", + "Who is your favourite in-law?", + "Who is your favourite person and why?", + "Who is your favourite singer?", + "Who is your favourite teacher, and why?", + "Who is your least favourite in-law?", + "Who is/was your celebrity crush?", + "Who taught you how to tie your shoes?", + "Would you drop out of school if you were to win the lottery?", + "Would you ever consider being a nudist?", + "Would you ever get on a dating website?", + "Would you leave your partner if it meant that you would be very rich?", + "Would your partner trade you for a million dollars?", + "What personality trait does you least like about your partner/best friend?", + "What prejudice do you secretly harbour?", + "What celebrity are you obsessed with?", + "What country would like to live in if you had the chance?", + "What app on your phone do you waste the most time on?", + "What secret about yourself did you tell someone in confidence and then they told a lot of other people?", + "What terrible thing have you done that you lied to cover up?", + "Your house is burning down and you have to get out fast. You can take one thing with you, what would it be?", + "What weird food combinations do you really enjoy?", + "What social stigma does society need to get over?", + "What food have you never eaten but would really like to try?", + "What's something you really resent paying for?", + "What would a world populated by clones of you be like?", + "Do you think that aliens exist?", + "What are you currently worried about?", + "Where are some unusual places you've been?", + "Where do you get your news?", + "What are some red flags to watch out for in daily life?", + "What movie can you watch over and over without ever getting tired of?", + "When you are old, what do you think children will ask you to tell stories about?", + "If you could switch two movie characters, what switch would lead to the most inappropriate movies?", + "What inanimate object would be the most annoying if it played loud upbeat music while being used?", + "When did something start out badly for you but in the end, it was great?", + "How would your country change if everyone, regardless of age, could vote?", + "What animal would be cutest if scaled down to the size of a cat?", + "If your job gave you a surprise three day paid break to rest and recuperate, what would you do with those three days?", + "What's wrong but sounds right?", + "What's the most epic way you've seen someone quit or be fired?", + "If you couldn't be convicted of any one type of crime, what criminal charge would you like to be immune to?", + "What's something that will always be in fashion, no matter how much time passes?", + "What actors or actresses play the same character in almost every movie or show they do?", + "In the past people were buried with the items they would need in the afterlife, what would you want buried with you so you could use it in the afterlife?", + "What's the best / worst practical joke that you've played on someone or that was played on you?", + "Who do you go out of your way to be nice to?", + "Where do you get most of the decorations for your home?", + "What food is delicious but a pain to eat?", + "Who was your craziest / most interesting teacher", + "What 'old person' things do you do?", + "What was the last photo you took?", + "What is the most amazing slow motion video you've seen?", + "Which celebrity do you think is the most down to earth?", + "What would be the worst thing to hear as you are going under anesthesia before heart surgery?", + "What's the spiciest thing you've ever eaten?", + "What's the most expensive thing you've broken?", + "What obstacles would be included in the World's most amazing obstacle course?", + "What makes you roll your eyes every time you hear it?", + "What do you think you are much better at than you actually are?", + "Should kidneys be able to be bought and sold?", + "What's the most creative use of emojis you've ever seen?", + "When was the last time you got to tell someone 'I told you so.'?", + "What riddles do you know?", + "What's your cure for hiccups?", + "What invention doesn't get a lot of love, but has greatly improved the world?", + "What's the most interesting building you've ever seen or been in?", + "What mythical creature do you wish actually existed?", + "What are your most important rules when going on a date?", + "How do you judge a person?", + "If someone narrated your life, who would you want to be the narrator?", + "What was the most unsettling film you've seen?", + "What unethical experiment would have the biggest positive impact on society as a whole?", + "When was the last time you were snooping, and found something you wish you hadn't?", + "Which celebrity or band has the worst fan base?", + "What are you interested in that most people aren't?", + "If you were given a PhD degree, but had no more knowledge of the subject of the degree besides what you have now, what degree would you want to be given to you?", + "What smartphone feature would you actually be excited for a company to implement?", + "What's something people don't worry about but really should?", + "What movie quotes do you use on a regular basis?", + "Do you think that children born today will have better or worse lives than their parents?", + "What's the funniest joke you know by heart?", + "When was the last time you felt you had a new lease on life?", + "What's the funniest actual name you've heard of someone having?", + "Which charity or charitable cause is most deserving of money?", + "What TV show character would it be the most fun to change places with for a week?", + "What was cool when you were young but isn't cool now?", + "If you were moving to another country, but could only pack one carry-on sized bag, what would you pack?", + "What's the most ironic thing you've seen happen?", + "If magic was real, what spell would you try to learn first?", + "If you were a ghost and could possess people, what would you make them do?", + "What goal do you think humanity is not focused enough on achieving?", + "What problem are you currently grappling with?", + "What character in a movie could have been great, but the actor they cast didn't fit the role?", + "What game have you spent the most hours playing?", + "What's the most comfortable bed or chair you've ever been in?", + "What's the craziest conversation you've overheard?", + "What's the hardest you've ever worked?", + "What movie, picture, or video always makes you laugh no matter how often you watch it?", + "What artist or band do you always recommend when someone asks for a music recommendation?", + "If you could have an all-expenses paid trip to see any famous world monument, which monument would you choose?", + "If animals could talk, which animal would be the most annoying?", + "What's the most addicted to a game you've ever been?", + "What's the coldest you've ever been?", + "Which protagonist from a book or movie would make the worst roommate?", + "Do you eat food that's past its expiration date if it still smells and looks fine?", + "What's the most ridiculous thing you have bought?", + "What's the funniest comedy skit you've seen?", + "What's the most depressing meal you've eaten?", + "What tips or tricks have you picked up from your job / jobs?", + "What outdoor activity haven't you tried, but would like to?", + "What songs hit you with a wave of nostalgia every time you hear them?", + "What's the worst backhanded compliment you could give someone?", + "What's the most interesting documentary you've ever watched?", + "What was the last song you sang along to?", + "What's the funniest thing you've done or had happen while your mind was wandering?", + "What app can you not believe someone hasn't made yet?", + "When was the last time you face palmed?", + "If you were given five million dollars to open a small museum, what kind of museum would you create?", + "Which of your vices or bad habits would be the hardest to give up?", + "What really needs to be modernized?", + "When was the last time you slept more than nine hours?", + "How comfortable are you speaking in front of large groups of people?", + "What's your worst example of procrastination?", + "Who has zero filter between their brain and mouth?", + "What was your most recent lie?", + "When was the last time you immediately regretted something you said?", + "What would be the best thing you could reasonably expect to find in a cave?", + "What did you think was going to be amazing but turned out to be horrible?", + "What bit of trivia do you know that is very interesting but also very useless?", + "What's the silliest thing you've seen someone get upset about?", + "What animal or plant do you think should be renamed?", + "What was the best thing that happened to you today?", + "As a child, what did you think would be awesome about being an adult, but isn't as awesome as you thought it would be?", + "When's censorship warranted?", + "What's the most boring super hero you can come up with?", + "What would be some of the downsides of certain superpowers?", + "What word is a lot of fun to say?", + "What current trend do you hope will go on for a long time?", + "What actors or actresses can't play a different character because they played their most famous character too well?", + "Where's your go to restaurant for amazing food?", + "What's something that all your friends agree on?", + "What's your best story from a wedding?", + "What languages do you wish you could speak?", + "What's the most pleasant sounding accent?", + "What's something that everyone, absolutely everyone, in the entire world can agree on?", + "What country is the strangest?", + "What's the funniest word in the English language?", + "What's some insider knowledge that only people in your line of work have?", + "Who do you wish you could get back into contact with?", + "How do you make yourself sleep when you can't seem to get to sleep?", + "If people receive a purple heart for bravery, what would other color hearts represent?", + "What are some of the best vacations you've had?", + "If there was a book of commandments for the modern world, what would some of the rules be?", + "What's the craziest video you've ever seen?", + "What's your 'Back in my day, we…'?", + "If you could know the truth behind every conspiracy, but you would instantly die if you hinted that you knew the truth, would you want to know?", + "What animal would be the most terrifying if it could speak?", + "What's the worst hairstyle you've ever had?", + "What habit do you have now that you wish you started much earlier?", + "If you were given one thousand acres of land that you didn't need to pay taxes on but couldn't sell, what would you do with it?", + "What about the opposite sex confuses you the most?", + "When was the last time you yelled at someone?", + "What's the opposite of a koala?", + "What kinds of things do you like to cook or are good at cooking?", + "What life skills are rarely taught but extremely useful?", + "What movie universe would be the worst to live out your life in?", + "If you could hack into any one computer, which computer would you choose?", + "Who do you feel like you know even though you've never met them?", + "What's the most ridiculous animal on the planet?", + "What's the worst thing you've eaten out of politeness?", + "What's the most historic thing that has happened in your lifetime?", + "What happens in your country regularly that people in most countries would find strange or bizarre?", + "What has been blown way out of proportion?", + "When was a time you acted nonchalant but were going crazy inside?", + "What's about to get much better?", + "What are some clever examples of misdirection you've seen?", + "What's your funniest story involving a car?", + "What would be the click-bait titles of some popular movies?", + "If you built a themed hotel, what would the theme be and what would the rooms look like?", + "What scientific discovery would change the course of humanity overnight if it was discovered?", + "Do you think that humans will ever be able to live together in harmony?", + "What would your perfect bar look like?", + "What's the scariest non-horror movie?", + "What's the most amazing true story you've heard?", + "What's the grossest food that you just can't get enough of?", + "What brand are you most loyal to?", + "What's the most awkward thing that happens to you on a regular basis?", + "If you had to disappear and start a whole new life, what would you want your new life to look like?", + "What movie or book do you know the most quotes from?", + "What was one of the most interesting concerts you've been to?", + "Where are you not welcome anymore?", + "What do you think could be done to improve the media?", + "What's the most recent show you've binge watched?", + "What's the worst movie trope?", + "What's a common experience for many people that you've never experienced?", + "What are some misconceptions about your hobby?", + "What's the smartest thing you've seen an animal do?", + "What's the most annoying noise?", + "What's your haunted house story?", + "What did you Google last?", + "What's the dumbest thing someone has argued with you about?", + "If money and practicality weren't a problem, what would be the most interesting way to get around town?", + "What's the longest rabbit hole you've been down?", + "What's the saddest scene in a movie or TV series?", + "What's the most frustrating product you own?", + "What inconsequential super power would you like to have?", + "What qualities do all your friends have in common?", + "What odd smell do you really enjoy?", + "What's the coolest animal you've seen in the wild?", + "What's the best lesson you've learned from a work of fiction?", + "What food do you crave most often?", + "Who in your life has the best / worst luck?", + "What fashion trend makes you cringe or laugh every time you see it?", + "What's your best story of you or someone else trying to be sneaky and failing miserably?", + "Which apocalyptic dystopia do you think is most likely?", + "If you had a HUD that showed three stats about any person you looked at, what three stats would you want it to show?", + "What's the funniest thing you've seen a kid do?", + "What's your secret talent?", + "What's the best way you or someone you know has gotten out of a ticket / trouble with the law?", + "Tear gas makes people cry and laughing gas makes people giggle, what other kinds of gases do you wish existed?", + "uda Warwick Long Bay Beach", + "What's the most beautiful beach you've been to?", + "What's the most anxiety inducing thing you do on a regular basis?", + "What's something that everyone agrees we should change, but somehow it never changes?", + "What trend are you tired of?", + "What's incredibly cheap and you would pay way more for?", + "What's your grossest bug story?", + "What would the adult version of an ice-cream truck sell and what song would it play?", + "What company do you despise?", + "When was the most inappropriate time you busted out in laughter?", + "What would be an accurate tag line for each month?", + "What's the most overrated product out on the market?", + "What word do you always misspell?", + "What naps are the most satisfying?", + "What's the weirdest thing you've found lying on the ground / side of the road?", + "What's the funniest TV show you've ever seen?", + "What's the most embarrassing story from your childhood?", + "What animal is the most majestic?", + "What's something that everyone knows is true, but we don't like to admit it?", + "What's the weirdest text or email you've gotten?", + "What always cheers you up when you think about it?", + "What sport could you play the longest in a televised game, without anyone discovering you aren't a professional athlete?", + "If you could talk to animals and they would understand you, but you couldn't understand them, what would you do with that power?", + "What's the most boring sport, and what would you do to make it more exciting?", + "What's the creepiest tech out there?", + "Who did you use to look up to, but they screwed up and you lost faith in them?", + "What's fine in small numbers but terrifying in large numbers?", + "Do you like things to be carefully planned or do you prefer to just go with the flow?", + "What animal would you most like to eat?", + "What fictional characters have you had a crush on over the years?", + "What would the box with all your hopes and dreams inside look like?", + "What was the worst shopping experience you've ever had?", + "What story you've heard has stayed with you and always disturbs you every time you think about it?", + "What was the most important appointment or deadline you missed?", + "If you were a clown themed super hero, what powers would you have?", + "If you could airdrop anything you want, worth two million dollars or less, anywhere you want, what would you airdrop and where would you airdrop it?", + "If you lived in a virtual reality world of your own creation, what would it look like?", + "What escalated very quickly?", + "What two things are terrible when separate but great when you put them together?", + "What did you believe for way too long as a child?", + "What big event do you think will happen soon that most people aren't expecting?", + "What still makes you cringe when you think back on it?", + "What current trend makes no sense to you?", + "If you owned a restaurant, what kind of food would it serve?", + "Which celebrity is the most likely to have a collection of canes that are just for show?", + "What's the weirdest crush you've had?", + "What do a lot of people have very strong opinions about, even though they know very little about it?", + "What's your go to casino game?", + "An epic feast is held in your honor, what's on the table?", + "What's your favorite holiday movie?", + "Who is the most manipulative person you've ever met?", + "Who is the most creative person you know?", + "What's the funniest pick up line you've heard?", + "What seemingly innocent question makes you think 'It's a trap!'?", + "How ambitious are you?", + "What did you like / dislike about where you grew up?", + "What elements of pop culture will be forever tied in your mind to your childhood?", + "What's your good luck charm?", + "What's legal now, but probably won't be in 25 years?", + "Would you want the ability to hear the thoughts of people near you if you couldn't turn the ability off?", + "When was the last time you stayed up through the entire night?", + "What's something that people think makes them look cool, but actually has the opposite effect?", + "What's the oldest thing you own?", + "What has someone borrowed but never given back?", + "Where is the best place you've been for taking walks?", + "If cartoon physics suddenly replaced real physics, what are some things you would want to try?", + "What from the present will withstand the test of time?", + "Who in your life is the worst at using technology?", + "What's the weirdest conversation you've eavesdropped on?", + "What just around the corner tech are you eager to get your hands on?", + "What was the darkest movie you've ever seen?", + "What do you do when you hear something fall in the middle of the night while you are in bed?", + "What outfit could you put together from clothes you own to get the most laughs?", + "What's the most disgusting sounding word in the English language?", + "What was ruined because it became popular?", + "What outdated slang do you use on a regular basis?", + "What was the biggest realization you had about yourself?", + "What's your best example of easy come, easy go?", + "What small change greatly improves a person's appearance?", + "What topic could you spend hours talking about?", + "What happens regularly that would horrify a person from 100 years ago?", + "What do a lot of people hope will happen but is just not going to happen?", + "What's the weirdest thing that has happened to you while working at your job?", + "What questions would you like to ask a time traveler from 200 years in the future?", + "Which way should toilet paper hang, over or under?", + "What's the most physically painful thing you've ever experienced?", + "What horror story do you have from a job you've had?", + "What's the most rage inducing game you've ever played?", + "What's the biggest overreaction you've ever seen?", + "What are some of the most common misconceptions?", + "What job doesn't exist now but will exist in the future?", + "What awful movie do you love?", + "What normally delicious food gets ruined when you wrap it in a tortilla?", + "What's your best example of fake it till you make it?", + "What were you completely certain of until you found out you were wrong?", + "What's something commonly done that gets progressively weirder the more you think about it?", + "What's the cutest thing you can imagine? Something so cute it's almost painful.", + "If you were given unlimited resources, how would you lure the worst of humanity into one stadium at the same time?", + "What do you think about when you hear the word 'classy'?", + "What near future predictions do you have?", + "What do you need help with most often?", + "What piece of 'art' would you create if you had to pretend to be an artist and submit something to a gallery?", + "What do you do to make the world a better place?", + "What's the best and worst thing about the country you are from?", + "If you were in charge renaming things so that their names would be more accurate, what names would you come up with?", + "What's better broken than whole?", + "What values are most important to you?", + "What's the best sandwich you've ever had?", + "What's the worst thing you ate from a fast food restaurant?", + "What's something that I don't know?", + "What profession doesn't get enough credit or respect?", + "What memory of yours feels real but is most likely false?", + "What's your 'and then it got worse' story?", + "What was the most amazing physical feat you've managed to pull off?", + "What's the most annoying thing about the social media platform you use most often?", + "If you were hired to show tourists what life is really like where you live, what would you show them / have them do?", + "What would be the most unsettling thing to keep occasionally finding around your house?", + "What nicknames do you have for people in your life?", + "What does the opposite sex do that you wish that you could do, but it's not anatomically feasible or it's socially frowned upon?", + "How much do you plan / prepare for the future?", + "What do you hate most and love most about your car?", + "What weird potato chip flavor that doesn't exist would you like to try?", + "What's the silliest thing you've convinced someone of?", + "How much do you think names affect the outcomes of people's lives?", + "What product or service is way more expensive than it needs to be?", + "What's the shadiest thing you've seen someone do?", + "What was the last situation where some weird stuff went down and everyone acted like it was normal, and you weren't sure if you were crazy or everyone around you was crazy?", + "What did you eat so much of that now you hate it?", + "What are some of the dumbest lyrics you've heard in a song?", + "Where's the line between soup and cereal?", + "What word do you always mispronounce?", + "What do you think you do better than 90% of people?", + "What would be the worst food to be liquefied and drunk through a straw?", + "What's the weirdest thing about modern life that people just accept as normal?", + "How much of your body would you cybernetically enhance if you could?", + "If you wanted to slowly drive a roommate insane using only notes, what kind of notes would you leave around the house?", + "If you had a giraffe that you needed to hide, where would you hide it?", + "What's the clumsiest thing you've done?", + "What songs do you only know the chorus to?", + "Think of a brand, now what would an honest slogan for that brand be?", + "What's something common from your childhood that will seem strange to future generations?", + "n forest canopy", + "What's the most amazing place in nature you've been?", + "What's quickly becoming obsolete?", + "Where is the most uncomfortable place you have ever slept?", + "What's the most annoying animal you've encountered?", + "What's your best example of correlation not equaling causation?", + "In what situations, do you wish you could throw down a smoke bomb and disappear?", + "When was the last time you were hopelessly lost?", + "What songs do you feel compelled to sing along with when you hear them, even if you don't totally know all the words?", + "What product do you wish a company would make a 'smart' version of?", + "What two films would you like to combine into one?" + ] +} \ No newline at end of file diff --git a/assets/videos/crab.mp4 b/assets/videos/crab.mp4 new file mode 100644 index 0000000..6910d12 Binary files /dev/null and b/assets/videos/crab.mp4 differ diff --git a/cogs/economy/economy.py b/cogs/economy/economy.py deleted file mode 100644 index f61ef92..0000000 --- a/cogs/economy/economy.py +++ /dev/null @@ -1,530 +0,0 @@ -import discord -import random -from discord.ext import commands -import json - -with open('./database/shop.json') as f: - mainshop = json.load(f) - -def is_it_me(ctx): - return ctx.author.id == 624076054969188363 - -async def get_bank_data(): - with open('./database/economy.json', 'r') as f: - users = json.load(f) - - return users - - -async def open_account(user): - - users = await get_bank_data() - - if str(user.id) in users: - return False - else: - users[str(user.id)] = {} - users[str(user.id)]["wallet"] = 0 - users[str(user.id)]["bank"] = 0 - with open('./database/economy.json', 'w') as f: - json.dump(users, f, indent=4) - return True - - -def conv2num(amount): - lenght = 0 - for i in amount: - lenght += 1 - lenght -= 1 - - last_letter = amount[lenght] - - rest = amount[:-1] - - try: - rest = int(rest) - if last_letter.lower() == "k": - amount = rest*1000 - - elif last_letter.lower() == "m": - amount = rest*1000000 - - except: - print("Not a number") - - return amount - - -async def update_bank(user, change=0, mode='wallet'): - users = await get_bank_data() - - users[str(user.id)][mode] += change - - with open('./database/economy.json', 'w') as f: - json.dump(users, f, indent=4) - bal = users[str(user.id)]['wallet'], users[str(user.id)]['bank'] - return bal - - -async def sell_this(user, item_name, amount, price=None): - item_name = item_name.lower() - name_ = None - for item in mainshop: - name = item["name"].lower() - if name == item_name: - name_ = name - if price == None: - price = 0.7 * item["price"] - if item['sell'] == False: - return - break - - if name_ == None: - return [False, 1] - - cost = price*amount - - users = await get_bank_data() - - bal = await update_bank(user) - - try: - index = 0 - t = None - for thing in users[str(user.id)]["bag"]: - n = thing["item"] - if n == item_name: - old_amt = thing["amount"] - new_amt = old_amt - amount - if new_amt < 0: - return [False, 2] - users[str(user.id)]["bag"][index]["amount"] = new_amt - t = 1 - break - index += 1 - if t == None: - return [False, 3] - except: - return [False, 3] - for item in users[str(user.id)]["bag"]: - if item["amount"] == 0: - users[str(user.id)]["bag"].remove(item) - with open("./database/economy.json", "w") as f: - json.dump(users, f, indent=4) - await update_bank(user, cost, "wallet") - - return [True, "Worked"] - - -async def buy_this(user, amount, item_name): - item_name = item_name.lower() - name_ = None - for item in mainshop: - name = item["name"].lower() - if name == item_name: - name_ = name - price = item["price"] - break - - if name_ == None: - return [False, 1] - - cost = price*amount - - users = await get_bank_data() - - bal = await update_bank(user) - - if bal[0] < cost: - return [False, 2] - - try: - index = 0 - t = None - for thing in users[str(user.id)]["bag"]: - n = thing["item"] - if n == item_name: - old_amt = thing["amount"] - new_amt = old_amt + amount - users[str(user.id)]["bag"][index]["amount"] = new_amt - t = 1 - break - index += 1 - if t == None: - obj = {"item": item_name, "amount": amount} - users[str(user.id)]["bag"].append(obj) - except: - obj = {"item": item_name, "amount": amount} - users[str(user.id)]["bag"] = [obj] - with open("./database/economy.json", "w") as f: - json.dump(users, f, indent=4) - - await update_bank(user, cost*-1, "wallet") - - return [True, "Worked"] - - -class Economy(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(aliases=['bal']) - async def balance(self, ctx, person: discord.Member = None): - print(person) - if person == None: - print(ctx.author) - await open_account(ctx.author) - user = ctx.author - - users = await get_bank_data() - - wallet_amt = users[str(user.id)]["wallet"] - bank_amt = users[str(user.id)]["bank"] - - # round - wallet_amt = round(wallet_amt, 2) - bank_amt = round(bank_amt, 2) - - wallet_amt = "{:,}".format(wallet_amt) - bank_amt = "{:,}".format(bank_amt) - - em = discord.Embed( - title=f'{ctx.author.name} Balance', color=discord.Color.red()) - em.add_field(name="Wallet Balance", value=wallet_amt) - em.add_field(name='Bank Balance', value=bank_amt) - await ctx.send(embed=em) - else: - await open_account(person) - user = person - - users = await get_bank_data() - - wallet_amt = users[str(user.id)]["wallet"] - bank_amt = users[str(user.id)]["bank"] - - # round - wallet_amt = round(wallet_amt, 2) - bank_amt = round(bank_amt, 2) - - wallet_amt = "{:,}".format(wallet_amt) - bank_amt = "{:,}".format(bank_amt) - - em = discord.Embed( - title=f'{person.name} Balance', color=discord.Color.red()) - em.add_field(name="Wallet Balance", value=wallet_amt) - em.add_field(name='Bank Balance', value=bank_amt) - await ctx.send(embed=em) - - @commands.command() - @commands.cooldown(1, 30, commands.BucketType.user) - async def beg(self, ctx): - await open_account(ctx.author) - user = ctx.author - - users = await get_bank_data() - - earnings = random.randrange(101) - - await ctx.send(embed=discord.Embed(title=f'{ctx.author.display_name} Here you filty peasant', description=f'Got `{earnings}` coins!!')) - - users[str(user.id)]["wallet"] += earnings - with open("./database/economy.json", 'w') as f: - json.dump(users, f, indent=4) - - @commands.command() - @commands.cooldown(1, 60*1440, commands.BucketType.user) - async def daily(self, ctx): - await open_account(ctx.author) - user = ctx.author - - users = await get_bank_data() - - earnings = 10000 - - await ctx.send(embed=discord.Embed(title=f'{ctx.author.display_name}', description='Got `{:,}` coins!!'.format(earnings))) - - users[str(user.id)]["wallet"] += earnings - with open("./database/economy.json", 'w') as f: - json.dump(users, f, indent=4) - - - @commands.command(aliases=['with']) - async def withdraw(self, ctx, amount=None): - await open_account(ctx.author) - if amount == None: - await ctx.send("Please enter the amount") - return - - user = ctx.author - - users = await get_bank_data() - - wallet = users[str(user.id)]["wallet"] - bank = users[str(user.id)]["bank"] - - try: - amount = int(amount) - - except: - if amount.lower() == "max": - amount = bank - - elif amount.lower() == "all": - amount = bank - - else: - amount = conv2num(amount) - - bal = await update_bank(ctx.author) - - amount = int(amount) - - if amount > bal[1]: - await ctx.send('You do not have sufficient balance') - return - if amount < 0: - await ctx.send('Amount must be positive!') - return - - await update_bank(ctx.author, amount) - await update_bank(ctx.author, -1*amount, 'bank') - await ctx.send('{} You withdrew {:,} coins'.format(ctx.author.mention, amount)) - - @commands.command(aliases=['dep']) - async def deposit(self, ctx, amount=None): - await open_account(ctx.author) - if amount == None: - await ctx.send("Please enter the amount") - return - - user = ctx.author - - users = await get_bank_data() - - wallet = users[str(user.id)]["wallet"] - bank = users[str(user.id)]["bank"] - - try: - amount = int(amount) - - except: - if amount.lower() == "max": - amount = wallet - - elif amount.lower() == "all": - amount = wallet - - else: - amount = conv2num(amount) - - bal = await update_bank(ctx.author) - - amount = int(amount) - - if amount > bal[0]: - await ctx.send('You do not have sufficient balance') - return - if amount < 0: - await ctx.send('Amount must be positive!') - return - - await update_bank(ctx.author, -1*amount) - await update_bank(ctx.author, amount, 'bank') - await ctx.send('{} You deposited {:,} coins'.format(ctx.author.mention, amount)) - - @commands.command(aliases=['sm', 'donate', 'give']) - async def send(self, ctx, member: discord.Member, amount=None): - await open_account(ctx.author) - await open_account(member) - if amount == None: - await ctx.send("Please enter the amount i havent got all day") - return - try: - amount = int(amount) - except: - amount = conv2num(amount) - - bal = await update_bank(ctx.author) - if amount == 'all': - amount = bal[0] - - amount = int(amount) - - if amount > bal[1]: - await ctx.send('Get some more coins\nYou do not have sufficient balance in your banl') - return - if amount < 0: - await ctx.send('Amount must be positive!') - return - - await update_bank(ctx.author, -1*amount, 'bank') - await update_bank(member, amount, 'wallet') - await ctx.send(f'{ctx.author.mention} You gave {member} `{amount}` coins') - - @commands.command(aliases=['rb', 'steal']) - @commands.cooldown(1, 30, commands.BucketType.user) - async def rob(self, ctx, member: discord.Member): - await open_account(ctx.author) - await open_account(member) - bal = await update_bank(member) - - if bal[0] < 100: - await ctx.send('It is useless to rob them :(\nThey dont even have 100 coins') - return - - earning = random.randrange(0, bal[0]) - - await update_bank(ctx.author, earning) - await update_bank(member, -1*earning) - await ctx.send(f'{ctx.author.mention} You robbed {member} and got `{earning}` coins') - - @commands.command() - @commands.cooldown(1, 10, commands.BucketType.user) - async def gamble(self, ctx, amount=None): - await open_account(ctx.author) - if amount == None: - await ctx.send("Please enter the amount") - return - bal = await update_bank(ctx.author) - - user = ctx.author - - users = await get_bank_data() - - wallet = users[str(user.id)]["wallet"] - bank = users[str(user.id)]["bank"] - - try: - amount = int(amount) - - except: - if amount.lower() == "max": - amount = wallet - - elif amount.lower() == "all": - amount = wallet - - else: - amount = conv2num(amount) - - if amount > bal[0]: - await ctx.send('You do not have sufficient balance') - return - if amount < 0: - await ctx.send('Amount must be positive!') - return - - win = False - num = random.randint(1, 3) - if num == 2: - win = True - - if win == True: - await update_bank(ctx.author, 2*amount) - await ctx.send(f'{ctx.author.mention} You won :) `{2*amount}`') - else: - await update_bank(ctx.author, -1*amount) - await ctx.send(f'{ctx.author.mention} You lose :( `{amount}`') - - @commands.command() - async def shop(self, ctx, *, item_=None): - img = None - if item_ == None: - em = discord.Embed(title="Shop") - - for item in mainshop: - name = item["name"] - price = item["price"] - price = "{:,}".format(price) - desc = item["description"] - buy = item['buy'] - if buy == True: - em.add_field(name=f"**{name}**", value=f"$`{price}`") - else: - em = discord.Embed(title="Shop") - inshop = False - for item in mainshop: - if item["name"].lower() == item_.lower(): - name = item["name"] - price = item["price"] - price = "{:,}".format(price) - sell = float(item["price"])*0.7 - sell = "{:,}".format(sell) - desc = item["description"] - buy = item['buy'] - em.add_field( - name=f"**Item Name/Id: {name}**", value=f"Buy: $`{price}`\nSell: $`{sell}`\n{desc}") - if item["icon_path"] == None: - img = None - else: - img = True - file = discord.File(f"./database/shopimages/{item['icon_path']}") - em.set_thumbnail(url=f"attachment://./database/shopimages/{item['icon_path']}") - inshop = True - break - if inshop == False: - em.add_field(name=item_, value="Item not in shop") - if img == None: - await ctx.send(embed=em) - if img == True: - await ctx.send(file=file, embed=em) - - @commands.command(aliases=['purchase']) - async def buy(self, ctx, amount=1, *, item=None): - - await open_account(ctx.author) - - res = await buy_this(ctx.author, amount, item) - - if not res[0]: - if res[1] == 1: - await ctx.send("That Object isn't there!") - return - if res[1] == 2: - await ctx.send(f"You don't have enough coins in your wallet to buy {amount} {item}") - return - - await ctx.send(f"You just bought {amount} {item}") - - @commands.command(aliases=['inv', 'inventory']) - async def bag(self, ctx): - await open_account(ctx.author) - user = ctx.author - users = await get_bank_data() - - try: - bag = users[str(user.id)]["bag"] - except: - bag = [] - - em = discord.Embed(title="Bag") - for item in bag: - name = item["item"] - amount = item["amount"] - - em.add_field(name=name, value=amount) - - await ctx.send(embed=em) - - @commands.command() - async def sell(self, ctx, amount=1, *, item): - await open_account(ctx.author) - - res = await sell_this(ctx.author, item, amount) - - if not res[0]: - if res[1] == 1: - await ctx.send("You dont have that item!") - return - if res[1] == 2: - await ctx.send(f"You don't have {amount} {item} in your bag.") - return - if res[1] == 3: - await ctx.send(f"You don't have {item} in your bag.") - return - - await ctx.send(f"You just sold {amount} {item}.") - - -def setup(client): - client.add_cog(Economy(client)) diff --git a/cogs/events/errors.py b/cogs/events/errors.py deleted file mode 100644 index e661693..0000000 --- a/cogs/events/errors.py +++ /dev/null @@ -1,287 +0,0 @@ -import discord -from discord.ext.commands import CheckFailure -import datetime -from discord.ext import commands -from utils import Log -import traceback -import sys -from discord.errors import InteractionResponded - -log = Log() - - -class Errors(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.Cog.listener() - async def on_command_error(self, ctx, error): - if hasattr(ctx.command, 'on_error'): - return - - cog = ctx.cog - if cog: - if cog._get_overridden_method(cog.cog_command_error) is not None: - return - - ignored = (commands.CommandNotFound,) - error = getattr(error, 'original', error) - - err = [('Ignoring exception in command {}:'.format(ctx.command)), - traceback.format_exception(type(error), error, error.__traceback__)] - log.log_command_error(err) - - if isinstance(error, ignored): - return - - elif isinstance(error, commands.CommandOnCooldown): - async def better_time(cd: int): - time = f"{cd}s" - if cd > 60: - minutes = cd - (cd % 60) - seconds = cd - minutes - minutes = int(minutes / 60) - time = f"{minutes}min {seconds}s" - if minutes > 60: - hoursglad = minutes - (minutes % 60) - hours = int(hoursglad / 60) - minutes = minutes - (hours*60) - time = f"{hours}h {minutes}min {seconds}s" - return time - - cd = round(error.retry_after) - if cd == 0: - cd = 1 - retry_after = await better_time(cd) - em = discord.Embed( - title="Wow buddy, Slow it down\nThis command is on cooldown", - description=f"```fix\nTry again in **{retry_after}** ```", - color=discord.Color.red() - ) - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, InteractionResponded): - pass - - elif isinstance(error, CheckFailure): - pass - - elif isinstance(error, commands.MissingRequiredArgument): - em = discord.Embed( - title="Missing a requred value / argument", - description=f"```fix\nYou haven't passed in all the required values for this command ```", - color=discord.Color.red() - ) - em.add_field(name=f"You have not passed in:", - value=f"`{error.param}`") - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.MissingPermissions): - em = discord.Embed( - title="Missing permissions", - description="```fix\nYou don't have permissions to use this commands ```", - color=discord.Color.red() - ) - em.add_field(name="Permissions you need:", - value=f"`{', '.join(error.missing_permissions)}`") - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.MessageNotFound): - em = discord.Embed( - title="Message not found", - description="```fix\nThe bot failed to find the message ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.MemberNotFound): - em = discord.Embed( - title="Member not found", - description="```fix\nThe bot failed to find the member ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.GuildNotFound): - em = discord.Embed( - title="Guild not found", - description="```fix\nThe bot faield to find the guild ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.UserNotFound): - em = discord.Embed( - title="User not found", - description="```fix\nThe bot failed to find the user ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.ChannelNotFound): - em = discord.Embed( - title="Channel not found", - description="```fix\nThe bot failed to find the channel``` ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.ChannelNotReadable): - em = discord.Embed( - title="Channel not readable", - description="```fix\nThe bot is unable to read this channel ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.RoleNotFound): - em = discord.Embed( - title="Role not found", - description="```fix\nThe bot was unable to find the role ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.ThreadNotFound): - em = discord.Embed( - title="Thread not found", - description="```fix\nThe bot was unable to fund the thread ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.BotMissingPermissions): - em = discord.Embed( - title="Bot missing permissions", - description="```fix\nWhy bot does not have the permissions do execute this command. Please gimme them perms ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.MissingRole): - em = discord.Embed( - title="Missing Role", - description="```fix\nUser does not have the role to run this command ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.BotMissingRole): - em = discord.Embed( - title="Bot Missing Role", - description="```fix\nWhy bot does not have the role to run this command, Gimme them roles ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.NSFWChannelRequired): - em = discord.Embed( - title="NSFW Only", - description="```fix\nThis command can only be used in an nsfw channel ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.DisabledCommand): - em = discord.Embed( - title="Command Disabled", - description="```fix\nThis command has been disabled ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, commands.CommandInvokeError): - em = discord.Embed( - title="Command failed to run", - description=f"```fix\nThis is not good, please use `{ctx.prefix}bug ` to report this if you think its a bug ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - elif isinstance(error, discord.HTTPException): - em = discord.Embed( - title="Error 404 Not Found", - description=f"```fix\n{error.code} {error.text} ```", - color=discord.Color.red() - ) - - em.timestamp = datetime.datetime.utcnow() - error_msg = await ctx.send(embed=em) - - await ctx.message.add_reaction("āš ļø") - - - else: - print('Ignoring exception in command {}:'.format( - ctx.command), file=sys.stderr) - traceback.print_exception( - type(error), error, error.__traceback__, file=sys.stderr) - - -def setup(client): - client.add_cog(Errors(client)) diff --git a/cogs/events/event_logging.py b/cogs/events/event_logging.py deleted file mode 100644 index fd5f81c..0000000 --- a/cogs/events/event_logging.py +++ /dev/null @@ -1,141 +0,0 @@ -import discord -import datetime -from discord.ext import commands -from utils import get_log_channel -import json - -async def plugin_enabled(guild): - with open("./database/db.json") as f: - data = json.load(f) - - data = data[str(guild.id)] - - if data["settings"]['plugins']["Logging"]: - return True - return False - - -class Log(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.Cog.listener() - async def on_message_edit(self, before, after): - if await plugin_enabled(before.guild) == False: - return - if after.author.id == self.client.user.id: - return - em = discord.Embed(color=discord.Color.blue(), - title="Message Edit", description=f"{before.author} edited their message", timestamp = datetime.datetime.utcnow()) - em.add_field(name="Before", value=before.content) - em.add_field(name="After", value=after.content) - - channel = await get_log_channel(self.client, before.guild) - if channel == None: - return - else: - - await channel.send(embed=em) - - @commands.Cog.listener() - async def on_message_delete(self, message): - if await plugin_enabled(message.guild) == False: - return - if message.author.id == self.client.user.id: - return - - em = discord.Embed(color=discord.Color.blue(), - title="Message Delete", description=f"{message.author} has deleted the message", timestamp = datetime.datetime.utcnow()) - em.add_field(name="Content:", value=f"{message.content}") - - channel = await get_log_channel(self.client, message.guild) - if channel == None: - return - else: - - await channel.send(embed=em) - - @commands.Cog.listener() - async def on_member_ban(self, guild, user): - if await plugin_enabled(guild) == False: - return - em = discord.Embed(color=discord.Color.blue(), - title="Member Banned!", description=f"{user.name} Has been banned from the server", timestamp = datetime.datetime.utcnow()) - channel = await get_log_channel(self.client, guild) - if channel == None: - return - else: - - await channel.send(embed=em) - - @commands.Cog.listener() - async def on_member_unban(self, guild, user): - if await plugin_enabled(guild) == False: - return - em = discord.Embed(color=discord.Color.blue(), - title="Member Unbanned!", description=f"{user.name} Has been unbanned from the server", timestamp = datetime.datetime.utcnow()) - channel = await get_log_channel(self.client, guild) - - if channel == None: - return - else: - await channel.send(embed=em) - - @commands.Cog.listener() - async def on_member_update(self, before, after): - if await plugin_enabled(before.guild) == False: - return - if before.nick is not None and after.nick is None: - em = discord.Embed(color=discord.Color.blue(), title="Nick Change", - description=f"{before.name} has unicked", timestamp = datetime.datetime.utcnow()) - em.add_field(name="Before:", value=before.nick) - em.add_field(name="After:", value="No Nick") - - if before.nick is None and after.nick is not None: - em = discord.Embed(color=discord.Color.blue(), title="Nick Change", - description=f"{before.name} Has nicked", timestamp = datetime.datetime.utcnow()) - em.add_field(name="Before:", value="No Nick") - em.add_field(name="After:", value=after.nick) - - elif before.nick != after.nick: - em = discord.Embed(color=discord.Color.blue(), - title="Nick Change", description=f"{before.name} Has changed their nick", timestamp = datetime.datetime.utcnow()) - em.add_field(name="Before:", value=before.nick) - em.add_field(name="After:", value=after.nick) - - channel = await get_log_channel(self.client, after.guild) - if channel == None: - return - else: - - await channel.send(embed=em) - - @commands.Cog.listener() - async def on_guild_channel_create(self, channel): - if await plugin_enabled(channel.guild) == False: - return - em = discord.Embed(color=discord.Color.blue(), title="Channel Created", - description=f"`{channel.name}` Has been created", timestamp = datetime.datetime.utcnow()) - channel = await get_log_channel(self.client, channel.guild) - if channel == None: - return - else: - - await channel.send(embed=em) - - @commands.Cog.listener() - async def on_guild_channel_delete(self, channel): - if await plugin_enabled(channel.guild) == False: - return - em = discord.Embed(color=discord.Color.blue(), title="Channel Delete", - description=f"`{channel.name}` Has been deleted", timestamp = datetime.datetime.utcnow()) - channel = await get_log_channel(self.client, channel.guild) - if channel == None: - return - else: - - await channel.send(embed=em) - - -def setup(client): - client.add_cog(Log(client)) diff --git a/cogs/events/guild.py b/cogs/events/guild.py deleted file mode 100644 index 56502fb..0000000 --- a/cogs/events/guild.py +++ /dev/null @@ -1,167 +0,0 @@ -import discord -import datetime -from discord.ext import commands -import json -from utils import Log, is_it_me -from utils import update_activity - -log = Log() - -async def startguildsetup(client, id): - file = { - "guild_id": id, - "prefix": "?", - "counting_channel": None, - "lastcounter": None, - "log_channel": None, - "welcome_channel": None, - "announcement_channel" : None, - "warnings": { - }, - "settings": { - "autocalc":True, - "plugins": { - "Counting": True, - "Moderation": True, - "Economy": True, - "TextConvert": True, - "Search": True, - "Welcome": True, - "Leveling": True, - "Music": True, - "Onping": True, - "Ticket": True, - "Minecraft": True, - "Utilities": True, - "Fun": True - } - }, - "autorole": { - "all": None, - "bot": None - }, - "welcome" : { - "bg_color" : None, - "text_color" : None, - "text_footer" : None, - "bg_image" : None - } - } - data = await client.get_db() - - if str(id) in data: - pass - else: - data[str(id)] = file - await client.update_db(data) - - newtickettemplate = {"ticket-counter": 0, "valid-roles": [],"pinged-roles": [], "ticket-channel-ids": [], "verified-roles": []} - with open(f"./database/tickets/ticket{id}.json", 'w') as f: - json.dump(newtickettemplate, f, indent=4) - with open(f"./database/counting.json") as f: - dataa = json.load(f) - dataa[f"{id}"] = 0 - with open(f"./database/counting.json", 'w') as f: - json.dump(dataa, f, indent=4) - - - -class Events(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.Cog.listener() - async def on_guild_remove(self,guild): - await update_activity(self.client) - cha = self.client.get_channel(925513395883606129) - em = discord.Embed(title="Leave", description=f"Left: {guild.name}", color=discord.Color.red()) - em.timestamp = datetime.datetime.utcnow() - await cha.send(embed=em) - - @commands.Cog.listener() - async def on_guild_join(self, guild): - chaem = discord.Embed(title="Join", description=f"Joined: {guild.name}", color=discord.Color.green()) - chaem.timestamp = datetime.datetime.utcnow() - cha = self.client.get_channel(925513395883606129) - await cha.send(embed=chaem) - await update_activity(self.client) - await startguildsetup(self.client, guild.id) - embed = discord.Embed(color=discord.Color.blue()) - embed.set_author(name="Here's some stuff to get you started:") - embed.add_field(name="Default Prefix: `?`", - value="This can be changed later using `?setprefix`") - embed.add_field(name="Channel Setting", - value="To set the counting, mod and welcome channel use `/set`") - embed.add_field( - name="Settings", value="You can use `?settings` to change some bot settings") - embed.set_footer( - text=f"Thank You - Why bot is now on {len(self.client.guilds)} servers!") - embed.timestamp = datetime.datetime.utcnow() - - await guild.system_channel.send(content="**Thanks for inviting me! :wave: **", embed=embed) - - - @commands.command() - @commands.check(is_it_me) - async def update_all_non_db(self, ctx): - for guild in self.client.guilds: - id = guild.id - file = { - "guild_id": id, - "prefix": "?", - "counting_channel": None, - "lastcounter": None, - "log_channel": None, - "welcome_channel": None, - "announcement_channel" : None, - "warnings": { - }, - "settings": { - "autocalc":True, - "plugins": { - "Counting": True, - "Moderation": True, - "Economy": True, - "TextConvert": True, - "Search": True, - "Welcome": True, - "Leveling": True, - "Music": True, - "Onping": True, - "Ticket": True, - "Minecraft": True, - "Utilities": True, - "Fun": True - } - }, - "autorole": { - "all": None, - "bot": None - }, - "welcome" : { - "bg_color" : None, - "text_color" : None, - "text_footer" : None, - "bg_image" : None - } - } - data = await self.client.get_db() - - - if str(id) in data: - pass - else: - data[str(id)] = file - await self.client.update_db(data) - - newtickettemplate = {"ticket-counter": 0, "valid-roles": [],"pinged-roles": [], "ticket-channel-ids": [], "verified-roles": []} - with open(f"./database/tickets/ticket{id}.json", 'w') as f: - json.dump(newtickettemplate, f, indent=4) - with open(f"./database/counting.json") as f: - dataa = json.load(f) - dataa[f"{id}"] = 0 - with open(f"./database/counting.json", 'w') as f: - json.dump(dataa, f, indent=4) - -def setup(client): - client.add_cog(Events(client)) \ No newline at end of file diff --git a/cogs/events/onping.py b/cogs/events/onping.py deleted file mode 100644 index 32dc48d..0000000 --- a/cogs/events/onping.py +++ /dev/null @@ -1,184 +0,0 @@ -import discord -from utils.checks import plugin_enabled -from discord.ext import commands -import datetime - - -class Onping(commands.Cog): - def __init__(self, client): - self.client = client - - - @commands.group(aliases=["on_pinged", 'pinged', 'onping'], help="This command is used to set the Onping message when you get pinged\nYou can use: onpinged set - to set your onpinged message\nYou can use: onpinged clear to clear your onping message\nYou can use this command without a subcommand and it will display the message", extras={"category": "Onping"}, usage="onpinged [set/clear(optional)]", description="Sets your Onpinged message") - @commands.check(plugin_enabled) - async def onpinged(self, ctx): - if ctx.invoked_subcommand is None: - user = await self.client.get_user_db() - user = user[str(ctx.author.id)] - - on_pinged_message = user['on_pinged'] - em = discord.Embed() - em.timestamp = datetime.datetime.utcnow() - - if on_pinged_message["title"] == None and on_pinged_message["description"] == None: - return await ctx.send(embed=discord.Embed(title="You have no on pinged message set.", description=f"Use `{ctx.prefix}onpinged set` to set one")) - - if on_pinged_message["title"] == None: - pass - else: - em.title = on_pinged_message["title"] - - if on_pinged_message["description"] == None: - pass - else: - em.description = on_pinged_message["description"] - - if on_pinged_message["color"] == None: - pass - else: - em.color = on_pinged_message["color"] - - await ctx.send(embed=em) - - - @onpinged.command() - @commands.check(plugin_enabled) - async def set(self, ctx): - - def check(m): - return m.channel == ctx.channel and m.author == ctx.author - - colors = { - "none": None, - "blue": 0x3498db, - "blurple": 0x5865f2, - "brand_green": 0x57f287, - "brand_red": 0xed4245, - "dark_blue": 0x206694, - "dark_gold": 0xc27c0e, - "dark_gray": 0x607d8b, - "dark_green": 0x1f8b4c, - "dark_grey": 0x607d8b, - "dark_magenta": 0xad1457, - "dark_orange": 0xa84300, - "dark_purple": 0x71368a, - "dark_red": 0x992d22, - "dark_teal": 0x11806a, - "dark_theme": 0x36393f, - "darker_gray": 0x546e7a, - "darker_grey": 0x546e7a, - "fuchsia": 0xeb459e, - "gold": 0xf1c40f, - "green": 0x2ecc71, - "greyple": 0x99aab5, - "light_gray": 0x979c9f, - "light_grey": 0x979c9f, - "lighter_gray": 0x95a5a6, - "lighter_grey": 0x95a5a6, - "magenta": 0xe91e63, - "nitro_pink": 0xf47fff, - "og_blurple": 0x7289da, - "orange": 0xe67e22, - "purple": 0x9b59b6, - "random": 0x00d1ff, - "red": 0xe74c3c, - "teal": 0x1abc9 - } - - await ctx.send("Type the title for the embed (or type none if you dont want one)") - title = await self.client.wait_for("message", check=check, timeout=300) - title = title.content - if title.lower() == 'none': - title = None - - await ctx.send("Type the description for the embed (or type none if you dont want one)") - description = await self.client.wait_for("message", check=check, timeout=300) - description = description.content - if description.lower() == 'none': - description = None - - await ctx.send("Choose color from this list:\nEnter the color you want (or type none if you want the default:)") - color = await self.client.wait_for("message", check=check, timeout=300) - color = color.content - color = color.lower() - - if color.lower() in colors.keys(): - color = colors[color] - else: - color = None - - data = await self.client.get_user_db() - - data[str(ctx.author.id)]['on_pinged']['title'] = title - data[str(ctx.author.id)]['on_pinged']['description'] = description - data[str(ctx.author.id)]['on_pinged']['color'] = color - - await self.client.update_user_db(data) - - - @onpinged.command(aliases=['reset']) - @commands.check(plugin_enabled) - async def clear(self, ctx): - data = await self.client.get_user_db() - data[str(ctx.author.id)]["on_pinged"]["title"] = None - data[str(ctx.author.id)]["on_pinged"]["description"] = None - data[str(ctx.author.id)]["on_pinged"]["color"] = None - await self.client.update_user_db(data) - await ctx.send(embed=discord.Embed(title="On-Ping", description="RESET", color=ctx.author.color)) - - - @onpinged.command() - @commands.check(plugin_enabled) - async def toggle(self, ctx): - data = await self.client.get_user_db() - if data[str(ctx.author.id)]["on_pinged_toggled"] == True: - data[str(ctx.author.id)]["on_pinged_toggled"] = False - await ctx.send(embed=discord.Embed(title="On-Ping", description="Toggled Off", color=ctx.author.color)) - - elif data[str(ctx.author.id)]["on_pinged_toggled"] == False: - data[str(ctx.author.id)]["on_pinged_toggled"] = True - await ctx.send(embed=discord.Embed(title="On-Ping", description="Toggled On", color=ctx.author.color)) - - await self.client.update_user_db(data) - - - @commands.Cog.listener() - async def on_message(self, message): - if message.guild is None: - return - if message.author.bot: - return - - data = await self.client.get_db() - if data[str(message.guild.id)]['settings']['plugins']['Onping'] == False: - return - - user_data = await self.client.get_user_db() - - for key, value in user_data.items(): - if message.reference != None: - return - - em = discord.Embed() - em.timestamp = datetime.datetime.utcnow() - em.title = value["on_pinged"]["title"] - em.description = value["on_pinged"]["description"] - - if em.title and em.description is None: - return - if value["on_pinged"]["color"] == None: - pass - else: - em.color = value["on_pinged"]["color"] - - if f"<@!{value['user_id']}>" in message.content or f"<@{value['user_id']}>" in message.content: - if value['user_id'] == message.author.id: - return - try: - return await message.reply(embed=em) - except Exception as err: - print(err) - - -def setup(client): - client.add_cog(Onping(client)) diff --git a/cogs/events/welcome.py b/cogs/events/welcome.py deleted file mode 100644 index c36ea0c..0000000 --- a/cogs/events/welcome.py +++ /dev/null @@ -1,171 +0,0 @@ -import discord -from io import BytesIO -from discord.ext import commands -from easy_pil import Editor, Canvas, Font, load_image_async, Text -import os -from utils import plugin_enabled - -async def generate_welcome_message(client, member, guild): - data = await client.get_db() - data = data[str(guild.id)] - welcome_bg_color = data['welcome']["bg_color"] - welcome_bg_image = data['welcome']["bg_image"] - welcome_text_color = data['welcome']["text_color"] - welcome_text_footer = data['welcome']["text_footer"] - - welcome_profile_url = "https://cdn.logojoy.com/wp-content/uploads/20210422095037/discord-mascot.png" - - welcome_image = Editor(Canvas((900, 270))) - - if welcome_bg_color is None: - welcome_bg_color = "#23272a" - - # Background Color - welcome_image.rectangle( - (0, 0), width=970, height=270, fill=welcome_bg_color) - - # Fonts to use with different size - poppins_big = Font.poppins(variant="bold", size=50) - poppins_mediam = Font.poppins(variant="bold", size=40) - poppins_regular = Font.poppins(variant="regular", size=30) - poppins_thin = Font.poppins(variant="regular", size=20) - - # Background - if welcome_bg_image is not None: - try: - bg_img_url = await load_image_async(str(welcome_bg_image)) - bg_img = Editor(bg_img_url).resize((970, 270)).blur(amount=3) - welcome_image.paste(bg_img, (0, 0)) - except Exception as err: - print(err) - - card_left_shape = [(0, 0), (0, 270), (330, 270), (260, 0)] - - welcome_image.polygon(card_left_shape, "#2C2F33") - # Profile Picture - if member.avatar is not None: - profile_image = await load_image_async(str(member.avatar.url)) - else: - profile_image = await load_image_async(str(welcome_profile_url)) - profile = Editor(profile_image).resize((200, 200)).circle_image() - welcome_image.paste(profile, (40, 35)) - - # Text - if welcome_text_color is None: - welcome_text_color = "#FFFFFF" - welcome_image.text((600, 35), "WELCOME", font=poppins_big, color=str( - welcome_text_color), align="center") - welcome_image.text((600, 85), str(member.name), font=poppins_regular, color=str( - welcome_text_color), align="center") - welcome_image.text((600, 135), "THANKS FOR JOINING", font=poppins_mediam, color=str( - welcome_text_color), align="center") - welcome_image.text((600, 175), str(member.guild.name), font=poppins_regular, color=str( - welcome_text_color), align="center") - if welcome_text_footer is None: - welcome_text_footer = "Hope you enjoy your stay at this amazing server" - welcome_image.text((620, 237), str(welcome_text_footer), font=poppins_thin, color=str( - welcome_text_color), align="center",) - - d = BytesIO() - d.seek(0) - welcome_image.save(d, "PNG") - d.seek(0) - return d - - -class Welcome(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.Cog.listener() - async def on_member_join(self, member): - data = await self.client.get_db() - - if data[str(member.guild.id)]['settings']['plugins']['Welcome'] == False: - return - file_path = await generate_welcome_message(self.client, member, member.guild) - - cha = data[str(member.guild.id)]["welcome_channel"] - if cha is not None: - channel = await self.client.fetch_channel(int(cha)) - # Send welcome message in server welcome channel - await channel.send(content=member.mention, file=discord.File(file_path, "welcome.png")) - - @commands.Cog.listener() - async def on_member_remove(self, member): - try: - await member.send(f"Goodbye {member.name}") - except: - pass - data = await self.client.get_db() - if data[str(member.guild.id)]['settings']['plugins']['Welcome'] == False: - return - cha = data[str(member.guild.id)]['welcome_channel'] - if cha is not None: - cha.send(f"Goodbye: {member.name}") - - @commands.group() - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def welcome(self, ctx): - if ctx.invoked_subcommand is not None: - return - file_path = await generate_welcome_message(self.client, ctx.author, ctx.guild) - await ctx.send(file=discord.File(file_path, "welcome.png"), embed=discord.Embed(title="This is the image that will show as the welcome message", description=f"`{ctx.prefix}welcome textcolor`\n`{ctx.prefix}welcome image`\n`{ctx.prefix}welcome bgcolor`\n`{ctx.prefix}welcome text\n`", color=ctx.author.color)) - - @welcome.command() - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def textcolor(self, ctx, color: str): - if color.lower() == "none": - color = None - data = await self.client.get_db() - data[str(ctx.guild.id)]['welcome']["text_color"] = color - - await self.client.update_db(data) - - await ctx.send(embed=discord.Embed(title="Welcome", description=f"Welcome text color set!\nUse `{ctx.prefix}welcome` with no subcommand to see the welcome message that will display", color=ctx.author.color)) - - @welcome.command() - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def bgcolor(self, ctx, color: str): - if color.lower() == "none": - color = None - data = await self.client.get_db() - data[str(ctx.guild.id)]['welcome']["bg_color"] = color - - await self.client.update_db(data) - - await ctx.send(embed=discord.Embed(title="Welcome", description=f"Welcome background color set!\nUse `{ctx.prefix}welcome` with no subcommand to see the welcome message that will display", color=ctx.author.color)) - - @welcome.command() - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def text(self, ctx, *, text): - if len(text) > 55: - return await ctx.send("Text is to big") - if text.lower() == "none": - text = None - data = await self.client.get_db() - data[str(ctx.guild.id)]['welcome']["text_footer"] = text - - await self.client.update_db(data) - await ctx.send(embed=discord.Embed(title="Welcome", description=f"Welcome test set!\nUse `{ctx.prefix}welcome` with no subcommand to see the welcome message that will display", color=ctx.author.color)) - - @welcome.command() - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def image(self, ctx, url: str): - if url.lower() == "none": - url = None - data = await self.client.get_db() - data[str(ctx.guild.id)]['welcome']["bg_image"] = url - - await self.client.update_db(data) - - await ctx.send(embed=discord.Embed(title="Welcome", description=f"Welcome background image set!\nUse `{ctx.prefix}welcome` with no subcommand to see the welcome message that will display", color=ctx.author.color)) - - -def setup(client): - client.add_cog(Welcome(client)) diff --git a/cogs/fun/embed.py b/cogs/fun/embed.py deleted file mode 100644 index 2068c46..0000000 --- a/cogs/fun/embed.py +++ /dev/null @@ -1,106 +0,0 @@ -import discord -from discord.ext import commands -import base64 -import json -from urllib.parse import unquote - -async def decode(url): - url = url.split("?data=") - b64_string = url[1] - b64_string = b64_string.replace("%3D", "=") - base_decode = base64.b64decode(b64_string) - json_data = json.loads(unquote(base_decode)) - - return json_data - - -class CustomEmbed(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command() - async def customembed(self, ctx, url:str): - await ctx.message.delete() - if "/account/embed?data=" not in url: - return await ctx.send(embed=discord.Embed(title="Link Invalid", description="Go to [why dashboard](https://why.fusionsid.repl.co/account/embed) to create embed then copy the link")) - - json_data = await decode(url) - - em = discord.Embed() - - if "embed" not in json_data and "message" not in json_data: - return await ctx.send("cant send an empty message") - - if json_data["embed"]: - print("e") - embed = json_data["embed"] - if "title" in embed: - em.title = embed["title"] - - if "description" in embed: - em.description = embed["description"] - - if "color" in embed: - em.color = embed["color"] - - if "timestamp" in embed: - em.timestamp = embed["timestamp"] - - if "url" in embed: - em.url = embed["url"] - - if "author" in embed: - if "name" in embed["author"]: - author_name = embed["author"]["name"] - else: - author_name = None - - if "url" in embed["author"]: - author_url = embed["author"]["url"] - else: - author_url = None - - if "icon_url" in embed["author"]: - author_icon_url = embed["author"]["icon_url"] - else: - author_icon_url = None - - em.set_author(author_name, author_url, author_icon_url) - - if "footer" in embed: - if "name" in embed["footer"]: - footer_text = embed["footer"]["text"] - else: - footer_text = None - - if "url" in embed["footer"]: - footer_icon = embed["footer"]["url"] - else: - footer_icon = None - - if footer_icon is None: - em.set_footer(text=footer_text) - else: - em.set_footer(text=footer_text, icon_url=footer_icon) - - if "thumbnail" in embed: - em.set_thumbnail(embed['thumbnail']["url"]) - - if "image" in embed: - em.set_thumbnail(embed['image']["url"]) - - if "fields" in embed: - for i in embed["fields"]: - em.add_field(name= i["name"], value=i["value"], inline=i["inline"]) - - else: - em=None - - if "content" in json_data: - await ctx.send(json_data["content"], embed=em) - else: - await ctx.send(embed=em) - - -def setup(client): - client.add_cog(CustomEmbed(client)) \ No newline at end of file diff --git a/cogs/fun/fun.py b/cogs/fun/fun.py deleted file mode 100644 index e062fa7..0000000 --- a/cogs/fun/fun.py +++ /dev/null @@ -1,355 +0,0 @@ -import discord -import asyncio -import pyfiglet -import json -import random -from discord.ext import commands -import asyncio -from discord.utils import get -import dotenv -from utils.checks import plugin_enabled -import datetime -from utils import post_get_json, kwarg_to_embed, is_it_me -from discord.ui import View - -dotenv.load_dotenv() -languages = ['awk', 'bash', 'befunge93', 'brachylog', 'brainfuck', 'c', 'c++', 'cjam', 'clojure', 'cobol', 'coffeescript', 'cow', 'crystal', 'csharp', 'csharp.net', 'd', 'dart', 'dash', 'dragon', 'elixir', 'emacs', 'erlang', 'file', 'forte', 'fortran', 'freebasic', 'fsharp.net', 'fsi', 'go', 'golfscript', 'groovy', 'haskell', 'husk', 'iverilog', 'japt', 'java', 'javascript', 'jelly', 'julia', 'kotlin', 'lisp', 'llvm_ir', 'lolcode', 'lua', 'matl', 'nasm', 'nasm64', 'nim', 'ocaml', 'octave', 'osabie', 'paradoc', 'pascal', 'perl', 'php', 'ponylang', 'powershell', 'prolog', 'pure', 'pyth', 'python', 'python2', 'racket', 'raku', 'retina', 'rockstar', 'rscript', 'ruby', 'rust', 'scala', 'sqlite3', 'swift', 'typescript', 'basic', 'basic.net', 'vlang', 'vyxal', 'yeethon', 'zig,'] - -async def get_roast(): - with open('./database/roastlist.json') as f: - data = json.load(f) - return random.choice(data) - -class MyView(View): - def __init__(self): - super().__init__(timeout=500) - - @discord.ui.button(style=discord.ButtonStyle.green, label="Claim", custom_id="b1") - async def button1(self, button, interaction): - button.style = discord.ButtonStyle.red - button.label = "Claimed" - button.disabled=True - await interaction.response.edit_message(view=self) - await interaction.followup.send("https://imgur.com/NQinKJB",ephemeral=True) - with open("./database/other.json") as f: - data = json.load(f) - data["claimrickroll"] += 1 - count = data["claimrickroll"] - await interaction.followup.send(f"You were the {count} person to get rick rolled",ephemeral=True) - with open("./database/other.json", 'w') as f: - json.dump(data, f, indent=4) - - -class Fun(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(help="This command gives you free coins. Click claim.", extras={"category":"Fun"}, usage="claim", description="Free Coins") - @commands.check(plugin_enabled) - @commands.cooldown(1, 5, commands.BucketType.user) - async def claim(self,ctx): - em = discord.Embed(title="Claim 100k Why Coins", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em, view=MyView()) - - @commands.command(help="This command gives you free coins. Click claim.", extras={"category":"Fun"}, usage="nitro", description="Free Coins") - @commands.check(is_it_me) - @commands.check(plugin_enabled) - async def nitro(self,ctx): - em = discord.Embed(title="Claim Free Nitro", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_image(url="https://gudstory.s3.us-east-2.amazonaws.com/wp-content/uploads/2021/02/08150513/Discord-Nitro.png") - await ctx.send(embed=em, view=MyView()) - - @commands.command() - @commands.check(plugin_enabled) - async def ascii(self, ctx, *, message): - font = "big" - message = pyfiglet.figlet_format(message, font=font) - await ctx.send('```\n{}\n```'.format(message)) - - - - @commands.command() - @commands.check(plugin_enabled) - async def fascii(self, ctx, font:str, *,message): - if font not in ["1943____","3-d","3x5","4x4_offr","5slalineoblique","5x7","5x8","64f1____","6x10","6x9","a_zooloo","acrobatic","advenger","alligator","alligator2","alphabet","aquaplan","arrows","asc_____","ascii___","assalt_m","asslt__m","atc_____","atc_gran","avatar","b_m__200","banner","banner3","banner3-D","banner4","barbwire","basic","battle_s","battlesh","baz__bil","beer_pub","bell","big","bigchief","binary","block","brite","briteb","britebi","britei","broadway","bubble","bubble__","bubble_b","bulbhead","c1______","c2______","c_ascii_","c_consen","calgphy2","caligraphy","catwalk","caus_in_","char1___","char2___","char3___","char4___","charact1","charact2","charact3","charact4","charact5","charact6","characte","charset_","chartr","chartri","chunky","clb6x10","clb8x10","clb8x8","cli8x8","clr4x6","clr5x10","clr5x6","clr5x8","clr6x10","clr6x6","clr6x8","clr7x10","clr7x8","clr8x10","clr8x8","coil_cop","coinstak","colossal","com_sen_","computer","contessa","contrast","convoy__","cosmic","cosmike","cour","courb","courbi","couri","crawford","cricket","cursive","cyberlarge","cybermedium","cybersmall","d_dragon","dcs_bfmo","decimal","deep_str","defleppard","demo_1__","demo_2__","demo_m__","devilish","diamond","digital","doh","doom","dotmatrix","double","drpepper","druid___","dwhistled","e__fist_","ebbs_1__","ebbs_2__","eca_____","eftichess","eftifont","eftipiti","eftirobot","eftitalic","eftiwall","eftiwater","epic","etcrvs__","f15_____","faces_of","fair_mea","fairligh","fantasy_","fbr12___","fbr1____","fbr2____","fbr_stri","fbr_tilt","fender","finalass","fireing_","flyn_sh","fourtops","fp1_____","fp2_____","fraktur","funky_dr","future_1","future_2","future_3","future_4","future_5","future_6","future_7","future_8","fuzzy","gauntlet","ghost_bo","goofy","gothic","gothic__","graceful","gradient","graffiti","grand_pr","greek","green_be","hades___","heavy_me","helv","helvb","helvbi","helvi","heroboti","hex","high_noo","hills___","hollywood","home_pak","house_of","hypa_bal","hyper___","inc_raw_","invita","isometric1","isometric2","isometric3","isometric4","italic","italics_","ivrit","jazmine","jerusalem","joust___","katakana","kban","kgames_i","kik_star","krak_out","larry3d","lazy_jon","lcd","lean","letter_w","letters","letterw3","lexible_","linux","lockergnome","mad_nurs","madrid","magic_ma","marquee","master_o","maxfour","mayhem_d","mcg_____","mig_ally","mike","mini","mirror","mnemonic","modern__","morse","moscow","mshebrew210","nancyj","nancyj-fancy","nancyj-underlined","new_asci","nfi1____","nipples","notie_ca","npn_____","ntgreek","nvscript","o8","octal","odel_lak","ogre","ok_beer_","os2","outrun__","p_s_h_m_","p_skateb","pacos_pe","panther_","pawn_ins","pawp","peaks","pebbles","pepper","phonix__","platoon2","platoon_","pod_____","poison","puffy","pyramid","r2-d2___","rad_____","rad_phan","radical_","rainbow_","rally_s2","rally_sp","rampage_","rastan__","raw_recu","rci_____","rectangles","relief","relief2","rev","ripper!_","road_rai","rockbox_","rok_____","roman","roman___","rot13","rounded","rowancap","rozzo","runic","runyc","sans","sansb","sansbi","sansi","sblood","sbook","sbookb","sbookbi","sbooki","script","script__","serifcap","shadow","shimrod","short","skate_ro","skateord","skateroc","sketch_s","slant","slide","slscript","sm______","small","smisome1","smkeyboard","smscript","smshadow","smslant","smtengwar","space_op","spc_demo","speed","stacey","stampatello","standard","star_war","starwars","stealth_","stellar","stencil1","stencil2","stop","straight","street_s","subteran","super_te","t__of_ap","tanja","tav1____","taxi____","tec1____","tec_7000","tecrvs__","tengwar","term","thick","thin","threepoint","ti_pan__","ticks","ticksslant","tiles","times","timesofl","tinker-toy","tomahawk","tombstone","top_duck","trashman","trek","triad_st","ts1_____","tsalagi","tsm_____","tsn_base","tty","ttyb","tubular","twin_cob","twopoint","type_set","ucf_fan_","ugalympi","unarmed_","univers","usa_____","usa_pq__","usaflag","utopia","utopiab","utopiabi","utopiai","vortron_","war_of_w","wavy","weird","whimsy","xbrite","xbriteb","xbritebi","xbritei","xchartr","xchartri","xcour","xcourb","xcourbi","xcouri","xhelv","xhelvb","xhelvbi","xhelvi","xsans","xsansb","xsansbi","xsansi","xsbook","xsbookb","xsbookbi","xsbooki","xtimes","xtty","xttyb","yie-ar__","yie_ar_k","z-pilot_","zig_zag_","zone7___"]: - return await ctx.send("Invalid Font") - message = pyfiglet.figlet_format(message, font=font) - await ctx.send('```\n{}\n```'.format(message)) - - @commands.command(aliases=['rockpaperscissors'], extras={"category":"Fun"}, usage="rps [rock/paper/scissors]", help="This command if for playing rock paper scissors with the bot.", description="Play a game of rock paper scissors against the bot") - @commands.check(plugin_enabled) - async def rps(self, ctx, rps: str): - choices = ["rock", "paper", "scissors"] - cpu_choice = random.choice(choices) - em = discord.Embed(title="Rock Paper Scissors", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - rps = rps.lower() - if rps == 'rock': - if cpu_choice == 'rock': - em.description = "It's a tie!" - elif cpu_choice == 'scissors': - em.description = "You win!" - elif cpu_choice == 'paper': - em.description = "You lose!" - - elif rps == 'paper': - if cpu_choice == 'paper': - em.description = "It's a tie!" - elif cpu_choice == 'rock': - em.description = "You win!" - elif cpu_choice == 'scissors': - em.description = "You lose!" - - elif rps == 'scissors': - if cpu_choice == 'scissors': - em.description = "It's a tie!" - elif cpu_choice == 'paper': - em.description = "You win!" - elif cpu_choice == 'rock': - em.description = "You lose!" - - else: - em.description = "Invalid Input" - - em.add_field(name="Your Choice", value=rps) - em.add_field(name="Bot Choice", value=cpu_choice) - await ctx.send(embed=em) - - - @commands.command(aliases=['roastme'], extras={"category":"Fun"}, usage="roast", help="The bot sends a roast into the chat", description="Bot roasts you") - @commands.check(plugin_enabled) - async def roast(self, ctx): - await ctx.message.delete() - roast = await get_roast() - em = discord.Embed(title=roast, color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - - @commands.command(aliases=['sendmsg'], extras={"category":"Fun"}, usage="dm [@user] [message]", help="You can use this command to send a dm to a user. The bot will send the message to the user.", description="Bot sends a message on your behalf") - @commands.check(plugin_enabled) - async def dm(self, ctx, member: discord.Member, *, message): - await ctx.message.delete() - embeddm = discord.Embed(title=message, color=ctx.author.color) - embeddm.timestamp = datetime.datetime.utcnow() - embeddm.set_footer(text=f"Message sent by: {ctx.author}") - await member.send(embed=embeddm) - - - @commands.command(aliases=['sr'], extras={"category":"Fun"}, usage="sendroast [@user]", help="The bot picks a random roast from a list and send it to a person of your choosing", description="The bots send a roast to someone on your behalf") - @commands.check(plugin_enabled) - async def sendroast(self, ctx, member: discord.Member): - await ctx.message.delete() - message = await get_roast() - embeddm = discord.Embed( - title=message, description="Imagine being roasted by a bot", color=ctx.author.color) - embeddm.timestamp = datetime.datetime.utcnow() - embeddm.set_footer(text=f"Message sent by: {ctx.author}") - await member.send(embed=embeddm) - - - @commands.command(aliases=['8ball'], extras={"category":"Fun"}, name="8 ball", usage="8ball [question]", help="The bot asks the magical 8ball and gets you the result", description="Asks the 8ball a question") - @commands.check(plugin_enabled) - async def _8ball(self, ctx, *, question): - _8ballans = [ - "As I see it, yes", - "It is certain", - "It is decidedly so", - "Most likely", - "Outlook good", - "Signs point to yes", - "Without a doubt", - "Yes", - "Yes - definitely", - "You may rely on it", - "Reply hazy, try again", - "Ask again later", - "Better not tell you now", - "Cannot predict now", - "Concentrate and ask again", - "Don't count on it", - "My reply is no", - "My sources say no", - "Outlook not so good", - "Very doubtful" - ] - em = discord.Embed(title="__8 Ball__",description=f"{question}\nAnswer: {random.choice(_8ballans)}") - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - @commands.command(aliases=['em'], extras={"category":"Fun"}, usage="embed --title test --desc test --channel 123456789 --color blue --timestamp yes --fields 2", help="This command is used to make an embeded message.\nThe bot will create a nice embed and then send it to the channel youre in or the channel you want.", description="Makes an embed") - @commands.check(plugin_enabled) - async def embed(self, ctx, *, kwargs): - data = await kwarg_to_embed(self.client, ctx, kwargs) - if data is None: - return - em = data[0] - channel = data[1] - - await ctx.message.delete() - await channel.send(embed=em) - - @commands.command(usage = "runcode [language] [code]", description = "Runs code", help = "This command is used to run code. It supports many languages.", extras={"category": "Fun"}) - @commands.check(plugin_enabled) - async def runcode(self, ctx, lang:str, *, code): - code = code.replace("`", "") - data = { - "language": lang, - "source" : f"""{code}""" - } - url = "https://emkc.org/api/v1/piston/execute" - - data = await post_get_json(url, data) - print(data) - if data['ran'] == True: - await ctx.send(f"```py\n{data['output']}```") - if data['stderr'] != "": - await ctx.send(f"```Errors\n{data['stderr']}```") - - @commands.command(aliases=['noembed'], extras={"category":"Fun"}, usage="say [text]", help="The bot speaks text that you want", description="Bot sends text") - @commands.check(plugin_enabled) - async def say(self, ctx, *, text): - await ctx.message.delete() - await ctx.send(f"{text}\n\n|| On behalf of {ctx.author.name} ||") - - @commands.command() - @commands.check(plugin_enabled) - async def whypp(self, ctx): - await ctx.send( -""" -**| WHY PP |** -** **<:why:932912321544728576> - <:why:932912321544728576> - <:why:932912321544728576> - <:why:932912321544728576> - <:why:932912321544728576> -<:why:932912321544728576> ** ** <:why:932912321544728576> -""" - ) - - # Polls - - @commands.command(aliases=['yahornah', 'yn'], extras={"category":"Fun"}, usage="yesorno [question]", help="This command makes a small poll which users can vote either yes, or no", description="Makes a Yah or Nah poll") - @commands.check(plugin_enabled) - async def yesorno(self, ctx, *, message): - msg = await ctx.send(embed=discord.Embed(title="Yah or Nah?", description=message, color=ctx.author.color)) - await msg.add_reaction('šŸ‘') - await msg.add_reaction('šŸ‘Ž') - - @commands.command(pass_context=True, aliases=['makepoll', 'question'], extras={"category":"Fun"}, usage="poll [time:seconds] '[title]' [each option followed by a space]", help="This command creates a poll which can have up to 10 options to vote to.\nThe poll will last for a certain amount of seconds that you choose, and after those seconds you will get the results.", description="Makes a poll") - @commands.check(plugin_enabled) - async def poll(self, ctx, time: int, question, *options: str): - if len(options) <= 1: - await ctx.send('You need more than one option to make a poll!') - return - if len(options) > 10: - await ctx.send('You cannot make a poll for more than 10 things!') - return - - if len(options) == 2 and options[0] == 'yes' and options[1] == 'no': - reactions = ['āœ…', 'āŒ'] - else: - reactions = ['1⃣', '2⃣', '3⃣', '4⃣', - '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', 'šŸ”Ÿ'] - - description = [] - reacting = [] - for x, option in enumerate(options): - description += '\n{} = {}'.format(reactions[x], option) - embed = discord.Embed(title=question, description=''.join(description), color=ctx.author.color) - embed.timestamp = datetime.datetime.utcnow() - embed.set_footer(text="Please don't vote twice") - react_message = await ctx.send(embed=embed) - for reaction in reactions[:len(options)]: - await react_message.add_reaction(reaction) - reacting.append(reaction) - - await asyncio.sleep(time) - message = await ctx.channel.fetch_message(react_message.id) - results = {} - for i in reacting: - reaction = get(message.reactions, emoji=i) - count = reaction.count-1 - results[i] = f"{count} votes" - results = f'{results}' - results = results.replace("{", "") - results = results.replace("}", "") - results = results.replace("'", "") - results = results.replace(",", "\n") - results = results.replace(":", " got") - embed.description = f"{embed.description}\n** **" - embed.add_field(name=f"Results:", value=f"** **\n {results}") - embed.set_footer(text="Voting is closed") - # await message.edit(embed=embed) - await message.reply(embed=discord.Embed(title=f"Poll Results For {question}:", description=f"**Votes:**\n {results}", color=ctx.author.color)) - - @commands.command(extras={"category":"Fun"}, usage="reactemoji [message_id] [word]", help="This command reacts a word to a message. If the word has more then one letter thats the same it wont work", description="React a word to a message") - @commands.check(plugin_enabled) - async def reactemoji(self, ctx,msg:int, *, text): - text = text.lower() - alpha = ['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'] - emojis = ["šŸ‡¦", "šŸ‡§", "šŸ‡Ø", "šŸ‡©" ,"šŸ‡Ŗ", "šŸ‡«" ,"šŸ‡¬", "šŸ‡­" ,"šŸ‡®", "šŸ‡Æ", "šŸ‡°", "šŸ‡±", "šŸ‡²", "šŸ‡³" ,"šŸ‡“" ,"šŸ‡µ" ,"šŸ‡¶", "šŸ‡·" ,"šŸ‡ø", "šŸ‡¹" ,"šŸ‡ŗ", "šŸ‡»", "šŸ‡¼", "šŸ‡½", "šŸ‡¾", "šŸ‡æ"] - - emojis = dict(zip(alpha, emojis)) - message = await ctx.message.channel.fetch_message(msg) - for i in text: - try: - emoji = emojis[i] - await message.add_reaction(emoji) - except Exception as e: - print(e) - - - @commands.command(usage = "screenshot [url]", description = "Screenshot a url", help = "This command finds a url and screenshots it", extras={"category": "Fun"}) - @commands.check(plugin_enabled) - async def screenshot(self, ctx, *, url): - if "http://" not in url or "https://" not in url: - url = f"https://{url}" - em = discord.Embed( - title = f"Screenshot", - description = f"[Link]({url.replace('https://image.thum.io/get/', '')})", - color = ctx.author.color - ) - em.set_image(url=f"https://image.thum.io/get/{url}") - - await ctx.send(embed=em) - - - @commands.command(usage = "hack [@user]", description = "Hack someone", help = "This command totally 'hacks' someone :)", extras={"category": "Fun"}) - async def hack(self, ctx, member: discord.Member): - - email_ext = ["gmail.com","yahoo.com","hotmail.com","aol.com","hotmail.co.uk","hotmail.fr","msn.com","yahoo.fr","wanadoo.fr","orange.fr","comcast.net","yahoo.co.uk","yahoo.com.br","yahoo.co.in","live.com","rediffmail.com","free.fr","gmx.de","web.de","yandex.ru","ymail.com","libero.it","outlook.com","uol.com.br","bol.com.br","mail.ru","cox.net","hotmail.it","sbcglobal.net","sfr.fr","live.fr","verizon.net","live.co.uk",] - most_used_words = ['TrASh','gEt gUd','waSsUp','noOb', "LmAo", "lol", "lMfao", "e", "seNd nUkeS", "f&Ck", "sH#t", "nub", "b1T#h"] - passwords = ["123456","password","12345","123456789","password1","abc123","12345678","qwerty","111111","1234567","1234","iloveyou","sunshine","monkey","1234567890","123123","princess","baseball","dragon","football","shadow","michael","soccer","unknown","maggie","000000.","ashley","myspace1","purple","fuckyou","charlie","jordan","hunter","superman","tigger","michelle","buster","pepper","justin","andrew","harley","matthew","bailey","jennifer","samantha","ginger","anthony","qwerty123","qwerty1","peanut"] - - hack_message = await ctx.send(f"[ā––] Hacking {member.name} now...") - await asyncio.sleep(1.420) - await hack_message.edit(content='[ā–˜] Finding discord login... (2fa bypassed)') - await asyncio.sleep(1.69) - email = f"{member.name}.{random.randint(1, 100)}@{random.choice(email_ext)}" - await hack_message.edit(content=f"[ā–] `Email: {email}`\n `Password: {random.choice(passwords)}`") - await asyncio.sleep(1.420) - await hack_message.edit(content='[ā–—] IP address: 127.0.0.1:50') - await asyncio.sleep(1.69) - await hack_message.edit(content=f'[ā––] Most used words: {random.choice(most_used_words)}') - await asyncio.sleep(1.420) - await hack_message.edit(content=f"[ā–˜] Injecting trojan virus into discriminator: {member.discriminator}") - await asyncio.sleep(1.69) - await hack_message.edit(content='[ā–] Selling information to the government...') - await asyncio.sleep(1.420) - await hack_message.edit(content=f'[ā–—] Reporting account to discord for breaking TOS...') - await asyncio.sleep(1.69) - await hack_message.edit(content='[ā––] Hacking medical records...') - await asyncio.sleep(1.420) - await hack_message.edit(content=f"Finished hacking {member.mention}") - - await ctx.send("The *totally* real and dangerous hack is complete!") - # "[ā––] [ā–˜] [ā–] [ā–—]" - -def setup(client): - client.add_cog(Fun(client)) - - - diff --git a/cogs/fun/text.py b/cogs/fun/text.py deleted file mode 100644 index 6d4e2d7..0000000 --- a/cogs/fun/text.py +++ /dev/null @@ -1,140 +0,0 @@ -import discord -from utils.checks import plugin_enabled -import random -import binascii -from discord.ext import commands -from discord.ext.commands import clean_content - - -class TextConvert(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(aliases=['mock'], help="This command dRuNKiFies text", extras={"category":"Text"}, usage="drunkify [text]", description="Drunkifies Text") - @commands.check(plugin_enabled) - async def drunkify(self, ctx, *, s): - lst = [str.upper, str.lower] - newText = await commands.clean_content().convert(ctx, ''.join(random.choice(lst)(c) for c in s)) - if len(newText) <= 380: - await ctx.send(newText) - else: - try: - await ctx.author.send(newText) - await ctx.send(f"**{ctx.author.mention} The output too was too large, so I sent it to your DMs! :mailbox_with_mail:**") - except Exception: - await ctx.send(f"**{ctx.author.mention} There was a problem, and I could not send the output. It may be too large or malformed**") - - @commands.command(aliases=['exp'], help="This command e x p a n d s text", extras={"category":"Text"}, usage="expand [num] [text]", description="Expands Text") - @commands.check(plugin_enabled) - async def expand(self, ctx, num: int, *, s: clean_content): - spacing = "" - if num > 0 and num <= 10: - for _ in range(num): - spacing += " " - result = spacing.join(s) - if len(result) <= 200: - await ctx.send(result) - else: - try: - await ctx.author.send(result) - await ctx.send(f"**{ctx.author.mention} The output too was too large, so I sent it to your DMs! :mailbox_with_mail:**") - except Exception: - await ctx.send(f"**{ctx.author.mention} There was a problem, and I could not send the output. It may be too large or malformed**") - else: - await ctx.send("```fix\nError: The number can only be from 1 to 5```") - - @commands.command(aliases=['rev'], help="This command Reverses text like this:\nsiht ekil txet sesreveR dnammoc sihT", extras={"category":"Text"}, usage="reverse [text]", description="Reverses Text") - @commands.check(plugin_enabled) - async def reverse(self, ctx, *, s: clean_content): - result = await commands.clean_content().convert(ctx, s[::-1]) - if len(result) <= 350: - await ctx.send(f"{result}") - else: - try: - await ctx.author.send(f"{result}") - await ctx.send(f"**{ctx.author.mention} The output too was too large, so I sent it to your DMs! :mailbox_with_mail:**") - except Exception: - await ctx.send(f"**{ctx.author.mention} There was a problem, and I could not send the output. It may be too large or malformed**") - - @commands.command(aliases=['tth'], help="This command converts text to hexadecimal", extras={"category":"Text"}, usage="texttohex [text]", description="Converts Text to hex") - @commands.check(plugin_enabled) - async def texttohex(self, ctx, *, s): - try: - hexoutput = await commands.clean_content().convert(ctx, (" ".join("{:02x}".format(ord(c)) for c in s))) - except Exception as e: - await ctx.send(f"**Error: `{e}`. This probably means the text is malformed. Sorry, you can always try here: http://www.unit-conversion.info/texttools/hexadecimal/#data**") - if len(hexoutput) <= 479: - await ctx.send(f"```fix\n{hexoutput}```") - else: - try: - await ctx.author.send(f"```fix\n{hexoutput}```") - await ctx.send(f"**{ctx.author.mention} The output too was too large, so I sent it to your DMs! :mailbox_with_mail:**") - except Exception: - await ctx.send(f"**{ctx.author.mention} There was a problem, and I could not send the output. It may be too large or malformed**") - - @commands.command(aliases=['htt'], help="This command converts hexcadecimal text to text", extras={"category":"Text"}, usage="hextotext [text]", description="Converts hex to Text") - @commands.check(plugin_enabled) - async def hextotext(self, ctx, *, s): - try: - cleanS = await commands.clean_content().convert(ctx, bytearray.fromhex(s).decode()) - except Exception as e: - await ctx.send(f"**Error: `{e}`. This probably means the text is malformed. Sorry, you can always try here: http://www.unit-conversion.info/texttools/hexadecimal/#data**") - if len(cleanS) <= 479: - await ctx.send(f"```{cleanS}```") - else: - try: - await ctx.author.send(f"```{cleanS}```") - await ctx.send(f"**{ctx.author.mention} The output too was too large, so I sent it to your DMs! :mailbox_with_mail:**") - except Exception: - await ctx.send(f"**{ctx.author.mention} There was a problem, and I could not send the output. It may be too large or malformed**") - - @commands.command(aliases=['ttb'], help="This command converts text to binary 0s and 1s ", extras={"category":"Text"}, usage="texttobinary", description="Convert to binary") - @commands.check(plugin_enabled) - async def texttobinary(self, ctx, *, s): - try: - cleanS = await commands.clean_content().convert(ctx, ' '.join(format(ord(x), 'b') for x in s)) - except Exception as e: - await ctx.send(f"**Error: `{e}`. This probably means the text is malformed. Sorry, you can always try here: http://www.unit-conversion.info/texttools/convert-text-to-binary/#data**") - if len(cleanS) <= 479: - await ctx.send(f"```fix\n{cleanS}```") - else: - try: - await ctx.author.send(f"```fix\n{cleanS}```") - await ctx.send(f"**{ctx.author.mention} The output too was too large, so I sent it to your DMs! :mailbox_with_mail:**") - except Exception: - await ctx.send(f"**{ctx.author.mention} There was a problem, and I could not send the output. It may be too large or malformed**") - - @commands.command(aliases=['btt'], help="This command converts binary to text", extras={"category":"Text"}, usage="binarytotext", description="Convert binary to text") - @commands.check(plugin_enabled) - async def binarytotext(self, ctx, *, s): - try: - cleanS = await commands.clean_content().convert(ctx, ''.join([chr(int(s, 2)) for s in s.split()])) - except Exception as e: - await ctx.send(f"**Error: `{e}`. This probably means the text is malformed. Sorry, you can always try here: http://www.unit-conversion.info/texttools/convert-text-to-binary/#data**") - if len(cleanS) <= 479: - await ctx.send(f"```{cleanS}```") - else: - try: - await ctx.author.send(f"```{cleanS}```") - await ctx.send(f"**{ctx.author.mention} The output too was too large, so I sent it to your DMs! :mailbox_with_mail:**") - except Exception: - await ctx.send(f"**{ctx.author.mention} There was a problem, and I could not send the output. It may be too large or malformed**") - - @commands.command(aliases=['emoji'], help="This command converts text to emojies", extras={"category":"Text"}, usage="emojify [text]", description="Emojifies Text") - @commands.check(plugin_enabled) - async def emojify(self, ctx, *, text): - emojis = [] - for s in text.lower(): - if s.isdecimal(): - num2emo = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', - 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine'} - emojis.append(f':{num2emo.get(s)}:') - elif s.isalpha(): - emojis.append(f':regional_indicator_{s}:') - else: - emojis.append(s) - await ctx.send(' '.join(emojis)) - - -def setup(client): - client.add_cog(TextConvert(client)) diff --git a/cogs/fun/trivia.py b/cogs/fun/trivia.py deleted file mode 100644 index a1450dd..0000000 --- a/cogs/fun/trivia.py +++ /dev/null @@ -1,113 +0,0 @@ -import discord -import asyncio -from utils import plugin_enabled, get_url_json -from discord.ext import commands -from discord.ui import Button -import urllib.parse - - -class TriviaView(discord.ui.View): - def __init__(self, json_response, ctx): - - self.ctx = ctx - - if json_response["difficulty"] == "easy": - timeout = 10 - if json_response["difficulty"] == "medium": - timeout = 15 - if json_response["difficulty"] == "hard": - timeout = 20 - - super().__init__(timeout=timeout) - - self.json = json_response - - self.question = json_response["question"] - self.correct_answer = json_response["correct_answer"] - self.answers = json_response["incorrect_answers"] - self.answers.append(json_response["correct_answer"]) - self.category = json_response["category"] - - async def callback(interaction): - ans = "" - await interaction.response.send_message(f"You answered: {ans}") - - for i in self.answers: - try: - i = urllib.parse.unquote(i) - except Exception as e: - print(e) - button = Button(label=i, style=discord.ButtonStyle.green, row=1) - button.callback = callback - self.add_item(button) - - - async def interaction_check(self, interaction) -> bool: - if interaction.user != self.ctx.author: - await interaction.response.send_message("This isnt for you",ephemeral=True) - return False - else: - return True - -class Trivia(commands.Cog): - def __init__(self, client): - self.client = client - self.url = "https://opentdb.com/api.php?amount=1" - - @commands.command() - @commands.check(plugin_enabled) - async def trivia(self, ctx, mode : str = None): - - if mode is not None and mode in ["easy", "medium", "hard"]: - - url = self.url + f"&difficulty={mode}" +"&type=multiple" - else: - url = self.url +"&type=multiple" - - url += "&encode=url3986" - data = await get_url_json(url) - data = data["results"][0] - - # decode that shit - for key, value in data.items(): - if type(value) != list: - data[key] = urllib.parse.unquote(value) - continue - for item, index in enumerate(value): - value.remove(index) - try: - value.append(urllib.parse.unquote(item)) - except Exception: - continue - - if data["difficulty"] == "easy": - timeout = 10 - if data["difficulty"] == "medium": - timeout = 15 - if data["difficulty"] == "hard": - timeout = 20 - - em = discord.Embed( - title = data["question"], - color = ctx.author.color, - description= f"You have `{timeout}`s to answer this question" - ) - em.set_author(icon_url=ctx.author.avatar.url, name=f"{ctx.author.name}'s Trivia:") - em.add_field(name="Category:", value=f"`{data['category']}`") - em.add_field(name="Difficulty:", value=f"`{(data['difficulty']).capitalize()}`") - - view = TriviaView(data, ctx) - - message = await ctx.send(embed=em, view=view) - await asyncio.sleep(timeout) - for i in view.children: - i.disabled = True - try: - return await message.edit(view=view) - except Exception: - return - - - -def setup(client): - client.add_cog(Trivia(client)) \ No newline at end of file diff --git a/cogs/games/counting.py b/cogs/games/counting.py deleted file mode 100644 index 083fa79..0000000 --- a/cogs/games/counting.py +++ /dev/null @@ -1,131 +0,0 @@ -import discord -import discord -from discord.ext import commands -import json -import datetime -from utils.checks import plugin_enabled -import numexpr as ne - - -async def get_counting_channel(guild): - with open("./database/db.json") as f: - data = json.load(f) - if data[str(guild.id)]['counting_channel'] == None: - return None - return int(data[str(guild.id)]["counting_channel"]) - -# Counting - - -async def counting(msg, guild, channel, m): - for i in ['this', 'that', 'is', 'not', "false", ]: - if i in msg.lower(): - return - try: - msg = int(msg) - calcm = False - except: - try: - calc = ne.evaluate(msg) - _msg = msg - msg = int(calc) - calcm = True - with open("./database/db.json") as f: - data = json.load(f) - if data[str(guild.id)]['settings']['autocalc'] == True: - with open("./database/goose_mode.json") as f: - goose_data = json.load(f) - if str(guild.id) in goose_data and msg == 19 and "9" in str(_msg) and "10" in str(_msg) and "+" in str(_msg): - if goose_data[str(guild.id)]: - await m.reply("21") - else: - await m.reply(msg) - else: - await m.reply(msg) - except Exception: - return - with open("./database/db.json") as f: - data = json.load(f) - if data[str(m.guild.id)]['settings']['plugins']['Counting'] == False: - return - cc = await get_counting_channel(guild) - - if cc is None: - return - if channel.id == cc: - with open("./database/counting.json") as f: - data = json.load(f) - with open('./database/db.json') as f: - data2 = json.load(f) - - if data2[str(guild.id)]['lastcounter'] == None: - data2[str(guild.id)]['lastcounter'] = m.author.id - elif data2[str(guild.id)]['lastcounter'] == m.author.id: - data[f"{guild.id}"] = 0 - data2[str(guild.id)]['lastcounter'] = None - await m.add_reaction("āŒ") - em = discord.Embed(title=f"{m.author.name}, You ruined it!", - description="Only one person at a time\nCount reset to zero", color=discord.Color.blue()) - em.timestamp = datetime.datetime.utcnow() - with open("./database/counting.json", 'w') as f: - json.dump(data, f, indent=4) - with open("./database/db.json", 'w') as f: - json.dump(data2, f, indent=4) - return await channel.send(embed=em) - else: - data2[str(guild.id)]['lastcounter'] = m.author.id - - if (data[f"{guild.id}"] + 1) == msg: - data[f"{guild.id}"] += 1 - if calcm == True: - # await m.reply(msg) - pass - else: - pass - await m.add_reaction("āœ…") - else: - await m.add_reaction("āŒ") - em = discord.Embed(title=f"{m.author.name}, You ruined it!", description=f"You were supposed to type `{(data[f'{guild.id}']+1)}`\nCount reset to zero", color=discord.Color.blue()) - em.timestamp = datetime.datetime.utcnow() - data2[f"{guild.id}"]['lastcounter'] = None - data[f"{guild.id}"] = 0 - await channel.send(embed=em) - with open("./database/counting.json", 'w') as f: - json.dump(data, f, indent=4) - with open("./database/db.json", 'w') as f: - json.dump(data2, f, indent=4) - - -class Counting(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(aliases=['num', 'nrn'], extras={"category": "Counting"}, usage="numrn", help="This command shows the current number for the Counting game.\nYou can use /set Counting Channel [#channel] to set the counting channel.\nThis commamd is very useful if somebody deletes/edited their message and you dont know whats the next number", description="Current number for the counting game") - @commands.check(plugin_enabled) - async def numrn(self, ctx): - guild = ctx.guild - with open('./database/counting.json') as f: - data = json.load(f) - guildid = f'{guild.id}' - numrn = data[guildid] - em = discord.Embed( - title=f"Current number is {numrn}", description=f"So the next number to count is: {numrn+1}", color=discord.Color.green()) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - @commands.Cog.listener() - async def on_message(self, message): - if message.guild is None: - return - if message.author.bot: - return - - channel = message.channel - msg = message.content - guild = message.guild - - await counting(msg, guild, channel, message) - - -def setup(client): - client.add_cog(Counting(client)) diff --git a/cogs/games/minecraft.py b/cogs/games/minecraft.py deleted file mode 100644 index c8a0915..0000000 --- a/cogs/games/minecraft.py +++ /dev/null @@ -1,231 +0,0 @@ -import discord -import random -import os -import json -from discord.ext import commands -import datetime -import dotenv -from utils.checks import plugin_enabled -import aiohttp -import aiofiles -from mcstatus import MinecraftServer, MinecraftBedrockServer - -dotenv.load_dotenv() - -api_key = os.environ['HYPIXEL'] - - -async def get_uuid(user): - url = f'https://api.mojang.com/users/profiles/minecraft/{user}?' - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - uuid = await response.json() - uuid = uuid['id'] - return uuid - - -async def get_user_uuid(ctx): - with open('./database/igns.json', 'r') as f: - users = json.load(f) - - for user in users: - if user["id"] == ctx.author.id: - uuid = user["uuid"] - return uuid - await ctx.send("You havent set your ign yet. Use setign to set it") - - -async def get_hydata(uuid): - url = f"https://api.hypixel.net/player?key={api_key}&uuid={uuid}" - async with aiohttp.ClientSession() as session: - response = await session.get(url) - return await response.json() - - -class Minecraft(commands.Cog): - - def __init__(self, client): - self.client = client - - @commands.command(aliases=['uuid'], extras={"category":"Minecraft"}, usage="getuuid [ign(optional)]", help="This command will return your minecraft ign.\nIf you have used setign before then you wont have to specify the user.", description="Returns your minecraft ign") - @commands.check(plugin_enabled) - async def getuuid(self, ctx, player=None): - if player == None: - uuid = await get_user_uuid(ctx) - else: - uuid = await get_uuid(str(player)) - await ctx.send(embed=discord.Embed(title="Your uuid:", description=f'{uuid}', color=ctx.author.color)) - - # Register IGN - - @commands.command(aliases=['ign'], extras={"category":"Minecraft"}, usage="setign", help="This command sets your minecraft ign.\nIf you use this command you wont have to specify the ign in other mincraft commands", description="Sets you minecraft ign") - @commands.check(plugin_enabled) - async def setign(self, ctx): - client = self.client - confirm = False - - def wfcheck(m): - return m.channel == ctx.channel and m.author == ctx.author - - with open('./database/igns.json', 'r') as f: - users = json.load(f) - - for user in users: - if user["id"] == ctx.author.id: - await ctx.send("You've already set you ign, Would you like to change it?\ny/n") - confirm = await client.wait_for("message", check=wfcheck, timeout=300) - confirm = confirm.content - if confirm.lower() == "y": - await ctx.send("Enter your Minecraft ign:") - ign = await client.wait_for("message", check=wfcheck, timeout=300) - ign = str(ign.content) - uuid = await get_uuid(ign) - user["uuid"] = uuid - with open('./database/igns.json', 'w') as f: - json.dump(users, f, indent=4) - return - else: - return - await ctx.send("Enter your Minecraft ign:") - ign = await client.wait_for("message", check=wfcheck, timeout=300) - ign = str(ign.content) - uuid = await get_uuid(ign) - user = {"id": ctx.author.id, "uuid": uuid} - with open('./database/igns.json') as f: - users = json.load(f) - users.append(user) - with open('./database/igns.json', 'w') as f: - json.dump(users, f, indent=4) - - # Hypixel image - - @commands.command(aliases=['hypixel'], extras={"category":"Minecraft"}, usage="hystats [ign(optional)]", help="This command shows the hypixel stats for a Hypixel user.\nYou can specify an ign or if youve used setign befre you wont have to", description="Shows a nice pic of your hypixel stats") - @commands.check(plugin_enabled) - async def hystats(self, ctx, player=None): - if player == None: - uuid = await get_user_uuid(ctx) - else: - uuid = await get_uuid(str(player)) - - url = "https://hypixel.paniek.de/signature/{}/general-tooltip".format( - uuid) - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - if resp.status == 200: - f = await aiofiles.open('./tempstorage/hypixel_pic.png', mode='wb') - await f.write(await resp.read()) - await f.close() - await ctx.send(file=discord.File('./tempstorage/hypixel_pic.png')) - os.remove('./tempstorage/hypixel_pic.png') - - - @commands.command(aliases=['bw', 'bedwars'], extras={"category":"Minecraft"}, usage="bwstats [ign(optional)", help="This command shows the hypixel bedwars stats for a Hypixel user.\nYou can specify an ign or if youve used setign befre you wont have to", description="Shows your bedwars stats") - @commands.check(plugin_enabled) - async def bwstats(self, ctx, player=None): - if player == None: - uuid = await get_user_uuid(ctx) - else: - uuid = await get_uuid(str(player)) - response = await get_hydata(uuid) - - player = response["player"] - stats = player["stats"] - player_name = player["displayname"] - - # Stats - bw_stats = stats["Bedwars"] - - # Bedwars - bw_level = player["achievements"]["bedwars_level"] - bw_wins = bw_stats["wins_bedwars"] - bw_losses = bw_stats["losses_bedwars"] - bw_winstreak = bw_stats["winstreak"] - bw_coins = bw_stats["coins"] - bw_gold = bw_stats["gold_resources_collected_bedwars"] - bw_iron = bw_stats["iron_resources_collected_bedwars"] - bw_dias = bw_stats["diamond_resources_collected_bedwars"] - bw_ems = bw_stats["emerald_resources_collected_bedwars"] - bw_resources = bw_stats["resources_collected_bedwars"] - bw_kills = bw_stats["kills_bedwars"] - bw_deaths = bw_stats["deaths_bedwars"] - bw_beds = bw_stats["beds_broken_bedwars"] - bw_finals = bw_stats["final_kills_bedwars"] - - if "monthlyPackageRank" in player: - rank = "MVP++" - full_ign = "{} {}".format(rank, player_name) - elif "newPackageRank" in player: - rank = player["newPackageRank"] - if "_PLUS" in rank: - rank = rank.replace("_PLUS", '+') - full_ign = "{} {}".format(rank, player_name) - else: - rank = None - full_ign = player_name - - em = discord.Embed(title="Bedwars Stats:", - description="For {}".format(full_ign), color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - stats_values = [bw_level, bw_wins, bw_losses, bw_winstreak, bw_coins, bw_gold, - bw_iron, bw_dias, bw_ems, bw_resources, bw_kills, bw_deaths, bw_beds, bw_finals] - stat_keys = ["Bedwars Level", "Wins", "Losses", "Current Winstreak", "Coins", - "Gold Collected", "Iron Collected", "Diamonds Collected", "Emeralds Collected", "Overall Resources", "Kills", "Deaths", "Beds Broken", "Final Kills"] - - dictionary = dict(zip(stat_keys, stats_values)) - - for i in stat_keys: - key = i - value_ = dictionary[key] - em.add_field(name=key, value=value_) - await ctx.send(embed=em) - - - @commands.command(help="Gets the status of a minecraft server", usage="mcstatus [bedrock/java] [server_ip]", description="Gets mc status", extras={"category" : "Search"}) - async def mcstatus(self, ctx, type_:str, server_ip:str): - if type_.lower() == "bedrock": - server = MinecraftBedrockServer.lookup(server_ip) - - status = server.status() - - em = discord.Embed( - title = "Minecraft Server Status:", - description = f"Looking for a bedrock server: {server_ip}", - color = discord.Color.random() - ) - - em.add_field( - name = "Ping", - value = f"{round(status.latency, 2)}ms" - ) - em.add_field( - name = "Players Online:", - value = status.players_online - ) - - await ctx.send(embed=em) - if type_.lower() == "java": - server = MinecraftServer.lookup(server_ip) - - status = server.status() - - em = discord.Embed( - title = "Minecraft Server Status:", - description = f"Looking for a java server: {server_ip}", - color = discord.Color.random() - ) - - em.add_field( - name = "Ping", - value = f"{round(status.latency, 2)}ms" - ) - em.add_field( - name = "Players Online:", - value = status.players.online - ) - - await ctx.send(embed=em) - - - -def setup(client): - client.add_cog(Minecraft(client)) diff --git a/cogs/games/tictactoe.py b/cogs/games/tictactoe.py deleted file mode 100644 index 923f682..0000000 --- a/cogs/games/tictactoe.py +++ /dev/null @@ -1,122 +0,0 @@ -import discord -from discord.ext import commands -from typing import List -from utils import plugin_enabled - -class TicTacToeButton(discord.ui.Button["TicTacToe"]): - def __init__(self, x: int, y: int, p1, p2): - super().__init__(style=discord.ButtonStyle.secondary, label="\u200b", row=y) - self.x = x - self.y = y - self.p1 = p1.id # o - self.p2 = p2.id # x - - async def callback(self, interaction: discord.Interaction): - assert self.view is not None - view: TicTacToe = self.view - state = view.board[self.y][self.x] - if state in (view.X, view.O): - return - - if view.current_player == view.X and interaction.user.id == self.p1: - self.style = discord.ButtonStyle.danger - self.label = "X" - self.disabled = True - view.board[self.y][self.x] = view.X - view.current_player = view.O - content = "It is now O's turn" - elif view.current_player == view.O and interaction.user.id == self.p2: - self.style = discord.ButtonStyle.success - self.label = "O" - self.disabled = True - view.board[self.y][self.x] = view.O - view.current_player = view.X - content = "It is now X's turn" - else: - return await interaction.response.send_message("Its not your turn", ephemeral=True) - - winner = view.check_board_winner() - if winner is not None: - if winner == view.X: - content = "X won!" - elif winner == view.O: - content = "O won!" - else: - content = "It's a tie!" - - for child in view.children: - child.disabled = True - - view.stop() - - await interaction.response.edit_message(content=content, view=view) - - -class TicTacToe(discord.ui.View): - children: List[TicTacToeButton] - X = -1 - O = 1 - Tie = 2 - - def __init__(self, p1, p2): - self.p1 = p1 - self.p2 = p2 - super().__init__() - self.current_player = self.X - self.board = [ - [0, 0, 0], - [0, 0, 0], - [0, 0, 0], - ] - - - for x in range(3): - for y in range(3): - self.add_item(TicTacToeButton(x, y, self.p1, self.p2)) - - - def check_board_winner(self): - for across in self.board: - value = sum(across) - if value == 3: - return self.O - elif value == -3: - return self.X - - - for line in range(3): - value = self.board[0][line] + self.board[1][line] + self.board[2][line] - if value == 3: - return self.O - elif value == -3: - return self.X - - - diag = self.board[0][2] + self.board[1][1] + self.board[2][0] - if diag == 3: - return self.O - elif diag == -3: - return self.X - - diag = self.board[0][0] + self.board[1][1] + self.board[2][2] - if diag == 3: - return self.O - elif diag == -3: - return self.X - - if all(i != 0 for row in self.board for i in row): - return self.Tie - - return None - -class Games(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(help="This command plays a game of tic tac toe", description="Tic Tac Toe", aliases=['ttt'], usage="tictactoe", extras={"category":"Games"}) - @commands.check(plugin_enabled) - async def tictactoe(self, ctx, person:discord.Member): - await ctx.send(content="Tic Tac Toe: X goes first", view=TicTacToe(ctx.author, person)) - -def setup(client): - client.add_cog(Games(client)) \ No newline at end of file diff --git a/cogs/leveling/leveling.py b/cogs/leveling/leveling.py deleted file mode 100644 index 8bd5e2e..0000000 --- a/cogs/leveling/leveling.py +++ /dev/null @@ -1,202 +0,0 @@ -import discord -import json -from discord.ext import commands -from discordLevelingSystem import DiscordLevelingSystem, LevelUpAnnouncement -from easy_pil import Editor, Canvas, Font, load_image_async, Text -import os -from utils.checks import is_it_me, plugin_enabled - -lvlembed = discord.Embed(color=discord.Color.green()) -lvlembed.set_author(name=LevelUpAnnouncement.Member.name) -lvlembed.description = f'Congrats {LevelUpAnnouncement.Member.mention}! You are now level {LevelUpAnnouncement.LEVEL} šŸ˜Ž' - -announcement = LevelUpAnnouncement(lvlembed) - -lvl = DiscordLevelingSystem(rate=1, per=10.0,level_up_announcement=announcement) -lvl.connect_to_database_file('database/DiscordLevelingSystem.db') - -async def lb(self, ctx): - data = await lvl.each_member_data(ctx.guild, sort_by='rank') - - leaderboard_image = Editor(Canvas((680, 800))) - bg = await load_image_async("https://i.imgur.com/FRJXi4k.png") - bg_img = Editor(bg).rotate(90.0) - leaderboard_image.rectangle((0, 0), width=680, height=800, fill="#23272A") - leaderboard_image.paste(bg_img, (-600, 0)) - - n = 0 - yp = 5 - - for i in data: - try: - member = ctx.guild.get_member(i.id_number) - - person = Editor(Canvas((670, 75), color="#5663F7")) - - if member.avatar is None: - profile_image = await load_image_async("https://cdn.logojoy.com/wp-content/uploads/20210422095037/discord-mascot.png") - person_avatar = Editor(profile_image).resize((60, 60)).circle_image() - else: - profile_image = await load_image_async(str(member.avatar.url)) - person_avatar = Editor(profile_image).resize((60, 60)).circle_image() - - person.paste(person_avatar, (7, 7)) - - poppins_medium = Font.poppins(variant="bold", size=25) - - person.text((100, 20), f"#{n+1} ā— {member.display_name} ā— LVL: {i.level}", font=poppins_medium, color="white", align="left") - - - leaderboard_image.paste(person, (5,yp)) - - yp += 80 - n += 1 - except Exception as e: - print(e) - if n == 10: - break - - leaderboard_image.save(f"./tempstorage/leveling{ctx.author.id}.png") - - await ctx.send(file=discord.File(f"./tempstorage/leveling{ctx.author.id}.png")) - -class Leveling(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(aliases=['lvl'], extras={"category":"Leveling"}, usage="rank [@user(optional)]", help="This command shows your rank for the leveling system.", description="Shows your rank image") - @commands.check(plugin_enabled) - async def rank(self, ctx, member:discord.Member=None): - if member == None: - data = await lvl.get_data_for(ctx.author) - else: - data = await lvl.get_data_for(member) - - LEVELS_AND_XP = { - '0': 0,'1': 100,'2': 255,'3': 475, - '4': 770,'5': 1150,'6': 1625,'7': 2205,'8': 2900,'9': 3720,'10': 4675,'11': 5775,'12': 7030, - '13': 8450,'14': 10045,'15': 11825,'16': 13800,'17': 15980,'18': 18375,'19': 20995,'20': 23850, - '21': 26950,'22': 30305,'23': 33925,'24': 37820,'25': 42000,'26': 46475,'27': 51255,'28': 56350, - '29': 61770,'30': 67525,'31': 73625,'32': 80080,'33': 86900,'34': 94095,'35': 101675,'36': 109650, - '37': 118030,'38': 126825,'39': 136045,'40': 145700,'41': 155800,'42': 166355,'43': 177375,'44': 188870, - '45': 200850,'46': 213325,'47': 226305,'48': 239800,'49': 253820,'50': 268375,'51': 283475,'52': 299130, - '53': 315350,'54': 332145,'55': 349525,'56': 367500,'57': 386080,'58': 405275,'59': 425095,'60': 445550, - '61': 466650,'62': 488405,'63': 510825,'64': 533920,'65': 557700,'66': 582175,'67': 607355,'68': 633250, - '69': 659870,'70': 687225,'71': 715325,'72': 744180,'73': 773800,'74': 804195,'75': 835375,'76': 867350, - '77': 900130,'78': 933725,'79': 968145,'80': 1003400,'81': 1039500,'82': 1076455,'83': 1114275,'84': 1152970, - '85': 1192550,'86': 1233025,'87': 1274405,'88': 1316700,'89': 1359920,'90': 1404075,'91': 1449175,'92': 1495230, - '93': 1542250,'94': 1590245,'95': 1639225,'96': 1689200,'97': 1740180,'98': 1792175,'99': 1845195,'100': 1899250 - } - - if member == None: - member = ctx.author - else: - pass - arank = data.xp - brank = LEVELS_AND_XP[f"{data.level+1}"] - frac = arank/brank - percentage = "{:.0%}".format(frac) - percentage = int(percentage[:-1]) - - user_data = { # Most likely coming from database or calculation - "name": f"{member.name}", # The user's name - "xp": arank, - "level": data.level, - "next_level_xp": brank, - "percentage": percentage, - "rank": data.rank - } - - background = Editor(Canvas((934, 282), "#23272a")) - profile_image = await load_image_async(str(member.avatar.url)) - profile = Editor(profile_image).resize((150, 150)).circle_image() - - - poppins = Font.poppins(size=30) - - background.rectangle((20, 20), 894, 242, "#2a2e35") - background.paste(profile, (50, 50)) - background.rectangle((260, 180), width=630, height=40, fill="#484b4e", radius=20) - background.bar( - (260, 180), - max_width=630, - height=40, - percentage=user_data["percentage"], - fill="#00fa81", - radius=20, - ) - - background.text((270, 120), user_data["name"], font=poppins, color="#00fa81") - background.text( - (870, 125), - f"{user_data['xp']} / {user_data['next_level_xp']}", - font=poppins, - color="#00fa81", - align="right", - ) - - rank_level_texts = [ - Text("Rank ", color="#00fa81", font=poppins), - Text(f"{user_data['rank']}", color="#1EAAFF", font=poppins), - Text(" Level ", color="#00fa81", font=poppins), - Text(f"{user_data['level']}", color="#1EAAFF", font=poppins), - ] - - background.multicolor_text((850, 30), texts=rank_level_texts, align="right") - - # send - background.save(f"tempstorage/rank{member.id}.png") - await ctx.send(file=discord.File(f"tempstorage/rank{member.id}.png")) - os.remove(f"tempstorage/rank{member.id}.png") - - - @commands.command(aliases=['lb'], extras={"category":"Leveling"}, usage="leaderboard", help="This command shows the leaderboard for this server.\nIt is sorted by most highest level to lowest.", description="Shows the leaderboard for your server") - @commands.check(plugin_enabled) - async def leaderboard(self, ctx): - await lb(self, ctx) - - - @commands.command() - @commands.check(is_it_me) - async def addxp(self, ctx, member:discord.Member, amount:int): - await ctx.message.delete() - await lvl.add_xp(member=member, amount=amount) - - - @commands.command() - @commands.check(is_it_me) - async def removexp(self, ctx, member:discord.Member, amount:int): - await ctx.message.delete() - await lvl.remove_xp(member=member, amount=amount) - - - @commands.command() - @commands.check(is_it_me) - async def setlvl(self,ctx, member:discord.Member, level:int): - await ctx.message.delete() - await lvl.set_level(member=member, level=level) - - @commands.command() - async def givexp(self, ctx, member:discord.Member, amount:int): - await lvl.remove_xp(member=ctx.author, amount=amount) - await lvl.add_xp(member=member, amount=amount) - await ctx.send(f"Gave {amount} xp to {member.name}, Removed {amount} xp from {ctx.author.name}") - - @commands.Cog.listener() - async def on_message(self, message): - if message.guild is None: - return - data = await self.client.get_db() - if data[str(message.guild.id)]['settings']['plugins']['Leveling'] == False: - return - try: - await lvl.award_xp(amount=[15, 25], message=message) - except Exception: - return - - @commands.Cog.listener() - async def on_member_remove(self, member): - await lvl.reset_member(member) - -def setup(client): - client.add_cog(Leveling(client)) \ No newline at end of file diff --git a/cogs/moderation/moderation.py b/cogs/moderation/moderation.py deleted file mode 100644 index 98114aa..0000000 --- a/cogs/moderation/moderation.py +++ /dev/null @@ -1,279 +0,0 @@ -import discord -from utils import plugin_enabled -import datetime as datetim -from datetime import datetime -from discord.ext import commands -import dotenv -from utils import get_log_channel -import humanfriendly - -dotenv.load_dotenv() - -async def create_voice(guild, name, cat, limit=None): - category = await guild.get_category_by_name(guild, cat) - await guild.create_voice_channel(name, category=category, user_limit=limit) - - -class Moderation(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(aliases=['rp'],help="This command is very useful. It lets you report bugs, messages and members. You need to /set Mod/Log channel for the member/message reports to work but bug reports will be sent to me.", extras={"category":"Moderation"}, usage="report [message/member/bug]", description="Report member/message to your server mods and report bugs to me") - @commands.check(plugin_enabled) - async def report(self, ctx, type_: str): - def wfcheck(m): - return m.channel == ctx.channel and m.author == ctx.author - - channel = await get_log_channel(self.client, ctx.guild) - if channel is None and type_.lower() != "bug": - return await ctx.send("You dont have a log channel set on your server") - - em = discord.Embed(title="REPORT", color=ctx.author.color) - em.timestamp = datetime.utcnow() - - if type_.lower() == "member": - - await ctx.send("Enter the @ of the member") - member = await self.client.wait_for("message", timeout=300, check=wfcheck) - member = member.content - await ctx.send("Please give a short description about why you are reporting this person") - reason = await self.client.wait_for("message", check=wfcheck, timeout=300) - reporter = reason.author - reason = reason.content - em.description = "Member Report" - em.add_field(name="Member:", value=member) - em.add_field(name="Reason:", value=reason) - em.add_field(name="Report By:", value=reporter) - await channel.send(embed=em) - - elif type_.lower() == "message": - - await ctx.send("Enter the id of the message") - messageid = await self.client.wait_for("message", check=wfcheck, timeout=300) - messageid = messageid.content - - try: - int(messageid) - except Exception: - return - - await ctx.send("Please give a short description about why you are reporting this message") - reason = await self.client.wait_for("message", check=wfcheck, timeout=300) - reporter = reason.author - reason = reason.content - - message = await ctx.channel.fetch_message(messageid) - messagecontent = message.content - messageauthor = message.author - - em.description = "Message Report" - em.add_field(name="Reason:", value=reason, inline=False) - em.add_field(name="Message Content:", - value=messagecontent, inline=False) - em.add_field(name="Message Author:", - value=messageauthor, inline=False) - em.add_field(name="Report By:", value=reporter, inline=False) - - await channel.send(embed=em) - - elif type_.lower() == "bug": - - await ctx.send("Please give a short description about the issure/bug") - reason = await self.client.wait_for("message", check=wfcheck, timeout=300) - reporter = reason.author - reason = reason.content - em.description = "Bug Report" - em.add_field(name="Reason", value=reason) - em.add_field(name="Report By:", value=reporter) - - cha = await self.client.fetch_channel(940469380054126633) - await cha.send(content=ctx.author.id, embed=em) - - @commands.command(usage = "bug [bug]", description = "Report a bug", help = "This command lets you report a bug to the Why bot dev/s", extras={"category": "Moderation"}) - @commands.check(plugin_enabled) - async def bug(self, ctx, *, bug): - em = discord.Embed(title="REPORT", color=ctx.author.color) - em.timestamp = datetime.utcnow() - em.description = "Bug Report" - em.add_field(name="Bug", value=bug) - em.add_field(name="Report By:", value=ctx.author.name) - - cha = await self.client.fetch_channel(940469380054126633) - await cha.send(content=ctx.author.id, embed=em) - - - @commands.command(help="This command is used to ban a member", extras={"category":"Moderation"}, usage="ban [@user]", description="Ban a member") - @commands.check(plugin_enabled) - @commands.has_permissions(ban_members=True) - @commands.cooldown(1, 5, commands.BucketType.user) - async def ban(self, ctx, member: discord.Member, *, reason=None): - if ctx.author.top_role.position > member.top_role.position: - if reason is not None: - reason = f"{reason} - Requested by {ctx.author.name} ({ctx.author.id})" - await member.ban(reason="".join(reason if reason != None else f"Requested by {ctx.author} ({ctx.author.id})")) - await ctx.send(f"Banned {member} successfully.") - else: - await ctx.reply("Sorry, you cannot perform that action due to role hierarchy") - channel = await get_log_channel(self.client, ctx.guild) - if channel != None: - return await channel.send(embed=discord.Embed(title="Ban", description=f"***{member.mention}*** has been banned", color=ctx.author.color)) - await ctx.send(f"User {member} has been banned") - - @commands.command(help="This command is used to kick a member", extras={"category":"Moderation"}, usage="kick [@user]", description="Kick a member") - @commands.check(plugin_enabled) - @commands.has_permissions(kick_members=True) - @commands.cooldown(1, 5, commands.BucketType.user) - async def kick(self, ctx, member: discord.Member, *, reason=None): - if ctx.author.top_role.position > member.top_role.position: - if reason is not None: - reason = f"{reason} - Requested by {ctx.author.name} ({ctx.author.id})" - await member.kick(reason="".join(reason if reason != None else f"Requested by {ctx.author} ({ctx.author.id})")) - await ctx.send(f"Kicked {member} successfully.") - else: - await ctx.reply("Sorry, you cannot perform that action due to role hierarchy") - channel = await get_log_channel(self.client, ctx.guild) - if channel != None: - return await channel.send(embed=discord.Embed(title="Kick", description=f"***{member.mention}*** has been kicked", color=ctx.author.color)) - await ctx.send(f"User {member} has been kicked") - - @commands.command(aliases=['lock'], help="This command is used to put a discord text channel into lockdown", extras={"category":"Moderation"}, usage="lockdown [#channel]", description="Lockdown a channel") - @commands.check(plugin_enabled) - @commands.has_permissions(manage_channels=True) - async def lockdown(self, ctx, channel: discord.TextChannel = None): - if channel == None: - channel = ctx.channel - await channel.send("Channel is now in lockdown") - await channel.set_permissions(ctx.guild.default_role, send_messages=False) - cha = await get_log_channel(self, ctx) - if cha == None: - return await cha.send(embed=discord.Embed(title="Lockdown", description=f"***{channel.mention}*** is now in lockdown")) - - @commands.command(help="This command is the unlocks a discord text channel from lockdown", extras={"category":"Moderation"}, usage="unlock [#channel]", description="Unlock a channel") - @commands.check(plugin_enabled) - @commands.has_permissions(manage_channels=True) - async def unlock(self, ctx, channel: discord.TextChannel = None): - if channel == None: - channel = ctx.channel - await channel.send("Channel is no longer in lockdown") - await channel.set_permissions(ctx.guild.default_role, send_messages=True) - cha = await get_log_channel(self, ctx) - if cha == None: - return await cha.send(embed=discord.Embed(title="Unlock", description=f"***{channel.mention}*** is no longer in lockdown", color=ctx.author.color)) - - @commands.command(help="This command deletes a certain amount of message from a channel. Limit it 50 messages", extras={"category":"Moderation"}, usage="clear [amount]", description="Delete messages") - @commands.check(plugin_enabled) - @commands.cooldown(1, 5, commands.BucketType.user) - @commands.has_permissions(manage_channels=True) - async def clear(self, ctx, amount: int = 10): - if amount > 50: - amount = 50 - await ctx.channel.purge(limit=amount+1) - else: - await ctx.channel.purge(limit=amount+1) - channel = await get_log_channel(self.client, ctx.guild) - if channel != None: - return await channel.send(embed=discord.Embed(title="Message Clear", description=f"***{amount}*** messages have been cleared from ***{ctx.channel.name}***")) - - - - @commands.command(help="This command creates a discord Text Channel", extras={"category":"Moderation"}, usage="make_channel [name]", description="Create a text channel") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def make_channel(self, ctx, *, name): - guild = ctx.guild - channel = await guild.create_text_channel(name) - channel = await get_log_channel(self.client, ctx.guild) - if channel != None: - return await channel.send(embed=discord.Embed(title="Create Channel", description=f"***{name}*** text channel has been created", color=ctx.author.color)) - - @commands.command(help="This command creates a discord Voice Channel", extras={"category":"Moderation"}, usage="make_vc [limit(If none change to None)] [name]", description="Create a VC") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def make_vc(self, ctx, limit=None, *, name): - guild = ctx.guild - if limit == "None": - channel = await guild.create_voice_channel(name) - else: - channel = await guild.create_voice_channel(name, user_limit=limit) - channel = await get_log_channel(self.client, ctx.guild) - if channel != None: - return await channel.send(embed=discord.Embed(title="Create Voice Channel", description=f"***{name}*** voice channel has been created", color=ctx.author.color)) - - - @commands.command(aliases=['nick'],help="This command is used to change the nick of a user", extras={"category":"Moderation"}, usage="nick [@user] [nickname]", description="Change a users nick") - @commands.check(plugin_enabled) - @commands.has_permissions(manage_nicknames=True) - @commands.cooldown(1, 5, commands.BucketType.user) - async def nickname(self, ctx, member: discord.Member, *, nickname: str = "no nick"): - if ctx.author.top_role.position > member.top_role.position: - await member.edit(nick=nickname) - else: - await ctx.reply("Sorry, you cannot perform that action due to role hierarchy") - - @commands.command(help="This command is used to unban a user who is banned from the guild.", extras={"category":"Moderation"}, usage="unban [member id]", description="Unban a banned member") - @commands.check(plugin_enabled) - @commands.has_permissions(ban_members=True) - @commands.cooldown(1, 5, commands.BucketType.user) - async def unban(self, ctx, memberid: int = None): - member = discord.Object(id=memberid) - try: - await ctx.guild.unban(member) - except Exception: - await ctx.send("Sorry, a user with that id was not found or isn't a previously banned member.") - - @commands.command(help="This command is used to put the discord channel youre in into slow mode", extras={"category":"Moderation"}, usage="slowmode [seconds]", description="Set channel to slowmode") - @commands.check(plugin_enabled) - async def slowmode(self, ctx, seconds: int = 5): - await ctx.channel.edit(slowmode_delay=seconds) - await ctx.send(f"Set the slowmode delay in this channel to {seconds} seconds!") - - @commands.command(help="This command is used to put a channel thats in slowmode no longer into slowmode", extras={"category":"Moderation"}, usage="rslowmode", description="Removes slowmode from a channel") - @commands.check(plugin_enabled) - async def rslowmode(self, ctx): - await ctx.channel.edit(slowmode_delay=0) - await ctx.send('removed slowmode for the channel') - - @commands.command(name="pin", help="Pins the message with the specified ID to the current channel",extras={"category":"Moderation"}, usage="pin [message id]", description="Pin a message") - @commands.check(plugin_enabled) - @commands.has_permissions(manage_messages=True) - async def pin(self, ctx, _id: int): - message = await ctx.channel.fetch_message(_id) - await message.pin() - await ctx.send("Successfully pinned that msg") - - @commands.command(name="unpin", help="Unpins the message with the specified ID from the current channel", extras={"category":"Moderation"}, usage="unpin [id]", description="Unpins a message") - @commands.check(plugin_enabled) - @commands.has_permissions(manage_messages=True) - async def unpin(self, ctx, _id: int): - pinned_messages = await ctx.channel.pins() - message = discord.utils.get(pinned_messages, id=_id) - await message.unpin() - await ctx.send("Successfully unpinned that msg") - - @commands.command(name="removereactions", help="Clear reactions from a message in the current channel", extras={"category":"Moderation"}, usage="removereactions [message id]", description="Removes reactions from a message") - @commands.check(plugin_enabled) - @commands.has_permissions(manage_messages=True) - async def removereactions(self, ctx, _id: int): - message = await ctx.channel.fetch_message(_id) - await message.clear_reactions() - await ctx.send("Removed") - - - @commands.command(aliases=["mute"]) - @commands.has_guild_permissions(moderate_members=True) - async def timeout(self, ctx, member: discord.Member, time, *, reason=None): - if reason is None: - reason = "No reason" - time = humanfriendly.parse_timespan(time) - await member.timeout(until=discord.utils.utcnow() + datetim.timedelta(seconds=time), reason=reason) - channel = await get_log_channel(self.client, ctx.guild) - if channel != None: - return await channel.send(embed=discord.Embed(title="Timeout", description=f"***{member.mention}*** has been muted", color=ctx.author.color)) - await ctx.send(embed=discord.Embed( - title= "Timeout", - description=f"{member.mention} has been muted for {time} seconds.\nReason: {reason}", - color = ctx.author.color)) - - -def setup(client): - client.add_cog(Moderation(client)) diff --git a/cogs/moderation/roles.py b/cogs/moderation/roles.py deleted file mode 100644 index b9407c9..0000000 --- a/cogs/moderation/roles.py +++ /dev/null @@ -1,159 +0,0 @@ -import discord -from discord.ext import commands -from utils import plugin_enabled, get_log_channel, kwarg_to_embed -import datetime -import json - -class Roles(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(aliases=['grole'], help="This command is used to give a role to a user.", extras={"category":"Moderation"}, usage="giverole [@role] [@member]", description="Give role to a member") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def giverole(self, ctx, role: discord.Role, user: discord.Member): - await user.add_roles(role) - channel = await get_log_channel(self, ctx) - if channel != None: - return await channel.send(embed=discord.Embed(title="Give Role", description=f"***{user.mention}*** has been given the ***{role.mention}*** role", color=ctx.author.color)) - await ctx.send(f"{user} has been given the {role} role") - - @commands.command(aliases=['trole'], help="This commmand is used to remove a role from a member", extras={"category":"Moderation"}, usage="takerole [@role] [@member]", description="Remove roles form member") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def takerole(self, ctx, role: discord.Role, user: discord.Member): - await user.remove_roles(role) - channel = await get_log_channel(self, ctx) - if channel != None: - return await channel.send(embed=discord.Embed(title="Remove Role", description=f"***{role.mention}*** has been removed from ***{user.mention}***", color=ctx.author.color)) - await ctx.send(f"{role} has been removed from {user}") - - @commands.command(help="This command creates a simple react role that users can react to to get a role. ", extras={"category":"Moderation"}, usage="reactrole [:emoji:] [@role] [message text]", description="Creates a reactrole") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def reactrole(self, ctx, emoji, role: discord.Role, *, message): - if message.startswith("--embed"): - kwargs = message.replace("--embed ", "") - data = await kwarg_to_embed(self.client, ctx, kwargs) - em = data[0] - else: - em = discord.Embed(description=message, color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - - msg = await ctx.channel.send(embed=em) - await msg.add_reaction(emoji) - with open("./database/react.json") as json_file: - data = json.load(json_file) - - new_react_role = { - "role_name": role.name, - "role_id": role.id, - "emoji": emoji, - "message_id": msg.id, - } - - data.append(new_react_role) - - with open("./database/react.json", "w") as f: - json.dump(data, f, indent=4) - - @commands.Cog.listener() - @commands.check(plugin_enabled) - async def on_raw_reaction_add(self, payload): - if not payload.guild_id: - return - if payload.member.bot: - return - with open("./database/react.json") as f: - data = json.load(f) - for x in data: - if x['emoji'] == payload.emoji.name and x["message_id"] == payload.message_id: - guild = await self.client.fetch_guild(payload.guild_id) - role = guild.get_role(x['role_id']) - await payload.member.add_roles(role) - else: - pass - - @commands.Cog.listener() - @commands.check(plugin_enabled) - async def on_raw_reaction_remove(self, payload): - if not payload.guild_id: - return - with open("./database/react.json") as f: - data = json.load(f) - for x in data: - if x['emoji'] == payload.emoji.name and x["message_id"] == payload.message_id: - guild = await self.client.fetch_guild(payload.guild_id) - role = guild.get_role(x['role_id']) - member = await guild.fetch_member(payload.user_id) - await member.remove_roles(role) - else: - pass - - @commands.command(aliases=['sar'],help="This command is used to set the autorole for your server. It has 2 types: all and bot\nThe all type sets the autorole for all members who join the server and the bot type sets the autorole for all bots that join the server.", extras={"category":"Moderation"}, usage="autorole [all/bot] [@role]", description="Sets the autorole role for the server") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def autorole(self, ctx, role_type: str, roles: commands.Greedy[discord.Role]): - data = await self.client.get_db() - for role in roles: - if role_type.lower() == 'all': - if role.id in data[str(ctx.guild.id)]['autorole']['all']: - roles.remove(role) - continue - data[str(ctx.guild.id)]['autorole']['all'].append(role.id) - elif role_type.lower() == 'bot': - if role.id in data[str(ctx.guild.id)]['autorole']['bot']: - roles.remove(role) - continue - data[str(ctx.guild.id)]['autorole']['bot'].append(role.id) - else: - return await ctx.send(f"{ctx.prefix}autorole [all/bot] @role\nYou must specify if roles if for all or for bots") - await self.client.update_db(data) - if len (roles) == 0: - return - await ctx.send(f"{[role.mention for role in roles]} set as autorole for this server") - - - #remove react role commands: - @commands.command(aliases=['rar'], help="This command is used to remove the autorole for your server. It has 2 types: all and bot\nThe all type sets the autorole for all members who join the server and the bot type sets the autorole for all bots that join the server.", extras={"category":"Moderation"}, usage="remove_autorole [all/bot] [@role]", description="Sets the autorole role for the server") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def remove_autorole(self, ctx, role : discord.Role): - data = await self.client.get_db() - - return - - await self.client.update_db(data) - - - @commands.Cog.listener() - async def on_member_join(self, member): - data = await self.client.get_db() - if data[str(member.guild.id)]['settings']['plugins']['Moderation'] == False: - return - await self.client.update_db(data) - - if member.bot: - if len(data[str(member.guild.id)]['autorole']['bot']) != 0: - for role in data[str(member.guild.id)]['autorole']['bot']: - try: - role = member.guild.get_role(role) - await member.add_roles(role) - except Exception as e: - continue - return - - if len(data[str(member.guild.id)]['autorole']['all']) != 0: - for role in data[str(member.guild.id)]['autorole']['all']: - try: - role = member.guild.get_role(role) - await member.add_roles(role) - except: - return - - - - - -def setup(client): - client.add_cog(Roles(client)) \ No newline at end of file diff --git a/cogs/moderation/ticket.py b/cogs/moderation/ticket.py deleted file mode 100644 index 338f96d..0000000 --- a/cogs/moderation/ticket.py +++ /dev/null @@ -1,317 +0,0 @@ -import discord -from utils.checks import plugin_enabled -from discord.ext import commands -from discord.ext.commands import has_permissions, MissingPermissions -import json -import datetime -import asyncio - -homepath = "./database/tickets/" -newtickettemplate = {"ticket-counter": 0, "valid-roles": [], - "pinged-roles": [], "ticket-channel-ids": [], "verified-roles": []} - - -def createticketfile(ctx): - try: - with open(f"./database/tickets/{ctx.guild.id}.json") as f: - print("Success") - except FileNotFoundError: - with open(f"./database/tickets/{ctx.guild.id}.json", 'w') as f: - json.dump(newtickettemplate, f, indent=4) - - -class Ticket(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(aliases=['new'], help="This command is use to create a new ticket.", extras={"category":"Ticket"}, usage="newticket", description="Creates a ticket") - @commands.check(plugin_enabled) - async def newticket(self, ctx, *, args=None): - createticketfile(ctx) - - await self.client.wait_until_ready() - - if args == None: - message_content = f"Please wait, we will be with you shortly!\nUse {ctx.prefix}closeticket to close the ticket" - - else: - message_content = "Please wait, we will be with you shortly!\nYour Message: {}\nUse {ctx.prefix}closeticket to close the ticket".format( - args, ctx.prefix) - - with open(f"./database/tickets/ticket{ctx.guild.id}.json") as f: - data = json.load(f) - - ticket_number = int(data["ticket-counter"]) - ticket_number += 1 - - ticket_channel = await ctx.guild.create_text_channel("{}'s Ticket'".format(ctx.author.name)) - await ticket_channel.set_permissions(ctx.guild.get_role(ctx.guild.id), send_messages=False, read_messages=False) - - for role_id in data["valid-roles"]: - role = ctx.guild.get_role(role_id) - - await ticket_channel.set_permissions(role, send_messages=True, read_messages=True, add_reactions=True, embed_links=True, attach_files=True, read_message_history=True, external_emojis=True) - - await ticket_channel.set_permissions(ctx.author, send_messages=True, read_messages=True, add_reactions=True, embed_links=True, attach_files=True, read_message_history=True, external_emojis=True) - - em = discord.Embed(title="New ticket from {}#{}".format( - ctx.author.name, ctx.author.discriminator), description="{}".format(message_content), color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - - await ticket_channel.send(embed=em) - - pinged_msg_content = "" - non_mentionable_roles = [] - - if data["pinged-roles"] != []: - - for role_id in data["pinged-roles"]: - role = ctx.guild.get_role(role_id) - - pinged_msg_content += role.mention - pinged_msg_content += " " - - if role.mentionable: - pass - else: - await role.edit(mentionable=True) - non_mentionable_roles.append(role) - - await ticket_channel.send(pinged_msg_content) - - for role in non_mentionable_roles: - await role.edit(mentionable=False) - - data["ticket-channel-ids"].append(ticket_channel.id) - - data["ticket-counter"] = int(ticket_number) - with open(f"./database/tickets/ticket{ctx.guild.id}.json", 'w') as f: - json.dump(data, f, indent=4) - - created_em = discord.Embed(title="Why Tickets", description="Your ticket has been created at {}".format( - ticket_channel.mention), color=0x00a8ff) - created_em.timestamp = datetime.datetime.utcnow() - - await ctx.send(embed=created_em) - - @commands.command(aliases=['close'], help="This command is used to close a ticket", extras={"category":"Ticket"}, usage="closeticket", description="Close a ticket") - @commands.check(plugin_enabled) - async def closeticket(self, ctx): - createticketfile(ctx) - with open(f'./database/tickets/ticket{ctx.guild.id}.json') as f: - data = json.load(f) - - if ctx.channel.id in data["ticket-channel-ids"]: - - channel_id = ctx.channel.id - - def check(message): - return message.author == ctx.author and message.channel == ctx.channel and message.content.lower() == "close" - - try: - - em = discord.Embed( - title="Why Tickets", description="Are you sure you want to close this ticket? Reply with `close` if you are sure.", color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - - await ctx.send(embed=em) - await self.client.wait_for('message', check=check, timeout=60) - await ctx.channel.delete() - - index = data["ticket-channel-ids"].index(channel_id) - del data["ticket-channel-ids"][index] - - with open(f'./database/tickets/ticket{ctx.guild.id}.json', 'w') as f: - json.dump(data, f, indent=4) - - except asyncio.TimeoutError: - em = discord.Embed( - title="Why Tickets", description="You have run out of time to close this ticket. Please run the command again.", color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - @commands.command(help="This command is used to add access to a role for the tickets.\nWhen a new ticket is creates theses roles will have access to that ticket.", extras={"category":"Ticket"}, usage="addaccess [roleid]", description="Gives a role access to tickets") - @commands.check(plugin_enabled) - async def addaccess(self, ctx, role_id=None): - createticketfile(ctx) - with open(f'./database/tickets/ticket{ctx.guild.id}.json') as f: - data = json.load(f) - - valid_user = False - - for role_id in data["verified-roles"]: - try: - if ctx.guild.get_role(role_id) in ctx.author.roles: - valid_user = True - except: - pass - - if valid_user or ctx.author.guild_permissions.administrator: - role_id = int(role_id) - - if role_id not in data["valid-roles"]: - - try: - role = ctx.guild.get_role(role_id) - - with open(f"./database/tickets/ticket{ctx.guild.id}.json") as f: - data = json.load(f) - data["valid-roles"].append(role_id) - - with open(f'./database/tickets/ticket{ctx.guild.id}.json', 'w') as f: - json.dump(data, f, indent=4) - - em = discord.Embed(title="Why Tickets", description="You have successfully added `{}` to the list of roles with access to tickets.".format( - role.name), color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - - await ctx.send(embed=em) - - except: - em = discord.Embed( - title="Why Tickets", description="That isn't a valid role ID. Please try again with a valid role ID.") - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - else: - em = discord.Embed( - title="Why Tickets", description="That role already has access to tickets!", color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - else: - em = discord.Embed( - title="Why Tickets", description="Sorry, you don't have permission to run that command.", color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - @commands.command(help="This command removes a role from accessing the tickets", extras={"category":"Ticket"}, usage="delaccess [roleid]", description="Removes a role from accessing tickets") - @commands.check(plugin_enabled) - async def delaccess(self, ctx, role_id=None): - createticketfile(ctx) - with open(f'./database/tickets/ticket{ctx.guild.id}.json') as f: - data = json.load(f) - - valid_user = False - - for role_id in data["verified-roles"]: - try: - if ctx.guild.get_role(role_id) in ctx.author.roles: - valid_user = True - except Exception as e: - pass - - if valid_user or ctx.author.guild_permissions.administrator: - - try: - role_id = int(role_id) - role = ctx.guild.get_role(role_id) - with open(f"./database/tickets/{ctx.guild.id}.json") as f: - data = json.load(f) - - valid_roles = data["valid-roles"] - - if role_id in valid_roles: - index = valid_roles.index(role_id) - - del valid_roles[index] - - data["valid-roles"] = valid_roles - with open(f'./database/tickets/ticket{ctx.guild.id}.json', 'w') as f: - json.dump(data, f, indent=4) - - em = discord.Embed(title="Why Tickets", description="You have successfully removed `{}` from the list of roles with access to tickets.".format( - role.name), color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - - await ctx.send(embed=em) - - else: - - em = discord.Embed( - title="Why Tickets", description="That role already doesn't have access to tickets!", color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - except: - em = discord.Embed( - title="Why Tickets", description="That isn't a valid role ID. Please try again with a valid role ID.") - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - else: - em = discord.Embed( - title="Why Tickets", description="Sorry, you don't have permission to run that command.", color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - @commands.command(help="This command sets an admin role for the ticket system", extras={"category":"Ticket"}, usage="addadminrole [roleid]", description="Adds an admin role for tickets") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def addadminrole(self, ctx, role_id=None): - createticketfile(ctx) - - try: - role_id = int(role_id) - role = ctx.guild.get_role(role_id) - with open(f"./database/tickets/ticket{ctx.guild.id}.json") as f: - data = json.load(f) - - data["verified-roles"].append(role_id) - with open(f'./database/tickets/ticket{ctx.guild.id}.json', 'w') as f: - json.dump(data, f, indent=4) - - em = discord.Embed( - title="Why Tickets", description="You have successfully added `{}` to the list of roles that can run admin-level commands!".format(role.name), color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - except: - em = discord.Embed( - title="Why Tickets", description="That isn't a valid role ID. Please try again with a valid role ID.") - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - @commands.command(help="This command removes an admin role from accessing the tickets", extras={"category":"Ticket"}, usage="deladminrole [roleid]", description="Removes an admin role from accessing tickets") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def deladminrole(self, ctx, role_id=None): - createticketfile(ctx) - try: - role_id = int(role_id) - role = ctx.guild.get_role(role_id) - with open(f"./database/tickets/ticket{ctx.guild.id}.json") as f: - data = json.load(f) - - admin_roles = data["verified-roles"] - - if role_id in admin_roles: - index = admin_roles.index(role_id) - - del admin_roles[index] - - data["verified-roles"] = admin_roles - with open(f'./database/tickets/ticket{ctx.guild.id}.json', 'w') as f: - json.dump(data, f, indent=4) - - em = discord.Embed(title="Why Tickets", description="You have successfully removed `{}` from the list of roles that get pinged when new tickets are created.".format( - role.name), color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - - await ctx.send(embed=em) - - else: - em = discord.Embed( - title="Why Tickets", description="That role isn't getting pinged when new tickets are created!", color=0x00a8ff) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - except: - em = discord.Embed( - title="Why Tickets", description="That isn't a valid role ID. Please try again with a valid role ID.") - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - -def setup(client): - client.add_cog(Ticket(client)) diff --git a/cogs/moderation/warnings.py b/cogs/moderation/warnings.py deleted file mode 100644 index db20915..0000000 --- a/cogs/moderation/warnings.py +++ /dev/null @@ -1,63 +0,0 @@ -import discord -from discord.ext import commands -from utils import plugin_enabled, get_log_channel -import datetime - - -class Warnings(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(help="This command is used to warn a user\nThe warning gets added and you can use the warnings command to check the users warnings", extras={"category":"Moderation"}, usage="warn [@user] [reason]", description="Warns a user") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def warn(self, ctx, member: discord.Member, *, reason=None): - if member.id in [ctx.author.id, self.client.user.id]: - return await ctx.send("You cant warn yourself/me LMAO") - - now = datetime.datetime.utcnow() - - time = now.strftime("%Y-%m-%d %H:%M:%S") - if reason == None: - reason = "None" - - data = await self.client.get_db() - - warn = {'time': time, 'reason': reason} - try: - data[str(ctx.guild.id)]['warnings'][f"{member.id}"].append(warn) - except Exception: - data[str(ctx.guild.id)]['warnings'][f"{member.id}"] = [] - data[str(ctx.guild.id)]['warnings'][f"{member.id}"].append(warn) - - await self.client.update_db(data) - channel = await get_log_channel(self, ctx.guild) - if channel != None: - return await channel.send(embed=discord.Embed(title="Warn", description=f"***{member.mention}*** has been warned", color=ctx.author.color)) - await ctx.send(embed=discord.Embed(title="Warn", description=f"***{member.mention}*** has been warned", color=ctx.author.color)) - - @commands.command(help="This commands shows all the warnings that a user has", extras={"category":"Moderation"}, usage="warnings [@user]", description="Displays a users warnings") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator=True) - async def warnings(self, ctx, member: discord.Member): - data = await self.client.get_db() - warns = data[str(ctx.guild.id)]['warnings'] - try: - warnings = warns[f'{member.id}'] - except Exception: - return await ctx.send("This person has no warnings") - - em = discord.Embed(title="WARNINGS:", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - for i in warnings: - t = i["time"] - r = i["reason"] - em.add_field(name=t, value=f"Reason: {r}") - - await ctx.send(embed=em) - - - -def setup(client): - client.add_cog(Warnings(client)) - \ No newline at end of file diff --git a/cogs/owner/blacklist.py b/cogs/owner/blacklist.py deleted file mode 100644 index 3431122..0000000 --- a/cogs/owner/blacklist.py +++ /dev/null @@ -1,45 +0,0 @@ -import discord -from discord.ext import commands -from utils import is_it_me - -class Blacklisted(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(aliases=['bl']) - @commands.check(is_it_me) - async def blacklist(self, ctx, user_id: int): - user = await self.client.fetch_user(user_id) - await self.client.blacklist_user(user_id) - - await ctx.send(f"User ({user.name}) has been blacklisted") - - - @commands.command(aliases=['wl']) - @commands.check(is_it_me) - async def whitelist(self, ctx, user_id: int): - user = await self.client.fetch_user(user_id) - await self.client.whitelist_user(user_id) - - await ctx.send(f"User ({user.name}) has been whitelisted") - - - @commands.command(aliases=['blacklisted']) - @commands.check(is_it_me) - async def listblack(self, ctx): - blacklisted = await self.client.blacklisted_users - blacklisted_users = [] - for user in blacklisted: - try: - await self.client.fetch_user(user) - blacklisted_users.append(user.name) - except: - blacklisted_users.append(user) - continue - await ctx.send(embed=discord.Embed( - title="Blacklisted Users:", - description=", ".join(blacklisted_users) - )) - -def setup(client): - client.add_cog(Blacklisted(client)) \ No newline at end of file diff --git a/cogs/owner/cog_tools.py b/cogs/owner/cog_tools.py deleted file mode 100644 index 64f2e20..0000000 --- a/cogs/owner/cog_tools.py +++ /dev/null @@ -1,70 +0,0 @@ -import discord -from discord.ext import commands -from utils import is_it_me -import datetime - -async def get_cog(client, ext): - for cog in client.cogs_list: - if ext.lower() in cog: - return cog - -class CogTools(commands.Cog): - def __init__(self, client): - self.client = client - - - @commands.command() - @commands.check(is_it_me) - async def reload(self, ctx, extension): - # self.client.reload_extension(await get_cog(self.client, extension)) - self.client.reload_extension(f"cogs.{extension}") - embed = discord.Embed(title='Reload', description=f'{extension} successfully reloaded', color=ctx.author.color) - embed.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=embed) - - - @commands.command() - @commands.check(is_it_me) - async def load(self, ctx, extension): - # self.client.load_extension(await get_cog(self.client, extension)) - self.client.load_extension(f"cogs.{extension}") - embed = discord.Embed(title='Load', description=f'{extension} successfully loaded', color=ctx.author.color) - embed.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=embed) - - - @commands.command() - @commands.check(is_it_me) - async def unload(self, ctx, extension): - # self.client.unload_extension(await get_cog(self.client, extension)) - self.client.unload_extension(f"cogs.{extension}") - embed = discord.Embed(title='Unload', description=f'{extension} successfully unloaded', color=ctx.author.color) - embed.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=embed) - - @commands.command() - @commands.check(is_it_me) - async def listcogs(self, ctx): - return await ctx.send("\n".join(self.client.cogs_list)) - - - @commands.command(aliases=['rall']) - @commands.check(is_it_me) - async def reloadall(self, ctx): - cogs = self.client.cogs_list - - cogs.remove("cogs.leveling.leveling") - try: - for cogs in cogs: - try: - self.client.reload_extension(cogs) - except: - continue - await ctx.send("All Reloaded") - - except Exception as e: - print(e) - - -def setup(client): - client.add_cog(CogTools(client)) \ No newline at end of file diff --git a/cogs/owner/dmreply.py b/cogs/owner/dmreply.py deleted file mode 100644 index 72a9e99..0000000 --- a/cogs/owner/dmreply.py +++ /dev/null @@ -1,121 +0,0 @@ -import discord -import asyncio -import datetime -from discord.ext import commands -from utils import is_it_me -import json - -async def put_on_cooldown(self, member): - self.users_on_cooldown.append(member.id) - await asyncio.sleep(2) - self.users_on_cooldown.remove(member.id) - -class DMReply(commands.Cog): - def __init__(self, client): - self.client = client - self.dm_channel = 926232260166975508 - - self.users_on_cooldown = [] - - self.user_message_count = {} - - @commands.command() - @commands.check(is_it_me) - async def dm_ban(self, ctx, id:int): - with open("./database/dm_banned.json") as f: - banned = json.load(f) - if id not in banned: - banned.append(id) - await ctx.send("User Banned") - with open("./database/dm_banned.json", 'w') as f: - json.dump(banned, f, indent=4) - - - @commands.command() - @commands.check(is_it_me) - async def dm_unban(self, ctx, id:int): - with open("./database/dm_banned.json") as f: - banned = json.load(f) - if id in banned: - banned.remove(id) - await ctx.send("User Unbanned") - with open("./database/dm_banned.json", 'w') as f: - json.dump(banned, f, indent=4) - - - @commands.Cog.listener() - async def on_message(self, message): - - if message.author.bot: - return - - if isinstance(message.channel, discord.DMChannel): - with open("./database/dm_banned.json") as f: - banned = json.load(f) - if message.author.id in banned: - return await message.author.send('You have been dm banned') - if message.author.id in self.users_on_cooldown: - return await message.author.send("Youre on cooldown") - await put_on_cooldown(self,message.author) - - cha = await self.client.fetch_channel(self.dm_channel) - em = discord.Embed(title="New DM", description=f"From {message.author.name}", color=message.author.color) - em.timestamp = datetime.datetime.utcnow() - - if message.content != "": - em.add_field(name="Content", value=f"{message.content}") - await cha.send(content=f"{message.author.id}", embed=em) - - if message.attachments is not None: - for attachment in message.attachments: - if "image/" not in str(attachment.content_type): - return await cha.send(attachment.url) - em = discord.Embed(title="** **", color=discord.Color.blue()) - em.timestamp = datetime.datetime.utcnow() - em.set_image(url=attachment.url) - await cha.send(embed=em) - - # reply - try: - if message.channel.id == self.dm_channel and message.author.id == self.client.owner_id: - if message.reference is None: - return - else: - id = message.reference.message_id - id = await message.channel.fetch_message(id) - id = int(id.content) - person = await self.client.fetch_user(id) - - if message.content is None: - pass - else: - sent_message = await person.send(message.content) - try: - await message.add_reaction("āœ…") - except Exception as e: - print(e) - - if message.attachments is None: - return - else: - for i in message.attachments: - if "image/" not in str(i.content_type): - return await person.send(i.url) - em = discord.Embed(color=message.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_image(url=i.url) - await person.send(embed=em) - - except Exception: - return - - @commands.command() - async def dm_delete(self, ctx, channel_id : int, message_id:int): - channel = await self.client.fetch_channel(channel_id) - message = await channel.fetch_message(message_id) - - await message.delete() - await ctx.message.add_reaction("āœ…") - -def setup(client): - client.add_cog(DMReply(client)) \ No newline at end of file diff --git a/cogs/owner/fusion.py b/cogs/owner/fusion.py deleted file mode 100644 index 69bb41b..0000000 --- a/cogs/owner/fusion.py +++ /dev/null @@ -1,213 +0,0 @@ -import discord -from discord.ui import Button, View -import json -from discord.ext import commands -import datetime -import dotenv -from utils import is_it_me, Log, kwarg_to_embed -import time - -log = Log() - -dotenv.load_dotenv() - -class BotInfoView(View): - def __init__(self): - super().__init__(timeout=None) - - button1 = Button(style=discord.ButtonStyle.grey, label="Vote:",url="https://discordbotlist.com/bots/why") - button2 = Button(style=discord.ButtonStyle.grey, label="Source:",url="https://github.com/FusionSid/Why-Bot") - button3 = Button(style=discord.ButtonStyle.grey,label="Discord:", url="https://discord.gg/ryEmgnpKND") - button4 = Button(style=discord.ButtonStyle.grey,label="Website:", url="https://fusionsid.xyz/whybot") - - self.add_item(button1) - self.add_item(button2) - self.add_item(button3) - self.add_item(button4) - - -class Fusion(commands.Cog): - def __init__(self, client): - self.client = client - - - @commands.command() - @commands.check(is_it_me) - async def serverlist(self, ctx): - servers = list(self.client.guilds) - await ctx.send(f"Connected on {str(len(servers))} servers:") - await ctx.send('\n'.join(guild.name for guild in servers)) - - - @commands.command() - @commands.check(is_it_me) - async def svrls(self, ctx): - await ctx.message.delete() - for guild in self.client.guilds: - em = discord.Embed(title=guild.name, color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.add_field(name="ID:", value=guild.id) - em.add_field(name="Owner name", value=guild.owner.name) - em.add_field(name="Member Count", value=guild.member_count) - await ctx.author.send(embed=em) - - - @commands.command() - @commands.check(is_it_me) - async def message_servers(self, ctx, *, kwargs): - data = await kwarg_to_embed(self.client, ctx, kwargs) - em = data[0] - - data = await self.client.get_db() - for guild in self.client.guilds: - if data[str(guild.id)]["announcement_channel"] is None: - try: - await guild.system_channel.send(embed=em) - except Exception: - continue - else: - try: - channel = await self.client.fetch_channel(int(data[str(guild.id)]["announcement_channel"])) - await channel.send(embed=em) - except Exception: - continue - - - @commands.command() - @commands.check(is_it_me) - async def msgserver(self, ctx, id:int, *, message): - for guild in self.client.guilds: - if guild.id == id: - return await guild.text_channels[0].send(message) - await ctx.send("guild not found") - - - @commands.command() - @commands.check(is_it_me) - async def logs(self, ctx): - file = discord.File("./database/log.txt") - await ctx.author.send(file=file) - - - @commands.command() - @commands.check(is_it_me) - async def ssinfo(self, ctx, g:int): - guild = self.client.get_guild(g) - print(guild) - em = discord.Embed(title="Server Info:", description=f"For: {guild.name}", color=ctx.author.color) - em.set_thumbnail(url=guild.icon.url) - em.set_author(name=f"Guild Owner: {guild.owner.name}", icon_url=guild.owner.avatar.url) - em.add_field(name="Member Count:", value=guild.member_count) - em.add_field(name="Created: ", value=f"") - em.add_field(name="ID:", value=guild.id) - await ctx.send(embed=em) - - - @commands.command() - @commands.check(is_it_me) - async def finduser(self, ctx, id:int): - user = await self.client.fetch_user(id) - em = discord.Embed(title="User Info:", description=f"For: {user.name}", color=user.color) - em.add_field(name="ID:", value=user.id, inline=False) - em.set_thumbnail(url=user.avatar.url) - em.add_field(name="Created Account:",value=f"", inline=False) - shared_guilds = [] - for guild in self.client.guilds: - if user in guild.members: - shared_guilds.append(guild.name) - em.add_field(name=f"Shared Guilds: ({len(shared_guilds)})", value=", ".join(shared_guilds)) - - with open('./database/userdb.json') as f: - data = json.load(f) - cuse = data[str(user.id)]["command_count"] - - em.add_field(name="Command Use:", value=f"User has used Why Bot: {cuse} times") - - em.timestamp = datetime.datetime.now() - await ctx.send(embed=em) - - - @commands.command() - @commands.check(is_it_me) - async def needhelp(self, ctx): - needhelp = [] - for i in self.client.commands: - if i.help is None: - needhelp.append(f"{i.name} | {i.cog_name}") - await ctx.send("\n".join(needhelp)) - - - @commands.command() - @commands.check(is_it_me) - async def cmdtojson(self, ctx): - commandlist = [] - for i in self.client.commands: - print(i) - if i.usage is None: - pass - else: - if len(i.aliases) == 0: - aliases = None - else: - aliases = i.aliases - data = { - "name" : i.name, - "aliases" : aliases, - "description" : i.description, - "help" : i.help, - "category" : i.extras['category'].lower(), - "usage" : i.usage - } - commandlist.append(data) - with open("./commands.json", 'w') as f: - json.dump(commandlist, f, indent=4) - - - @commands.command() - async def show_bot_info_message(self, ctx): - em = discord.Embed( - title = "šŸ”— Why Bot - Info and Links", - description = "Why bot is an open source multi-purpose discord bot", - color = discord.Color.red(), - timestamp = datetime.datetime.now() - ) - em.add_field( - inline=False, - name = "**Help Command**", - value = "```diff\n- If you need help with Why Bot you can use the command: ?help```" - ) - em.add_field( - inline=False, - name = "**Bugs:**", - value = "```diff\n- If you find a bug and want to report it, Use the command: ?bug ```" - ) - em.add_field( - inline=False, - name = "**Suggestions:**", - value = "```diff\n- If you have a suggestion for Why bot, Use the command ?suggest or the slash command: /suggest to make a suggestion.```" - ) - em.add_field( - inline=False, - name = "**Dev:**", - value = "```diff\n- Why bot is open-source so if you would like to contribute to Why Bot - Checkout the github. You can also contact FusionSid: (@FusionSid#3645)```" - ) - em.add_field( - inline=False, - name = "**DM The Bot**", - value = "```diff\n- If you want you can always DM the bot. You can do this if you need help, want to report something, want to suggest something or if you just want to talk. ```" - ) - em.add_field( - inline=False, - name = "Mostly made by: FusionSid", - value = "`Twitter (Which I don't really use):` [@Fusion_Sid](https://twitter.com/Fusion_Sid)\n`My Github profile:` [FusionSid](https://github.com/FusionSid)" - ) - - em.set_thumbnail(url=self.client.user.avatar.url) - em.set_footer(text="To invite Why, Click the bot and hit add to server or user ?botinvite") - - await ctx.send(embed=em, view=BotInfoView()) - - - -def setup(client): - client.add_cog(Fusion(client)) diff --git a/cogs/owner/git.py b/cogs/owner/git.py deleted file mode 100644 index c0a7020..0000000 --- a/cogs/owner/git.py +++ /dev/null @@ -1,59 +0,0 @@ -import discord -from discord.ext import commands -from utils import is_it_me -from subprocess import run - -class Git(commands.Cog): - def __init__(self, client): - self.client = client - self.cwd = "/usr/bin/" - - @commands.group() - @commands.check(is_it_me) - async def git(self, ctx): - if ctx.invoked_subcommand is not None: - return - else: - return await ctx.send(embed=discord.Embed(title=f"Git Commands", description="?git pull\n?git status\n?git add\n?git commit\n?git push", color=ctx.author.color)) - - @git.command() - @commands.check(is_it_me) - async def pull(self, ctx): - output = run([f"{self.cwd}git", "pull"], capture_output=True).stdout - - await ctx.send(output.decode()) - - - @git.command() - @commands.check(is_it_me) - async def status(self, ctx): - output = run([f"{self.cwd}git", "status"], capture_output=True).stdout - - await ctx.send(output.decode()) - - - @git.command() - @commands.check(is_it_me) - async def add(self, ctx): - output = run([f"{self.cwd}git", "add", "."], capture_output=True).stdout - - await ctx.send(output.decode()) - - - @git.command() - @commands.check(is_it_me) - async def commit(self, ctx): - output = run([f"{self.cwd}git", "commit", "-m", "'Updated File'"], capture_output=True).stdout - - await ctx.send(output.decode()) - - - @git.command() - @commands.check(is_it_me) - async def push(self, ctx): - output = run([f"{self.cwd}git", "push"], capture_output=True).stdout - - await ctx.send(output.decode()) - -def setup(client): - client.add_cog(Git(client)) \ No newline at end of file diff --git a/cogs/search/amazon.py b/cogs/search/amazon.py deleted file mode 100644 index 5e7c678..0000000 --- a/cogs/search/amazon.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class Amazon(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(Amazon(client)) \ No newline at end of file diff --git a/cogs/search/comics.py b/cogs/search/comics.py deleted file mode 100644 index ff2f180..0000000 --- a/cogs/search/comics.py +++ /dev/null @@ -1,13 +0,0 @@ -import discord -from discord.ext import commands -from dotenv import load_dotenv - - -class Comics(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(Comics(client)) \ No newline at end of file diff --git a/cogs/search/crypto.py b/cogs/search/crypto.py deleted file mode 100644 index abdec1c..0000000 --- a/cogs/search/crypto.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class Crypto(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(Crypto(client)) \ No newline at end of file diff --git a/cogs/search/currency.py b/cogs/search/currency.py deleted file mode 100644 index f55f19e..0000000 --- a/cogs/search/currency.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class Currency(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(Currency(client)) \ No newline at end of file diff --git a/cogs/search/dictionary.py b/cogs/search/dictionary.py deleted file mode 100644 index 5e0935c..0000000 --- a/cogs/search/dictionary.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class Dictionary(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(Dictionary(client)) \ No newline at end of file diff --git a/cogs/search/ebay.py b/cogs/search/ebay.py deleted file mode 100644 index 64ef11c..0000000 --- a/cogs/search/ebay.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class Ebay(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(Ebay(client)) \ No newline at end of file diff --git a/cogs/search/food.py b/cogs/search/food.py deleted file mode 100644 index 20540ba..0000000 --- a/cogs/search/food.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class Food(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(Food(client)) \ No newline at end of file diff --git a/cogs/search/genius.py b/cogs/search/genius.py deleted file mode 100644 index a9bb840..0000000 --- a/cogs/search/genius.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class Genius(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(Genius(client)) \ No newline at end of file diff --git a/cogs/search/github.py b/cogs/search/github.py deleted file mode 100644 index a466884..0000000 --- a/cogs/search/github.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class Github(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(Github(client)) \ No newline at end of file diff --git a/cogs/search/google.py b/cogs/search/google.py deleted file mode 100644 index b2b5785..0000000 --- a/cogs/search/google.py +++ /dev/null @@ -1,50 +0,0 @@ -import discord -from utils.checks import plugin_enabled -from discord.ext import commands -import random -from googleapiclient.discovery import build -import re -import datetime -import urllib.request -import os -import dotenv - -dotenv.load_dotenv() -isapi_key = os.environ['ISAPI'] - -class Google(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(aliases=['is'], help="This command is used to search for images on google.", extras={"category":"Search"}, usage="imagesearch [search query]", description="Find an image from google") - @commands.check(plugin_enabled) - async def imagesearch(self, ctx, *, search): - ran = random.randint(0, 9) - resource = build("customsearch", "v1", developerKey=isapi_key).cse() - result = resource.list( - q=f"{search}", cx="54c1117c3e104029b", searchType="image" - ).execute() - url = result["items"][ran]["link"] - embed1 = discord.Embed(title=f"Search:({search.title()})", color=ctx.author.color) - embed1.timestamp = datetime.datetime.utcnow() - embed1.set_image(url=url) - await ctx.send(embed=embed1) - - @commands.command(aliases=['yt'], help="This command gets searches through youtube to find a video.", extras={"category":"Search"}, usage="youtube [search query]", description="Searches through youtube for videos") - @commands.check(plugin_enabled) - async def youtube(self, ctx, *, search_): - search_ = search_.replace(" ", "+") - html = urllib.request.urlopen( - f'http://www.youtube.com/results?search_query={search_}') - ids = re.findall(r"watch\?v=(\S{11})", html.read().decode()) - base_url = "https://www.youtube.com/watch?v=" - em = discord.Embed(title="Youtube Search", - description="Showing first 5 urls", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - videos = [ids[0], ids[1], ids[2], ids[3], ids[4]] - for video in videos: - em.add_field(name=f"{base_url}{video}", value="** **") - await ctx.send(embed=em) - -def setup(client): - client.add_cog(Google(client)) \ No newline at end of file diff --git a/cogs/search/movies.py b/cogs/search/movies.py deleted file mode 100644 index 1311a17..0000000 --- a/cogs/search/movies.py +++ /dev/null @@ -1,45 +0,0 @@ -import discord -from utils import plugin_enabled -from discord.ext import commands -from imdb import Cinemagoer - -ia = Cinemagoer() - -class Movies(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command() - @commands.check(plugin_enabled) - async def top_movies(self, ctx): - top = ia.get_popular100_movies() - - await ctx.send(top[0]) - - - @commands.command() - @commands.check(plugin_enabled) - async def imdb_find_person(self, ctx, *, query): - people = ia.search_person(query) - person = people[0] - - em = discord.Embed( - title = person["name"], - description = f"First result for search: `{query}`", - color = ctx.author.color - ) - em.add_field(name="Summary:", value=person["biography"]) - em.set_thumbnail(url=person['headshot']) - await ctx.send(embed=em) - - - @commands.command() - @commands.check(plugin_enabled) - async def imdb_find_movie(self, ctx, *, movie): - movies = ia.search_movie(movie) - movie = movies[0] - - - -def setup(client): - client.add_cog(Movies(client)) \ No newline at end of file diff --git a/cogs/search/nasa.py b/cogs/search/nasa.py deleted file mode 100644 index 592ed06..0000000 --- a/cogs/search/nasa.py +++ /dev/null @@ -1,38 +0,0 @@ -import discord -import datetime -import urllib.request -from discord.ext import commands -from dotenv import load_dotenv -import os -from utils import get_url_json, get_url_image, plugin_enabled -import json - -load_dotenv() - -API_KEY = os.environ['NASA'] - -class Nasa(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(usage = "apod", description = "Astronomy Picture of the day", help = "This command shows the NASA astronomy picture of the day", extras={"category": "Search"}) - @commands.check(plugin_enabled) - async def apod(self, ctx): - url = f"https://api.nasa.gov/planetary/apod?api_key={API_KEY}&thumbs=True" - data = await get_url_json(url) - - em = discord.Embed( - title = data['title'], - color = ctx.author.color - ) - em.description = data["explanation"] - if data["media_type"] == "image": - em.set_image(url=data["hdurl"]) - elif data["media_type"] == "video": - em.set_image(url=data['thumbnail']) - em.timestamp = datetime.datetime.now() - em.set_footer(text=data['copyright']) - await ctx.send(embed=em) - -def setup(client): - client.add_cog(Nasa(client)) diff --git a/cogs/search/news.py b/cogs/search/news.py deleted file mode 100644 index d6ca21c..0000000 --- a/cogs/search/news.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class News(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(News(client)) \ No newline at end of file diff --git a/cogs/search/reddit.py b/cogs/search/reddit.py deleted file mode 100644 index 49119b9..0000000 --- a/cogs/search/reddit.py +++ /dev/null @@ -1,102 +0,0 @@ -import discord -from discord.ext import commands -import asyncpraw -import os -from dotenv import load_dotenv -from utils import plugin_enabled -import datetime -import random - -load_dotenv() - -def reddit_client(): - client = asyncpraw.Reddit( - client_id=os.environ['CLIENT_ID'], - client_secret=os.environ['CLIENT_SECRET'], - user_agent="memes-fastapi" - ) - return client - - -def is_image(post): - try: - return post.post_hint == "image" - except AttributeError: - return False - - -async def get_img_url(client: asyncpraw.Reddit, sub_name: str, limit: int): - hot_memes = (await client.subreddit(sub_name)).hot(limit=limit) - image_urls = [] - async for post in hot_memes: - if is_image(post): - image_urls.append(post.url) - return image_urls - - -async def get_url(client: asyncpraw.Reddit, sub_name: str, limit: int): - hot_memes = (await client.subreddit(sub_name)).hot(limit=limit) - urls = [] - async for post in hot_memes: - urls.append(post.url) - return urls - - -class Reddit(commands.Cog): - def __init__(self, client): - self.client = client - - - @commands.command(aliases=['rimg'], help="This command looks through a reddit subreddit of your choice and finds an image from that subreddit", extras={"category":"Search"}, usage="redditimg [subreddit]", description="Find a image from a subreddit") - @commands.cooldown(1, 5, commands.BucketType.user) - @commands.check(plugin_enabled) - async def redditimg(self, ctx, subreddit: str): - rclient = reddit_client() - urls = await get_img_url(client=rclient, sub_name=subreddit, limit=50) - url = random.choice(urls) - em = discord.Embed(title="Reddit Image Search:", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_image(url=url) - await ctx.send(embed=em) - await rclient.close() - - - @commands.command(aliases=['getmeme'], help="This command looks in r/memes to find a meme", extras={"category":"Search"}, usage="meme", description="Gets a meme") - @commands.cooldown(1, 5, commands.BucketType.user) - @commands.check(plugin_enabled) - async def meme(self, ctx): - reddit = reddit_client() - subreddit = await reddit.subreddit("memes") - hot = subreddit.hot(limit=50) - urls = [] - async for i in hot: - urls.append(i) - ran_sub = random.choice(urls) - name = ran_sub.title - url = ran_sub.url - ups = ran_sub.ups - downs = ran_sub.downs - author = ran_sub.author - em = discord.Embed(title=name, color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_image(url=url) - em.set_footer(text=f"{author} | šŸ‘ {ups}") - await ctx.send(embed=em) - await reddit.close() - - - @commands.command(aliases=['redditsearch'], help="This command looks though a reddit subreddit of your choice and finds a random post.", extras={"category":"Search"}, usage="reddit [subreddit]", description="Find a random reddit post") - @commands.cooldown(1, 5, commands.BucketType.user) - @commands.check(plugin_enabled) - async def reddit(self, ctx, subreddit: str): - rclient = reddit_client() - urls = await get_url(client=rclient, sub_name=subreddit, limit=50) - url = random.choice(urls) - em = discord.Embed(title="Reddit Search:", description=url, color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - await rclient.close() - - -def setup(client): - client.add_cog(Reddit(client)) \ No newline at end of file diff --git a/cogs/search/search.py b/cogs/search/search.py deleted file mode 100644 index df1caba..0000000 --- a/cogs/search/search.py +++ /dev/null @@ -1,277 +0,0 @@ -import discord -from utils.checks import plugin_enabled -from discord.ext import commands -import random -import aiohttp -import aiofiles -from googleapiclient.discovery import build -import re -import datetime -import urllib.request -import os -import praw -import asyncio -import wikipedia -import dotenv - -async def get_wiki(query): - page = wikipedia.page(query, auto_suggest=True, preload=True) - summary = page.summary - - embed = discord.Embed(title="Wikipedia:", description=summary, color=0x00ff00) - embed.timestamp = datetime.datetime.utcnow() - embed.set_author(name=f"Searching for {query}") - embed.set_thumbnail(url=random.choice(page.images)) - embed.add_field(name="Link", value=f"[Wikipedia Link]({page.url})") - - return embed - - -class Search(commands.Cog): - def __init__(self, client): - self.client = client - self.animal_urls = { - "dog" : "https://some-random-api.ml/animal/dog", - "cat" : "https://some-random-api.ml/animal/cat", - "fox" : "https://some-random-api.ml/animal/fox", - "panda" : "https://some-random-api.ml/animal/panda", - "bird" : "https://some-random-api.ml/animal/bird", - } - - - @commands.command(help="This command returns a random rock image", extras={"category":"Search"}, usage="rock", description="Rock Image") - @commands.check(plugin_enabled) - async def rock(self, ctx): - url = "https://mrconos.pythonanywhere.com/rock/random" - while True: - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - response = await resp.json() - if response['image'] == "none": - pass - else: - break - em = discord.Embed(title=response['name'], description=response['desc'], color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - rating = response["rating"] - em.add_field(name="Rating", value=f"{rating}/5") - em.set_image(url=response["image"]) - await ctx.send(embed=em) - - - # Animals: - @commands.command(help="This command show a picture of a dog", extras={"category":"Search"}, usage="dog", description="Dog Image") - @commands.check(plugin_enabled) - async def dog(self, ctx): - url = self.animal_urls["dog"] - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - r = await resp.json() - em = discord.Embed(title="Dog!", description=r['fact'], color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_image(url=r['image']) - - await ctx.send(embed=em) - - @commands.command(help="This command show a picture of a cat", extras={"category":"Search"}, usage="cat", description="Cat Image") - @commands.check(plugin_enabled) - async def cat(self, ctx): - url = self.animal_urls["cat"] - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - r = await resp.json() - em = discord.Embed(title="Cat!", description=r['fact'], color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_image(url=r['image']) - - await ctx.send(embed=em) - - @commands.command(help="This command show a picture of a panda", extras={"category":"Search"}, usage="panda", description="Panda Image") - @commands.check(plugin_enabled) - async def panda(self, ctx): - url = self.animal_urls["panda"] - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - r = await resp.json() - em = discord.Embed(title="Panda!", description=r['fact'], color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_image(url=r['image']) - - await ctx.send(embed=em) - - @commands.command(help="This command show a picture of a fox", extras={"category":"Search"}, usage="fox", description="Fox Image") - @commands.check(plugin_enabled) - async def fox(self, ctx): - url = self.animal_urls["fox"] - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - r = await resp.json() - em = discord.Embed(title="Fox!", description=r['fact'], color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_image(url=r['image']) - - await ctx.send(embed=em) - - @commands.command(help="This command show a picture of a bird", extras={"category":"Search"}, usage="bird", description="Bird Image") - @commands.check(plugin_enabled) - async def bird(self, ctx): - url = self.animal_urls["bird"] - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - r = await resp.json() - em = discord.Embed(title="Bird!", description=r['fact'], color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_image(url=r['image']) - - await ctx.send(embed=em) - - - @commands.command(help="This command shows a fake tweet image with the user you wants name on it and the message you want", extras={"category":"Search"}, usage="tweet [@person] [message]", description="Fake tweet image") - @commands.check(plugin_enabled) - async def tweet(self, ctx, member: discord.Member, *, message): - data = { - "username" : member.name, - "displayname" : member.display_name, - "avatar" : member.avatar.url, - "comment" : message - } - - url = "https://some-random-api.ml/canvas/tweet/" - async with aiohttp.ClientSession() as session: - async with session.get(url, data=data) as resp: - f = await aiofiles.open(f'./tempstorage/tweet{ctx.author.id}.png', mode='wb') - await f.write(await resp.read()) - await f.close() - file = discord.File(f"./tempstorage/tweet{ctx.author.id}.png") - await ctx.send(file=file) - os.remove(f"./tempstorage/tweet{ctx.author.id}.png") - - @commands.command(aliases=['ytc'], help="This command shows a fake youtube comment image with the user you wants name on it and the message you want", extras={"category":"Search"}, usage="ytcomment [@person] [message]", description="Fake youtube comment image") - @commands.check(plugin_enabled) - async def ytcomment(self, ctx, member: discord.Member, *, message): - data = { - "username" : member.name, - "avatar" : member.avatar.url, - "comment" : message - } - - url = "https://some-random-api.ml/canvas/youtube-comment/" - async with aiohttp.ClientSession() as session: - async with session.get(url, data=data) as resp: - f = await aiofiles.open(f'./tempstorage/yt{ctx.author.id}.png', mode='wb') - await f.write(await resp.read()) - await f.close() - file = discord.File(f"./tempstorage/yt{ctx.author.id}.png") - await ctx.send(file=file) - os.remove(f"./tempstorage/yt{ctx.author.id}.png") - - @commands.command(help="This command shows a memeber profile pic on a simp card", extras={"category":"Search"}, usage="simp [@member]", description="Simp card") - @commands.check(plugin_enabled) - async def simp(self, ctx, member: discord.Member=None): - if member is None: - member = ctx.author - data = { - "avatar" : member.avatar.url, - } - - url = "https://some-random-api.ml/canvas/simpcard/" - async with aiohttp.ClientSession() as session: - async with session.get(url, data=data) as resp: - f = await aiofiles.open(f'./tempstorage/simp{ctx.author.id}.png', mode='wb') - await f.write(await resp.read()) - await f.close() - file = discord.File(f"./tempstorage/simp{ctx.author.id}.png") - await ctx.send(file=file) - os.remove(f"./tempstorage/simp{ctx.author.id}.png") - - @commands.command(help="This command shows your licence to be horny card", extras={"category":"Search"}, usage="horny [@member]", description="Horny licence") - @commands.check(plugin_enabled) - async def horny(self, ctx, member: discord.Member=None): - if member is None: - member = ctx.author - data = { - "avatar" : member.avatar.url, - } - - url = "https://some-random-api.ml/canvas/horny/" - async with aiohttp.ClientSession() as session: - async with session.get(url, data=data) as resp: - f = await aiofiles.open(f'./tempstorage/horny{ctx.author.id}.png', mode='wb') - await f.write(await resp.read()) - await f.close() - file = discord.File(f"./tempstorage/horny{ctx.author.id}.png") - await ctx.send(file=file) - os.remove(f"./tempstorage/horny{ctx.author.id}.png") - - @commands.command(help="This command is actually a bunch of commands. You type the command and then specify an overlay type, these are the options:\ngay, glass, wasted, passed, jail, comrade, triggered\nAfter you pick one the bot will put that as an overlay on top of the member you picked profile pictire", extras={"category":"Search"}, usage="overlay [type] [@member]", description="Image overlays for you discord profile pic") - @commands.check(plugin_enabled) - async def overlay(self, ctx, type:str=None, member: discord.Member=None): - overlays = ["gay","glass","wasted","passed","jail","comrade","triggered"] - if type is None or type.lower() not in overlays: - return await ctx.send(embed=discord.Embed(title="Overlays:", description="\n".join(overlays), color=ctx.author.color)) - - if member is None: - member = ctx.author - - data = { - "avatar" : member.avatar.url, - } - - url = f"https://some-random-api.ml/canvas/{type}/" - - async with aiohttp.ClientSession() as session: - async with session.get(url, data=data) as resp: - f = await aiofiles.open(f'./tempstorage/overlay{ctx.author.id}.png', mode='wb') - await f.write(await resp.read()) - await f.close() - file = discord.File(f"./tempstorage/overlay{ctx.author.id}.png") - await ctx.send(file=file) - os.remove(f"./tempstorage/overlay{ctx.author.id}.png") - - @commands.command(help="This command tells a (not funny) joke", extras={"category":"Search"}, usage="joke", description="Joke") - @commands.check(plugin_enabled) - async def joke(self, ctx): - url = "https://some-random-api.ml/joke" - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - r = await resp.json() - em=discord.Embed(title=r['joke'], color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - - await ctx.send(embed=em) - - @commands.command(help="This command shows lyrics for a song. You input the title and the bot tries to find the lyrics for that song", extras={"category":"Search"}, usage="lyrics [song name]", description="Song lyrics command") - @commands.check(plugin_enabled) - async def lyrics(self, ctx, *, song): - url = "https://some-random-api.ml/lyrics" - song = song.replace(" ", "+") - data = { - "title" : song - } - - async with aiohttp.ClientSession() as session: - async with session.get(url, data=data) as resp: - r = await resp.json() - if 'error' in r: - return await ctx.send(r['error']) - - em = discord.Embed( - title=r['title'], - description=f"{r['lyrics']}\n\n[Link on genius]({r['links']['genius']})", color=ctx.author.color - ) - em.set_author(name=r['author']) - em.set_thumbnail(url=r['thumbnail']['genius']) - em.color = ctx.author.color - em.timestamp = datetime.datetime.utcnow() - - await ctx.send(embed=em) - - @commands.command(aliases=['wkp', 'wikipedia'], help="This command looks through wikipedia and finds you a page based on your search query", usage='wiki [search]', description="Wikipedia Search", extras={"category":"Search"}) - @commands.check(plugin_enabled) - async def wiki(self, ctx, *, query): - embed = await get_wiki(query) - embed.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=embed) - -def setup(client): - client.add_cog(Search(client)) diff --git a/cogs/search/sidapi.py b/cogs/search/sidapi.py deleted file mode 100644 index b6b90c5..0000000 --- a/cogs/search/sidapi.py +++ /dev/null @@ -1,90 +0,0 @@ -import discord -import aiohttp -from utils import return_url_image -from discord.ext import commands -from io import BytesIO - -class SidAPI(commands.Cog): - def __init__(self, client): - self.client = client - self.url = "https://fusionsidapi.herokuapp.com/api/" - self.filehost_url = "https://filehost.fusionsid.repl.co/api/" - - @commands.command() - async def filehost(self, ctx): - if ctx.message.attachments is None: - return await ctx.send("You must attach a file to upload it") - - else: - file = ctx.message.attachments[0].url - file = await return_url_image(file) - - async with aiohttp.ClientSession() as session: - async with session.post(f"{self.filehost_url}upload", data={'file': file}) as resp: - data = await resp.json() - - em = discord.Embed(title="File Uploaded!", description=f"[Link:]({data['url']}) {data['url']}", color=ctx.author.color) - em.add_field(name="Code:", value=data["code"]) - em.set_image(url=data["url"]) - await ctx.send(embed=em) - - - @commands.command(help = "This function generates a meme", description="Aborted meme", extras={"category", "Search"}, usage="aborted [@member]") - async def aborted(self, ctx, member: discord.Member = None): - if member is None: - member = ctx.author - url = f"{self.url}aborted?image_url={member.avatar.url}" - image = await return_url_image(url=url) - file = BytesIO(image) - file.seek(0) - await ctx.send(file=discord.File(file, "aborted.png")) - - @commands.command(help = "This function generates a meme", description="Armor meme", extras={"category", "Search"}, usage="armor [text]") - async def armor(self, ctx, *, text): - url = f"{self.url}armor?text={text.replace(' ', '+')}" - image = await return_url_image(url=url) - file = BytesIO(image) - file.seek(0) - await ctx.send(file=discord.File(file, "aborted.png")) - - @commands.command(help = "This function generates a meme", description="Affect meme", extras={"category", "Search"}, usage="affect [@member]") - async def affect(self, ctx, member: discord.Member = None): - if member is None: - member = ctx.author - url = f"{self.url}affect?image_url={member.avatar.url}" - image = await return_url_image(url=url) - file = BytesIO(image) - file.seek(0) - await ctx.send(file=discord.File(file, "affect.png")) - - @commands.command(help = "This function generates an image", description="Wanted image", extras={"category", "Search"}, usage="wanted [@member]") - async def wanted(self, ctx, member: discord.Member = None): - if member is None: - member = ctx.author - url = f"{self.url}wanted?image_url={member.avatar.url}" - image = await return_url_image(url=url) - file = BytesIO(image) - file.seek(0) - await ctx.send(file=discord.File(file, "wanted.png")) - - @commands.command(help = "This function generates a Trash meme of someone", description="Trash Meme", extras={"category", "Search"}, usage="trash [@member]") - async def trash(self, ctx, member: discord.Member = None): - if member is None: - member = ctx.author - url = f"{self.url}trash?image_url={member.avatar.url}" - image = await return_url_image(url=url) - file = BytesIO(image) - file.seek(0) - await ctx.send(file=discord.File(file, "trash.png")) - - @commands.command(help = "This function generates a meme", description="Abandon meme", extras={"category", "Search"}, usage="abandon [text]") - async def abandon(self, ctx, *, text): - url = f"{self.url}abandon?text={text.replace(' ', '+')}" - image = await return_url_image(url=url) - file = BytesIO(image) - file.seek(0) - await ctx.send(file=discord.File(file, "abandon.png")) - - -def setup(client): - client.add_cog(SidAPI(client)) \ No newline at end of file diff --git a/cogs/search/sports.py b/cogs/search/sports.py deleted file mode 100644 index a3daed7..0000000 --- a/cogs/search/sports.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class Sports(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(Sports(client)) \ No newline at end of file diff --git a/cogs/search/steam.py b/cogs/search/steam.py deleted file mode 100644 index 9c4fb8f..0000000 --- a/cogs/search/steam.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class Steam(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(Steam(client)) \ No newline at end of file diff --git a/cogs/search/urlshort.py b/cogs/search/urlshort.py deleted file mode 100644 index 7c8d74c..0000000 --- a/cogs/search/urlshort.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from discord.ext import commands - -class URLShort(commands.Cog): - def __init__(self, client): - self.client = client - - - -def setup(client): - client.add_cog(URLShort(client)) \ No newline at end of file diff --git a/cogs/search/weather.py b/cogs/search/weather.py deleted file mode 100644 index 3ba6c65..0000000 --- a/cogs/search/weather.py +++ /dev/null @@ -1,152 +0,0 @@ -import discord -from discord.ext import commands -from dotenv import load_dotenv -import os -import datetime -from utils import get_url_json, plugin_enabled - -load_dotenv() - -class Dropdown(discord.ui.Select): - def __init__(self, embeds): - self.embeds = embeds - options = [ - discord.SelectOption(label="General", description="Weather"), - discord.SelectOption(label="Temperature", description="Temperature info"), - discord.SelectOption(label="Info", description="More info on weather"), - discord.SelectOption(label="Sunrise/Sunset", description="Sunrise and sunset"), - ] - - super().__init__( - placeholder="More Info...", - min_values=1, - max_values=1, - options=options, - ) - - async def callback(self, interaction: discord.Interaction): - if self.values[0] == "General": - em = self.embeds[0] - elif self.values[0] == "Temperature": - em = self.embeds[1] - elif self.values[0] == "Info": - em = self.embeds[2] - elif self.values[0] == "Sunrise/Sunset": - em = self.embeds[3] - await interaction.response.edit_message(embed=em) - - -class DropdownView(discord.ui.View): - def __init__(self, embeds): - super().__init__(timeout=100) - self.add_item(Dropdown(embeds)) - -async def get_weather_data(city): - BASE_URL = "https://api.openweathermap.org/data/2.5/weather?" - API_KEY = os.environ['WEATHER'] - - url = f"{BASE_URL}q={city}&appid={API_KEY}&units=metric" - - data = await get_url_json(url) - - return data - -async def format_weather_data(data): - weather = data['weather'][0] - main = data['main'] - info = data['sys'] - name = data['name'] - - em1 = discord.Embed( - title = "Weather Info:", - description="General", - color = discord.Color.random() - ) - - em1.add_field( - name = "City:", - value = name - ) - - em1.add_field( - name = "Country:", - value = info['country'] - ) - - em1.add_field( - name = "Weather Type:", - value = weather['main'] - ) - - em2 = discord.Embed( - title = "Weather Info:", - description="Temperature", - color = discord.Color.random() - ) - - em2.add_field( - name = "Temperature:", - value = f"{round(main['temp'])}°" - ) - - em2.add_field( - name = "Min/Max", - value = f"{round(main['temp_min'])}° | {round(main['temp_max'])}°" - ) - - em2.add_field( - name = "Feels Like:", - value = f"{round(main['feels_like'])}°" - ) - - em3 = discord.Embed( - title = "Weather Info:", - description="Info", - color = discord.Color.random() - ) - - em3.add_field(name="Wind Speed:", value=str(data['wind']['speed'])+"m/s") - em3.add_field(name="Wind Direction:", value=str(data['wind']["deg"])+"°") - em3.add_field(name="Pressure:", value=str(main['pressure'])+"hPa") - em3.add_field(name="Humidity:", value=str(main["humidity"])+"%") - - em4 = discord.Embed( - title = "Weather Info:", - description="Sunrise/Sunset", - color = discord.Color.random() - ) - - em4.add_field( - name = "Sunrise:", - value=f"" - ) - - em4.add_field( - name = "Sunset:", - value=f"" - ) - - em1.timestamp = datetime.datetime.now() - em2.timestamp = datetime.datetime.now() - em3.timestamp = datetime.datetime.now() - em4.timestamp = datetime.datetime.now() - - return [em1, em2, em3, em4] - -class Weather(commands.Cog): - def __init__(self, client): - self.client = client - - - @commands.command(usage = "weather [city]", description = "Get weather", help = "Gets the weather for a city", extras={"category": "Search"}) - @commands.check(plugin_enabled) - async def weather(self, ctx, *, city): - data = await get_weather_data(city) - embeds = await format_weather_data(data) - view = DropdownView(embeds) - await ctx.send(embed=embeds[0], view=view) - - -def setup(client): - client.add_cog(Weather(client)) - diff --git a/cogs/setup_help/help.py b/cogs/setup_help/help.py deleted file mode 100644 index 49a65a3..0000000 --- a/cogs/setup_help/help.py +++ /dev/null @@ -1,240 +0,0 @@ -import discord -import datetime -from discord.ext import commands -from discord.ui import Button, View -from discord_colorize import colorize -from utils import LinkView - -colors = colorize.Colors() - - -class Dropdown(discord.ui.Select): - def __init__(self, client): - self.client = client - categories = ["Counting", "Fun", "Leveling", "Logs", "Minecraft", "Moderation", "Music", "Ping", "Search", 'Settings', "Text", "Ticket", "Utilities", "Voice", "Welcome", "Economy", "Games"] - - for category in categories: - index = categories.index(category) - categories[index] = discord.Embed(title=category, color=discord.Color.blue()) - - for cmd in client.commands: - try: - if cmd.extras is None: - pass - else: - for category in categories: - if cmd.extras['category'].lower() == category.title.lower(): - index = categories.index(category) - categories[index].add_field(name=cmd.name, value=cmd.description, inline=False) - except: - pass - - options = [] - emojis = ["šŸ”¢","šŸ˜‚","šŸ†","šŸ“","šŸŽ®","šŸ› ļø","šŸŽµ","āš ļø","šŸ”Ž","āš™ļø","šŸ”€","šŸŽ«","šŸ“±","šŸŽ¤","šŸ‘‹","šŸ’µ", "šŸ•¹ļø"] - for i in categories: - index = categories.index(i) - options.append(discord.SelectOption(emoji=emojis[index],label=i.title, description=f"Get help with the {i.title} commands")) - - - super().__init__( - placeholder="Choose the category you want help with...", - min_values=1, - max_values=1, - options=options, - ) - - async def callback(self, interaction: discord.Interaction): - categories = ["Counting", "Fun", "Leveling", "Logs", "Minecraft", "Moderation", "Music", "Ping", "Search", 'Settings', "Text", "Ticket", "Utilities", "Voice", "Welcome", "Economy", "Games"] - - cat = self.values[0] - - for category in categories: - index = categories.index(category) - categories[index] = discord.Embed(title=category, color=discord.Color.blue()) - - for cmd in self.client.commands: - try: - if cmd.extras is None: - pass - else: - for category in categories: - if cmd.extras['category'].lower() == category.title.lower(): - index = categories.index(category) - categories[index].add_field(name=cmd.name, value=cmd.description, inline=False) - except: - pass - - for category in categories: - if cat.lower() == "logs": - em = discord.Embed(title="Logs", description=f"`help [command]` for more info on command", color=discord.Color.blue()) - em.timestamp = datetime.datetime.utcnow() - em.add_field(name="Use the /set command to set the mod/log channel", value="This category doesn't have any commands because it works on events.\nIf you use the `/set Mod/Log Channel #channel` command properly and set the right channel the bot will log things like Bans, Unbans, Messages being deleted/edited, Nick changes and more") - await interaction.response.edit_message(embed=em) - - elif cat.lower() == "welcome": - em = discord.Embed(title="Welcome", description=f"`help [command]` for more info on command", color=discord.Color.blue()) - em.timestamp = datetime.datetime.utcnow() - em.add_field(name="This system is used to send welcome messages to a user/into a channel when a member joins", value="Use the `/set Welcome Channel #channel` to set the welcome channel") - em.add_field(name=f"Using `welcome` without a subcommand will display the welcome image",value=f"Configure your welcome message:\n`welcome textcolor`\n`welcome image`\n`welcome bgcolor`\n`welcome text`") - await interaction.response.edit_message(embed=em) - - elif cat.lower() == "economy": - em = discord.Embed(title="Economy", description=f"`help [command]` for more info on command", color=discord.Color.blue()) - em.timestamp = datetime.datetime.utcnow() - em.add_field(name="This plugin/category is still under construction", value="** **") - await interaction.response.edit_message(embed=em) - - if cat.lower() == category.title.lower(): - await interaction.response.edit_message(embed=category) - - async def interaction_check(self, interaction) -> bool: - if interaction.user != self.ctx.author: - await interaction.response.send_message("This isnt for you",ephemeral=True) - return False - else: - return True - -class HelpView(View): - def __init__(self, client, embed, ctx): - self.embed=embed - self.ctx = ctx - super().__init__(timeout=30) - - button1 = Button(style=discord.ButtonStyle.grey, label="Vote:",url="https://discordbotlist.com/bots/why", row=1) - button2 = Button(style=discord.ButtonStyle.grey, label="Source:",url="https://github.com/FusionSid/Why-Bot", row=1) - button3 = Button(style=discord.ButtonStyle.grey,label="Discord:", url="https://discord.gg/ryEmgnpKND", row=1) - button4 = Button(style=discord.ButtonStyle.grey, label="Todo:",url="https://github.com/FusionSid/Why-Bot/issues/13", row=1) - button5 = Button(style=discord.ButtonStyle.grey,label="Website:", url="https://fusionsid.xyz/whybot", row=1) - - self.add_item(button1) - self.add_item(button2) - self.add_item(button3) - self.add_item(button4) - self.add_item(button5) - - self.add_item(Dropdown(client)) - - @discord.ui.button(label="Back To Home", emoji="ā¬…ļø", style=discord.ButtonStyle.danger, row=2) - async def back_home(self, button, interaction): - await interaction.response.edit_message(embed=self.embed) - - @discord.ui.button(label="Delete", emoji="ā›”", style=discord.ButtonStyle.danger, row=2) - async def delete(self, button, interaction): - await interaction.message.delete() - - async def interaction_check(self, interaction) -> bool: - if interaction.user != self.ctx.author: - await interaction.response.send_message("This isnt for you",ephemeral=True) - return False - else: - return True - - - -class Help(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command() - async def help(self, ctx, cat=None): - - if cat is None: - pass - - elif cat.lower() == "logs": - em = discord.Embed(title="Logs", description=f"`{ctx.prefix}help [command]` for more info on command", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.add_field(name="Use the /set command to set the mod/log channel", value="This category doesn't have any commands because it works on events.\nIf you use the `/set Mod/Log Channel #channel` command properly and set the right channel the bot will log things like Bans, Unbans, Messages being deleted/edited, Nick changes and more") - return await ctx.send(embed=em) - - elif cat.lower() == "welcome": - em = discord.Embed(title="Welcome", description=f"`{ctx.prefix}help [command]` for more info on command", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.add_field(name="This system is used to send welcome messages to a user/into a channel when a member joins", value="Use the `/set Welcome Channel #channel` to set the welcome channel") - em.add_field(name=f"Using `{ctx.prefix}welcome` without a subcommand will display the welcome image",value=f"Configure your welcome message:\n`{ctx.prefix}welcome textcolor`\n`{ctx.prefix}welcome image`\n`{ctx.prefix}welcome bgcolor`\n`{ctx.prefix}welcome text`") - return await ctx.send(embed=em) - - elif cat.lower() == "economy": - em = discord.Embed(title="Economy", description=f"`{ctx.prefix}help [command]` for more info on command", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.add_field(name="This plugin/category is still under construction", value="** **") - return await ctx.send(embed=em) - - categories = ["Counting", "Fun", "Leveling", "Logs", "Minecraft", "Moderation", "Music", "Ping", "Search", 'Settings', "Text", "Ticket", "Utilities", "Voice", "Welcome", "Economy", "Games"] - - if cat is None: - em = discord.Embed(title="Why Help", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.add_field(inline=False, name=f"Use `{ctx.prefix}help all`", value=f"""```diff\n-For all commands```""") - em.add_field(inline=False, name=f"`{ctx.prefix}help [command]`", value=f"""```diff\n- Give information about a specific command```""") - em.add_field(inline=False, name=f"`{ctx.prefix}help [category]`", value=f"""```diff\n- Give information about a specific category```""") - em.add_field(inline=False, name="Useful Commands:",value=f"""```diff\n- /set, {ctx.prefix}settings, {ctx.prefix}setprefix, {ctx.prefix}report```""") - em.add_field(inline=False, name="Email Why:", value=f"""```diff\n- whybot@fusionsid.xyz```""") - em.add_field(inline=False, name="Dm Bot",value=f"""```diff\n- You can always just dm the bot for help, suggestions, bugreports or if you just want to talk.Why bot will always reply (unless its spam)```""") - em.add_field(inline=False, name="Categories", value=f"""```diff\n- {", ".join(categories)}```""") - em.add_field(inline=False, name="Links",value="[Why Bot Support Server](https://discord.gg/ryEmgnpKND) • [Contribute/Source Code](https://github.com/FusionSid/Why-Bot)") - - view= HelpView(self.client, em, ctx) - message = await ctx.send(embed=em, view=view) - res = await view.wait() - if res: - for i in view.children: - i.disabled = True - try: - return await message.edit(view=view) - except Exception: - return - - for category in categories: - index = categories.index(category) - categories[index] = discord.Embed(title=category, color=ctx.author.color) - - for cmd in self.client.commands: - try: - if cmd.extras is None: - pass - else: - for category in categories: - if cmd.extras['category'].lower() == category.title.lower(): - index = categories.index(category) - categories[index].add_field(name=cmd.name, value=cmd.description, inline=False) - except: - pass - - for category in categories: - if cat.lower() == category.title.lower(): - return await ctx.send(embed=category) - - for cmd in self.client.commands: - if cmd.name.lower() == cat.lower(): - em = discord.Embed(title="Why Help", description=f"""```diff\n- {ctx.prefix}help [command] for more info on command```""", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.add_field(name=f"Name", value=f"""```diff\n- {cmd.name}```""", inline=False) - if len(cmd.aliases) == 0: - em.add_field(name="Aliases:", value=f"""```diff\n- None"```""", inline=False) - else: - em.add_field(name="Aliases:", value=f"""```diff\n- {', '.join(cmd.aliases)}```""", inline=False) - em.add_field(name="Usage: ", value=f"""```diff\n- {ctx.prefix+cmd.usage}```""", inline=False) - help_message = (cmd.help).replace('\n', ' ') - em.add_field(name="Description:", value=f"""```diff\n- {help_message}```""", inline=False) - return await ctx.send(embed=em) - await ctx.send(embed=discord.Embed(title="Command/Category Not Found", color=ctx.author.color)) - - - @commands.command(aliases=['contribute', 'src']) - async def source(self, ctx): - view = LinkView("https://github.com/FusionSid/Why-Bot", "Code") - - await ctx.send(embed=discord.Embed(title="**Why Bot** Source Code:", color=ctx.author.color), view=view) - - - @commands.command(aliases=["support", "discord_server", "server", "discord"]) - async def discordserver(self, ctx): - - view = LinkView("https://discord.gg/ryEmgnpKND", "Discord Server") - - await ctx.send(embed=discord.Embed(title="**Why Bot** Discord Server:", color=ctx.author.color), view=view) - - -def setup(client): - client.add_cog(Help(client)) diff --git a/cogs/setup_help/settings.py b/cogs/setup_help/settings.py deleted file mode 100644 index fd8eb33..0000000 --- a/cogs/setup_help/settings.py +++ /dev/null @@ -1,218 +0,0 @@ -import discord -import json -from discord.ext import commands -from utils import Paginator -import datetime - -async def enabled_cogs(client, ctx): - data = await client.get_db() - plugins = data[str(ctx.guild.id)]['settings']['plugins'] - em = discord.Embed( - title="Plugins:", description="These are all the plugins that have been enabled on your server", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - for key, value in plugins.items(): - if value == True: - emoji = "Enabled āœ…" - else: - emoji = "Disabled āŒ" - em.add_field(name=key, value=emoji) - em.set_footer(text=f"Use {ctx.prefix}plugins to toggle plugins") - return em - - -async def get_channels(self, ctx): - data = await self.client.get_db() - em = discord.Embed(title="Channels", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_footer(text="Use /set to set these") - if data[str(ctx.guild.id)]['counting_channel'] == None: - counting = "Not Set" - else: - channel = await self.client.fetch_channel(data[str(ctx.guild.id)]['counting_channel']) - counting = channel - if data[str(ctx.guild.id)]['welcome_channel'] == None: - welcome = "Not Set" - else: - channel = await self.client.fetch_channel(data[str(ctx.guild.id)]['welcome_channel']) - welcome = channel - if data[str(ctx.guild.id)]['log_channel'] == None: - log = "Not Set" - else: - channel = await self.client.fetch_channel(data[str(ctx.guild.id)]['log_channel']) - log = channel - em.add_field(name="Counting:", value=counting) - em.add_field(name="Welcome:", value=welcome) - em.add_field(name="Log:", value=log) - return em - - -async def autorole(self, ctx): - data = await self.client.get_db() - em = discord.Embed(title="Autorole", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_footer(text="Use ?autorole [all/bot] [@roles (you can do more than one)] to set the autorole") - autorole = data[str(ctx.guild.id)]['autorole'] - all_roles = [] - bot_roles = [] - if len(autorole['all']) == 0: - em.add_field(name="All", value="Not set") - else: - for role in autorole['all']: - role = ctx.guild.get_role(role) - all_roles.append(role) - em.add_field(name="All", value=f"{[role.mention for role in all_roles]}") - - if len(autorole['bot']) == 0: - em.add_field(name="Bot", value="Not set") - else: - for role in autorole['bot']: - role = ctx.guild.get_role(role) - all_roles.append(role) - em.add_field(name="Bot", value=f"{[role.mention for role in all_roles]}") - return em - - -async def welcome_text(self, ctx): - data = await self.client.get_db() - em = discord.Embed(title="Welcome Text", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_footer(text="Use ?welcome text [text] to set the text") - wt = data[str(ctx.guild.id)]['welcome']['text_footer'] - em.add_field(name="Text:", value=wt) - return em - - -async def autocalc(self, ctx): - data = await self.client.get_db() - - autocalc = data[str(ctx.guild.id)]['settings']['autocalc'] - if autocalc == True: - status = "Enabled āœ…" - if autocalc == False: - status = "Disabled āŒ" - - em = discord.Embed(title='Auto Calculator',description=status, color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - - return em - - -class Settings(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(help="This command is used to show the settings for you discord server. You can use this command to quickly check all the current settings to know if you want to change it", extras={"category": "Settings"}, usage="settings", description="Shows the servers Why Bot settings") - async def settings(self, ctx): - plugins = await enabled_cogs(self.client, ctx) - - em = discord.Embed( - title="Settings", description="Use the arrows to look throught the settings", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - - prefix = discord.Embed(title= "Prefix", description=f"The prefix is `{ctx.prefix}`", color=ctx.author.color) - prefix.timestamp = datetime.datetime.utcnow() - prefix.set_footer( - text=f"You can use {ctx.prefix}setprefix [prefix] to set the prefix") - - channels = await get_channels(self, ctx) - autoroles = await autorole(self, ctx) - welcometext = await welcome_text(self, ctx) - auto_calc = await autocalc(self, ctx) - - ems = [em, plugins, prefix, channels, - autoroles, auto_calc, welcometext] - view = Paginator(ctx=ctx, ems=ems) - - message = await ctx.send(embed=em, view=view) - res = await view.wait() - if res: - for i in view.children: - i.disabled = True - return await message.edit(view=view) - - @commands.group(help="This command is used to enable/disable plugins for your server. You can stop certain categories from working on this server\nThe plugin name is case sensitive", extras={"category": "Settings"}, usage="plugins [enable/disable] [plugin name]", description="Enable/Disable Plugins for Why bot") - async def plugins(self, ctx): - if ctx.invoked_subcommand is None: - em = discord.Embed( - title="Plugins", description=f"Use `{ctx.prefix}plugins [enable/disable] [plugin name]`", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.add_field( - name="Plugin List:", value="Counting\nModeration\nEconomy\nTextConvert\nSearch\nWelcome\nLeveling\nMusic\nOnping\nTicket\nMinecraft\nUtilities\nLogging") - em.set_footer( - text="This command is case sensitive so please use capital letters") - await ctx.send(embed=em) - - @plugins.group() - @commands.has_permissions(administrator=True) - async def enable(self, ctx, plugin: str): - plist = ["Counting", "Moderation","Economy","TextConvert","Search","Welcome","Leveling","Music","Onping","Ticket","Minecraft","Utilities", "Fun", "Logging"] - if plugin not in plist: - return await ctx.send(f"Plugin not found, use `{ctx.prefix}plugins` for a list of them") - data = await self.client.get_db() - data[str(ctx.guild.id)]['settings']['plugins'][plugin] = True - - await self.client.update_db(data) - - await ctx.send(f"{plugin} has been enabled") - - @plugins.group() - @commands.has_permissions(administrator=True) - async def disable(self, ctx, plugin: str): - plist = ["Counting", "Moderation","Economy","TextConvert","Search","Welcome","Leveling","Music","Onping","Ticket","Minecraft","Utilities", "Fun", "Logging"] - if plugin not in plist: - return await ctx.send(f"Plugin not found, use `{ctx.prefix}plugins` for a list of them") - data = await self.client.get_db() - data[str(ctx.guild.id)]['settings']['plugins'][plugin] = False - - await self.client.update_db(data) - - await ctx.send(f"{plugin} has been disabled") - - @commands.command(usage = "goose_mode", description = "This command toggles goose_mode", help = "This command toggles goose mode which changed autocalc to 9 + 10 = 19 (which is wrong) to the correct answer 21 - Named after my friend who requested this", extras={"category": "Settings"}) - @commands.has_permissions(administrator=True) - async def goose_mode(self, ctx): - with open("./database/goose_mode.json") as f: - data = json.load(f) - if str(ctx.guild.id) not in data: - data[str(ctx.guild.id)] = False - await ctx.send("goose mode off") - - elif data[str(ctx.guild.id)] == False: - data[str(ctx.guild.id)] = True - await ctx.send("goose mode on") - - elif data[str(ctx.guild.id)] == True: - data[str(ctx.guild.id)] = False - await ctx.send("goose mode off") - - with open("./database/goose_mode.json", "w") as f: - json.dump(data, f, indent=4) - - - - @commands.command(help="This command is used to set the prefix for the server. Default prefix is ?", extras={"category": "Settings"}, usage="setprefix [prefix]", description="Sets the server prefix") - @commands.has_permissions(administrator=True) - async def setprefix(self, ctx, pref: str): - data = await self.client.get_db() - data[str(ctx.guild.id)]["prefix"] = pref - await self.client.update_db(data) - await ctx.send(embed=discord.Embed(title=f"Prefix has been set to `{pref}`", color=ctx.author.color)) - - @commands.command(help="This command turns on autocalc for the server.\nAuto calc basicaly autocalculates any thing you type. For example in chat you type\n1 + 1\nin chat, The bot will reply 2", extras={"category": "Settings"}, usage="autocalc [True/false]", description="Toggles autocalc for this server") - @commands.has_permissions(administrator=True) - async def autocalc(self, ctx, ena): - data = await self.client.get_db() - if ena.lower() == "true": - status = "Enabled āœ…" - data[str(ctx.guild.id)]["settings"]['autocalc'] = True - elif ena.lower() == "false": - data[str(ctx.guild.id)]["settings"]['autocalc'] = False - status = "Disabled āŒ" - else: - await ctx.send("Invalid option. Only `true` or `false`") - await self.client.update_db(data) - await ctx.send(embed=discord.Embed(title=f"Autocalc is {status}", color=ctx.author.color)) - - -def setup(client): - client.add_cog(Settings(client)) diff --git a/cogs/slash_cmds/slash.py b/cogs/slash_cmds/slash.py deleted file mode 100644 index a5eabd5..0000000 --- a/cogs/slash_cmds/slash.py +++ /dev/null @@ -1,161 +0,0 @@ -# Slash commands -import datetime -import discord -from discord.commands import slash_command -from discord.ext import commands -from discord import Option -import json -import random -import os -from utils import is_it_me - -async def get_cog(client, ext): - for cog in client.cogs_list: - if ext.lower() in cog: - return cog - -async def get_roast(): - with open('./database/roastlist.json') as f: - data = json.load(f) - return random.choice(data) - - -class Slash(commands.Cog): - def __init__(self, client): - self.client = client - - @slash_command(name="hi", description="Hello") - async def hello(self, ctx, user: Option(discord.Member, "The user", required=False) = None): - if user is None: - await ctx.respond("Hello") - else: - await ctx.respond(f"Hello {user.mention}") - - @slash_command(name="set", description="Set Channels") - @commands.has_permissions(administrator=True) - async def set(self, ctx, category: Option(str, "Category", required=True, choices=["Mod/Log Channel", "Counting Channel", "Welcome Channel", "Announcement Channel"]), channel: Option(discord.TextChannel, "The channel", required=True)): - channel_id = channel.id - data = await self.client.get_db() - - if category == "Mod/Log Channel": - data[str(ctx.guild.id)]["log_channel"] = channel_id - - elif category == "Counting Channel": - data[str(ctx.guild.id)]["counting_channel"] = channel_id - - elif category == "Welcome Channel": - data[str(ctx.guild.id)]["welcome_channel"] = channel_id - - elif category == "Announcement Channel": - data[str(ctx.guild.id)]["announcement_channel"] = channel_id - - await ctx.respond(f"{channel.name} successfully set as {category}") - await self.client.update_db(data) - - @slash_command(name="rps", description="rock paper scissors") - async def rps(self, ctx, rps: Option(str, "Rock Paper or Scissors", required=True, choices=["Rock", "Paper", "Scissors"])): - choices = ["rock", "paper", "scissors"] - cpu_choice = random.choice(choices) - em = discord.Embed(title="Rock Paper Scissors", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - rps = rps.lower() - if rps == 'rock': - if cpu_choice == 'rock': - em.description = "It's a tie!" - elif cpu_choice == 'scissors': - em.description = "You win!" - elif cpu_choice == 'paper': - em.description = "You lose!" - - elif rps == 'paper': - if cpu_choice == 'paper': - em.description = "It's a tie!" - elif cpu_choice == 'rock': - em.description = "You win!" - elif cpu_choice == 'scissors': - em.description = "You lose!" - - elif rps == 'scissors': - if cpu_choice == 'scissors': - em.description = "It's a tie!" - elif cpu_choice == 'paper': - em.description = "You win!" - elif cpu_choice == 'rock': - em.description = "You lose!" - - else: - em.description = "Invalid Input" - - em.add_field(name="Your Choice", value=rps) - em.add_field(name="Bot Choice", value=cpu_choice) - await ctx.respond(embed=em) - - @slash_command(name="roast", description="Bot roasts you") - async def roast(self, ctx): - roast = await get_roast() - em = discord.Embed(title=roast, color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - await ctx.respond(embed=em) - - @slash_command(name="dm", description="The bot dms someone for you") - async def dm(self, ctx, member: Option(discord.Member, "The person you want to dm", required=True), message: Option(str, 'The message', required=True)): - embeddm = discord.Embed(title=message, color=ctx.author.color) - embeddm.timestamp = datetime.datetime.utcnow() - await member.send(embed=embeddm) - await ctx.respond("Done") - - @slash_command(name="sendroast", description="the bot sends someone a roast") - async def sendroast(self, ctx, member: Option(discord.Member, "The person you want to roast", required=True)): - message = await get_roast() - embeddm = discord.Embed( - title=message, description="Imagine being roasted by a bot", color=ctx.author.color) - embeddm.timestamp = datetime.datetime.utcnow() - await member.send(embed=embeddm) - await ctx.respond("Done") - - @slash_command(name="say", description="The bot says what you want") - async def say(self, ctx, text: Option(str, "The text", required=True)): - await ctx.respond(text) - - @slash_command(name="invite", description="Creates 10 day invite for this server") - async def invite(self, ctx): - link = await ctx.channel.create_invite(max_age=10) - await ctx.respond(link) - - @slash_command(name="botinvite", description="Invite why to your server :)") - async def botinvite(self, ctx): - await ctx.respond(embed=discord.Embed(title="Invite **Why?** to your server:", description="[Why invite link](https://discord.com/api/oauth2/authorize?client_id=896932646846885898&permissions=8&scope=bot%20applications.commands)", color=ctx.author.color)) - - @slash_command(name="suggest", description="Suggest something for why bot") - async def suggest(self, ctx, suggestion: Option(str, "The suggestion", required=True)): - sid = await self.client.fetch_channel(925157029092413460) - em = discord.Embed( - title= "Suggestion:", - description=f"By: {ctx.author.name}\n\n{suggestion}", - color=discord.Color.random() - ) - await sid.send(embed=em, content=ctx.author.id) - await ctx.respond("Thank you for you suggestion!") - - @slash_command(name="ping", description="shows you the bots ping") - async def ping(self, ctx): - await ctx.respond(f"{round(self.client.latency * 1000)}ms") - - @slash_command(name="reload", description="reloads a cog") - @commands.check(is_it_me) - async def reload(self, ctx, extension:Option(str, "Cog Name", required=True)): - self.client.reload_extension(await get_cog(self.client, extension)) - embed = discord.Embed(title='Reload', description=f'{extension} successfully reloaded',color=ctx.author.color) - embed.timestamp = datetime.datetime.utcnow() - await ctx.respond(embed=embed) - - @slash_command(name="setprefix", description="Sets the server prefix for Why Bot") - @commands.has_permissions(administrator=True) - async def setprefix(self, ctx, pref: Option(str, "Prefix", required=True)): - data = await self.client.get_db() - data[str(ctx.guild.id)]["prefix"] = pref - await self.client.update_db(data) - await ctx.respond(embed=discord.Embed(title=f"Prefix has been set to `{pref}`", color=ctx.author.color)) - -def setup(client): - client.add_cog(Slash(client)) diff --git a/cogs/utilities/info.py b/cogs/utilities/info.py deleted file mode 100644 index 9cce324..0000000 --- a/cogs/utilities/info.py +++ /dev/null @@ -1,155 +0,0 @@ -import discord -import datetime -import json -from utils.checks import plugin_enabled -import os -import time -import platform -from discord.ext import commands -import psutil - -async def get_lines(): - lines = 0 - files = [] - for i in os.listdir(): - if i.endswith(".py"): - files.append(i) - for i in os.listdir("cogs/"): - for file in os.listdir('cogs/'+i): - if file.endswith(".py"): - files.append(f"cogs/{i}/{file}") - for i in os.listdir("utils/"): - if i.endswith(".py"): - files.append(f"utils/{i}") - for i in files: - count = 0 - with open(i, 'r') as f: - for line in f: - count += 1 - lines += count - return lines - -class Info(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(extras={"category": "Utilities"}, usage="info [@user]", help="This command shows a message with info on a user.", description="Returns info on a user") - @commands.check(plugin_enabled) - async def info(self, ctx, member: discord.Member = None): - if member == None: - return await ctx.send(f"{ctx.prefix}info person <@person>\nYou didnt @ the member") - roles = [role for role in member.roles] - em = discord.Embed(title="Person Info", - description=f"For: {member.name}", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - if str(member.status) == "online": - status = "🟢 Online" - elif str(member.status) == "offline": - status = "šŸ”“ Offline" - elif str(member.status) == "dnd": - status = 'ā›” Do not disturb' - elif str(member.status) == "invisible": - status = "šŸ”“ Invisible" - elif str(member.status) == "idle": - status = "šŸŒ™ Idle" - elif str(member.status) == "streaming": - status = "šŸ“· Streaming" - else: - status = member.status - em.add_field(name="Status:", value=status, inline=False) - - em.add_field(name="ID:", value=member.id, inline=False) - em.set_thumbnail(url=member.avatar.url) - em.add_field(name="Created Account:", - value=f"", inline=False) - em.add_field(name="Joined Server:", - value=f"", inline=False) - em.add_field(name="Highest Role:", - value=member.top_role.mention, inline=False) - - data = await self.client.get_db() - warnings = data[str(ctx.guild.id)]['warnings'] - if str(member.id) not in warnings: - em.add_field(name="Warnings", value="This user has no warnings") - else: - em.add_field(name="Warnings", value=len(warnings[str(member.id)])) - if len(roles) > 15: - em.add_field(name="Roles:", value=f"{len(roles)}", inline=False) - else: - em.add_field(name=f"Roles ({len(roles)}):", value=" ".join( - role.mention for role in roles), inline=False) - await ctx.send(embed=em) - - @commands.command(extras={"category": "Utilities"}, usage="ping", help="Shows the bots ping", description="Shows bot ping") - @commands.check(plugin_enabled) - async def ping(self, ctx): - await ctx.send(f"Pong! jk\n{round(self.client.latency * 1000)}ms") - - @commands.command(extras={"category": "Utilities"}, usage="serverinfo", help="Returns info on this server.", description="shows server info") - @commands.check(plugin_enabled) - async def serverinfo(self, ctx): - em = discord.Embed( - title="Server Info:", description=f"For: {ctx.guild.name}", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.set_thumbnail(url=ctx.guild.icon.url) - em.set_author( - name=f"Guild Owner: {ctx.guild.owner.name}", icon_url=ctx.guild.owner.avatar.url) - em.add_field( - name="Channels:", value=f"**Text:** {len(ctx.guild.text_channels)}\n**Voice:** {len(ctx.guild.voice_channels)}") - em.add_field(name="Roles:", value=len(ctx.guild.roles)) - bots = 0 - members = 0 - for i in ctx.guild.members: - if i.bot: - bots += 1 - else: - members += 1 - em.add_field( - name="Members:", value=f"**Total:** {ctx.guild.member_count}\n**Humans:** {members}\n**Bots:** {bots}") - em.add_field( - name="Created: ", value=f"") - em.add_field(name="ID:", value=ctx.guild.id) - await ctx.send(embed=em) - - @commands.command(extras={"category": "Utilities"}, usage="botinfo", help="This command shows info on the bot", description="Info about Why bot") - @commands.check(plugin_enabled) - async def botinfo(self, ctx): - with open("./database/userdb.json") as f: - data = json.load(f) - active = len(data) - em = discord.Embed(title='Why Bot', description='Just Why?', color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - em.add_field(inline=True, name="Server Count", - value=f"{len(self.client.guilds)}") - mlist = [] - for i in list(self.client.get_all_members()): - mlist.append(i.name) - em.add_field(inline=True, name="User Count", value=len(mlist)) - em.add_field(inline=True, name="Command Count", - value=f"{len(self.client.commands)} commands") - em.add_field(inline=True, name="Active User Count", value=active) - em.add_field(inline=True, name="Ping", - value=f"{round(self.client.latency * 1000)}ms") - em.add_field(inline=True, name="Uptime", value=(await self.client.uptime)) - em.set_footer(text="Made by FusionSid#3645") - em.add_field(name='CPU Usage', - value=f'{psutil.cpu_percent()}%', inline=True) - em.add_field(name='Memory Usage', - value=f'{psutil.virtual_memory().percent}% of ({round((psutil.virtual_memory().total/1073741824), 2)}GB)', inline=True) - em.add_field(name='Available Memory', - value=f'{round(psutil.virtual_memory().available * 100 / psutil.virtual_memory().total)}%', inline=True) - em.add_field(inline=True, name="Python version", - value=f"{platform.python_version()}") - em.add_field(inline=True, name="Running on", - value=f"{platform.system()} {platform.release()}") - em.add_field(name="Python code", value=f"{(await get_lines())} of code") - await ctx.send(embed=em) - - @commands.command() - @commands.check(plugin_enabled) - async def uptime(self, ctx): - await ctx.send(embed=discord.Embed(title="Uptime:", description=f"I have been up for: **{(await self.client.uptime)}**")) - - -def setup(client): - client.add_cog(Info(client)) \ No newline at end of file diff --git a/cogs/utilities/todo.py b/cogs/utilities/todo.py deleted file mode 100644 index d50b893..0000000 --- a/cogs/utilities/todo.py +++ /dev/null @@ -1,20 +0,0 @@ -import discord -from discord.ext import commands -import aiosqlite - -async def get_data(user): - pass - - -class Todo(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command() - async def todo(self, ctx): - pass - - - -def setup(client): - client.add_cog(Todo(client)) \ No newline at end of file diff --git a/cogs/utilities/utilities.py b/cogs/utilities/utilities.py deleted file mode 100644 index 9cb88f8..0000000 --- a/cogs/utilities/utilities.py +++ /dev/null @@ -1,228 +0,0 @@ -import discord -import json -from utils.checks import plugin_enabled -from discord.ext import commands -import qrcode -from simpcalc import simpcalc -from discord.ui import Button, View -import numexpr as ne - - -class InteractiveView(discord.ui.View): - def __init__(self, ctx): - super().__init__(timeout=100) - self.expr = "" - self.ctx = ctx - # if you are using the above function, no need to declare this! - self.calc = simpcalc.Calculate() - - @discord.ui.button(style=discord.ButtonStyle.blurple, label="1", row=0) - async def one(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "1" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.blurple, label="2", row=0) - async def two(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "2" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.blurple, label="3", row=0) - async def three(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "3" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.green, label="+", row=0) - async def plus(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "+" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.blurple, label="4", row=1) - async def last(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "4" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.blurple, label="5", row=1) - async def five(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "5" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.blurple, label="6", row=1) - async def six(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "6" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.green, label="/", row=1) - async def divide(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "/" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.blurple, label="7", row=2) - async def seven(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "7" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.blurple, label="8", row=2) - async def eight(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "8" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.blurple, label="9", row=2) - async def nine(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "9" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.green, label="*", row=2) - async def multiply(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "*" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.blurple, label=".", row=3) - async def dot(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "." - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.blurple, label="0", row=3) - async def zero(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "0" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.green, label="=", row=3) - async def equal(self, button: discord.ui.Button, interaction: discord.Interaction): - try: - self.expr = await self.calc.calculate(self.expr) - except: # if you are function only, change this to BadArgument - return await interaction.response.send_message("Um, looks like you provided a wrong expression....") - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.green, label="-", row=3) - async def minus(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "-" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.green, label="(", row=4) - async def left_bracket(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += "(" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.green, label=")", row=4) - async def right_bracket(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr += ")" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.red, label="C", row=4) - async def clear(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr = "" - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - @discord.ui.button(style=discord.ButtonStyle.red, label="<==", row=4) - async def back(self, button: discord.ui.Button, interaction: discord.Interaction): - self.expr = self.expr[:-1] - await interaction.message.edit(content=f"```\n{self.expr}\n```") - - async def interaction_check(self, interaction) -> bool: - if interaction.user != self.ctx.author: - await interaction.response.send_message("This isnt for you", ephemeral=True) - return False - else: - return True - -class Utilities(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(extras={"category": "Utilities"}, usage="invite", help="This command creates a quick invite for your server", description="Creates a 10 day invite for your discord server.") - @commands.check(plugin_enabled) - async def invite(self, ctx): - link = await ctx.channel.create_invite(max_age=10) - await ctx.send(link) - - @commands.command(aliases=['bot'], extras={"category": "Utilities"}, usage="botinvite", help="Creates an invite link so you can invite Why to your server", description="Invite why to your server") - @commands.check(plugin_enabled) - async def botinvite(self, ctx): - await ctx.send(embed=discord.Embed(title="Invite **Why?** to your server:", description="[Why Invite Link](https://discord.com/api/oauth2/authorize?client_id=896932646846885898&permissions=8&scope=bot%20applications.commands)", color=ctx.author.color)) - - - @commands.command(usage = "avatar [member]", description = "User avatar", help = "Gets a members avatar and shows it", extras={"category": "Utilities"}) - @commands.check(plugin_enabled) - async def avatar(self, ctx, member:discord.Member=None): - if member is None: - member = ctx.author - em = discord.Embed(title=f"{member.name}'s Avatar:") - em.set_image(url=member.avatar.url) - await ctx.send(embed=em) - - - @commands.command(aliases=['sug'], extras={"category": "Utilities"}, usage="suggest [suggestion]", help="This command is used to suggest new features/commands to us", description="Suggest something for Why") - @commands.check(plugin_enabled) - async def suggest(self, ctx, *, suggestion): - sid = await self.client.fetch_channel(925157029092413460) - em = discord.Embed( - title= "Suggestion:", - description=f"By: {ctx.author.name}\n\n{suggestion}", - color=discord.Color.random() - ) - await sid.send(embed=em, content=ctx.author.id) - await ctx.send("Thank you for you suggestion!") - - - - @commands.command(aliases=['qr'], extras={"category": "Utilities"}, usage="qrcode [url]", help="This command takes in a url and makes a qrcode.", description="Creates a qrcode") - @commands.check(plugin_enabled) - async def qrcode(self, ctx, *, url): - qr = qrcode.QRCode( - version=1, - error_correction=qrcode.constants.ERROR_CORRECT_H, - box_size=10, - border=4, - ) - qr.add_data(str(url)) - qr.make(fit=True) - img = qr.make_image(fill_color="black", - back_color="white").convert('RGB') - img.save('./tempstorage/qrcode.png') - await ctx.send(file=discord.File('./tempstorage/qrcode.png')) - - @commands.command(aliases=['calculator'], extras={"category": "Utilities"}, usage="calculator", help="This command shows an interactive button calculator", description="Interactive button calculator") - @commands.check(plugin_enabled) - async def calculate(self, ctx): - view = InteractiveView(ctx) - message = await ctx.send("```\n```", view=view) - res = await view.wait() - if res: - for i in view.children: - i.disabled = True - return await message.edit(view=view) - - @commands.command(extras={"category": "Utilities"}, usage="calc [query]", help="This command returns a calculated result. It supports binary, and bit shifts and squareroots", description="Calculates your query") - @commands.check(plugin_enabled) - async def calc(self, ctx, *, query): - try: - calc = ne.evaluate(query) - msg = int(calc) - await ctx.send(msg) - except Exception as e: - await ctx.send(f"Calculation Error\n{e}") - - @commands.command(extras={"category": "Utilities"}, usage="vote", help="This command allows you to vote for Why bot", description="Vote for why bot") - @commands.check(plugin_enabled) - async def vote(self, ctx): - button = Button(style=discord.ButtonStyle.grey, label="Vote link:", - url="https://discordbotlist.com/bots/why") - view = View(timeout=15) - view.add_item(button) - await ctx.send(embed=discord.Embed(title="Vote for Why Bot here:", color=ctx.author.color), view=view) - - @commands.command(extras={"category": "Utilities"}, usage="cuse [@user(optional)]", help="Shows how many times you have used Why bot", description="How many times have you used Why?") - @commands.check(plugin_enabled) - async def cuse(self, ctx, member: discord.Member = None): - if member is None: - member = ctx.author - with open('./database/userdb.json') as f: - data = json.load(f) - cuse = data[str(member.id)]["command_count"] - await ctx.send(embed=discord.Embed(title=f"You have used Why Bot {cuse} times", color=ctx.author.color)) - - - -def setup(client): - client.add_cog(Utilities(client)) diff --git a/cogs/utilities/whosplaying.py b/cogs/utilities/whosplaying.py deleted file mode 100644 index 9cbaf8e..0000000 --- a/cogs/utilities/whosplaying.py +++ /dev/null @@ -1,100 +0,0 @@ -import discord -from discord.ext import commands -import random -import datetime -from utils import plugin_enabled -import operator - -class WhoPlay(commands.Cog): - def __init__(self, client): - self.client = client - - @commands.command(no_pm=True, help="This command is used to check whos playing a certain game/activity", extras={"category":"Utilities"}, usage="whosplaying [game/activity]", description="Check whosplaying activity") - @commands.check(plugin_enabled) - async def whosplaying(self, ctx, *, game): - if len(game) <= 1: - await ctx.send("```The game should be at least 2 characters long...```", delete_after=5.0) - return - - guild = ctx.message.guild - members = guild.members - playing_game = "" - count_playing = 0 - - for member in members: - if not member: - continue - if not member.activity or not member.activity.name: - continue - if member.bot: - continue - if game.lower() in member.activity.name.lower(): - count_playing += 1 - if count_playing <= 15: - emote = random.choice( - [":trident:", ":high_brightness:", ":low_brightness:", ":beginner:", ":diamond_shape_with_a_dot_inside:"]) - playing_game += f"{emote} {member.name} ({member.activity.name})\n" - - if playing_game == "": - await ctx.send("```Search results:\nNo users are currently playing that game.```") - else: - msg = playing_game - if count_playing > 15: - showing = "(Showing 15/{})".format(count_playing) - else: - showing = "({})".format(count_playing) - - em = discord.Embed( - description=msg, colour=discord.Colour(value=0x36393e)) - em.timestamp = datetime.datetime.utcnow() - await ctx.send(embed=em) - - - @commands.command(no_pm=True, help="This command is used to show the most played games right now", extras={"category":"Utilities"}, usage="currentgames", description="Show current games being played") - @commands.check(plugin_enabled) - async def currentgames(self, ctx): - """Shows the most played games right now""" - guild = ctx.message.guild - members = guild.members - - freq_list = {} - for member in members: - if not member: - continue - if not member.activity or not member.activity.name: - continue - if member.bot: - continue - if member.activity.name not in freq_list: - freq_list[member.activity.name] = 0 - freq_list[member.activity.name] += 1 - - sorted_list = sorted(freq_list.items(), - key=operator.itemgetter(1), - reverse=True) - - if not freq_list: - await ctx.send("```Search results:\nNo users are currently playing any games. Odd...```") - else: - # Create display and embed - msg = "" - max_games = min(len(sorted_list), 10) - - em = discord.Embed( - description=msg, colour=discord.Colour(value=0x36393e)) - em.timestamp = datetime.datetime.utcnow() - for i in range(max_games): - game, freq = sorted_list[i] - if int(freq_list[game]) < 2: - amount = "1 person" - else: - amount = f"{int(freq_list[game])} people" - em.add_field(name=game, value=amount) - em.set_thumbnail(url=guild.icon.url) - em.set_footer( - text=f"Do {ctx.prefix}whosplaying to see whos playing a specific game") - await ctx.send(embed=em) - - -def setup(client): - client.add_cog(WhoPlay(client)) \ No newline at end of file diff --git a/cogs/voice_channel/music.py b/cogs/voice_channel/music.py deleted file mode 100644 index 56744df..0000000 --- a/cogs/voice_channel/music.py +++ /dev/null @@ -1,899 +0,0 @@ -import discord -import datetime -import importlib -import pyttsx3 -from utils.checks import plugin_enabled -import shutil -from discord import interactions -import wget -import requests -import asyncio -from discord.ext import commands -import youtube_dl -from youtubesearchpython import VideosSearch -from multiprocessing import Pool -import os -import discord.voice_client -import json -from gtts import gTTS -from mutagen.mp3 import MP3 -from discord.ui import Button, View -from discord import Option -import random - -cookies = "./database/cookies.txt" -ffmpeg_options = { # ffmpeg options - "before_options": "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5", - "options": "-vn" -} - - -loops = {} # for +loop command -queues = {} # for queues -now_playing_pos = {} # to display the current song in +queue command correctly -all_queues_info = {} # all queue info, for +queue command - -# function, which splitting the queue into queues of 10 songs - -class MusicView(View): - def __init__(self, ctx, client): - super().__init__(timeout=600) - self.ctx = ctx - self.em = discord.Embed(title="Music", color=ctx.author.color) - self.client = client - - @discord.ui.button(style=discord.ButtonStyle.green, emoji="ā–¶ļø", custom_id="button") - - async def play(self, button, interaction): - if self.ctx.voice_client is None: - self.em.description = "I am not playing any songs for you." - - if self.ctx.author.voice is None: - self.em.description = f"{self.ctx.author.mention}, You have to be connected to a voice channel." - - if self.ctx.author.voice.channel.id != self.ctx.voice_client.channel.id: - self.em.description = "You are in the wrong channel." - - self.ctx.voice_client.resume() # resume music - self.em.description = "Successfully resumed." - - await interaction.response.edit_message(embed=self.em) - - @discord.ui.button(style=discord.ButtonStyle.red, emoji="āøļø", custom_id="button2") - async def pause(self, button, interaction): - if self.ctx.voice_client is None: - self.em.description = "I am not playing any songs for you." - - if self.ctx.author.voice is None: - self.em.description = f"{self.ctx.author.mention}, You have to be connected to a voice channel." - - if self.ctx.author.voice.channel.id != self.ctx.voice_client.channel.id: - self.em.description = "You are in the wrong channel." - - self.ctx.voice_client.pause() # stopping a music - self.em.description = "Successfully paused." - - await interaction.response.edit_message(embed=self.em) - - @discord.ui.button(style=discord.ButtonStyle.green, emoji="ā­ļø", custom_id="button3") - async def skip(self, button, interaction): - if self.ctx.voice_client is None: - self.em.description = "I am not playing any songs for you." - - if self.ctx.author.voice is None: - self.em.description = f"{self.ctx.author.mention}, You have to be connected to a voice channel." - - if self.ctx.author.voice.channel.id != self.ctx.voice_client.channel.id: - self.em.description = "You are in the wrong channel." - - self.ctx.voice_client.stop() # skipping current track - self.em.description = "Successfully skipped." - await interaction.response.edit_message(embed=self.em) - - @discord.ui.button(style=discord.ButtonStyle.grey, label="Leave VC", custom_id="button4", row=2) - async def leave(self, button, interaction): - if self.ctx.voice_client is None: - self.em.description = "I am not playing any songs for you." - - if self.ctx.author.voice is None: - self.em.description = f"{self.ctx.author.mention}, You have to be connected to a voice channel." - - if self.ctx.author.voice.channel.id != self.ctx.voice_client.channel.id: - self.em.description = "You are in the wrong channel." - - await self.ctx.voice_client.disconnect() # leaving a voice channel - self.em.description = "Successfully disconnected." - await interaction.response.edit_message(embed=self.em) - - @discord.ui.button(style=discord.ButtonStyle.grey, label="Join VC", custom_id="button5", row=2) - async def join(self, button, interaction): - if self.ctx.author.voice is None: - self.em.description = f"{self.ctx.author.mention}, You have to be connected to a voice channel." - - channel = self.ctx.author.voice.channel - - if self.ctx.voice_client is None: # if bot is not connected to a voice channel, connecting to a voice channel - await channel.connect() - else: # else, just moving to ctx author voice channel - await self.ctx.voice_client.move_to(channel) - - # self deaf - await self.ctx.guild.change_voice_state(channel=channel, self_mute=False, self_deaf=True) - - self.em.description = f"Successfully joined to `{channel}`" - await interaction.response.edit_message(embed=self.em) - - -def split(arr, size): - arrays = [] - while len(arr) > size: - pice = arr[:size] - arrays.append(pice) - arr = arr[size:] - arrays.append(arr) - return arrays - - -# function, which "loop" loops, checks the queue and replays the remaining links -def check_new_songs(guild_id, vc): - global queues - global now_playing_pos - global all_queues_info - - # if the bot is not playing any songs, deleting the queue - if not vc.is_connected(): - if guild_id in queues: - del queues[guild_id] - if guild_id in all_queues_info: - del all_queues_info[guild_id] - if guild_id in loops: - del loops[guild_id] - if guild_id in now_playing_pos: - del now_playing_pos[guild_id] - return - - # "loop" loops - if guild_id in loops: - if loops[guild_id] == "current track": - src_video_url = queues[guild_id][0]["src_url"] - - # play music - try: - vc.play(discord.FFmpegPCMAudio( - src_video_url, - before_options=ffmpeg_options["before_options"], - options=ffmpeg_options["options"] - ), after=lambda a: check_new_songs(guild_id, vc)) - except discord.errors.ClientException as e: - print("ClientException") - - return - - if queues[guild_id]: - queues[guild_id].pop(0) - - try: - src_video_url = queues[guild_id][0]["src_url"] - now_playing_pos[guild_id] += 1 - except IndexError: - # if queue is empty and there is no queue loop, deleting the variables and return - if guild_id not in loops or loops[guild_id] != "queue": - del queues[guild_id] - del all_queues_info[guild_id] - del now_playing_pos[guild_id] - return - - # else looping queue - for track in all_queues_info[guild_id]: - queues[guild_id].append( - {"url": track["url"], "src_url": track["src_url"]}) - now_playing_pos[guild_id] = 0 - src_video_url = queues[guild_id][0]["src_url"] - - # play music - try: - vc.play(discord.FFmpegPCMAudio( - src_video_url, - before_options=ffmpeg_options["before_options"], - options=ffmpeg_options["options"] - ), after=lambda a: check_new_songs(guild_id, vc)) - except discord.errors.ClientException: - return - - -# this function extracts all info from video url -def extract_info(url): - try: - return youtube_dl.YoutubeDL({"format": "bestaudio" }).extract_info(url, download=False) - # if there is an error, changing format - except (youtube_dl.utils.ExtractorError, youtube_dl.utils.DownloadError): - return youtube_dl.YoutubeDL({"format": "95"}).extract_info(url, download=False) - - -async def playy(ctx, video=None): - global queues - global now_playing_pos - global all_queues_info - - if ctx.author.voice is None: - return await ctx.send(f"{ctx.author.mention}, You have to be connected to a voice channel.") - - channel = ctx.author.voice.channel - if ctx.voice_client is None: # if bot is not connected to a voice channel, connecting to a voice channel - await channel.connect() - else: # else, just moving to ctx author voice channel - await ctx.voice_client.move_to(channel) - - # self deaf - await ctx.guild.change_voice_state(channel=channel, self_mute=False, self_deaf=True) - - if video is None: - return - - # searching for a video - video_search = video - if "https://www.youtube.com/" in video or "https://youtu.be/" in video: - video_url = "" - for el in video.split(): - if "https://www.youtube.com/" in el or "https://youtu.be/" in el: - if "list=" in el: - await ctx.send("Loading playlist...") - video_url = el - else: - video = VideosSearch(video, limit=1) - video_url = video.result()["result"][0]["link"] - - # finding source video url - pool = Pool() # creating new pool which will extract all video info - information = pool.apply_async( - func=extract_info, args=(video_url,)).get() - pool.close() # closing pool - pool.join() - - # if it's not a playlist, playing the song as usual - if "_type" not in information: - src_video_url = information["formats"][0]["url"] # source url - video_title = information["title"] - - # filling queues - if ctx.guild.id in queues: - queues[ctx.guild.id].append( - {"url": video_url, "src_url": src_video_url}) - all_queues_info[ctx.guild.id].append( - {"name": video_title, "url": video_url, "src_url": src_video_url}) - else: - queues[ctx.guild.id] = [ - {"url": video_url, "src_url": src_video_url}] - now_playing_pos[ctx.guild.id] = 0 - all_queues_info[ctx.guild.id] = [ - {"name": video_title, "url": video_url, "src_url": src_video_url}] - - else: # else queueing playlist - src_video_url = information["entries"][0]["url"] - video_title = information["title"] - - # queuing first song - if ctx.guild.id in queues: - queues[ctx.guild.id].append( - {"url": video_url, "src_url": src_video_url}) - all_queues_info[ctx.guild.id].append( - {"name": information["entries"][0]["title"], "url": video_url, "src_url": src_video_url}) - else: - queues[ctx.guild.id] = [ - {"url": video_url, "src_url": src_video_url}] - now_playing_pos[ctx.guild.id] = 0 - all_queues_info[ctx.guild.id] = [ - {"name": information["entries"][0]["title"], "url": video_url, "src_url": src_video_url}] - - # queuing another songs - for v in information["entries"]: - if information["entries"].index(v) != 0: - queues[ctx.guild.id].append( - {"url": video_url, "src_url": v["url"]}) - all_queues_info[ctx.guild.id].append( - {"name": v["title"], "url": video_url, "src_url": src_video_url}) - - vc = ctx.voice_client - - try: - vc.play(discord.FFmpegPCMAudio( - src_video_url, - before_options=ffmpeg_options["before_options"], - options=ffmpeg_options["options"] - # calling the check_new_songs function after playing the current music - ), after=lambda a: check_new_songs(ctx.guild.id, vc)) - except Exception as e: - print(e) - -class Music(commands.Cog): - def __init__(self, client): - self.client = client - - # join command - @commands.command(aliases=["j"], help="Running this command lets the bot join the discord VC you are in.", extras={"category":"Music"}, usage="join", description="Bot joins VC") - @commands.check(plugin_enabled) - async def join(self, ctx): - if ctx.author.voice is None: - return await ctx.send(f"{ctx.author.mention}, You have to be connected to a voice channel.") - - channel = ctx.author.voice.channel - - if ctx.voice_client is None: # if bot is not connected to a voice channel, connecting to a voice channel - await channel.connect() - else: # else, just moving to ctx author voice channel - await ctx.voice_client.move_to(channel) - - # self deaf - await ctx.guild.change_voice_state(channel=channel, self_mute=False, self_deaf=True) - - await ctx.send(f"āœ… Successfully joined to `{channel}`") - - # play command - # noinspection PyTypeChecker - - @commands.command(aliases=["p"], help="This command is used to play songs. You can type in the name, yt url or yt playlist url and the bot will play the song.", extras={"category":"Music"}, usage="play [name/url]", description="Play a song") - @commands.check(plugin_enabled) - async def play(self, ctx, *, video=None): - global queues - global now_playing_pos - global all_queues_info - - if ctx.author.voice is None: - return await ctx.send(f"{ctx.author.mention}, You have to be connected to a voice channel.") - - channel = ctx.author.voice.channel - if ctx.voice_client is None: # if bot is not connected to a voice channel, connecting to a voice channel - await channel.connect() - else: # else, just moving to ctx author voice channel - await ctx.voice_client.move_to(channel) - - # self deaf - await ctx.guild.change_voice_state(channel=channel, self_mute=False, self_deaf=True) - - if video is None: - return - - # searching for a video - video_search = video - if "https://www.youtube.com/" in video or "https://youtu.be/" in video: - video_url = "" - for el in video.split(): - if "https://www.youtube.com/" in el or "https://youtu.be/" in el: - if "list=" in el: - await ctx.send("Loading playlist...") - video_url = el - else: - video = VideosSearch(video, limit=1) - video_url = video.result()["result"][0]["link"] - - # finding source video url - pool = Pool() # creating new pool which will extract all video info - information = pool.apply_async( - func=extract_info, args=(video_url,)).get() - pool.close() # closing pool - pool.join() - - # if it's not a playlist, playing the song as usual - if "_type" not in information: - src_video_url = information["formats"][0]["url"] # source url - video_title = information["title"] - - # filling queues - if ctx.guild.id in queues: - queues[ctx.guild.id].append( - {"url": video_url, "src_url": src_video_url}) - all_queues_info[ctx.guild.id].append( - {"name": video_title, "url": video_url, "src_url": src_video_url}) - else: - queues[ctx.guild.id] = [ - {"url": video_url, "src_url": src_video_url}] - now_playing_pos[ctx.guild.id] = 0 - all_queues_info[ctx.guild.id] = [ - {"name": video_title, "url": video_url, "src_url": src_video_url}] - - else: # else queueing playlist - src_video_url = information["entries"][0]["url"] - video_title = information["title"] - - # queuing first song - if ctx.guild.id in queues: - queues[ctx.guild.id].append( - {"url": video_url, "src_url": src_video_url}) - all_queues_info[ctx.guild.id].append( - {"name": information["entries"][0]["title"], "url": video_url, "src_url": src_video_url}) - else: - queues[ctx.guild.id] = [ - {"url": video_url, "src_url": src_video_url}] - now_playing_pos[ctx.guild.id] = 0 - all_queues_info[ctx.guild.id] = [ - {"name": information["entries"][0]["title"], "url": video_url, "src_url": src_video_url}] - - # queuing another songs - for v in information["entries"]: - if information["entries"].index(v) != 0: - queues[ctx.guild.id].append( - {"url": video_url, "src_url": v["url"]}) - all_queues_info[ctx.guild.id].append( - {"name": v["title"], "url": video_url, "src_url": src_video_url}) - - vc = ctx.voice_client - - try: - vc.play(discord.FFmpegPCMAudio( - src_video_url, - before_options=ffmpeg_options["before_options"], - options=ffmpeg_options["options"] - # calling the check_new_songs function after playing the current music - ), after=lambda a: check_new_songs(ctx.guild.id, vc)) - except discord.errors.ClientException: - print("ClientException") - - # Adding embed, depending on the queue - if len(queues[ctx.guild.id]) != 1: - embed = discord.Embed( - title="Queue", - description=f"šŸ”Ž Searching for `{video_search}`\n\n" + - f"""āœ… [{video_title}]({video_url}) - successfully added to queue.""", - color=0x515596) - embed.timestamp = datetime.datetime.utcnow() - else: - embed = discord.Embed( - title="Now playing", - description=f"āœ… Successfully joined to `{channel}`\n\n" + - f"šŸ”Ž Searching for `{video_search}`\n\n" + - f"""ā–¶ļø Now playing - [{video_title}]({video_url})""", - color=0x515596) - embed.timestamp = datetime.datetime.utcnow() - - await ctx.send(embed=embed) - - # skip command - - @commands.command(aliases=["s"], help="This command is used to skip the song that is playing", extras={"category":"Music"}, usage="skip", description="Skips the playing song") - @commands.check(plugin_enabled) - async def skip(self, ctx): - if ctx.voice_client is None: - return await ctx.send("I am not playing any songs for you.") - - if ctx.author.voice is None: - return await ctx.send(f"{ctx.author.mention}, You have to be connected to a voice channel.") - - if ctx.author.voice.channel.id != ctx.voice_client.channel.id: - return await ctx.send("You are in the wrong channel.") - - ctx.voice_client.stop() # skipping current track - await ctx.message.add_reaction("āœ…") # adding a reaction - await ctx.send("Successfully skipped.") - - # leave command - - @commands.command(aliases=["l", "disconnect", "d"], help="This command is used to disconnect/make the bot leave the VC.", extras={"category":"Music"}, usage="leave", description="Leave vc") - @commands.check(plugin_enabled) - async def leave(self, ctx): - if ctx.voice_client is None: - return await ctx.send("I am not playing any songs for you.") - - if ctx.author.voice is None: - return await ctx.send(f"{ctx.author.mention}, You have to be connected to a voice channel.") - - if ctx.author.voice.channel.id != ctx.voice_client.channel.id: - return await ctx.send("You are in the wrong channel.") - - await ctx.voice_client.disconnect() # leaving a voice channel - await ctx.message.add_reaction("āœ…") # adding a reaction - await ctx.send("Successfully disconnected.") - - # clearing queues and loops - if ctx.guild.id in queues: - del queues[ctx.guild.id] - if ctx.guild.id in all_queues_info: - del all_queues_info[ctx.guild.id] - if ctx.guild.id in loops: - del loops[ctx.guild.id] - if ctx.guild.id in now_playing_pos: - del now_playing_pos[ctx.guild.id] - - # stop command - - @commands.command(aliases=["stop"], help="This command is used to pause the current plauing song", extras={"category":"Music"}, usage="pause", description="Pause song") - @commands.check(plugin_enabled) - async def pause(self, ctx): - if ctx.voice_client is None: - return await ctx.send("I am not playing any songs for you.") - - if ctx.author.voice is None: - return await ctx.send(f"{ctx.author.mention}, You have to be connected to a voice channel.") - - if ctx.author.voice.channel.id != ctx.voice_client.channel.id: - return await ctx.send("You are in the wrong channel.") - - ctx.voice_client.pause() # stopping a music - await ctx.message.add_reaction("āœ…") # adding a reaction - await ctx.send("āøļø Successfully paused.") - - # continue command - - @commands.command(aliases=["continue", "unpause"], help="This command is used to resume the currently playing song", extras={"category":"Music"}, usage="resume", description="Resume the paused song") - @commands.check(plugin_enabled) - async def resume(self, ctx): - if ctx.voice_client is None: - return await ctx.send("I am not playing any songs for you.") - - if ctx.author.voice is None: - return await ctx.send(f"{ctx.author.mention}, You have to be connected to a voice channel.") - - if ctx.author.voice.channel.id != ctx.voice_client.channel.id: - return await ctx.send("You are in the wrong channel.") - - ctx.voice_client.resume() # resume music - await ctx.message.add_reaction("āœ…") # adding a reaction - await ctx.send("ā–¶ļø Successfully resumed.") - - # queue command - - @commands.command(aliases=["q"], help="This command is used to dispay all the songs in the queue", extras={"category":"Music"}, usage="queue", description="Show queue") - @commands.check(plugin_enabled) - async def queue(self, ctx): - global all_queues_info - - # if queue is empty, sending empty embed - if ctx.voice_client is None: - await ctx.send(embed=discord.Embed(title="Current Queue", description="Your current queue is empty!", color=0x515596)) - else: - position = 0 - if ctx.guild.id in all_queues_info: - # splitting the queue into queues of 10 songs - queue_info = split(all_queues_info[ctx.guild.id], 10) - else: - return await ctx.send(embed=discord.Embed( - title="Current Queue", - description="Your current queue is empty!", - color=0x515596) - ) - print(all_queues_info) - content = [] - for _ in queue_info: - content.append("") - page = 0 - - # filling content with songs - - for i in queue_info: - for j in i: - if position == now_playing_pos[ctx.guild.id]: - content[page] += f"""{position+1}. [{j["name"]}]({j["url"]}) ⟵ current track\n""" # output the current song - else: - content[page] += f"""{position+1}. [{j["name"]}]({j["url"]})\n""" - position += 1 - if page < len(queue_info) - 1: - page += 1 - - # getting information about loops - loops_info = "" - if ctx.guild.id not in loops or loops[ctx.guild.id] == "none": - loops_info = "šŸ” Queue loop: disabled | šŸ” Current track loop: disabled" - elif loops[ctx.guild.id] == "queue": - loops_info = "šŸ” Queue loop: enabled | šŸ” Current track loop: disabled" - elif loops[ctx.guild.id] == "current track": - loops_info = "šŸ” Queue loop: disabled | šŸ” Current track loop: enabled" - - # sending the entire queue of 10 songs for each message - print(content) - for songs in content: - if content.index(songs) == 0: - await ctx.send(embed=discord.Embed( - title="Current Queue", - description=songs, - color=0x515596) - .set_footer(text=loops_info)) - else: - await ctx.send(embed=discord.Embed( - description=songs, - color=0x515596) - .set_footer(text=loops_info)) - - # loop command - - @commands.command( help="This command is used to loop the queue/song", extras={"category":"Music"}, usage="loop", description="Loop queue/song") - @commands.check(plugin_enabled) - async def loop(self, ctx): - global loops - loops[ctx.guild.id] = "none" - - if ctx.voice_client is None: - return await ctx.send("I am not playing any songs for you.") - - if ctx.author.voice is None: - return await ctx.send(f"{ctx.author.mention}, You have to be connected to a voice channel.") - - if ctx.author.voice.channel.id != ctx.voice_client.channel.id: - return await ctx.send("You are in the wrong channel.") - - # sending a message depending on whether is loop turned on or off - if ctx.guild.id not in loops or loops[ctx.guild.id] == "none": - await ctx.send("šŸ” Queue loop enabled!") - loops[ctx.guild.id] = "queue" - elif loops[ctx.guild.id] == "queue": - await ctx.send("šŸ” Current track loop enabled!") - loops[ctx.guild.id] = "current track" - else: - await ctx.send("āŽ Loop disabled!") - loops[ctx.guild.id] = "none" - - - - # Playlists - - - @commands.command(aliases=['cp'], help="This command is used to create playlists", extras={"category":"Music"}, usage="createplaylist [playlist name]", description="Create a playlist") - @commands.check(plugin_enabled) - async def createplaylist(self, ctx, pname: str = None): - if pname == None: - return await ctx.send("You need to name the playlist") - - name = f"{ctx.author.id}" - pname = pname - - with open('./database/playlists.json') as f: - data = json.load(f) - if name in data: - data[name][pname] = [] - else: - plist = {pname: []} - data[name] = plist - with open('./database/playlists.json', 'w') as f: - json.dump(data, f, indent=4) - return await ctx.send(embed=discord.Embed(title=f"Playlist `{pname}` created!", description=f"To add to the playlist use {ctx.prefix}padd [playlistname] [song/songurl]", color=ctx.author.color)) - - @commands.command(help="This command is used to list all the songs in a playlist", extras={"category":"Music"}, usage="plist [playlist name]", description="Displays all the songs in a playlist") - @commands.check(plugin_enabled) - async def plist(self, ctx, pname: str): - with open('./database/playlists.json') as f: - data = json.load(f) - if f"{ctx.author.id}" in data: - pass - else: - return await ctx.send(embed=discord.Embed(title="You dont have any playlists!", description=f'Use {ctx.prefix}createplaylist [name] to create one', color=ctx.author.color)) - if pname in data[f"{ctx.author.id}"]: - pass - else: - return await ctx.send(embed=discord.Embed(title="This playlist doesnt exist!", description=f'Use {ctx.prefix}createplaylist [name] to create one', color=ctx.author.color)) - em = discord.Embed(title=f"Playlist: {pname}", description="Songs:") - em.timestamp = datetime.datetime.utcnow() - if len(data[f"{ctx.author.id}"][pname]): - c = 1 - for song in data[f"{ctx.author.id}"][pname]: - em.add_field(name=f"{c}. {song}", value="** **", inline=False) - c += 1 - await ctx.send(embed=em) - else: - await ctx.send(f"List is empty use {ctx.prefix}padd {pname} [songname/url]") - - @commands.command(help="Lets you add a song to a playlist", extras={"category":"Music"}, usage="padd [playlist name] [song/songurl]", description="Add song to a playlist") - @commands.check(plugin_enabled) - async def padd(self, ctx, pname: str, *, song: str): - with open('./database/playlists.json') as f: - data = json.load(f) - if f"{ctx.author.id}" in data: - pass - else: - return await ctx.send(embed=discord.Embed(title="You dont have any playlists!", description=f'Use {ctx.prefix}createplaylist [name] to create one', color=ctx.author.color)) - if pname in data[f"{ctx.author.id}"]: - pass - else: - return await ctx.send(embed=discord.Embed(title="This playlist doesnt exist!", description=f'Use {ctx.prefix}createplaylist [name] to create one', color=ctx.author.color)) - data[f"{ctx.author.id}"][pname].append(song) - await ctx.send(f"{song} Added to {pname}") - with open('./database/playlists.json', 'w') as f: - json.dump(data, f, indent=4) - - @commands.command( help="This command is used to play the songs in a playlist. The command works by adding all the songs in the playlist to queue.\nplease dont kick the bot from the vc while the command is running", extras={"category":"Music"}, usage="playlist [playlist name]", description="Play a playlist") - @commands.check(plugin_enabled) - async def playlist(self, ctx, pname: str): - with open('./database/playlists.json') as f: - data = json.load(f) - if f"{ctx.author.id}" in data: - pass - else: - return await ctx.send(embed=discord.Embed(title="You dont have any playlists!", description=f'Use {ctx.prefix}createplaylist [name] to create one', color=ctx.author.color)) - if pname in data[f"{ctx.author.id}"]: - pass - else: - return await ctx.send(embed=discord.Embed(title="This playlist doesnt exist!", description=f'Use {ctx.prefix}createplaylist [name] to create one', color=ctx.author.color)) - await ctx.send(embed=discord.Embed(title=f"Playing playlist: {pname}", description=f"Songs are being added to queue")) - if len(data[f"{ctx.author.id}"][pname]): - for song in data[f"{ctx.author.id}"][pname]: - try: - await playy(ctx, video=song) - except Exception as e: - print(e) - await asyncio.sleep(1) - else: - await ctx.send(f"List is empty use {ctx.prefix}add [song]") - - @commands.command(help="This command is used to play the songs in a playlist on shuffle. The command works by adding all the songs in the playlist to queue in a random order.\nplease dont kick the bot from the vc while the command is running", extras={"category":"Music"}, usage="shuffleplaylist [playlist name]", description="Shuffle a playlist") - @commands.check(plugin_enabled) - async def shuffleplaylist(self, ctx, pname: str): - with open('./database/playlists.json') as f: - data = json.load(f) - if f"{ctx.author.id}" in data: - pass - else: - return await ctx.send(embed=discord.Embed(title="You dont have any playlists!", description=f'Use {ctx.prefix}createplaylist [name] to create one', color=ctx.author.color)) - if pname in data[f"{ctx.author.id}"]: - pass - else: - return await ctx.send(embed=discord.Embed(title="This playlist doesnt exist!", description=f'Use {ctx.prefix}createplaylist [name] to create one', color=ctx.author.color)) - await ctx.send(embed=discord.Embed(title="Playing Playlist", description=f"Songs are being added to queue in random order")) - if len(data[f"{ctx.author.id}"][pname]): - slist = data[f"{ctx.author.id}"][pname] - def myfunction(): - return 0.1 - random.shuffle(slist, myfunction) - print(slist) - for song in slist: - try: - await playy(ctx, video=song) - except Exception as e: - print(e) - await asyncio.sleep(1) - else: - await ctx.send(f"List is empty use {ctx.prefix}add [song]") - - @commands.command( help="This command is used to delete songs from a playlist ", extras={"category":"Music"}, usage="pdel [playlist name]", description="Delete song from a playlist") - @commands.check(plugin_enabled) - async def pdel(self, ctx, pname: str): - with open('./database/playlists.json') as f: - data = json.load(f) - if f"{ctx.author.id}" in data: - pass - else: - return await ctx.send(embed=discord.Embed(title="You dont have any playlists!", description=f'Use {ctx.prefix}createplaylist [name] to create one', color=ctx.author.color)) - if pname in data[f"{ctx.author.id}"]: - pass - else: - return await ctx.send(embed=discord.Embed(title="This playlist doesnt exist!", description=f'Use {ctx.prefix}createplaylist [name] to create one', color=ctx.author.color)) - if len(data[f"{ctx.author.id}"][pname]): - n = 1 - for song in data[f"{ctx.author.id}"][pname]: - await ctx.send(f"{n}: {song}") - n += 1 - else: - await ctx.send(f"List is empty use {ctx.prefix}add [song]") - - def wfcheck(m): - return m.channel == ctx.channel and m.author == ctx.author - await ctx.send("Enter the number of the song you want to delete") - index = await self.client.wait_for("message", check=wfcheck, timeout=300) - index = index.content - try: - index = int(index) - index = index-1 - except Exception: - await ctx.send("Index must be a number") - try: - rm = data[f"{ctx.author.id}"][pname][index] - print(rm) - data[f"{ctx.author.id}"][pname].remove(rm) - except Exception as e: - print(e) - return - with open('./database/playlists.json', 'w') as f: - json.dump(data, f, indent=4) - await ctx.send("Song deleted from playlist.") - - # Text to speech - - @commands.command(aliases=['speak'], help="This command is used to play text in vc. You type text and the bot will text to speech the text in your vc", extras={"category":"Music"}, usage="tts [text]", description="Text to speech") - @commands.check(plugin_enabled) - async def tts(self, ctx, *, text): - text = str(text) - - language = 'en' - - output = gTTS(text=text, lang=language, slow=False) - - name = ctx.author.id - output.save(f"./tempstorage/{name}.mp3") - - if ctx.author.voice is None: - return await ctx.send(f"{ctx.author.mention}, You have to be connected to a voice channel.") - - channel = ctx.author.voice.channel - - if ctx.voice_client is None: # if bot is not connected to a voice channel, connecting to a voice channel - await channel.connect() - else: # else, just moving to ctx author voice channel - await ctx.voice_client.move_to(channel) - - # self deaf - await ctx.guild.change_voice_state(channel=channel, self_mute=False, self_deaf=True) - - guild = ctx.guild - voice_client: discord.VoiceClient = discord.utils.get( - self.client.voice_clients, guild=guild) - audio_source = discord.FFmpegPCMAudio(f'./tempstorage/{name}.mp3') - if not voice_client.is_playing(): - voice_client.play(audio_source, after=None) - else: - await ctx.send("Something is playling right now, Try using this command after its finished") - audio = MP3(f"./tempstorage/{name}.mp3") - wait = int(audio.info.length) - await asyncio.sleep(wait+5) - os.remove(f'./tempstorage/{name}.mp3') - - @commands.Cog.listener() - async def on_voice_state_update(self, member, before, after): - - if not member.id == self.client.user.id: - return - - elif before.channel is None: - voice = after.channel.guild.voice_client - while voice.is_playing(): #Checks if voice is playing - await asyncio.sleep(1) #While it's playing it sleeps for 1 second - else: - await asyncio.sleep(300) #If it's not playing it waits 15 seconds - while voice.is_playing(): #and checks once again if the bot is not playing - break #if it's playing it breaks - else: - await voice.disconnect() #if not it disconnects - - @commands.command(help="This command is used to play an mp3 file ", extras={"category":"Music"}, usage="mp3 [attach mp3 file]", description="Play an mp3 file") - @commands.check(plugin_enabled) - async def mp3(self, ctx): - - if ctx.author.voice is None: - return await ctx.send(f"{ctx.author.mention}, You have to be connected to a voice channel.") - - channel = ctx.author.voice.channel - - if ctx.voice_client is None: # if bot is not connected to a voice channel, connecting to a voice channel - await channel.connect() - else: # else, just moving to ctx author voice channel - await ctx.voice_client.move_to(channel) - - # self deaf - await ctx.guild.change_voice_state(channel=channel, self_mute=False, self_deaf=True) - - guild = ctx.guild - voice_client: discord.VoiceClient = discord.utils.get( - self.client.voice_clients, guild=guild) - if len(ctx.message.attachments) == 0: - return await ctx.send("You must provide an mp3 file for this to work") - attachment_url = ctx.message.attachments[0].url - if attachment_url.endswith(".mp3"): - pass - else: - return await ctx.send("Must be an mp3 file") - r = requests.get(attachment_url, stream=True) - filename=f"./tempstorage/{ctx.author.id}.mp3" - if r.status_code == 200: - with open(f"./tempstorage/{ctx.author.id}.mp3", 'wb') as f: - r.raw.decode_content = True - shutil.copyfileobj(r.raw, f) - audio_source = discord.FFmpegPCMAudio(filename) - if not voice_client.is_playing(): - voice_client.play(audio_source, after=None) - else: - await ctx.send("Something is playling right now, Try using this command after its finished") - audio = MP3(filename) - wait = int(audio.info.length) - await asyncio.sleep(wait+5) - os.remove(filename) - - @commands.command(help="This command is used to make a small button dashboard for the music", extras={"category":"Music"}, usage="music", description="Small music button dashboard") - @commands.check(plugin_enabled) - async def music(self, ctx): - em = discord.Embed(title="Music", color=ctx.author.color) - em.timestamp = datetime.datetime.utcnow() - view= MusicView(ctx, self.client) - message = await ctx.send(embed=em, view=view) - res = await view.wait() - if res: - for i in view.children: - i.disabled = True - return await message.edit(view=view) - -def setup(client): - client.add_cog(Music(client)) diff --git a/cogs/voice_channel/voice.py b/cogs/voice_channel/voice.py deleted file mode 100644 index 8f2cc16..0000000 --- a/cogs/voice_channel/voice.py +++ /dev/null @@ -1,145 +0,0 @@ -import discord -import asyncio -from discord.ext import commands -import sqlite3 -from utils import plugin_enabled - - -class Voice(commands.Cog): - def __init__(self, bot): - self.bot = bot - - @commands.Cog.listener() - async def on_voice_state_update(self, member, before, after): - conn = sqlite3.connect('./database/voice.db') - c = conn.cursor() - guildID = member.guild.id - c.execute("SELECT voiceChannelID FROM guild WHERE guildID = ?", (guildID,)) - voice=c.fetchone() - if voice is None: - pass - else: - voiceID = voice[0] - try: - if after.channel.id == voiceID: - c.execute("SELECT * FROM voiceChannel WHERE userID = ?", (member.id,)) - cooldown=c.fetchone() - if cooldown is None: - pass - elif member.author.id == 624076054969188363: - pass - else: - await member.send("Creating channels too quickly you've been put on a 15 second cooldown!") - await asyncio.sleep(15) - c.execute("SELECT voiceCategoryID FROM guild WHERE guildID = ?", (guildID,)) - voice=c.fetchone() - c.execute("SELECT channelName, channelLimit FROM userSettings WHERE userID = ?", (member.id,)) - setting=c.fetchone() - c.execute("SELECT channelLimit FROM guildSettings WHERE guildID = ?", (guildID,)) - guildSetting=c.fetchone() - if setting is None: - name = f"{member.name}'s channel" - if guildSetting is None: - limit = 0 - else: - limit = guildSetting[0] - else: - if guildSetting is None: - name = setting[0] - limit = setting[1] - elif guildSetting is not None and setting[1] == 0: - name = setting[0] - limit = guildSetting[0] - else: - name = setting[0] - limit = setting[1] - categoryID = voice[0] - id = member.id - category = self.bot.get_channel(categoryID) - channel2 = await member.guild.create_voice_channel(name,category=category) - channelID = channel2.id - await member.move_to(channel2) - await channel2.set_permissions(self.bot.user, connect=True,read_messages=True) - await channel2.edit(name= name, user_limit = limit) - c.execute("INSERT INTO voiceChannel VALUES (?, ?)", (id,channelID)) - conn.commit() - def check(a,b,c): - return len(channel2.members) == 0 - await self.bot.wait_for('voice_state_update', check=check) - await channel2.delete() - await asyncio.sleep(3) - c.execute('DELETE FROM voiceChannel WHERE userID=?', (id,)) - except: - pass - conn.commit() - conn.close() - - @commands.group( help="These commands are used to set the custom VC for your server. The custom vc is a voice channel which upon joining creates a new temporary discord Voice Channel and deletes said channel when all members leave the channel.\nYou can use voice set to set the channel and voice setlimit to set the limit. ", extras={"category":"Voice"}, usage="voice [set/setlimit]", description="Sets the custom vc for you voice channel") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator = True) - async def voice(self, ctx): - if ctx.invoked_subcommand is not None: - pass - else: - await ctx.send(f"`{ctx.prefix}voice set` To set the channel\n`{ctx.prefix}voice setlimit` To set the limit") - - @voice.command(extras={"category":"Voice"}, usage="voice setup", help="This command is used to set up the Custom Vc for your server.\nThis channel, upon joining will create a temporary vc with your name on it and once everyone leave that channel, it will be deleted", description="Sets the custom vc for the channel") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator = True) - async def setup(self, ctx): - conn = sqlite3.connect('./database/voice.db') - c = conn.cursor() - guildID = ctx.guild.id - id = ctx.author.id - def check(m): - return m.author.id == ctx.author.id - await ctx.channel.send("**You have 60 seconds to answer each question!**") - await ctx.channel.send(f"**Enter the name of the category you wish to create the channels in:(e.g Voice Channels)**") - try: - category = await self.bot.wait_for('message', check=check, timeout = 60.0) - except asyncio.TimeoutError: - await ctx.channel.send('Took too long to answer!') - else: - new_cat = await ctx.guild.create_category_channel(category.content) - await ctx.channel.send('**Enter the name of the voice channel: (e.g Join To Create)**') - try: - channel = await self.bot.wait_for('message', check=check, timeout = 60.0) - except asyncio.TimeoutError: - await ctx.channel.send('Took too long to answer!') - else: - try: - channel = await ctx.guild.create_voice_channel(channel.content, category=new_cat) - c.execute("SELECT * FROM guild WHERE guildID = ? AND ownerID=?", (guildID, id)) - voice=c.fetchone() - if voice is None: - c.execute ("INSERT INTO guild VALUES (?, ?, ?, ?)",(guildID,id,channel.id,new_cat.id)) - else: - c.execute ("UPDATE guild SET guildID = ?, ownerID = ?, voiceChannelID = ?, voiceCategoryID = ? WHERE guildID = ?",(guildID,id,channel.id,new_cat.id, guildID)) - await ctx.channel.send("**You are all setup and ready to go!**") - except: - await ctx.channel.send(f"You didn't enter the names properly.\nUse `{ctx.prefix}voice setup` again!") - conn.commit() - conn.close() - - @voice.command(extras={"category":"Voice"}, usage="voice setlimit", help="This command sets the limit for the custom vc.", description="Sets limit for custom vc") - @commands.check(plugin_enabled) - @commands.has_permissions(administrator = True) - async def setlimit(self, ctx, num): - conn = sqlite3.connect('./database/voice.db') - c = conn.cursor() - c.execute("SELECT * FROM guildSettings WHERE guildID = ?", (ctx.guild.id,)) - voice=c.fetchone() - if voice is None: - c.execute("INSERT INTO guildSettings VALUES (?, ?, ?)", (ctx.guild.id,f"{ctx.author.name}'s channel",num)) - else: - c.execute("UPDATE guildSettings SET channelLimit = ? WHERE guildID = ?", (num, ctx.guild.id)) - await ctx.send("You have changed the default channel limit for your server!") - conn.commit() - conn.close() - - @setup.error - async def info_error(self, ctx, error): - print(error) - -def setup(bot): - bot.add_cog(Voice(bot)) \ No newline at end of file diff --git a/database/new.py b/database/new.py deleted file mode 100644 index 1d131e9..0000000 --- a/database/new.py +++ /dev/null @@ -1,7 +0,0 @@ -import json - -with open("db.json") as f: - data = json.load(f) - -with open('db.json', 'w') as f: - json.dump(data, f, indent=4) \ No newline at end of file diff --git a/database/shopimages/car.png b/database/shopimages/car.png deleted file mode 100644 index a702298..0000000 Binary files a/database/shopimages/car.png and /dev/null differ diff --git a/database/shopimages/goose.png b/database/shopimages/goose.png deleted file mode 100644 index 1a56716..0000000 Binary files a/database/shopimages/goose.png and /dev/null differ diff --git a/database/shopimages/laptop.png b/database/shopimages/laptop.png deleted file mode 100644 index a2177f8..0000000 Binary files a/database/shopimages/laptop.png and /dev/null differ diff --git a/database/shopimages/pc.png b/database/shopimages/pc.png deleted file mode 100644 index 7e83636..0000000 Binary files a/database/shopimages/pc.png and /dev/null differ diff --git a/database/shopimages/sticker.png b/database/shopimages/sticker.png deleted file mode 100644 index 6d85939..0000000 Binary files a/database/shopimages/sticker.png and /dev/null differ diff --git a/docs/SELFHOSTING.md b/docs/SELFHOSTING.md new file mode 100644 index 0000000..99f1089 --- /dev/null +++ b/docs/SELFHOSTING.md @@ -0,0 +1 @@ +# todo \ No newline at end of file diff --git a/embedsite/embed_gen.py b/embedsite/embed_gen.py deleted file mode 100644 index 2cee5da..0000000 --- a/embedsite/embed_gen.py +++ /dev/null @@ -1,20 +0,0 @@ -from flask import Flask, redirect, request, jsonify, Response,render_template -from threading import Thread -app = Flask("Embed Generator") - -@app.route("/") -def home(): - return "Im alive" - -@app.route("/embed") -def embeds(): - return render_template("index.html") - -def run(): - #app.run(host='0.0.0.0', port=8080) - #app.run(host="150.107.175.160", port=8080) - app.run(host="192.168.68.109", port=9000) - -def keep_alive(): - t = Thread(target=run) - t.start() diff --git a/embedsite/static/index.js b/embedsite/static/index.js deleted file mode 100644 index f560fc5..0000000 --- a/embedsite/static/index.js +++ /dev/null @@ -1,430 +0,0 @@ -'use strict'; - -$(document).ready(function () { - var converter = new showdown.Converter(); - - var switches = { - title: false, - url: false, - icon: false, - useVars: false - }; - - var fields = 1; - - var source = ''; - - var embed = { - title: '', - author: { - name: '', - url: '', - icon: '' - }, - description: '', - url: '', - thumb_url: '', - color: '', - fields: [{}], - footer: '' - }; - - function resetEmbed() { - $('.embed-inner').html(''); - $('.embed-footer').remove(); - $('.embed-thumb').remove(); - } - - function updateEmbed(embed) { - resetEmbed(); - - // add basic embed generation to source - source = 'embed=discord.Embed('; - - if (embed.url) { - $('.embed-inner').append(''); - - // update source - if (switches.useVars) { - source += 'title=' + embed.title + ', url=' + embed.url; - } else { - source += 'title="' + embed.title + '", url="' + embed.url + '"'; - } - } else if (embed.title.length === 0) { - source += ""; - } else { - $('.embed-inner').append('
' + embed.title + '
'); - - // update source - if (switches.useVars) { - source += 'title=' + embed.title; - } else { - source += 'title="' + embed.title + '"'; - } - - } - - if (embed.description) { - $('.embed-inner').append('
' + converter.makeHtml(embed.description) + '
'); - - if (embed.title.length > 0 || embed.url.length > 0) { - source += ', ' - } - - // update source - if (switches.useVars) { - source += 'description=' + embed.description; - } else { - source += 'description="' + embed.description + '"'; - } - } - - if (embed.color) { - $('.side-colored').css('background-color', embed.color); - - if (embed.title.length > 0 || embed.url.length > 0) { - source += ', ' - } - - // update source - source += 'color=0x' + embed.color.substr(1); - } - - // finished the basic setup - source += ')\n'; - - if (embed.author.name) { - // add author to source - source += 'embed.set_author('; - - $('.embed-title').before(''); - - // update source - if (switches.useVars) { - source += 'name=' + embed.author.name; - } else { - source += 'name="' + embed.author.name + '"'; - } - - if(embed.author.url) { - source += ', '; - - if (switches.useVars) { - source += 'url=' + embed.author.url; - } else { - source += 'url="' + embed.author.url + '"'; - } - } - - if (embed.author.icon) { - $('.embed-author-name').before(''); - - source += ', '; - - // update source - if (switches.useVars) { - source += 'icon_url=' + embed.author.icon; - } else { - source += 'icon_url="' + embed.author.icon + '"'; - } - } - - // finish author - source += ')\n'; - } - - if (embed.thumb_url) { - // add thumbnail - source += 'embed.set_thumbnail('; - - $('.card.embed .card-block').append(''); - $('.embed-thumb').height($('.embed-thumb')[0].naturalHeight); - - // update source - if (switches.useVars) { - source += 'url=' + embed.thumb_url; - } else { - source += 'url="' + embed.thumb_url + '"'; - } - - // finish thumbnail - source += ')\n'; - } - - if (embed.fields.length > 0) { - $('.embed-inner').append('
'); - } - - for (var _iterator = embed.fields, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - var field = _ref; - - $('.embed-inner .fields').append('\n
\n
' + field.name + '
\n
' + converter.makeHtml(field.value) + '
\n
\n '); - - // add field - if (switches.useVars) { - source += 'embed.add_field(name=' + field.name + ', value=' + field.value + ', inline=' + (field.inline && 'True' || 'False') + ')\n'; - } else { - source += 'embed.add_field(name="' + field.name + '", value="' + field.value + '", inline=' + (field.inline && 'True' || 'False') + ')\n'; - } - } - - if (embed.footer) { - $('.card.embed').append(''); - - // add footer - if (switches.useVars) { - source += 'embed.set_footer(text=' + embed.footer + ')\n'; - } else { - source += 'embed.set_footer(text="' + embed.footer + '")\n'; - } - } - - // add send function - source += 'await ctx.send(embed=embed)\n'; - - // code - $('.source').text(source); - hljs.highlightBlock($('.source')[0]); - } - - // run once on startup - updateEmbed(embed); - - function generateInputFields(fields) { - // generate inputs for fields - $('.input-fields').html(''); - - var _loop = function _loop(i) { - $('.input-fields').append('
\n
\n \n
\n
\n \n
\n
\n
\n \n
\n
\n
\n \n
\n
'); - $('#field-' + i + '-name').keyup(function () { - updateFieldName(i, $('#field-' + i + '-name').val()); - }); - - $('#field-' + i + '-value').keyup(function () { - updateFieldValue(i, $('#field-' + i + '-value').val()); - }); - - $('#field-' + i + '-inline').click(function () { - updateFieldInline(i, $('#field-' + i + '-inline').is(':checked')); - }); - - $('#field-' + i + '-delete').click(function (e) { - e.preventDefault(); - deleteField(i); - }); - }; - - for (var i = 0; i < fields; i++) { - _loop(i); - } - $('.input-fields').append(''); - $('#add-field').click(function (e) { - e.preventDefault(); - addField(); - }); - } - - generateInputFields(fields); - - function updateFieldName(index, value) { - embed.fields[index].name = value; - updateEmbed(embed); - } - - function updateFieldValue(index, value) { - embed.fields[index].value = value; - updateEmbed(embed); - } - - function updateFieldInline(index, value) { - embed.fields[index].inline = value; - updateEmbed(embed); - } - - function deleteField(index) { - embed.fields.splice(index, 1); - updateEmbed(embed); - fields -= 1; - generateInputFields(fields); - } - - function addField() { - embed.fields.push({ inline: true }); - fields += 1; - generateInputFields(fields); - } - - function updateTitle(value) { - embed.title = value || ''; - updateEmbed(embed); - } - - function updateUrl(value) { - embed.url = value || ''; - updateEmbed(embed); - } - - function updateThumb(value) { - embed.thumb_url = value || false; - updateEmbed(embed); - } - - function updateDescription(value) { - embed.description = value || ''; - updateEmbed(embed); - } - - function updateColor(value) { - embed.color = value || false; - updateEmbed(embed); - } - - function updateAuthorName(value) { - embed.author.name = value || ''; - updateEmbed(embed); - } - - function updateAuthorUrl(value) { - embed.author.url = value || ''; - updateEmbed(embed); - } - - function updateAuthorIcon(value) { - embed.author.icon = value || ''; - updateEmbed(embed); - } - - function updateFooter(value) { - embed.footer = value || ''; - updateEmbed(embed); - } - - $('#form').submit(function (e) { - e.preventDefault(); - }); - - // checking helpers - function addWarning(item, type, message) { - item.addClass('form-control-warning'); - item.removeClass('form-control-success'); - item.parent().addClass('has-warning'); - item.parent().removeClass('has-success'); - if ($('#' + type + '-feedback').length === 0) { - item.after(''); - } - } - - function addSuccess(item, type) { - item.removeClass('form-control-warning'); - item.addClass('form-control-success'); - item.parent().addClass('has-success'); - item.parent().removeClass('has-warning'); - $('#' + type + '-feedback').remove(); - } - - $('#title').keyup(function () { - var item = $('#title'); - var title = item.val(); - - // update - updateTitle(title); - }); - - $('#url').keyup(function () { - var item = $('#url'); - var url = item.val(); - - if (url.substr(0, 4) !== 'http' && url.length !== 0 && !switches.useVars) { - addWarning(item, 'url', 'not a valid url'); - } else { - addSuccess(item, 'url'); - // update - updateUrl(url); - } - }); - - $('#icon').keyup(function () { - var item = $('#icon'); - var icon = item.val(); - - if (icon.substr(0, 4) !== 'http' && icon.length !== 0 && !switches.useVars) { - addWarning(item, 'icon', 'not a valid url'); - } else { - addSuccess(item, 'icon'); - // update - updateThumb(icon); - } - }); - - $('#description').keyup(function () { - var item = $('#description'); - var description = item.val(); - addSuccess(item, 'description'); - // update - updateDescription(description); - }); - - $('#color').change(function () { - updateColor($('#color').val()); - }); - - $('#author_name').keyup(function () { - var item = $('#author_name'); - var author_name = item.val(); - - addSuccess(item, 'author_name'); - // update - updateAuthorName(author_name); - }); - - $('#author_url').keyup(function () { - var item = $('#author_url'); - var author_url = item.val(); - - if (author_url.substr(0, 4) !== 'http' && author_url.length !== 0 && !switches.useVars) { - addWarning(item, 'author_url', 'not a valid url'); - } else { - addSuccess(item, 'author_url'); - // update - updateAuthorUrl(author_url); - } - }); - - $('#author_icon').keyup(function () { - var item = $('#author_icon'); - var author_icon = item.val(); - - if (author_icon.substr(0, 4) !== 'http' && author_icon.length !== 0 && !switches.useVars) { - addWarning(item, 'author_icon', 'not a valid url'); - } else { - addSuccess(item, 'author_icon'); - // update - updateAuthorIcon(author_icon); - } - }); - - $('#footer').keyup(function () { - var item = $('#footer'); - var footer = item.val(); - - addSuccess(item, 'footer'); - // update - updateFooter(footer); - }); - - $('#useVars').click(function () { - switches.useVars = !switches.useVars; - updateEmbed(embed); - }); -}); diff --git a/embedsite/static/style.css b/embedsite/static/style.css deleted file mode 100644 index c919b62..0000000 --- a/embedsite/static/style.css +++ /dev/null @@ -1,181 +0,0 @@ -a.patreon-link { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - box-sizing: border-box; - width: 100%; - height: auto; - z-index: 10; - text-decoration: none; -} - -.badge-container { - width: 100%; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - padding: 5px 10px; - background: rgba(0, 0, 0, 0.5); - border: 1px solid rgba(255, 255, 255, 0.5); - text-align: center; -} -.badge-container .badge-icon { - background: url("https://s3.amazonaws.com/patreon_public_assets/toolbox/patreon_white.png") center center no-repeat; - background-size: contain; - width: 100%; - height: 20px; -} -.badge-container .badge-text { - font-family: "Lato", sans-serif; - font-size: 12px; - padding: 2px; - color: #fff; -} -.badge-container .badge-text.badge-text-bottom { - position: relative; - top: -2px; -} - -.preview { - background: #36393e; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - box-orient: vertical; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; -} - -.wrapper { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - max-width: 520px; -} - -.container { - margin-top: 50px; -} - -.side-colored { - width: 4px; - border-radius: 3px 0 0 3px; - background: #4f545c; -} - -.embed { - border-radius: 0 3px 3px 0; - background: rgba(46, 48, 54, 0.3); - border-color: rgba(46, 48, 54, 0.6); - display: -webkit-box; - display: -ms-flexbox; - display: flex; - padding: 8px 10px; - color: rgba(255, 255, 255, 0.6); - font-size: 14px; -} -.embed .card-block { - padding: 0; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - margin-bottom: 10px; -} -.embed a { - color: #0096cf; -} -.embed img.embed-thumb { - max-height: 80px; - max-width: 80px; - border-radius: 3px; - -ms-flex-negative: 0; - flex-shrink: 0; - width: auto; - -o-object-fit: contain; - object-fit: contain; - margin-left: 20px; -} -.embed .embed-footer { - font-size: 12px; -} -.embed .embed-footer span { - color: rgba(255, 255, 255, 0.6); -} -.embed .embed-inner .embed-title { - color: #fff; -} -.embed .embed-inner .embed-author { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - margin-bottom: 5px; -} -.embed .embed-inner .embed-author img.embed-author-icon { - margin-right: 9px; - width: 20px; - height: 20px; - -o-object-fit: contain; - object-fit: contain; - border-radius: 50%; -} -.embed .embed-inner .embed-author .embed-author-name { - display: inline-block; - font-weight: 600; - font-size: 14px; - color: #fff !important; -} -.embed .embed-inner .fields { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - box-lines: miltiple; - margin-top: -10px; -} -.embed .embed-inner .fields .field { - -webkit-box-flex: 0; - -ms-flex: 0; - flex: 0; - box-flex: 1; - padding-top: 10px; - max-width: 506px; - min-width: 100%; -} -.embed .embed-inner .fields .field.inline { - box-flex: 1; - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; - min-width: 150px; - -ms-flex-preferred-size: auto; - flex-basis: auto; -} -.embed .embed-inner .fields .field .field-name { - color: #fff; - font-size: 14px; - margin-bottom: 4px; - font-weight: 600; -} -.embed .embed-inner .fields .field .field-value { - color: rgba(255, 255, 255, 0.7); - font-size: 14px; - font-weight: 500; - line-height: 1.1em; - white-space: pre-wrap; - margin-top: 6px; - word-wrap: break-word; -} diff --git a/embedsite/templates/index.html b/embedsite/templates/index.html deleted file mode 100644 index b759d37..0000000 --- a/embedsite/templates/index.html +++ /dev/null @@ -1,105 +0,0 @@ - - - - - Discord Embed Generator - - - - - - - - - - - - -
-
-
-
-
Embed Editor
-
-
-
- -
-
- - -
-
- -
-
- -
-
- - -
-
- - -
-
- - -
-
- -
-
- -
-
- -
-
-
- - -
-
-
-
-
-

-
-
-
Embed Preview
-
-
-
-
-
-
-
-
-
-
-
-

-
-
-
Python code
-
-
-
-
-
-
- - - - - - - - - - diff --git a/main.py b/main.py deleted file mode 100644 index 34cc90a..0000000 --- a/main.py +++ /dev/null @@ -1,384 +0,0 @@ -""" -Our program, who art in memory, - called by thy name; - thy operating system run; -thy function be done at runtime - as it was on development. -Give us this day our daily output. -And forgive us our code duplication, - as we forgive those who - duplicate code against us. -And lead us not into frustration; - but deliver us from GOTOs. - For thine is algorithm, -the computation, and the solution, - looping forever and ever. - Return; -""" - -# If you are reading this code - I'm sorry - -import os -import json -import datetime -import time -import discord -from discord.ext import commands, tasks -import dotenv -import pyfiglet -from utils import Log, update_activity -import sidspackage - -dotenv.load_dotenv() - -log = Log("./database/log.txt", timestamp=True) - -async def get_prefix(client, message): - """ - This function gets the command_prefix for the server - - Args: - client (discord.ext.commands.Bot) : The bot - message (discord.Message) : The discord message sent - - Returns: - str : The command prefix for the guild - """ - try: - data = await client.get_db() - return data[str(message.guild.id)]['prefix'] - - except Exception: # If any error occurs just return the default prefix: ? - return "?" - - -intents = discord.Intents.all() -allowed_mentions = discord.AllowedMentions(everyone=False) # Disables the bot from being able to @everyone - - -class WhyBot(commands.Bot): - """ - Why Bot: A subclass of `discord.ext.commands.Bot` - """ - def __init__(self): - super().__init__(command_prefix=get_prefix, intents=intents, help_command=None, owner_id=624076054969188363, case_insensitive=True,allowed_mentions=allowed_mentions) - - self.cp = sidspackage.ColorPrint() - - self.cogs_list = None # List of cogs - self.last_login_time = datetime.datetime.now() # Uptime - - self.why_ascii_art = pyfiglet.figlet_format("Why Bot") - self.cp.print(self.why_ascii_art, color="blue") - - - async def get_db(self): - """ - This function returns the main database file. - Yes i'm to lazy to make an actualy db so im using json - - Returns: - Dict : The json file - """ - with open("database/db.json") as f: - data = json.load(f) - return data - - - async def update_db(self, data): - """ - This function updates the main database file. - - Args: - data (Dict) : This is the json data that will be dumped into the file - """ - with open("database/db.json", 'w') as f: - json.dump(data, f, indent=4) - - - async def get_user_db(self): - """ - This function returns the user database file. - - Returns: - Dict : The json file - """ - with open("database/userdb.json") as f: - data = json.load(f) - return data - - - async def update_user_db(self, data): - """ - This function updates the user database file. - - Args: - data (Dict) : This is the json data that will be dumped into the file - """ - with open("database/userdb.json", 'w') as f: - json.dump(data, f, indent=4) - - - @property - async def uptime(self): - """ - This function returns the uptime for the bot. - - Returns: - str : Formated string with the uptime - """ - time_right_now = datetime.datetime.now() - seconds = int((time_right_now - self.last_login_time).total_seconds()) - time = f"{seconds}s" - if seconds > 60: - minutes = seconds - (seconds % 60) - seconds = seconds - minutes - minutes = int(minutes / 60) - time = f"{minutes}min {seconds}s" - if minutes > 60: - hoursglad = minutes - (minutes % 60) - hours = int(hoursglad / 60) - minutes = minutes - (hours*60) - time = f"{hours}h {minutes}min {seconds}s" - return time - - - @property - def get_why_emojies(self): - """ - This function returns the emojis for the bot - - Returns: - Dict : A dictionary of emojis - """ - return { - "why" : "<:why:932912321544728576>" - } - - - @property - def blacklisted_users(self): - """ - This function returns all the blacklisted users - - Returns: - List : List of blacklisted users - """ - with open("database/blacklisted.json") as f: - data = json.load(f) - return data - - - async def blacklist_user(self, user_id : int): - """ - This function is used to blacklist a user so they cant use why bot anymore - - Args: - user_id (int) : The id for the user. This will be appended to the List of blacklisted users - """ - with open("database/blacklisted.json") as f: - data = json.load(f) - - if user_id not in data: - data.append(user_id) - - with open('database/blacklisted.json', 'w') as f: - json.dump(data, f, indent=4) - - - async def whitelist_user(self, user_id : int): - """ - This function is used to whitelist a user so they can use why bot - - Args: - user_id (int) : The id for the user. This will be appended to the List of blacklisted users - """ - with open('database/blacklisted.json') as f: - data = json.load(f) - - if user_id in data: - data.remove(user_id) - - with open('database/blacklisted.json', 'w') as f: - json.dump(data, f, indent=4) - - - async def is_user_blacklisted(self, user_id : int): - """ - This function is used to check if a user is blacklisted or not - - Args: - user_id (int) : The id of the user you want to check - - Returns: - Boolean : If user is blacklisted it will return True - else it returns False - """ - with open('database/blacklisted.json') as f: - data = json.load(f) - - if user_id in data: - return True - - return False - - -client = WhyBot() - - -# On ready -@client.event -async def on_ready(): - """Called when the bot is ready""" - await update_activity(client) - channel = client.get_channel(925513395883606129) - art = pyfiglet.figlet_format("CONNECTED") - client.cp.print(art, "green") - await channel.send("Online") - log.log_message("Bot is online") - - -async def update_user_db(user_id): - """ - This function updates the command count for a user and - if they dont exist in the database it adds the user to it - - Args: - user_id (int) : The id of the user - - """ - data = await client.get_user_db() - - try: - data[str(user_id)]['command_count'] += 1 - - except KeyError: # If user is not in the database, Then add them to the database - user_data = { - "user_id": user_id, - "command_count": 1, - "settings": {}, - "on_pinged": {"title": None, "description": None, "color": None}, - "on_pinged_toggled" : True - } - data[str(user_id)] = user_data - - await client.update_user_db(data) - - -@tasks.loop(hours=2.0) -async def clear_stuff(): - """ - This is a loop that runs every 2h - It clears out all the files in the temporary storage directory - """ - _dir = 'tempstorage/' - for f in os.listdir(_dir): - os.remove(os.path.join(_dir, f)) - - -# On Message -@client.event -async def on_message(message): - """ - The on message event, Run when theres a message - This function checks if the user is blacklisted or if the client.user is mentioned - in the message content - - Args: - message (discord.Message) : The message - - """ - if message.author == client.user: - return # if bot - no - - # if blacklisted dont let them use bot - try: - user_blacklisted = await client.is_user_blacklisted(message.author.id) - - if user_blacklisted: - return - - prefix = await get_prefix(client, message) - - if prefix in message.content: - await update_user_db(message.author.id) - - await client.process_commands(message) - - except Exception: - await client.process_commands(message) - - # Check if why is mentioned in message - if client.user.mentioned_in(message) and message.mention_everyone == False: - if message.reference is not None: - msg = await message.channel.fetch_message(message.reference.message_id) - if msg.author.id == client.user.id: - return - em = discord.Embed( - title=f"Hi, my prefix is `{prefix}`", - color=message.author.color - ) - return await message.channel.send(embed=em) - - -def print_percent_done(index, total, bar_len=50, title='Loading Cogs:'): - """This function makes a cool loading bar to visualize the bot loading cogs""" - percent_done = (index+1)/total*100 - percent_done = round(percent_done, 1) - - done = round(percent_done/(100/bar_len)) - togo = bar_len-done - - done_str = 'ā–ˆ'*int(done) - togo_str = 'ā–‘'*int(togo) - - print(f'{title} [{done_str}{togo_str}] {percent_done}% done', end='\r') - - if round(percent_done) == 100: - print('Loaded All: āœ… ') - - -def start_bot(client): - """ - This function starts the discord bot - It loads all the cogs and starts the tasks - - Args: - client (discord.ext.commands.Bot) : This is the client - - """ - log.log_message("Starting up bot") - - cogs = [] - - all_categories = list(os.listdir("cogs")) - for category in all_categories: - for filename in os.listdir(f"cogs/{category}"): - if filename.endswith(".py"): - cogs.append(f"cogs.{category}.{filename[:-3]}") - else: - continue - - try: - # Loading all cogs with a progress Bar - i = 0 - for cog in cogs: - client.cogs_list = cogs - client.load_extension(cog) - print_percent_done(i, len(cogs)) - i+=1 - - time.sleep(1) - print("\n") - - clear_stuff.start() - - client.run(os.environ['TOKEN']) - - except Exception as e: - print(f"\n###################\nPOSSIBLE FATAL ERROR:\n{e}\nTHIS MEANS THE BOT HAS NOT STARTED CORRECTLY!") - log.log_error(f"\n###################\nPOSSIBLE FATAL ERROR:\n{e}\nTHIS MEANS THE BOT HAS NOT STARTED CORRECTLY!") - - -if __name__ == '__main__': - start_bot(client) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 7d70277..0000000 --- a/requirements.txt +++ /dev/null @@ -1,23 +0,0 @@ -Flask == 1.1.2 -aiofiles == 0.6.0 -aiohttp == 3.7.4.post0 -asyncpraw == 7.5.0 -discordLevelingSystem == 1.0.2 -easy_pil == 0.1.0 -gTTS == 2.2.2 -google_api_python_client == 2.29.0 -mutagen == 1.45.1 -numexpr == 2.8.1 -psutil == 5.9.0 -py_cord == 2.0.0b1 -pyfiglet == 0.8.post1 -python_dotenv == 0.15.0 -pyttsx3 == 2.90 -qrcode == 7.3.1 -requests == 2.25.1 -sidspackage == 0.0.4 -simpcalci == 0.0.2 -wget == 3.2 -wikipedia == 1.4.0 -youtube_dl == 2021.6.6 -youtube_search_python == 1.4.9 diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..8a12479 --- /dev/null +++ b/src/README.md @@ -0,0 +1,69 @@ +# Folder Structure: + +```ansi +. +ā”œā”€ā”€ README.md +ā”œā”€ā”€ cogs/ +ā”œā”€ā”€ config.yaml +ā”œā”€ā”€ core +│ ā”œā”€ā”€ db/ +│ ā”œā”€ā”€ helpers/ +│ ā”œā”€ā”€ models/ +│ └── utils/ +ā”œā”€ā”€ logfiles/ +ā”œā”€ā”€ main.py +ā”œā”€ā”€ requirements.txt +ā”œā”€ā”€ scripts/ +└── setup.py +``` +--- + +### cogs/ +This folder contains the cogs for the bot. It is full of subfolder for further orginization of the cogs. + +--- + +### config.yaml +This is created after running the setup script and it contains the config for the bot. + +--- + +### core/ + +This folder contains the rest of the bots code. All code thats not in one of the cog folder will be in here. Its full of useful classes, database functions/setup functions, helpers and utils + +#### db/ +This folder contains database related functions. Mostly for setting up tables or rows in the db. + +#### helpers/ +This folder is full of helper code for the bot. Things like checks, exeptions, logging, http and much more + +#### models/ +This folder is for the classes that help coding easier. + +#### utils/ +This folder has utilities to speedup development. Things like calculators and formatters are found here. + +--- + +### logfiles/ + +This folder wont be there until you run the bot. After running there will be 2 log files in this folder. `main.log` and `discord.log`. The discord log file is created by pycord while running and is reset everytime the bot is run. `main.log` however is created by the program and it contains error logs and debug messages which are caused at runtime. + +--- + +### main.py + +This file is a script and is used to start the bot. It handles loading all the cogs and connecting to the discord API. + +--- + +### scripts/ + +This folder contains scripts. + +--- + +### setup.py + +This file is used to setup the bot and the config files. Without running this the bot will NOT work as it is not set up correctly \ No newline at end of file diff --git a/src/cogs/audio/music.py b/src/cogs/audio/music.py new file mode 100644 index 0000000..1391832 --- /dev/null +++ b/src/cogs/audio/music.py @@ -0,0 +1,71 @@ +import discord +from discord.ext import commands +import pycord.wavelink as wavelink +from discord import ApplicationContext + +from core import BaseCog, WhyBot + + +class Music(BaseCog): + def __init__(self, client: WhyBot): + self.pool = wavelink.NodePool() + + client.loop.create_task(self.connect_nodes()) + super().__init__(client) + + async def connect_nodes(self): + """Connect to our Lavalink nodes.""" + await self.client.wait_until_ready() + + nodes = [ + { + "host": "168.138.102.186", + "port": 2333, + "password": "shitcodengl", + "https": False, + "region": discord.VoiceRegion.sydney, + "identifier": "MAIN", + }, + {"host": "lava.link", "port": 80, "password": "dismusic", "https": False}, + ] + + for node in nodes: + await self.pool.create_node(bot=self.client, **node) + + @commands.Cog.listener() + async def on_wavelink_node_ready(self, node: wavelink.Node): + self.client.console.print( + f"\n[bold green]Music Node ({node.identifier}) is ready!" + ) + + @commands.Cog.listener() + async def on_voice_state_update(self, member, before, after): + if not member.bot and after.channel is None: + pass + # members = [i for i in before.channel.members if not i.bot] + + @commands.slash_command() + async def play(self, ctx: ApplicationContext, search: str): + """Play a song with the given search query. + If not connected, connect to our voice channel. + """ + if not ctx.voice_client: + vc: wavelink.Player = await ctx.author.voice.channel.connect( + cls=wavelink.Player + ) + else: + vc: wavelink.Player = ctx.voice_client + print(vc.node.identifier) + search = await wavelink.YouTubeTrack.search(query=search, return_first=True) + print(search.author) + await vc.play(search) + + @commands.slash_command() + async def join( + self, ctx: discord.ApplicationContext, channel: discord.VoiceChannel = None + ): + pass + + +def setup(client): + client.add_cog(Music(client)) diff --git a/src/cogs/events/alert.py b/src/cogs/events/alert.py new file mode 100644 index 0000000..7b2db67 --- /dev/null +++ b/src/cogs/events/alert.py @@ -0,0 +1,135 @@ +import time + +import discord +from discord.ext import commands + +from core import BaseCog +from core.helpers import InputModalView, GUILD_IDS +from core.utils import discord_timestamp + + +class Alerts(BaseCog): + async def __setup_settings(self, member_id: int): + return await self.client.db.execute( + "INSERT INTO alerts_users (user_id) VALUES ($1)", member_id + ) + + @commands.Cog.listener() + async def on_application_command_completion(self, ctx: discord.ApplicationContext): + data = await self.client.db.fetch( + "SELECT * FROM alerts_users WHERE user_id=$1", ctx.author.id + ) + if not data: + await self.__setup_settings(ctx.author.id) + data = [[ctx.author.id, False, False]] + data = data[0] + + # data[1] if if they have already seen the alert + # data[2] is if they have alert notifications toggled on + if data[1] or data[2]: + return + + em = discord.Embed( + title="New Alert", + description="You have a new alert from the devs\nUse to check it out\n\ + Use to not show these types of messages", + color=discord.Color.random(), + ) + + try: # try to send the message + await ctx.followup.send(embed=em, ephemeral=True) + except discord.HTTPException: # frik it failed, probably perms or smth like that + return + + @commands.slash_command(description="Shows the latest update from the why bot devs") + async def alert(self, ctx: discord.ApplicationContext): + data = await self.client.db.fetch("SELECT * FROM alerts ORDER BY id") + data = data[::-1][0] + + description = data[2] + date = discord_timestamp(data[3], format_type="ts") + description += f"\n\nThis alert was made {date}" + + em = discord.Embed( + title=data[1], description=description, color=discord.Color.random() + ) + + em.set_footer(text=f"This alert was viewed {data[4]} times") + + await ctx.respond(embed=em) + + await self.client.db.execute( + "UPDATE alerts_users SET alert_viewed=true WHERE user_id=$1", ctx.author.id + ) + await self.client.db.execute( + "UPDATE alerts SET viewed=$1 WHERE id=$2", (data[4] + 1), data[0] + ) + + @commands.slash_command( + description="Disables the new alert message that shows when theres a new update" + ) + async def togglealerts(self, ctx: discord.ApplicationContext): + data = await self.client.db.fetch( + "SELECT * FROM alerts_users WHERE user_id=$1", ctx.author.id + ) + if not data: + await self.__setup_settings(ctx.author.id) + data = [[ctx.author.id, False, False]] + data = data[0] + + on_or_off = not data[2] + + await self.client.db.execute( + "UPDATE alerts_users SET ignore_alerts=$1 WHERE user_id=$2", + on_or_off, + ctx.guild.id, + ) + + await ctx.respond( + embed=discord.Embed( + title="Alerts Toggled!", + description=( + f"Alert notifications is now {'on āœ…' if on_or_off else 'off āŒ'}\nIf you" + f" wish to toggle it back {'off' if on_or_off else 'on'} run this" + " command again" + ), + color=discord.Color.green() if on_or_off else discord.Color.red(), + ) + ) + + @commands.slash_command(description="Creates a new alert", guild_ids=GUILD_IDS) + @commands.is_owner() + async def newalert(self, ctx, name): + input = InputModalView( + title="Alert Value", label="Enter the value of the alert:" + ) + await ctx.send_modal(input) + await input.wait() + + if input.value is None: + return await ctx.respond( + "Not creating alert as input was either None or invalid", ephemeral=True + ) + + # create tag + await self.client.db.execute( + """INSERT INTO alerts ( + alert_title, alert_message, time_created + ) VALUES ($1, $2, $3)""", + name, + input.value, + int(time.time()), + ) + + await self.client.db.execute("UPDATE alerts_users SET alert_viewed=false") + + await ctx.respond( + "Alert Created! It will look like this:", + embed=discord.Embed( + title=name, description=input.value, color=discord.Color.random() + ), + ) + + +def setup(client): + client.add_cog(Alerts(client)) diff --git a/src/cogs/events/on_error.py b/src/cogs/events/on_error.py new file mode 100644 index 0000000..3d8a18b --- /dev/null +++ b/src/cogs/events/on_error.py @@ -0,0 +1,227 @@ +import discord +from discord.ext import commands + +from core import WhyBot +from core.utils import format_seconds +from core.helpers import log_errors, ImageAPIFail + + +class OnError(commands.Cog): + def __init__(self, client: WhyBot): + self.client = client + + @commands.Cog.listener() + async def on_application_command_error( + self, ctx: discord.ApplicationContext, error + ): + + IGNORE = (commands.CommandNotFound, commands.CommandInvokeError) + + if isinstance(error, commands.CommandOnCooldown): + + retry_after = await format_seconds(int(error.retry_after)) + em = discord.Embed( + title="Wow buddy, Slow it down\nThis command is on cooldown", + description=( + "Try again" + f" {f'in **{retry_after}' if retry_after != '' else 'now'}**" + ), + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.MissingRequiredArgument): + em = discord.Embed( + title="Missing a requred value / argument", + description=( + "You haven't passed in all the required values for this command" + ), + color=discord.Color.red(), + ) + em.add_field(name="You have not passed in:", value=f"`{error.param}`") + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.MissingPermissions): + em = discord.Embed( + title="Missing permissions", + description="You don't have permissions to use this commands ", + color=discord.Color.red(), + ) + em.add_field( + name="Permissions you need:", + value=f"`{', '.join(error.missing_permissions)}`", + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.MessageNotFound): + em = discord.Embed( + title="Message not found", + description="The bot failed to find the message ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.MemberNotFound): + em = discord.Embed( + title="Member not found", + description="The bot failed to find the member ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.GuildNotFound): + em = discord.Embed( + title="Guild not found", + description="The bot faield to find the guild ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.UserNotFound): + em = discord.Embed( + title="User not found", + description="The bot failed to find the user ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.ChannelNotFound): + em = discord.Embed( + title="Channel not found", + description="The bot failed to find the channel ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.ChannelNotReadable): + em = discord.Embed( + title="Channel not readable", + description="The bot is unable to read this channel ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.RoleNotFound): + em = discord.Embed( + title="Role not found", + description="The bot was unable to find the role ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.ThreadNotFound): + em = discord.Embed( + title="Thread not found", + description="The bot was unable to fund the thread ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.BotMissingPermissions): + em = discord.Embed( + title="Bot missing permissions", + description=( + "Why bot does not have the permissions do execute this command." + " Please gimme them perms " + ), + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.MissingRole): + em = discord.Embed( + title="Missing Role", + description="User does not have the role to run this command ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.NotOwner): + em = discord.Embed( + title="Not Owner", + description=( + "You must be owner of Why Bot to be able to run this command" + ), + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.BotMissingRole): + em = discord.Embed( + title="Bot Missing Role", + description=( + "Why bot does not have the role to run this command, Gimme them" + " roles " + ), + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.NSFWChannelRequired): + em = discord.Embed( + title="NSFW Only", + description="This command can only be used in an nsfw channel ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.DisabledCommand): + em = discord.Embed( + title="Command Disabled", + description="This command has been disabled ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, commands.NoPrivateMessage): + em = discord.Embed( + title="Command not allowed in private message", + description=( + "This command has been disabled in private messages/dms and can be" + " only used in a server/guild" + ), + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, discord.HTTPException): + em = discord.Embed( + title="Error 404 Not Found", + description=f"{error.code} {error.text} ", + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + + elif isinstance(error, discord.CheckFailure): + em = discord.Embed( + title="You cannot use this command!", + description=( + "Reasons why it may not be working for you:\n- You are" + " blacklisted\n- The plugin where the command belongs to is" + " disabled" + ), + color=discord.Color.red(), + ) + await ctx.respond(embed=em, ephemeral=True) + elif isinstance(error, discord.ApplicationCommandInvokeError): + if isinstance(error.original, ImageAPIFail): + em = discord.Embed( + title="An error occured while trying to get the image", + description=( + "API basically had a skill issue.\nIf this persists and you are able to, " + "please report this as a bug with :)" + ), + color=discord.Colour.red(), + ) + return await ctx.respond(embed=em, ephemeral=True) + log_errors( + type(error.original), error.original, error.original.__traceback__ + ) + elif isinstance(error, IGNORE): + return + else: + log_errors(type(error), error, error.__traceback__) + + +def setup(client: WhyBot): + client.add_cog(OnError(client)) diff --git a/src/cogs/events/on_event.py b/src/cogs/events/on_event.py new file mode 100644 index 0000000..3817300 --- /dev/null +++ b/src/cogs/events/on_event.py @@ -0,0 +1,96 @@ +import datetime + +import discord +from discord.ext import commands + +from core.models import NewTicketView, WhyBot +from core.helpers import ( + InvalidDatabaseUrl, + log_normal, + update_activity, + create_connection_pool, + create_redis_connection, +) + + +class OnEvent(commands.Cog): + def __init__(self, client: WhyBot): + self.client = client + + @commands.Cog.listener() + async def on_message(self, message: discord.Message): + """ + This is the event that is called when a message is sent in a channel + It will check if the bot has been mentioned in the message and if so + it will reply with a message containing the guild prefix + """ + + if message.author.bot: + return + + if ( + self.client.user.mentioned_in(message) + and message.mention_everyone is False + and message.reference is None + ): + em = discord.Embed( + title=f"Hi, my name is {self.client.user.display_name}. Use for help", + color=message.author.color, + ) + return await message.channel.send(embed=em) + + @commands.Cog.listener() + async def on_ready(self): + """ + Runs when the bot is ready + Prints a message to console and updates the bot's activity + """ + # update the bots activity + await update_activity(self.client) + + # (try to) connect to the postgresql database + try: + self.client.db = await create_connection_pool() + except ValueError: + raise InvalidDatabaseUrl + + # connect to redis db and reset the cache + self.client.redis = await create_redis_connection() + await self.client.redis.flushall() # reset cache + + # Send online alert + online_alert_channel = self.client.config.get("online_alert_channel") + if online_alert_channel in (0, None): + return + + try: + channel = await self.client.fetch_channel(online_alert_channel) + except discord.errors.NotFound: + return + + await self.__setup_ticket_buttons() + + await channel.send( + embed=discord.Embed( + title="Bot is online", + color=discord.Color.green(), + timestamp=datetime.datetime.now(), + ) + ) + + if self.client.config.get("LOGGING"): + await log_normal("Bot is Online") + + # print to console that its ready + self.client.console.print("\n[bold green]Bot is ready") + + async def __setup_ticket_buttons(self): + guilds = await self.client.db.fetch( + "SELECT guild_id FROM ticket_guild WHERE create_button=true" + ) + for guild_id in map(lambda x: x[0], guilds): + self.client.add_view(NewTicketView(guild_id, self.client)) + + +def setup(client: WhyBot): + client.add_cog(OnEvent(client)) diff --git a/src/cogs/events/on_guild.py b/src/cogs/events/on_guild.py new file mode 100644 index 0000000..a2b0e35 --- /dev/null +++ b/src/cogs/events/on_guild.py @@ -0,0 +1,74 @@ +import datetime + +import discord +from discord.ext import commands + +from core.models import WhyBot +from core.helpers import log_normal, update_activity +from core.db import create_db_tables + + +class OnGuild(commands.Cog): + def __init__(self, client: WhyBot): + self.client = client + + @commands.Cog.listener() + async def on_guild_remove(self, guild: discord.Guild): + """ + Called when the bot is removed from a guild + It will update the bots activity + It will also send a message to the the leave_alert_channel which is set in the config + """ + await update_activity(self.client) + leave_alert_channel = self.client.config["leave_alert_channel"] + if leave_alert_channel in (0, None): + return + + try: + channel = self.client.get_channel(leave_alert_channel) + except discord.errors.NotFound: + return + + em = discord.Embed( + title="Leave", description=f"Left: {guild.name}", color=discord.Color.red() + ) + em.timestamp = datetime.datetime.utcnow() + await channel.send(embed=em) + + if self.client.config["LOGGING"]: + await log_normal(f"Left Guild: '{guild.name}'") + + @commands.Cog.listener() + async def on_guild_join(self, guild: discord.Guild): + """ + Called when the bot joins a guild + It will update the bots activity + It will also send a message to the the join_alert_channel which is set in the config + """ + await update_activity(self.client) + + join_alert_channel = self.client.config["join_alert_channel"] + if join_alert_channel in (0, None): + return + + try: + channel = self.client.get_channel(join_alert_channel) + except discord.errors.NotFound: + return + + em = discord.Embed( + title="Join", + description=f"Joined: {guild.name}", + color=discord.Color.green(), + ) + em.timestamp = datetime.datetime.utcnow() + await channel.send(embed=em) + + if self.client.config["LOGGING"]: + await log_normal(f"Joined Guild: '{guild.name}'") + + await create_db_tables(self.client.db, guild.id) + + +def setup(client: WhyBot): + client.add_cog(OnGuild(client)) diff --git a/src/cogs/fun/convert.py b/src/cogs/fun/convert.py new file mode 100644 index 0000000..422f8ec --- /dev/null +++ b/src/cogs/fun/convert.py @@ -0,0 +1,239 @@ +import io +import random + +import discord +import pyfiglet +import discord_colorize +from discord.commands import SlashCommandGroup + +from core import BaseCog +from core.helpers import get_request_bytes + +colors = discord_colorize.Colors() +fonts = pyfiglet.FigletFont.getFonts() +for idx, font in enumerate(fonts): + fonts[idx] = font.replace("_", "") +fonts = dict(zip(fonts, pyfiglet.FigletFont.getFonts())) + + +class TextConvert(BaseCog): + + textconvert = SlashCommandGroup("text", "Convert text to something else") + + @textconvert.command(description="Convert text to stickycaps") + async def stickycaps( + self, + ctx: discord.ApplicationContext, + text: discord.Option(str, "The text to convert"), + ): + functions = [str.upper, str.lower] + result = "".join(random.choice(functions)(char) for char in text) + if len(result) <= 1999: + return await ctx.respond(result) + await ctx.respond("Too long to send :(") + + @textconvert.command(description="Expand some text") + async def expand( + self, + ctx: discord.ApplicationContext, + space: int, + text: discord.Option(str, "The text to convert"), + ): + spacing = " " * space + result = spacing.join(text) + if len(result) <= 1999: + return await ctx.respond(result) + await ctx.respond("Too long to send :(") + + @textconvert.command(description="Reverse some text") + async def reverse( + self, + ctx: discord.ApplicationContext, + text: discord.Option(str, "The text to convert"), + ): + result = text[::-1] + if len(result) <= 1999: + return await ctx.respond(result) + await ctx.respond("Too long to send :(") + + @textconvert.command(description="Convert text to hex") + async def texttohex( + self, + ctx: discord.ApplicationContext, + text: discord.Option(str, "The text to convert"), + ): + try: + hex_output = " ".join(f"{ord(char):02x}" for char in text) + except Exception as e: + return await ctx.respond( + f"Error: `{e}`. This probably means the text is malformed" + ) + if len(hex_output) <= 1999: + return await ctx.respond(f"```fix\n{hex_output}```") + await ctx.respond("Too long to send :(") + + @textconvert.command(description="Convert hex to text") + async def hextotext( + self, + ctx: discord.ApplicationContext, + text: discord.Option(str, "The text to convert"), + ): + try: + text_output = bytearray.fromhex(text).decode() + except Exception as e: + return await ctx.respond( + f"**Error: `{e}`. This probably means the text is malformed**" + ) + if len(text_output) <= 1999: + return await ctx.respond(f"```fix\n{text_output}```") + await ctx.respond("Too long to send :(") + + @textconvert.command(description="Convert text to binary") + async def texttobinary( + self, + ctx: discord.ApplicationContext, + text: discord.Option(str, "The text to convert"), + ): + try: + binary_output = " ".join(format(ord(char), "b") for char in text) + except Exception as e: + return await ctx.respond( + f"**Error: `{e}`. This probably means the text is malformed." + ) + if len(binary_output) <= 1999: + return await ctx.respond(f"```fix\n{binary_output}```") + await ctx.respond("Too long to send :(") + + @textconvert.command(description="Convert binary to text") + async def binarytotext( + self, + ctx: discord.ApplicationContext, + text: discord.Option(str, "The text to convert"), + ): + try: + text_output = "".join([chr(int(char, 2)) for char in text.split()]) + except Exception as e: + await ctx.respond( + f"**Error: `{e}`. This probably means the text is malformed" + ) + if len(text_output) <= 1999: + return await ctx.respond(f"```fix\n{text_output}```") + await ctx.respond("Too long to send :(") + + @textconvert.command(description="Emojify some text") + async def emojify( + self, + ctx: discord.ApplicationContext, + text: discord.Option(str, "The text to convert"), + ): + emojis = [] + + extra = { + "?": ":question:", + "!": ":exclamation:", + } + numbers = { + 0: ":zero:", + 1: ":one:", + 2: ":two:", + 3: ":three:", + 4: ":four:", + 5: ":five:", + 6: ":six:", + 7: ":seven:", + 8: ":eight:", + 9: ":nine:", + } + + for char in text.lower(): + if char.isdecimal(): + emojis.append(numbers.get(char, "")) + elif char.isalpha(): + emojis.append(f":regional_indicator_{char}:") + elif char in extra: + emojis.append(extra.get(char, "")) + else: + emojis.append(char) + + await ctx.respond(" ".join(emojis)) + + @textconvert.command(description="Create ascii art from the given text") + async def ascii( + self, + ctx: discord.ApplicationContext, + message: str, + color: discord.Option( + str, + default="nocolor", + choices=["red", "yellow", "blue", "green", "gray", "pink", "cyan", "white"], + ), + font: discord.Option(str, "The font to do the ascii art with") = "big", + ): + if fonts.get(font) is None: + return await ctx.respond( + embed=discord.Embed( + title="Invalid Font!", + description=f'Available Fonts:\n{", ".join(fonts.keys())}', + color=discord.Color.red(), + ), + ephemeral=True, + ) + + if color == "nocolor": + message = f"```\n{pyfiglet.figlet_format(message, font=fonts[font])}\n```" + else: + message = f"```ansi\n{colors.colorize(pyfiglet.figlet_format(message, font=fonts[font]), fg=color)}\n```" + + if len(message) > 4000: + return await ctx.respond("Text to long to send :(", ephemeral=True) + await ctx.respond( + embed=discord.Embed(title="Ascii Art Output:", description=message) + ) + + @textconvert.command(description="Convert text to a font and show it as an image") + async def fontconvert( + self, + ctx: discord.ApplicationContext, + message: str, + font: str = None, + color: str = "black", + ): + if font is None: + return await ctx.respond( + embed=discord.Embed( + title="You must specify a font", + description=( + "[Click this to get a list of fonts you can" + " use](https://api.fusionsid.xyz/api/font/list)" + ), + color=discord.Colour.red(), + ), + ephemeral=True, + ) + URL = "https://api.fusionsid.xyz/api/font/convert" + + text_image = await get_request_bytes( + URL, + data={"font": font, "text": message, "text_color": color}, + bytes_io=True, + ) + + if not isinstance(text_image, io.BytesIO): + return await ctx.respond( + embed=discord.Embed( + title="An error occured while trying to get the image", + description=( + "This is most likely because you used an invalid font\n " + " [Click this to get a list of fonts you can" + " use](https://api.fusionsid.xyz/api/font/list)" + ), + color=discord.Colour.red(), + ), + ephemeral=True, + ) + + await ctx.respond(file=discord.File(text_image, "text.png")) + + +def setup(client): + client.add_cog(TextConvert(client)) diff --git a/src/cogs/fun/fun.py b/src/cogs/fun/fun.py new file mode 100644 index 0000000..e0165cc --- /dev/null +++ b/src/cogs/fun/fun.py @@ -0,0 +1,368 @@ +import io +import os +import random +import asyncio +import tempfile +import textwrap + +import discord +import validators +from discord.ext import commands +from PIL import Image, ImageDraw, ImageFont +from moviepy.editor import VideoFileClip, TextClip, CompositeVideoClip + +import __main__ +from core import BaseCog, WhyBot +from core.helpers import RickRollView + + +class Fun(BaseCog): + def __init__(self, client: WhyBot) -> None: + path = os.path.join( + os.path.dirname(__main__.__file__), "../assets/images/spongebob" + ) + images = {} + for image in os.listdir(path): + key = image.split(".")[0] + images[key] = image + + self.spongebob_images = images + self.spongebob_path = path + + super().__init__(client) + + @staticmethod + async def gen_crab(t1: str, t2: str, ctx: discord.ApplicationContext): + path = os.path.join( + os.path.dirname(__main__.__file__).replace("src", ""), + "assets/videos/crab.mp4", + ) + clip = VideoFileClip(path) + text = TextClip(t1, fontsize=48, color="white", font="Symbola") + text2 = ( + TextClip("____________________", fontsize=48, color="white", font="Verdana") + .set_position(("center", 210)) + .set_duration(15.4) + ) + text = text.set_position(("center", 200)).set_duration(15.4) + text3 = ( + TextClip(t2, fontsize=48, color="white", font="Verdana") + .set_position(("center", 270)) + .set_duration(15.4) + ) + + video = CompositeVideoClip( + [clip, text.crossfadein(1), text2.crossfadein(1), text3.crossfadein(1)] + ).set_duration(15.4) + file = tempfile.NamedTemporaryFile(suffix=".mp4") + video.write_videofile(file.name, threads=4, preset="superfast", verbose=False) + clip.close() + video.close() + file.seek(0) + await ctx.respond(file=discord.File(file.name, "crab.mp4")) + file.close() + + async def gen_spongebob( + self, + number: int, + unit: str, + ctx: discord.ApplicationContext, + background: str, + color: str, + ): + path = os.path.join(self.spongebob_path, background) + font_path = os.path.join( + os.path.dirname(__main__.__file__), "../assets/fonts/Some_Time_Later.otf" + ) + font = ImageFont.truetype(font_path, 100) + image = Image.open(path) + draw = ImageDraw.Draw(image) + text = f"{number} {unit} Later..." + para = textwrap.wrap(text, width=30) + width, height = image.size + current_h, pad = height // 2, 10 + for line in para: + w, h = draw.textsize(line, font=font) + try: + draw.text(((width - w) / 2, current_h), line, font=font, fill=color) + except ValueError: + draw.text(((width - w) / 2, current_h), line, font=font, fill=color) + color = "white" + current_h += h + pad + file = io.BytesIO() + image.save(file, "PNG") + file.seek(0) + await ctx.respond(file=discord.File(file, "spongebob.png")) + + @commands.slash_command(description="Generate a crab rave meme video") + @commands.cooldown(1, 15, commands.BucketType.user) + async def crab(self, ctx: discord.ApplicationContext, text1: str, text2: str): + await ctx.defer() + asyncio.create_task(self.gen_crab(text1, text2, ctx)) + + @commands.slash_command(description="Claim your why coins") + @commands.cooldown(1, 5, commands.BucketType.user) + async def claim(self, ctx: discord.ApplicationContext): + em = discord.Embed(title="Claim 100k Why Coins", color=discord.Color.blue()) + await ctx.respond(embed=em, view=RickRollView(self.client.db)) + + @commands.slash_command(description="Generate a spongebob timecard") + async def spongebob( + self, + ctx: discord.ApplicationContext, + number: int, + unit: str, + background: discord.Option( + str, + default="bamboo", + choices=[ + "title", + "flowers", + "heads", + "purplewood", + "blueseaweed", + "wood", + "tiles", + "seaweed", + "sand", + "steel", + "crosshatch", + "greenseaweed", + "bamboo", + ], + ), + color: str = "white", + ): + await ctx.defer() + self.client.loop.create_task( + self.gen_spongebob( + number, unit, ctx, self.spongebob_images[background], color + ) + ) + + @commands.slash_command( + name="8ball", description="Ask the magical 8ball a question" + ) + async def _8ball(self, ctx: discord.ApplicationContext, question: str): + responses = [ + "As I see it, yes", + "It is certain", + "It is decidedly so", + "Most likely", + "Outlook good", + "Signs point to yes", + "Without a doubt", + "Yes", + "Yes - definitely", + "You may rely on it", + "Reply hazy, try again", + "Ask again later", + "Better not tell you now", + "Cannot predict now", + "Concentrate and ask again", + "Don't count on it", + "My reply is no", + "My sources say no", + "Outlook not so good", + "Very doubtful", + "Yes", + "No", + "Never", + "Definitely", + "Ask again later", + "Does Siri know the answer?", + "What do you think?", + "Wait, what say that again", + "roll again", + "maybe ;)", + "stop", + "I don't understand you", + "Speak clearer", + "Does Alexa know the answer?", + "Think about it", + "Yeah, obviously", + "No... Yes", + "That's obviously a yes", + "There's an obvious answer, why are you asking me this?", + "Literally yes", + "Why are you asking me this?", + "Yeahhhh", + "I *totally* understand", + "L(%)/", + "When you think about it... the answer is yes", + "When you think about it..", + "the answer is no", + "When you think about it... the answer is so obvious", + "the answer is yes", + "the answer is no", + "I'm busy, ask me that later", + "That's not important right now", + "lol", + "good question", + ] + em = discord.Embed( + title="8 Ball šŸŽ±", + description=( + f"**Question:** {question}\n**Answer:** {random.choice(responses)}" + ), + color=ctx.author.color, + ) + await ctx.respond(embed=em) + + @commands.slash_command(description="Do a 100% legit hack on another member") + async def hack(self, ctx: discord.ApplicationContext, member: discord.Member): + await ctx.defer() + email_ext = [ + "gmail.com", + "yahoo.com", + "hotmail.com", + "aol.com", + "hotmail.co.uk", + "hotmail.fr", + "msn.com", + "yahoo.fr", + "wanadoo.fr", + "orange.fr", + "comcast.net", + "yahoo.co.uk", + "yahoo.com.br", + "yahoo.co.in", + "live.com", + "rediffmail.com", + "free.fr", + "gmx.de", + "web.de", + "yandex.ru", + "ymail.com", + "libero.it", + "outlook.com", + "uol.com.br", + "bol.com.br", + "mail.ru", + "cox.net", + "hotmail.it", + "sbcglobal.net", + "sfr.fr", + "live.fr", + "verizon.net", + "live.co.uk", + ] + most_used_words = [ + "TrASh", + "gEt gUd", + "waSsUp", + "noOb", + "LmAo", + "lol", + "lMfao", + "e", + "seNd nUkeS", + "f&Ck", + "sH#t", + "nub", + "b1T#h", + ] + passwords = [ + "123456", + "password", + "12345", + "123456789", + "password1", + "abc123", + "12345678", + "qwerty", + "111111", + "1234567", + "1234", + "iloveyou", + "sunshine", + "monkey", + "1234567890", + "123123", + "princess", + "baseball", + "dragon", + "football", + "shadow", + "michael", + "soccer", + "unknown", + "maggie", + "000000.", + "ashley", + "myspace1", + "purple", + "fuckyou", + "charlie", + "jordan", + "hunter", + "superman", + "tigger", + "michelle", + "buster", + "pepper", + "justin", + "andrew", + "harley", + "matthew", + "bailey", + "jennifer", + "samantha", + "ginger", + "anthony", + "qwerty123", + "qwerty1", + "peanut", + ] + + hack_message = await ctx.send(f"[ā––] Hacking {member.display_name} now...") + await asyncio.sleep(1.420) + await hack_message.edit(content="[ā–˜] Finding discord login... (2fa bypassed)") + await asyncio.sleep(1.69) + email = ( + f"{member.display_name}.{random.randint(1, 100)}@{random.choice(email_ext)}" + ) + await hack_message.edit( + content=f"[ā–] `Email: {email}`\n `Password: {random.choice(passwords)}`" + ) + await asyncio.sleep(1.420) + await hack_message.edit(content="[ā–—] IP address: 127.0.0.1:50") + await asyncio.sleep(1.69) + await hack_message.edit( + content=f"[ā––] Most used words: {random.choice(most_used_words)}" + ) + await asyncio.sleep(1.420) + await hack_message.edit( + content=( + f"[ā–˜] Injecting trojan virus into discriminator: {member.discriminator}" + ) + ) + await asyncio.sleep(1.69) + await hack_message.edit(content="[ā–] Selling information to the government...") + await asyncio.sleep(1.420) + await hack_message.edit( + content="[ā–—] Reporting account to discord for breaking TOS..." + ) + await asyncio.sleep(1.69) + await hack_message.edit(content="[ā––] Hacking medical records...") + await asyncio.sleep(1.420) + await hack_message.edit(content=f"Finished hacking {member.mention}") + + await ctx.respond("The *totally* real and dangerous hack is complete!") + + @commands.slash_command( + description="Display a screenshot of the page at the given link" + ) + async def screenshot(self, ctx: discord.ApplicationContext, url: str): + if not validators.url(url): + return await ctx.respond("Not a url", ephemeral=True) + + em = discord.Embed( + title="Screenshot", description=f"[Link]({url})", color=ctx.author.color + ) + em.set_image(url=f"https://image.thum.io/get/{url}") + await ctx.respond(embed=em) + + +def setup(client): + client.add_cog(Fun(client)) diff --git a/src/cogs/fun/poll.py b/src/cogs/fun/poll.py new file mode 100644 index 0000000..8fbc1bc --- /dev/null +++ b/src/cogs/fun/poll.py @@ -0,0 +1,150 @@ +import asyncio +import time as pytime + +import discord +from discord.utils import get +from discord.ext import commands + +from core import BaseCog +from core.utils.formatters import discord_timestamp + + +class Poll(BaseCog): + @commands.slash_command(description="Makes a Yah or Nah poll") + @commands.guild_only() + async def yesorno(self, ctx: discord.ApplicationContext, message: str): + msg = await ctx.respond( + embed=discord.Embed( + title="Yah or Nah?", description=message, color=ctx.author.color + ) + ) + msg = await msg.original_message() + await msg.add_reaction("šŸ‘") + await msg.add_reaction("šŸ‘Ž") + + @commands.slash_command(description="Create a poll") + @commands.guild_only() + async def poll( + self, + ctx: discord.ApplicationContext, + title: str, + choice1: str, + choice2: str, + choice3: str = None, + choice4: str = None, + choice5: str = None, + choice6: str = None, + choice7: str = None, + choice8: str = None, + choice9: str = None, + choice10: str = None, + end_poll_in: str = None, + ): + await ctx.defer() + options = [ + i + for i in [ + choice1, + choice2, + choice3, + choice4, + choice5, + choice6, + choice7, + choice8, + choice9, + choice10, + ] + if i is not None + ] + numbers = { + 1: "1ļøāƒ£", + 2: "2ļøāƒ£", + 3: "3ļøāƒ£", + 4: "4ļøāƒ£", + 5: "5ļøāƒ£", + 6: "6ļøāƒ£", + 7: "7ļøāƒ£", + 8: "8ļøāƒ£", + 9: "9ļøāƒ£", + 10: "šŸ”Ÿ", + } + reactions_todo = [] + desc = "" + for index, option in enumerate(options): + emoji = numbers[index + 1] + desc += f"\n{emoji} {option}" + reactions_todo.append(emoji) + + em = discord.Embed( + title=f'{ctx.author.display_name} asks: "{title}"', + description=desc, + color=discord.Color.random(), + ) + + if end_poll_in is not None: + if end_poll_in.isnumeric(): + time = int(end_poll_in) + else: + seconds_per_unit = {"m": 60, "h": 3600, "d": 86400, "w": 604800} + try: + time = int(end_poll_in[:-1]) * seconds_per_unit[end_poll_in[-1]] + except ValueError: + return await ctx.respond( + "Poll failed to be created" + f" {self.client.get_why_emojies.get('redcross', 'āŒ')}\nThe format code" + " was not found", + ephemeral=True, + ) + + if time > 604800: + return await ctx.respond( + "Poll failed to be created" + f" {self.client.get_why_emojies.get('redcross', 'āŒ')}\nTime can not" + " be longer than a week", + ephemeral=True, + ) + + timern = pytime.time() + + em.add_field( + name="Voting ends in:", + value=discord_timestamp(int(timern + time), "ts"), + ) + + message = await ctx.send(embed=em) + await ctx.respond( + f"Poll created successfuly {self.client.get_why_emojies.get('checkmark', 'āœ…')}", + ephemeral=True, + ) + for reaction in reactions_todo: + await message.add_reaction(reaction) + + if end_poll_in is None: + return + + await asyncio.sleep(time) + + message_later = await ctx.channel.fetch_message(message.id) + results = {} + for i in reactions_todo: + reaction = get(message_later.reactions, emoji=i) + count = reaction.count - 1 + results[i] = f"{count} vote{'s' if count > 1 else ''}" + + results = "\n".join([f"{key} got {value}" for key, value in results.items()]) + embed = discord.Embed( + title=f'Poll results for: "{title}"', + description=f"**Votes:**\n {results}", + color=ctx.author.color, + ) + embed.set_footer(text="Voting is closed") + try: + await message.clear_reactions() + except discord.HTTPException: + pass + return await message.edit(embed=embed) + + +def setup(client): + client.add_cog(Poll(client)) diff --git a/src/cogs/fun/rand.py b/src/cogs/fun/rand.py new file mode 100644 index 0000000..ad46cdd --- /dev/null +++ b/src/cogs/fun/rand.py @@ -0,0 +1,222 @@ +import io +import os +import json +import string +import random + +import discord +import aiofiles +from PIL import Image +import discord_colorize +from discord.commands import SlashCommandGroup + +import __main__ +from core import BaseCog +from core.helpers import ImageAPIFail +from core.helpers.http import get_request_bytes + + +class Random(BaseCog): + @staticmethod + async def open_json_fun(): + path = os.path.join( + os.path.dirname(__main__.__file__).replace("src", ""), + "assets/json_files/fun_text.json", + ) + async with aiofiles.open(path, mode="r") as f: + contents = await f.read() + data = json.loads(contents) + + return data + + random = SlashCommandGroup("random", "Random commands") + + @random.command(description="Show a random compliment") + async def compliment(self, ctx: discord.ApplicationContext): + data = await self.open_json_fun() + await ctx.respond(random.choice(data["compliment"])) + + @random.command(description="Show a random dare") + async def dare(self, ctx: discord.ApplicationContext): + data = await self.open_json_fun() + await ctx.respond(random.choice(data["dares"])) + + @random.command(description="Show a random fact") + async def fact(self, ctx: discord.ApplicationContext): + data = await self.open_json_fun() + await ctx.respond(random.choice(data["facts"])) + + @random.command(description="Show a random roast") + async def roast(self, ctx: discord.ApplicationContext): + data = await self.open_json_fun() + await ctx.respond(random.choice(data["roasts"])) + + @random.command(description="Show a random truth") + async def truth(self, ctx: discord.ApplicationContext): + data = await self.open_json_fun() + await ctx.respond(random.choice(data["truth"])) + + @random.command(description="Show a random truth and dare") + async def truth_or_dare(self, ctx: discord.ApplicationContext): + data = await self.open_json_fun() + await ctx.respond( + embed=discord.Embed( + title="Truth Or Dare", + description=( + f'**Truth:** {random.choice(data["truth"])}\n**Dare:**' + f' {random.choice(data["dares"])}\n\n**Computer Choice:**' + f' {random.choice(["truth", "dare"])}' + ), + color=ctx.author.color, + ) + ) + + @random.command(description="Pick a random number") + async def number(self, ctx: discord.ApplicationContext, stop: int, start: int = 0): + return await ctx.respond(random.randint(start, stop)) + + @random.command(description="Pick a random choice out of the options you give") + async def choice( + self, + ctx: discord.ApplicationContext, + choice1: str, + choice2: str, + choice3: str = None, + choice4: str = None, + choice5: str = None, + choice6: str = None, + choice7: str = None, + choice8: str = None, + choice9: str = None, + choice10: str = None, + ): + await ctx.defer() + choice = random.choice( + [ + i + for i in ( + choice1, + choice2, + choice3, + choice4, + choice5, + choice6, + choice7, + choice8, + choice9, + choice10, + ) + if i is not None + ] + ) + return await ctx.respond( + embed=discord.Embed( + title="Random Choice", + description=f"**Computer Choice:** {choice}", + color=ctx.author.color, + ) + ) + + @random.command(description="Choose a random card") + async def card(self, ctx: discord.ApplicationContext): + url = "https://api.fusionsid.xyz/api/image/random-card" + img = await get_request_bytes( + url, + bytes_io=True, + ) + + if not isinstance(img, io.BytesIO): + raise ImageAPIFail + + await ctx.respond(file=discord.File(img, "text.png")) + + @random.command(description="Flip a coin") + async def flipcoin(self, ctx: discord.ApplicationContext): + h_or_t = random.choice(["heads", "tails"]) + path = os.path.join( + os.path.dirname(__main__.__file__).replace("src", ""), + f"assets/images/{h_or_t}.png", + ) + await ctx.respond(f"Its {h_or_t}!", file=discord.File(path)) + + @random.command(description="Pick a random color") + async def color(self, ctx: discord.ApplicationContext): + color = tuple(random.randint(0, 255) for _ in range(3)) + img = Image.new("RGB", (500, 500), color) + send = io.BytesIO() + img.save(send, "PNG") + send.seek(0) + await ctx.respond(file=discord.File(send, "color.png")) + + @random.command(description="Pick a random letter") + async def letter(self, ctx: discord.ApplicationContext): + return await ctx.respond( + f"Your random letter is '{random.choice(string.ascii_lowercase)}'" + ) + + @random.command(description="Roll a dice") + async def diceroll(self, ctx: discord.ApplicationContext): + color = random.choice( + ["red", "yellow", "blue", "green", "gray", "pink", "cyan", "white"] + ) + + colors = discord_colorize.Colors() + dice = random.choice(self.dice) + message = f"```ansi\n{colors.colorize(dice, fg=color)}\n```" + await ctx.respond( + embed=discord.Embed( + title=f"Rolled a {self.dice.index(dice)+1}", + description=message, + color=discord.Color.random(), + ) + ) + + # credit to micfun123 for the dice ascii art + dice = [ + """\ +----- +| | +| o | +| | +----- +""", + """\ +----- +|o | +| | +| o| +----- +""", + """\ +----- +|o | +| o | +| o| +----- +""", + """\ +----- +|o o| +| | +|o o| +----- +""", + """\ +----- +|o o| +| o | +|o o| +----- +""", + """\ +----- +|o o| +|o o| +|o o| +----- +""", + ] + + +def setup(client): + client.add_cog(Random(client)) diff --git a/src/cogs/games/counting.py b/src/cogs/games/counting.py new file mode 100644 index 0000000..0367c32 --- /dev/null +++ b/src/cogs/games/counting.py @@ -0,0 +1,484 @@ +import json +import asyncio + +import discord +from discord.ext import commands +from discord.commands import SlashCommandGroup, default_permissions + +from core import BaseCog +from core.models import CountingData +from core.db import setup_counting +from core.utils import slow_safe_calculate + + +class Counting(BaseCog): + + counting = SlashCommandGroup("counting", "Commmands related to the counting game") + + @counting.command(description="Set the channel for the counting game") + @commands.guild_only() + @commands.has_guild_permissions(administrator=True) + async def setchannel( + self, + ctx: discord.ApplicationContext, + channel: discord.Option( + discord.TextChannel, "The counting channel", required=True # noqa + ), + ): + counting_data = await self.__get_counting_data(ctx.guild.id, skip_cache=True) + if counting_data is None: + return await setup_counting(self.client.db, ctx.guild.id) + + await self.client.db.execute( + "UPDATE counting SET counting_channel=$1 WHERE guild_id=$2", + channel.id, + ctx.guild.id, + ) + await ctx.respond( + embed=discord.Embed( + title="Counting Channel Set!", + description=f"Counting channel is now: {channel.mention}", + color=ctx.author.color, + ) + ) + + counting_data.counting_channel = channel.id + await self.__update_cache(counting_data) + + @counting.command(description="Enable the counting game for this server") + @commands.guild_only() + @commands.has_guild_permissions(administrator=True) + async def enable( + self, + ctx: discord.ApplicationContext, + ): + counting_data = await self.__get_counting_data(ctx.guild.id, skip_cache=True) + if counting_data is None: + return await setup_counting(self.client.db, ctx.guild.id) + elif counting_data.counting_channel is None: + return await ctx.respond( + embed=discord.Embed( + title="Counting channel not set", + description=( + "Please set the counting channel first before enabling the" + " game.\nUse `/counting setchannel` to set it" + ), + color=ctx.author.color, + ), + ephemeral=True, + ) + + try: + channel = await self.client.fetch_channel(counting_data.counting_channel) + except ( + discord.NotFound, + discord.HTTPException, + discord.ApplicationCommandInvokeError, + ): + return await ctx.respond( + embed=discord.Embed( + title="Something went wrong fetching the channel", + description=( + "Most likely because the counting channel set was" + " deleted.\nReset it with `/counting setchannel`" + ), + color=ctx.author.color, + ), + ephemeral=True, + ) + + await self.client.db.execute( + "UPDATE counting SET plugin_enabled=true WHERE guild_id=$1", + ctx.guild.id, + ) + + await ctx.respond( + embed=discord.Embed( + title="Counting Enabled!", + description=( + f"Counting game is now enabled and the channel is {channel.mention}" + ), + color=ctx.author.color, + ) + ) + + counting_data.plugin_enabled = True + await self.__update_cache(counting_data) + + @counting.command(description="Toggle the bot auto evaluating math expressions") + @commands.guild_only() + @commands.has_guild_permissions(administrator=True) + async def toggle_auto_calc( + self, + ctx: discord.ApplicationContext, + ): + counting_data = await self.__get_counting_data(ctx.guild.id, skip_cache=True) + if counting_data is None: + return await setup_counting(self.client.db, ctx.guild.id) + + on_or_off = not counting_data.auto_calculate + + await self.client.db.execute( + "UPDATE counting SET auto_calculate=$1 WHERE guild_id=$2", + on_or_off, + ctx.guild.id, + ) + + await ctx.respond( + embed=discord.Embed( + title="Auto calculate toggled!", + description=( + f"Auto calculate is now {'on āœ…' if on_or_off else 'off āŒ'}\nIf you" + f" wish to toggle it back {'off' if on_or_off else 'on'} run this" + " command again" + ), + color=ctx.author.color, + ) + ) + + counting_data.auto_calculate = on_or_off + await self.__update_cache(counting_data) + + @counting.command(description="Disable counting") + @commands.guild_only() + @commands.has_guild_permissions(administrator=True) + async def disable( + self, + ctx: discord.ApplicationContext, + ): + counting_data = await self.__get_counting_data(ctx.guild.id, skip_cache=True) + if counting_data is None: + return await setup_counting(self.client.db, ctx.guild.id) + + await self.client.db.execute( + "UPDATE counting SET plugin_enabled=false WHERE guild_id=$1", + ctx.guild.id, + ) + + await ctx.respond( + embed=discord.Embed( + title="Counting Disabled!", + description=( + "Counting is now disabled, If you ever want to re-enable it use" + " `/counting enable`" + ), + color=ctx.author.color, + ) + ) + + counting_data.plugin_enabled = False + await self.__update_cache(counting_data) + + @counting.command(description="Show the current number") + @commands.guild_only() + async def current_number(self, ctx: discord.ApplicationContext): + counting_data = await self.__get_counting_data(ctx.guild.id, skip_cache=True) + if counting_data is None: + await setup_counting(self.client.db, ctx.guild.id) + elif ( + counting_data.counting_channel is None + or counting_data.plugin_enabled is None + or counting_data.plugin_enabled is False + ): + return await ctx.respond( + embed=discord.Embed( + title="Counting not setup/enabled on this server", + description=( + "Use commands `/counting setchannel` and `/counting enable` to" + " setup counting on this server" + ), + color=ctx.author.color, + ) + ) + + await ctx.respond( + embed=discord.Embed( + title=f"Current number is: {counting_data.current_number}", + description=( + "That means the next number / the one you should type is:" + f" {counting_data.next_number}" + ), + color=ctx.author.color, + ) + ) + + await self.__update_cache(counting_data) + + @counting.command(description="Show the highest number this server has reached") + @commands.guild_only() + async def high_score(self, ctx: discord.ApplicationContext): + counting_data = await self.__get_counting_data(ctx.guild.id, skip_cache=True) + if counting_data is None: + await setup_counting(self.client.db, ctx.guild.id) + elif ( + counting_data.counting_channel is None + or counting_data.plugin_enabled is None + or counting_data.plugin_enabled is False + ): + return await ctx.respond( + embed=discord.Embed( + title="Counting not setup/enabled on this server", + description=( + "Use commands `/counting setchannel` and `/counting enable` to" + " setup counting on this server" + ), + color=ctx.author.color, + ) + ) + + await ctx.respond( + embed=discord.Embed( + title=f"Current high score is: {counting_data.high_score}", + description=( + "Use `/counting leaderboard` to see your position across all why" + " bot servers" + ), + color=ctx.author.color, + ) + ) + + await self.__update_cache(counting_data) + + @counting.command(description="Show the global leaderboard for counting") + @commands.guild_only() + async def leaderboard(self, ctx: discord.ApplicationContext): + await ctx.defer() + counting_data = await self.client.db.fetch( + "SELECT * FROM counting ORDER BY high_score" + ) + counting_data = reversed(counting_data[:10]) # crop to 10 guilds only + + embed = discord.Embed(title="Counting Leaderboard", color=ctx.author.color) + + place = 1 + for guild in counting_data: + try: + guild_name = await self.client.fetch_guild(guild[0]) + except ( + discord.NotFound, + discord.HTTPException, + discord.ApplicationCommandInvokeError, + ): + continue + + embed.add_field( + name=f"{place}: {guild_name}", + value=f"Counting high score: {guild[4]}", + inline=False, + ) + + place += 1 + + await ctx.respond(embed=embed) + + @counting.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def ban_user(self, ctx: discord.ApplicationContext, member: discord.Member): + counting_data = await self.__get_counting_data(ctx.guild.id, skip_cache=True) + if member.id in counting_data.banned_counters: + return await ctx.respond("User is already banned from counting") + await self.client.db.execute( + "UPDATE counting SET banned_users = array_append(banned_users, $1) WHERE guild_id=$2", + member.id, + ctx.guild.id, + ) + await ctx.respond( + embed=discord.Embed( + title="User Banned", + description=f"{member.mention} is banned from counting", + color=discord.Color.random(), + ) + ) + counting_data.banned_counters.append(member.id) + await self.__update_cache(counting_data) + + @counting.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def unban_user(self, ctx: discord.ApplicationContext, member: discord.Member): + counting_data = await self.__get_counting_data(ctx.guild.id, skip_cache=True) + if member.id not in counting_data.banned_counters: + return await ctx.respond("User is already unbanned from counting") + await self.client.db.execute( + "UPDATE counting SET banned_users = array_remove(banned_users, $1) WHERE guild_id=$2", + member.id, + ctx.guild.id, + ) + await ctx.respond( + embed=discord.Embed( + title="User Unbanned", + description=f"{member.mention} is no longer banned from counting", + color=discord.Color.random(), + ) + ) + counting_data.banned_counters.remove(member.id) + await self.__update_cache(counting_data) + + @commands.Cog.listener() + async def on_message(self, message: discord.Message): + if message.guild is None or message.author.bot: + return + + counting_data = await self.__get_counting_data(message.guild.id) + if counting_data is None: + return await setup_counting(self.client.db, message.guild.id) + + if ( + counting_data.plugin_enabled is None + or counting_data.counting_channel == 0 + or counting_data.counting_channel is None + ): + return + + potential_number = await slow_safe_calculate(message.content, only_int=True) + if potential_number is None: + return + + if counting_data.auto_calculate and not message.content.isnumeric(): + self.client.loop.create_task(self.__delete_msg(potential_number, message)) + + if ( + counting_data.counting_channel != message.channel.id + or counting_data.plugin_enabled is False + ): + return + + if message.author.id in counting_data.banned_counters: + return + + if potential_number != counting_data.next_number: + em = discord.Embed( + title=f"{message.author.display_name}, You ruined it!", + description=( + f"You were supposed to type `{counting_data.next_number}`\nCount" + " reset to zero" + ), + color=message.author.color, + ) + await self.__reset_count(counting_data) + await message.add_reaction("āŒ") + return await message.channel.send(embed=em) + + elif counting_data.last_counter == message.author.id: + em = discord.Embed( + title=f"{message.author.display_name}, You ruined it!", + description=( + "You fool, only one person can count a time and since you did" + f" {counting_data.current_number} you cant do" + f" {counting_data.next_number}\nCount reset to zero" + ), + color=message.author.color, + ) + await self.__reset_count(counting_data) + await message.add_reaction("āŒ") + return await message.channel.send(embed=em) + + await self.__update_count(counting_data, message) + await message.add_reaction("āœ…") + + match counting_data.current_number: + case 42: + self.client.loop.create_task( + self.__delete_msg("You have reached the meaning of life", message) + ) + case 69: + self.client.loop.create_task(self.__delete_msg("Nice", message)) + case 100: + await message.add_reaction("šŸ’Æ") + case 420: + await message.add_reaction(self.client.get_emoji(1053461527161741373)) + case 9001: + self.client.loop.create_task( + self.__delete_msg("You're over 9000", message) + ) + + async def __reset_count(self, data: CountingData): + data.last_counter = 0 + data.current_number = 0 + + await self.__update_cache(data) + + self.client.loop.create_task( + self.client.db.execute( + "UPDATE counting SET current_number=$1, last_counter=$2 WHERE" + " guild_id=$3", + 0, + 0, + data.guild_id, + ) + ) + + async def __update_count(self, data: CountingData, message: discord.Message): + # update count and last counter and highscore + data.current_number = data.next_number + data.last_counter = message.author.id + + await self.__update_cache(data) + self.client.loop.create_task( + self.client.db.execute( + "UPDATE counting SET current_number=$1, last_counter=$2 WHERE" + " guild_id=$3", + data.current_number, + data.last_counter, + data.guild_id, + ) + ) + self.client.loop.create_task(self.__update_high_score(data, message)) + + async def __update_high_score(self, data: CountingData, message: discord.Message): + if data.high_score is None or data.current_number > data.high_score: + await self.client.db.execute( + "UPDATE counting SET high_score=$1 WHERE guild_id=$2", + data.current_number, + data.guild_id, + ) + + async def __update_cache(self, data: CountingData): + await self.client.redis.set( + f"{data.guild_id}_counting", json.dumps(list(data.__dict__.values())) + ) + + async def __get_counting_data( + self, guild_id: int, skip_cache: bool = False + ) -> CountingData: + key = f"{guild_id}_counting" + + if not skip_cache: + if await self.client.redis.exists(key): + data = json.loads(await self.client.redis.get(key)) + return CountingData(*data) + + data = await self.client.db.fetch( + "SELECT * FROM counting WHERE guild_id=$1", guild_id + ) + + if not data: + return None + + await self.client.redis.set(key, json.dumps(list(data[0]))) + return CountingData(*data[0]) + + async def __delete_msg(self, message_content: int | str, message: discord.Message): + try: + msg = await message.reply(message_content) + except discord.Forbidden: + return + + def check(reaction: discord.reaction.Reaction, user: discord.User): + return ( + reaction.message.id == msg.id + and reaction.emoji == "šŸ—‘ļø" + and user.id == message.author.id + ) + + try: + await msg.add_reaction("šŸ—‘ļø") + await self.client.wait_for("reaction_add", timeout=15.0, check=check) + await msg.delete() + except asyncio.TimeoutError: + await msg.remove_reaction("šŸ—‘ļø", member=self.client.user) + + +def setup(client): + client.add_cog(Counting(client)) diff --git a/src/cogs/games/rps.py b/src/cogs/games/rps.py new file mode 100644 index 0000000..d12b669 --- /dev/null +++ b/src/cogs/games/rps.py @@ -0,0 +1,66 @@ +import discord +from discord.ext import commands + +from core import BaseCog +from core.helpers.views import ConfirmView +from core.models.rps import RockPaperScissorsView + + +class RockPaperScissors(BaseCog): + @commands.slash_command( + name="rps", description="Challenge someone to a game of rock paper scissors" + ) + async def rock_paper_scissors_command( + self, ctx: discord.ApplicationContext, opponent: discord.Member + ): + + if opponent == ctx.author: + return await ctx.respond("You can't play against yourself", ephemeral=True) + + await ctx.respond("Waiting for opponent to accept", ephemeral=True) + + view = ConfirmView(target=opponent) + em = discord.Embed( + title="Confirm Or Deny", + description=( + f"{ctx.author.mention} would like to play a game of rock paper scissors" + " with you\nDo you want to play?" + ), + color=discord.Color.random(), + ) + + await ctx.send(content=opponent.mention, embed=em, view=view) + await view.wait() + + if not view.accepted: + return await ctx.send( + embed=discord.Embed( + title="Rock Paper Scissors", + description=( + f"{opponent.mention} denied your request to rock paper scissors" + " with them" + ), + color=discord.Color.random(), + ), + ephemeral=True, + ) + + game = RockPaperScissorsView(ctx.author, opponent) + await ctx.respond( + f"{opponent.mention} accepted to play with you. Game is now starting...", + ephemeral=True, + ) + await ctx.send( + embed=discord.Embed( + title="Rock Paper Scissors", + description=( + f"{ctx.author.mention} & {opponent.mention} choose your move:" + ), + color=discord.Color.random(), + ), + view=game, + ) + + +def setup(client): + client.add_cog(RockPaperScissors(client)) diff --git a/src/cogs/games/tictactoe.py b/src/cogs/games/tictactoe.py new file mode 100644 index 0000000..614c68c --- /dev/null +++ b/src/cogs/games/tictactoe.py @@ -0,0 +1,76 @@ +import discord +from discord.commands import SlashCommandGroup + +from core import BaseCog +from core.helpers import ConfirmView +from core.models import TicTacToeAIView, TicTacToe2PlayerView + + +class TicTacToeCog(BaseCog): + + tictactoe_cmd = SlashCommandGroup("tictactoe", "Tic tac toe commands") + + @tictactoe_cmd.command( + name="tictactoe", description="Play against someone on your server" + ) + async def tictactoe_multiplayer( + self, ctx: discord.ApplicationContext, opponent: discord.Member + ): + if opponent == ctx.author: + return await ctx.respond("You can't play against yourself", ephemeral=True) + await ctx.respond("Waiting for opponent to accept", ephemeral=True) + + view = ConfirmView(target=opponent) + em = discord.Embed( + title="Confirm Or Deny", + description=( + f"{ctx.author.mention} would like to play a game of tic tac toe with" + " you\nDo you want to play?" + ), + color=discord.Color.random(), + ) + await ctx.send(content=opponent.mention, embed=em, view=view) + await view.wait() + + if not view.accepted: + return await ctx.respond( + embed=discord.Embed( + title="Tic Tac Toe", + description=( + f"{opponent.mention} denied your request to play tic tac toe" + " with them" + ), + color=discord.Color.random(), + ), + ephemeral=True, + ) + + game = TicTacToe2PlayerView(ctx.author, opponent) + await ctx.send( + embed=discord.Embed( + title="Tic Tac Toe", + description=( + f"X = {opponent.display_name}\nO =" + f" {ctx.author.display_name}\n{opponent.display_name} starts!" + ), + color=discord.Color.random(), + ), + view=game, + ) + + @tictactoe_cmd.command(name="ai", description="Play against the bot") + async def tictactoe_ai(self, ctx: discord.ApplicationContext): + + game = TicTacToeAIView(ctx.author) + await ctx.respond( + embed=discord.Embed( + title="Tic Tac Toe", + description=f"X = {ctx.author.display_name}\nO = Bot", + color=discord.Color.random(), + ), + view=game, + ) + + +def setup(client): + client.add_cog(TicTacToeCog(client)) diff --git a/src/cogs/image/animals.py b/src/cogs/image/animals.py new file mode 100644 index 0000000..cb9003d --- /dev/null +++ b/src/cogs/image/animals.py @@ -0,0 +1,190 @@ +from enum import Enum + +import discord +from discord.commands import SlashCommandGroup + +from core import BaseCog +from core.helpers import get_request, ImageAPIFail + + +def animal_embed(response: dict = None): + if response is None: + raise ImageAPIFail + + em = discord.Embed( + title=response["title"], + description=response["desc"], + color=discord.Color.random(), + ) + + em.set_image(url=response["image"]) + + return em + + +def animal_embed_randomapi(response: dict | None, title: str): + if response is None: + raise ImageAPIFail + + em = discord.Embed( + title=title, description=response["fact"], color=discord.Color.random() + ) + + em.set_image(url=response["image"]) + + return em + + +class AnimalURLS(Enum): + # some random api + dog = "https://some-random-api.ml/animal/dog" + cat = "https://some-random-api.ml/animal/cat" + fox = "https://some-random-api.ml/animal/fox" + panda = "https://some-random-api.ml/animal/panda" + bird = "https://some-random-api.ml/animal/bird" + kangaroo = "https://some-random-api.ml/animal/kangaroo" + koala = "https://some-random-api.ml/animal/koala" + raccoon = "https://some-random-api.ml/animal/raccoon" + red_panda = "https://some-random-api.ml/animal/red_panda" + + # other + capybara = "https://api.capy.lol/v1/capybara?json=true" + shibe = "https://shibe.online/api/shibes?count=1&urls=true&httpsUrls=true" + duck = "https://random-d.uk/api/v2/quack" + whale = "https://some-random-api.ml/img/whale" + + +class Animals(BaseCog): + + animal = SlashCommandGroup("animal", "Get images and facts about animals") + + @animal.command(description="Show a picture of a dog") + async def dog(self, ctx: discord.ApplicationContext): + url = AnimalURLS.dog.value + response = await get_request(url) + + await ctx.respond(embed=animal_embed_randomapi(response, "Dog!")) + + @animal.command(description="Show a picture of a cat") + async def cat(self, ctx: discord.ApplicationContext): + url = AnimalURLS.cat.value + response = await get_request(url) + + await ctx.respond(embed=animal_embed_randomapi(response, "Cat!")) + + @animal.command(description="Show a picture of a fox") + async def fox(self, ctx: discord.ApplicationContext): + url = AnimalURLS.fox.value + response = await get_request(url) + + await ctx.respond(embed=animal_embed_randomapi(response, "Fox!")) + + @animal.command(description="Show a picture of a panda") + async def panda(self, ctx: discord.ApplicationContext): + url = AnimalURLS.panda.value + response = await get_request(url) + + await ctx.respond(embed=animal_embed_randomapi(response, "Panda!")) + + @animal.command(description="Show a picture of a bird") + async def bird(self, ctx: discord.ApplicationContext): + url = AnimalURLS.bird.value + response = await get_request(url) + + await ctx.respond(embed=animal_embed_randomapi(response, "Bird!")) + + @animal.command(description="Show a picture of a kangaroo") + async def kangaroo(self, ctx: discord.ApplicationContext): + url = AnimalURLS.kangaroo.value + response = await get_request(url) + + await ctx.respond(embed=animal_embed_randomapi(response, "Kangaroo!")) + + @animal.command(description="Show a picture of a koala") + async def koala(self, ctx: discord.ApplicationContext): + url = AnimalURLS.koala.value + response = await get_request(url) + + await ctx.respond(embed=animal_embed_randomapi(response, "Koala!")) + + @animal.command(description="Show a picture of a racoon") + async def raccon(self, ctx: discord.ApplicationContext): + url = AnimalURLS.raccoon.value + response = await get_request(url) + + await ctx.respond(embed=animal_embed_randomapi(response, "Racoon!")) + + @animal.command(description="Show a picture of a red panda") + async def redpanda(self, ctx: discord.ApplicationContext): + url = AnimalURLS.red_panda.value + response = await get_request(url) + + await ctx.respond(embed=animal_embed_randomapi(response, "Red Panda!")) + + @animal.command(description="Show a picture of a capybara") + async def capybara(self, ctx: discord.ApplicationContext): + url = AnimalURLS.capybara.value + image = await get_request(url) + + if ( + image is None + or image.get("data") is None + or image["data"].get("url") is None + ): + await ctx.respond(embed=animal_embed(None)) + + response = { + "desc": "Ok I Pull Up!", + "image": image["data"]["url"], + "title": "Capybara!", + } + await ctx.respond(embed=animal_embed(response)) + + @animal.command(description="Show a picture of a duck") + async def duck(self, ctx: discord.ApplicationContext): + url = AnimalURLS.duck.value + image = await get_request(url) + + if image is None or image.get("url") is None: + await ctx.respond(embed=animal_embed(None)) + + response = { + "desc": "Quack!", + "image": image["url"], + "title": "Duck!", + } + await ctx.respond(embed=animal_embed(response)) + + @animal.command(description="Show a picture of a shiba inu") + async def shibe(self, ctx: discord.ApplicationContext): + url = AnimalURLS.shibe.value + image = await get_request(url) + + if image is None or not image: + await ctx.respond(embed=animal_embed(None)) + + response = { + "desc": "Certified good boi!", + "image": image[0], + "title": "Shiba Inu!", + } + await ctx.respond(embed=animal_embed(response)) + + @animal.command(description="Show a picture of a whale") + async def whale(self, ctx: discord.ApplicationContext): + url = AnimalURLS.whale.value + image = await get_request(url) + + if image is None or not image: + await ctx.respond(embed=animal_embed(None)) + + response = { + "desc": "Big boi!", + "image": image["link"], + "title": "Whale!", + } + await ctx.respond(embed=animal_embed(response)) + + +def setup(client): + client.add_cog(Animals(client)) diff --git a/src/cogs/image/colors.py b/src/cogs/image/colors.py new file mode 100644 index 0000000..665e26c --- /dev/null +++ b/src/cogs/image/colors.py @@ -0,0 +1,85 @@ +import io + +import discord +import aiohttp +from PIL import Image +from discord.ext import commands + +from core import BaseCog + + +class Colors(BaseCog): + @commands.slash_command(description="Get the colors in an image") + async def get_colors( + self, ctx: discord.ApplicationContext, file: discord.Attachment + ): + await ctx.defer() + file = await file.read() + async with aiohttp.ClientSession() as session: + async with session.post( + "https://api.fusionsid.xyz/api/image/get_colors/?show_hex=true", + data={"image": file}, + ) as resp: + response = await resp.json() + if resp.ok is False: + return await ctx.respond( + embed=discord.Embed( + title="An error occured while trying to get the image", + description=( + "This could be because you didnt upload an image\n" + "If not the API basically had a skill issue.\n" + "If this persists and you are able to, report this as a bug with :)" + ), + color=discord.Colour.red(), + ), + ephemeral=True, + ) + + palette_joined = "\n".join(response["palette"]) + em = discord.Embed( + title="Image Colors", + description=f"**Dominant Color:** {response['dominant_color']}\n**Palette:**\n{palette_joined}", + color=discord.Color.random(), + ) + + palette_img = Image.new("RGB", (300, 200)) + x, y = 0, 0 + for i in response["palette"]: + try: + palette_img.paste(Image.new("RGB", (100, 100), i), (x, y)) + except ValueError: + pass + x += 100 + if x == 400: + x = 0 + y += 100 + + palette_file = io.BytesIO() + palette_img.save(palette_file, "PNG") + palette_file.seek(0) + + try: + dcolor_img = Image.new("RGB", (150, 150), response["dominant_color"]) + except ValueError: + return await ctx.respond( + embed=em, + files=[ + discord.File(palette_file, "palette.png"), + ], + ) + + dcolor_file = io.BytesIO() + dcolor_img.save(dcolor_file, "PNG") + dcolor_file.seek(0) + + await ctx.respond( + embed=em, + files=[ + discord.File(dcolor_file, "dominant_color.png"), + discord.File(palette_file, "palette.png"), + ], + ) + + +def setup(client): + client.add_cog(Colors(client)) diff --git a/src/cogs/image/other.py b/src/cogs/image/other.py new file mode 100644 index 0000000..1a4c936 --- /dev/null +++ b/src/cogs/image/other.py @@ -0,0 +1,68 @@ +from enum import Enum + +import discord +from discord.ext import commands + +from core import BaseCog +from core.helpers import ImageAPIFail +from core.helpers import get_request_bytes + + +class ImageURLS(Enum): + ## Some Random API Endpoints + # overlays + comrade = "https://some-random-api.ml/canvas/overlay/comrade" + gay = "https://some-random-api.ml/canvas/overlay/gay" + wasted = "https://some-random-api.ml/canvas/overlay/wasted" + jail = "https://some-random-api.ml/canvas/overlay/jail" + triggered = "https://some-random-api.ml/canvas/overlay/triggered" + glass = "https://some-random-api.ml/canvas/overlay/glass" + passed = "https://some-random-api.ml/canvas/overlay/passed" + + # filters + blue = "https://some-random-api.ml/canvas/filter/blue" + red = "https://some-random-api.ml/canvas/filter/red" + blurple = "https://some-random-api.ml/canvas/filter/blurple" + green = "https://some-random-api.ml/canvas/filter/green" + invertgreyscale = "https://some-random-api.ml/canvas/filter/invertgreyscale" + sepia = "https://some-random-api.ml/canvas/filter/sepia" + color = "https://some-random-api.ml/canvas/filter/color" + greyscale = "https://some-random-api.ml/canvas/filter/greyscale" + brightness = "https://some-random-api.ml/canvas/filter/brightness" + + # misc + youtube = "https://some-random-api.ml/canvas/misc/youtube-comment" + blur = "https://some-random-api.ml/canvas/misc/blur" + spin = "https://some-random-api.ml/canvas/misc/spin" + circle = "https://some-random-api.ml/canvas/misc/circle" + pixelate = "https://some-random-api.ml/canvas/misc/pixelate" + lolice = "https://some-random-api.ml/canvas/misc/lolice" + oogway = "https://some-random-api.ml/canvas/misc/oogway" + heart = "https://some-random-api.ml/canvas/misc/heart" + tweet = "https://some-random-api.ml/canvas/misc/tweet" + horny = "https://some-random-api.ml/canvas/misc/horny" + lied = "https://some-random-api.ml/canvas/misc/lied" + nobitches = "https://some-random-api.ml/canvas/misc/nobitches" + simpcard = "https://some-random-api.ml/canvas/misc/simpcard" + stupid = "https://some-random-api.ml/canvas/misc/its-so-stupid" + + ## Other + unsplash = "https://source.unsplash.com/random" + + +class OtherImage(BaseCog): + @commands.slash_command(description="Get a random image from unsplash") + async def unsplash(self, ctx: discord.ApplicationContext, search_terms: str = None): + url = ImageURLS.unsplash.value + if search_terms is not None: + url += f"?{search_terms}" + + response = await get_request_bytes(url, bytes_io=True) + if response is None: + raise ImageAPIFail + + await ctx.respond(file=discord.File(response, "image.png")) + + +def setup(client): + client.add_cog(OtherImage(client)) diff --git a/src/cogs/leveling/leveling.py b/src/cogs/leveling/leveling.py new file mode 100644 index 0000000..788e11c --- /dev/null +++ b/src/cogs/leveling/leveling.py @@ -0,0 +1,375 @@ +import re +import asyncio + +import discord +from discord.ext import commands +from discord.commands import SlashCommandGroup + +from core import WhyBot, BaseCog +from core.helpers import ( + get_level_data, + get_member_data, + xp_needed, + update_member_data, + get_all_member_data, +) +from core.db import setup_leveling_guild + + +RATE = 1 +PER = 60 + + +class Leveling(BaseCog): + def __init__(self, client: WhyBot): + self.guild_cooldowns: dict[int, commands.CooldownMapping] = {} + super().__init__(client) + + leveling = SlashCommandGroup("leveling", "Leveling system related commands") + + async def is_member_cooldown(self, message: discord.Message): + bucket = self.guild_cooldowns[message.guild.id].get_bucket(message) + cooldown = bucket.update_rate_limit() + + if cooldown is None: + return False + + return True + + @commands.Cog.listener() + async def on_message(self, message: discord.Message): + if message.guild is None or message.author.bot: + return + + if self.guild_cooldowns.get(message.guild.id) is None: + self.guild_cooldowns[ + message.guild.id + ] = commands.CooldownMapping.from_cooldown( + RATE, PER, commands.BucketType.user + ) + + on_cooldown = await self.is_member_cooldown(message) + if on_cooldown is True: + return + + leveling_data = await get_level_data(self.client.db, message.guild.id) + if leveling_data is None: + return await setup_leveling_guild(self.client.db, message.guild.id) + + if ( + leveling_data.plugin_enabled is False + or message.channel.id in leveling_data.no_xp_channels + or bool( + set(leveling_data.no_xp_roles) + & {role.id for role in message.author.roles} + ) + ): + return + + member_data = await get_member_data( + self.client.db, message.author, message.guild.id + ) + + give_xp_amount = leveling_data.get_per_minute_xp() + member_data.member_total_xp += give_xp_amount + + next_level_xp = xp_needed(member_data.member_level + 1) + add_level, current_xp = divmod(member_data.member_total_xp, next_level_xp) + + if not add_level: + current_xp -= xp_needed(member_data.member_level) + + member_data.member_level += add_level + member_data.member_xp = current_xp + + await update_member_data(self.client.db, message, member_data) + + # Handle level up message: + if add_level: + # if its disabled + if not leveling_data.level_up_enabled: + return + levelup_message = leveling_data.level_up_text + member = message.author + replace_cases = { + "{member.name}": member.name, + "{member.displayname}": member.display_name, + "{member.mention}": member.mention, + "{server.name}": message.guild.name, + "{level.old}": member_data.member_level - add_level, + "{level}": member_data.member_level, + } + for code, replace_to in replace_cases.items(): + levelup_message = levelup_message.replace(code, str(replace_to)) + + def check(reaction: discord.reaction.Reaction, user): + return ( + reaction.message.id == msg.id + and reaction.emoji == "šŸ—‘ļø" + and user.id == message.author.id + ) + + try: + msg = await message.reply( + embed=discord.Embed( + title=member.display_name, + description=levelup_message, + color=discord.Color.green(), + ) + ) + try: + await msg.add_reaction("šŸ—‘ļø") + await self.client.wait_for( + "reaction_add", timeout=15.0, check=check + ) + await msg.delete() + except asyncio.TimeoutError: + await msg.remove_reaction("šŸ—‘ļø", member=self.client.user) + except (discord.Forbidden, discord.HTTPException): + pass # message failed to send (probably due to perms) + + @leveling.command(description="Toggle the leveling system") + @commands.guild_only() + @commands.has_guild_permissions(administrator=True) + async def toggle_leveling( + self, + ctx: discord.ApplicationContext, + ): + leveling_data = await get_level_data(self.client.db, ctx.guild.id) + if leveling_data is None: + return await setup_leveling_guild(self.client.db, ctx.guild.id) + + on_or_off = not leveling_data.plugin_enabled + + await self.client.db.execute( + "UPDATE leveling_guild SET plugin_enabled=$1 WHERE guild_id=$2", + on_or_off, + ctx.guild.id, + ) + + await ctx.respond( + embed=discord.Embed( + title="Leveling Toggled!", + description=( + f"Leveling system is now {'on āœ…' if on_or_off else 'off āŒ'}\nIf you" + f" wish to toggle it back {'off' if on_or_off else 'on'} run this" + " command again" + ), + color=discord.Color.green(), + ) + ) + + @leveling.command( + description="Calculate the amount of xp you need to reach a certain level" + ) + @commands.guild_only() + async def xp_needed(self, ctx: discord.ApplicationContext, level: int = None): + member_data = await get_member_data(self.client.db, ctx.author, ctx.guild.id) + if level is None: + next_level = member_data.member_level + 1 + next_level_xp = xp_needed(next_level) + return await ctx.respond( + embed=discord.Embed( + title="XP Calculator", + description=f"Amount of XP required for level {next_level} = {next_level_xp}\nAmount of XP" + f" you need to get to reach level {next_level} = {next_level_xp - member_data.member_total_xp}", + color=discord.Color.random(), + ) + ) + + level_xp = xp_needed(level) + return await ctx.respond( + embed=discord.Embed( + title="XP Calculator", + description=f"Amount of XP required for level {level} = {level_xp}\nAmount of " + f"XP you need to get to reach level {level_xp} = {level_xp - member_data.member_total_xp}", + color=discord.Color.random(), + ) + ) + + @leveling.command(description="Toggle the level up message from being sent") + @commands.guild_only() + @commands.has_guild_permissions(administrator=True) + async def toggle_level_up( + self, + ctx: discord.ApplicationContext, + ): + leveling_data = await get_level_data(self.client.db, ctx.guild.id) + if leveling_data is None: + return await setup_leveling_guild(self.client.db, ctx.guild.id) + + on_or_off = not leveling_data.level_up_enabled + + await self.client.db.execute( + "UPDATE leveling_guild SET level_up_enabled=$1 WHERE guild_id=$2", + on_or_off, + ctx.guild.id, + ) + + await ctx.respond( + embed=discord.Embed( + title="Level Up Message Toggled!", + description=( + "Level Up Message system is now" + f" {'on āœ…' if on_or_off else 'off āŒ'}\nIf you wish to toggle it" + f" back {'off' if on_or_off else 'on'} run this command again" + ), + color=discord.Color.green(), + ) + ) + + @leveling.command(description="Set the amount of xp given for leveling") + @commands.guild_only() + @commands.has_guild_permissions(administrator=True) + async def set_xp( + self, + ctx: discord.ApplicationContext, + xp: discord.Option( + str, + description="XP given per minute. Type a number like '5' or a range like '15-30'", + ), + ): + if not re.match("[0-9-]", xp): + return await ctx.respond("Invalid Input!") + + if xp.isnumeric(): + await self.client.db.execute( + "UPDATE leveling_guild SET per_minute=$1 WHERE guild_id=$2", + xp, + ctx.guild.id, + ) + return await ctx.respond( + embed=discord.Embed( + title="Per minute XP updated!", + description=f"Per minute xp set to: {xp}", + color=discord.Color.random(), + ) + ) + + split_range = xp.split("-") + + if ( + len(split_range) == 2 + and split_range[0].isnumeric() + and split_range[1].isnumeric() + ): + await self.client.db.execute( + "UPDATE leveling_guild SET per_minute=$1 WHERE guild_id=$2", + xp, + ctx.guild.id, + ) + return await ctx.respond( + embed=discord.Embed( + title="Per minute XP updated!", + description=f"Per minute xp set to a range from {split_range[0]} to {split_range[1]}", + color=discord.Color.random(), + ) + ) + return await ctx.respond("Invalid Input!") + + @leveling.command(description="Set the text displayed when a user levels up") + @commands.guild_only() + @commands.has_guild_permissions(administrator=True) + async def set_level_text( + self, + ctx: discord.ApplicationContext, + text: discord.Option( + str, "The text you want to be sent when a user levels up" + ) = None, + ): + if text is None: + options = [ + "{member.name}", + "{member.displayname}", + "{member.mention}", + "{server.name}", + "{level.old}", + "{level}", + ] + embed = discord.Embed( + title="Level up text help page", + description=( + "The message you set with this command will be shown when a user" + " levels up.\nYou can also set placeholder " + " codes in this text which will be replaced with the relevant" + " information later" + ), + color=discord.Color.green(), + ) + embed.add_field(name="Options:", value="\n".join(options), inline=False) + embed.add_field( + name="Example:", + value="GG {member.name}, you just reached level \\**{level}**!", + inline=False, + ) + return await ctx.respond(embed=embed) + + await self.client.db.execute( + "UPDATE leveling_guild SET level_up_text=$1 WHERE guild_id=$2", + text, + ctx.guild.id, + ) + + await ctx.respond( + embed=discord.Embed( + title="Level up text set!", + description=f"Text set to:\n{text}", + color=discord.Color.green(), + ) + ) + + @leveling.command(description="Show your rank for leveling") + @commands.guild_only() + async def rank(self, ctx: discord.ApplicationContext): + await ctx.defer() + data = await get_all_member_data(self.client.db, ctx.guild.id) + for _idx, _member in enumerate(data): + if _member[1] == ctx.author.id: + member = _member + idx = _idx + break + else: + return await ctx.respond( + "You were not found in the database\nMaybe send some messages or check" + " if counting is enabled" + ) + + current_level_xp = xp_needed(member[4]) + next_level_xp = xp_needed(member[4] + 1) + + await ctx.respond( + embed=discord.Embed( + title=f"{ctx.author.display_name} - Rank #{idx+1}", + description=( + f"Level: {member[4]}. XP: {member[3]}/{next_level_xp-current_level_xp}. Total XP:" + f" {member[5]}\n\n**This embed is a placeholder for an image card**" + ), + ) + ) + + @leveling.command(description="Show the leveling leaderboard for the server") + @commands.guild_only() + async def leaderboard(self, ctx: discord.ApplicationContext): + await ctx.defer() + data = await get_all_member_data(self.client.db, ctx.guild.id) + embed = discord.Embed( + title="Leaderboard", + description="**This embed is a placeholder for an image lb coming soon**", + ) + for rank, member in enumerate(data[:10]): + current_level_xp = xp_needed(member[4]) + next_level_xp = xp_needed(member[4] + 1) + + embed.add_field( + name=f"{member[2]} - Rank #{rank+1}", + value=( + f"Level: {member[4]}. XP: {member[3]}/{next_level_xp-current_level_xp}. Total XP:" + f" {member[5]}" + ), + inline=False, + ) + await ctx.respond(embed=embed) + + +def setup(client): + client.add_cog(Leveling(client)) diff --git a/src/cogs/moderation/banning.py b/src/cogs/moderation/banning.py new file mode 100644 index 0000000..63be978 --- /dev/null +++ b/src/cogs/moderation/banning.py @@ -0,0 +1,176 @@ +import re +import discord +from discord.ext import commands +from discord.commands import default_permissions + +from core import BaseCog + + +class Banning(BaseCog): + @commands.slash_command(description="Ban a member from the server") + @default_permissions(ban_members=True) + @commands.bot_has_permissions(ban_members=True) + @commands.has_permissions(ban_members=True) + @commands.cooldown(1, 5, commands.BucketType.user) + async def ban( + self, ctx: discord.ApplicationContext, member: discord.Member, reason=None + ): + if ( + ctx.author.top_role.position > member.top_role.position + and (ctx.guild.get_member(self.client.user.id)).top_role.position + > member.top_role.position + ): + if reason is not None: + reason = f"{reason} - Requested by {ctx.author.name} ({ctx.author.id})" + await member.ban( + reason="".join( + reason + if reason is not None + else f"Requested by {ctx.author} ({ctx.author.id})" + ) + ) + await ctx.respond(f"Banned {member} successfully.") + else: + await ctx.respond( + "Sorry, you cannot perform that action due to role hierarchy\nMake sure" + " both you and the bot have higher perms then the target member" + ) + + @commands.slash_command(description="Mass ban users from the server") + @default_permissions(ban_members=True) + @commands.bot_has_permissions(ban_members=True) + @commands.has_permissions(ban_members=True) + @commands.cooldown(1, 5, commands.BucketType.user) + async def massban( + self, + ctx: discord.ApplicationContext, + members: discord.Option(str, "User ids or ping users seperated by a space"), + reason=None, + ): + try: + members = [int(i) for i in re.sub("\\<|\\>|@", "", members).split(" ")] + except ValueError: + return await ctx.respond("Invalid input was provided", ephemeral=True) + banned_members = [] + for member in members: + try: + member = await self.client.fetch_user(member) + except discord.NotFound: + continue + if ( + ctx.author.top_role.position > member.top_role.position + and (ctx.guild.get_member(self.client.user.id)).top_role.position + > member.top_role.position + ): + if reason is not None: + reason = ( + f"{reason} - Requested by {ctx.author.name} ({ctx.author.id})" + ) + await member.ban( + reason="".join( + reason + if reason is not None + else f"Requested by {ctx.author} ({ctx.author.id})" + ) + ) + banned_members.append(member) + + names = "- \n".join([member.name for member in banned_members]) + em = discord.Embed( + title="Banned Members", + description=f"**Banned {len(banned_members)} members:**\n{names}", + color=ctx.author.color, + ) + em.add_field( + name=f"Could not ban {len(members)-len(banned_members)} members", + value="- \n".join( + [member.name for member in members if member not in banned_members] + ), + ) + await ctx.respond(embed=em) + + @commands.slash_command(description="Kick a specific member from the server") + @default_permissions(kick_members=True) + @commands.has_permissions(kick_members=True) + @commands.bot_has_permissions(kick_members=True) + @commands.cooldown(1, 5, commands.BucketType.user) + async def kick( + self, ctx: discord.ApplicationContext, member: discord.Member, reason=None + ): + if ( + ctx.author.top_role.position > member.top_role.position + and (ctx.guild.get_member(self.client.user.id)).top_role.position + > member.top_role.position + ): + if reason is not None: + reason = f"{reason} - Requested by {ctx.author.name} ({ctx.author.id})" + await member.kick( + reason="".join( + reason + if reason is not None + else f"Requested by {ctx.author} ({ctx.author.id})" + ) + ) + await ctx.respond(f"Kicked {member} successfully.") + else: + await ctx.respond( + "Sorry, you cannot perform that action due to role hierarchy\nMake sure" + " both you and the bot have higher perms then the target member" + ) + + @commands.slash_command(description="Mass kick members") + @default_permissions(kick_members=True) + @commands.bot_has_permissions(kick_members=True) + @commands.has_permissions(kick_members=True) + @commands.cooldown(1, 5, commands.BucketType.user) + async def masskick( + self, + ctx: discord.ApplicationContext, + members: discord.Option(str, "User ids or ping users seperated by a space"), + reason=None, + ): + try: + members = [int(i) for i in re.sub("\\<|\\>|@", "", members).split(" ")] + except ValueError: + return await ctx.respond("Invalid input was provided", ephemeral=True) + kicked_members = [] + for member in members: + try: + member = await self.client.fetch_user(member) + except discord.NotFound: + continue + if ( + ctx.author.top_role.position > member.top_role.position + and (ctx.guild.get_member(self.client.user.id)).top_role.position + > member.top_role.position + ): + if reason is not None: + reason = ( + f"{reason} - Requested by {ctx.author.name} ({ctx.author.id})" + ) + await member.kick( + reason="".join( + reason + if reason is not None + else f"Requested by {ctx.author} ({ctx.author.id})" + ) + ) + kicked_members.append(member) + + names = "- \n".join([member.name for member in kicked_members]) + em = discord.Embed( + title="kickned Members", + description=f"**Kicked {len(kicked_members)} members:**\n{names}", + color=ctx.author.color, + ) + em.add_field( + name=f"Could not kick {len(members)-len(kicked_members)} members", + value="- \n".join( + [member.name for member in members if member not in kicked_members] + ), + ) + await ctx.respond(embed=em) + + +def setup(client): + client.add_cog(Banning(client)) diff --git a/src/cogs/moderation/channel.py b/src/cogs/moderation/channel.py new file mode 100644 index 0000000..51cc5c8 --- /dev/null +++ b/src/cogs/moderation/channel.py @@ -0,0 +1,247 @@ +import asyncio + +import discord +from discord.ext import commands +from discord.commands import default_permissions + +from core import BaseCog + + +class Channels(BaseCog): + @commands.slash_command(description="Create a text channel") + @default_permissions(manage_channels=True) + @commands.has_permissions(manage_channels=True) + @commands.bot_has_permissions(manage_channels=True) + async def create_channel( + self, + ctx: discord.ApplicationContext, + channel_name: str, + category: discord.CategoryChannel = None, + slowmode: int = None, + nsfw: bool = False, + ): + try: + channel = await ctx.guild.create_text_channel( + channel_name, category=category, slowmode_delay=slowmode, nsfw=nsfw + ) + except discord.HTTPException: + return await ctx.respond("Failed to create thing") + + await ctx.respond( + embed=discord.Embed( + title="Created te Channel!", + description=f"Created text channel {channel.mention}", + color=discord.Color.green(), + ) + ) + + @commands.slash_command(description="Delete a text channel") + @default_permissions(manage_channels=True) + @commands.has_permissions(manage_channels=True) + @commands.bot_has_permissions(manage_channels=True) + async def delete_channel( + self, ctx: discord.ApplicationContext, channel: discord.TextChannel + ): + try: + await channel.delete() + except discord.HTTPException: + return await ctx.respond("Failed to delete the channel") + + await ctx.respond( + embed=discord.Embed( + title="Deleted Channel!", + description=f"Deleted channel {channel.name}", + color=discord.Color.green(), + ) + ) + + @commands.slash_command(description="Create a voice channel") + @default_permissions(manage_channels=True) + @commands.has_permissions(manage_channels=True) + @commands.bot_has_permissions(manage_channels=True) + async def create_vc( + self, + ctx: discord.ApplicationContext, + channel_name: str, + category: discord.CategoryChannel = None, + reason: str = None, + user_limit: int = None, + ): + try: + channel = await ctx.channel.create_voice_channel( + channel_name, category=category, reason=reason, user_limit=user_limit + ) + except discord.HTTPException: + return await ctx.respond("Failed to create channel") + + await ctx.respond( + embed=discord.Embed( + title="Created Channel!", + description=f"Created voice channel {channel.mention}", + color=discord.Color.green(), + ) + ) + + @commands.slash_command(description="Delete a voice channel") + @default_permissions(manage_channels=True) + @commands.has_permissions(manage_channels=True) + @commands.bot_has_permissions(manage_channels=True) + async def delete_vc( + self, ctx: discord.ApplicationContext, channel: discord.VoiceChannel + ): + try: + await channel.delete() + except discord.HTTPException: + return await ctx.respond("Failed to delete the channel") + + await ctx.respond( + embed=discord.Embed( + title="Deleted Channel!", + description=f"Deleted channel {channel.name}", + color=discord.Color.green(), + ) + ) + + @commands.slash_command(description="Put a channel into lockdown") + @default_permissions(manage_channels=True) + @commands.has_permissions(manage_channels=True) + @commands.bot_has_permissions(manage_channels=True) + @commands.cooldown(1, 5, commands.BucketType.user) + async def lockdown( + self, ctx: discord.ApplicationContext, channel: discord.TextChannel = None + ): + if channel is None: + channel = ctx.channel + await channel.set_permissions(ctx.guild.default_role, send_messages=False) + em = discord.Embed( + title="Lockdown", + description=f"Channel ({channel.mention}) is now in lockdown", + color=ctx.author.color, + ) + return await ctx.respond(embed=em) + + @commands.slash_command(description="Unlock a channel that was in lockdown") + @default_permissions(manage_channels=True) + @commands.has_permissions(manage_channels=True) + @commands.bot_has_permissions(manage_channels=True) + @commands.cooldown(1, 5, commands.BucketType.user) + async def unlock( + self, ctx: discord.ApplicationContext, channel: discord.TextChannel = None + ): + if channel is None: + channel = ctx.channel + await channel.set_permissions(ctx.guild.default_role, send_messages=True) + em = discord.Embed( + title="Unlocked", + description=f"Channel ({channel.mention}) is now longer in lockdown", + color=ctx.author.color, + ) + return await ctx.respond(embed=em) + + @commands.slash_command(description="Add slow mode to a channel") + @default_permissions(manage_channels=True) + @commands.has_permissions(manage_channels=True) + @commands.bot_has_permissions(manage_channels=True) + @commands.cooldown(1, 5, commands.BucketType.user) + async def slowmode( + self, + ctx: discord.ApplicationContext, + seconds: int = 5, + channel: discord.TextChannel = None, + ): + if channel is None: + channel = ctx.channel + if seconds > (60 * 60) * 6: + return await ctx.respond( + embed=discord.Embed( + title="To long!", + description="Can't set slowmode to more than 6 hours", + color=discord.Color.red(), + ephemeral=True, + ) + ) + elif seconds <= 0: + await channel.edit(slowmode_delay=0) + em = discord.Embed( + title="Slowmode", + description="Slowmode has been disabled", + color=ctx.author.color, + ) + return await ctx.respond(embed=em) + + await channel.edit(slowmode_delay=seconds) + em = discord.Embed( + title="Slowmode", + description=( + f"Slowmode has been set to {seconds} seconds for {channel.mention}" + ), + color=ctx.author.color, + ) + return await ctx.respond(embed=em) + + @commands.slash_command(description="Remove slowmode from a channel") + @default_permissions(manage_channels=True) + @commands.has_permissions(manage_channels=True) + @commands.bot_has_permissions(manage_channels=True) + @commands.cooldown(1, 5, commands.BucketType.user) + async def remove_slowmode( + self, ctx: discord.ApplicationContext, channel: discord.TextChannel = None + ): + if channel is None: + channel = ctx.channel + await channel.edit(slowmode_delay=0) + em = discord.Embed( + title="Slowmode", + description=f"Slowmode has been disabled for {channel.mention}", + color=ctx.author.color, + ) + return await ctx.respond(embed=em) + + @commands.slash_command( + description="Clear a certain amount of messages in a channel" + ) + @default_permissions(manage_channels=True) + @commands.has_permissions(manage_messages=True) + @commands.bot_has_permissions(manage_messages=True) + @commands.cooldown(1, 10, commands.BucketType.user) + async def clear(self, ctx: discord.ApplicationContext, amount: int = 10): + if amount < 50: + await ctx.channel.purge(limit=amount) + em = discord.Embed( + color=ctx.author.color, + title="Channel Message Purge", + description=f"Cleared {amount} messages from {ctx.channel.mention}", + ) + return await ctx.respond(embed=em, ephemeral=True) + + em = discord.Embed( + color=discord.Color.red(), + title="To many messages!", + description="Can't clear more than 50 messages at a time", + ) + await ctx.respond(embed=em, ephemeral=True) + + @commands.slash_command( + description="Purge all the messages sent by a specific member" + ) + @default_permissions(manage_channels=True) + @commands.has_guild_permissions(manage_messages=True) + @commands.has_guild_permissions(manage_messages=True) + async def purgeuser(self, ctx: discord.ApplicationContext, member: discord.Member): + tasks = [ + channel.purge(limit=1000, check=lambda message: member == message.author) + for channel in ctx.guild.text_channels + ] + + asyncio.gather(*tasks) + + em = discord.Embed( + color=ctx.author.color, + title="Channel Message Purge", + description=f"Purged messages from {member.mention}", + ) + return await ctx.send(embed=em) + + +def setup(client): + client.add_cog(Channels(client)) diff --git a/src/cogs/moderation/tickets.py b/src/cogs/moderation/tickets.py new file mode 100644 index 0000000..6c5c039 --- /dev/null +++ b/src/cogs/moderation/tickets.py @@ -0,0 +1,410 @@ +import io +import time + +import discord +import chat_exporter +from discord.ext import commands +from discord.commands import SlashCommandGroup, default_permissions + +from core import BaseCog +from core.helpers import ConfirmView +from core.db import setup_tickets +from core.utils import discord_timestamp +from core.models import ( + TicketGuild, + Ticket, + TicketView, + NewTicketView, + ClosedTicketView, +) + + +class Tickets(BaseCog): + ticket = SlashCommandGroup("ticket", "Ticket related commands") + + async def __get_ticket_config(self, guild_id: int): + data: list[list] = await self.client.db.fetch( + "SELECT * FROM ticket_guild WHERE guild_id=$1", guild_id + ) + if len(data) == 0: + await setup_tickets(self.client.db, guild_id) + default_data = [guild_id, [], [], False, None, []] + return TicketGuild(*default_data) + return TicketGuild(*data[0]) + + async def __get_tickets(self, guild_id: int): + data: list[list] = await self.client.db.fetch( + "SELECT * FROM tickets WHERE guild_id=$1", guild_id + ) + if len(data) == 0: + return None + return list(map(lambda x: Ticket(*x), data)) + + @ticket.command() + async def new(self, ctx: discord.ApplicationContext, reason=None): + await ctx.defer() + ticket_config = await self.__get_ticket_config(ctx.guild.id) + if ctx.author.id in ticket_config.banned_users: + return await ctx.respond( + "You are banned from making tickets", ephemeral=True + ) + + # figure out if to make it in a category or not + if ticket_config.category in (0, None): + category = None + else: + category = list( + filter(lambda i: i.id == ticket_config.category, ctx.guild.categories) + ) + category = None if len(category) == 0 else category[0] + + # create channel + channel = await ctx.guild.create_text_channel( + f"ticket-{ctx.author.id}", category=category + ) + + # set channel perms + await channel.set_permissions( + ctx.guild.get_role(ctx.guild.id), send_messages=False, read_messages=False + ) + perms = { + "send_messages": True, + "read_messages": True, + "add_reactions": True, + "embed_links": True, + "attach_files": True, + "read_message_history": True, + "external_emojis": True, + } + await channel.set_permissions(ctx.author, **perms) + for role in ticket_config.roles_allowed: + if (role := ctx.guild.get_role(role)) is not None: + await channel.set_permissions(role, **perms) + + ticket_data = [ctx.guild.id, channel.id, ctx.author.id, int(time.time())] + ticket_id = await self.client.db.fetch( + """INSERT INTO tickets ( + guild_id, channel_id, ticket_creator, time_created + ) VALUES ($1, $2, $3, $4) RETURNING id""", + *ticket_data, + ) + + ticket = Ticket(ticket_id[0][0], *ticket_data) + + embed = discord.Embed( + title=f"New ticket from {ctx.author.name}!", + description=f"**Please wait, support will be with you shortly!**\ + \n\nTicket Created: {discord_timestamp(ticket.time_created, 'ts')}", + color=discord.Color.random(), + ) + embed.add_field(name="Reason Provided:", value=str(reason)) + embed.add_field(name="Ticket ID:", value=ticket.ticket_id) + embed.set_footer(text="To close this ticket click the close button") + view = TicketView(ticket, ctx.author, self.client) + + who_to_ping = filter( + lambda x: x is not None, map(ctx.guild.get_role, ticket_config.ping_roles) + ) + await channel.send( + embed=embed, view=view, content="".join(i.mention for i in who_to_ping) + ) + + await ctx.respond(f"A new ticket has been created for you: {channel.mention}") + + @ticket.command() + async def close(self, ctx: discord.ApplicationContext): + await ctx.defer() + + tickets = await self.__get_tickets(ctx.guild.id) + ticket = list(filter(lambda t: t.channel_id == ctx.channel_id, tickets)) + if not ticket: + embed = discord.Embed( + title="Could Not Find Ticket", + description="This channel is not a ticket", + color=discord.Color.random(), + ) + return await ctx.respond(embed=embed, ephemeral=True) + ticket: Ticket = ticket[0] + are_you_sure = ConfirmView(target=ctx.author) + em = discord.Embed( + title="Close Ticket?", + description=( + f"{ctx.author.mention} are you sure you want to close this ticket?\nThis action removes \ + access for the person who made the ticket from the channel\nHowever this action can be reversed" + ), + color=discord.Color.random(), + ) + await ctx.respond(embed=em, view=are_you_sure, ephemeral=True) + await are_you_sure.wait() + + # if they hit no, do nothing + if not are_you_sure.accepted: + return + + # send the closed ticket view + member = ctx.guild.get_member(ticket.ticket_creator) + if member is None: + return + view = ClosedTicketView(ticket, ctx.author, self.client) + embed = discord.Embed( + title="Ticket has been closed!", + description=f"Closed by: {ctx.author.mention}\nTo view a transcript of messages in this \ + channel hit the transcript button.\nNote that this will be an html file \ + so you'll need to download it and open in a webbrowser", + ) + await ctx.send(embed=embed, view=view) + await ctx.channel.set_permissions( + member, send_messages=False, read_messages=False + ) + + @ticket.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def rename(self, ctx: discord.ApplicationContext, new_name: str): + await ctx.defer() + tickets = await self.__get_tickets(ctx.guild.id) + ticket = list(filter(lambda t: t.channel_id == ctx.channel_id, tickets)) + if not ticket: + embed = discord.Embed( + title="Could Not Find Ticket", + description="This channel is not a ticket", + color=discord.Color.random(), + ) + return await ctx.respond(embed=embed, ephemeral=True) + ticket = ticket[0] + await ctx.channel.edit(name=new_name) + await ctx.respond("Ticket name updated!") + + @ticket.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def transcript(self, ctx: discord.ApplicationContext): + transcript = await chat_exporter.export( + ctx.channel, + limit=1000, + tz_info="UTC", + military_time=True, + bot=self.client, + ) + + if transcript is None: + return await ctx.respond("Failed to generate transcript", ephemeral=True) + + self.transcript_file = io.BytesIO(transcript.encode()) + await ctx.respond( + file=discord.File( + self.transcript_file, + filename=f"transcript-{ctx.channel.name}.html", + ) + ) + + @ticket.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def button(self, ctx: discord.ApplicationContext): + view = NewTicketView(ctx.guild.id, self.client) + await ctx.respond("Created button view!", ephemeral=True) + await ctx.send( + embed=discord.Embed( + title="New Ticket", + description="Press the New Ticket button to create a new ticket!", + color=discord.Color.random(), + ), + view=view, + ) + await self.client.db.execute( + "UPDATE ticket_guild SET create_button=true WHERE guild_id=$1", ctx.guild.id + ) + + @ticket.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def delete(self, ctx: discord.ApplicationContext): + await ctx.defer() + tickets = await self.__get_tickets(ctx.guild.id) + ticket = list(filter(lambda t: t.channel_id == ctx.channel_id, tickets)) + if not ticket: + + embed = discord.Embed( + title="Could Not Find Ticket", + description="This channel is not a ticket", + color=discord.Color.random(), + ) + return await ctx.respond(embed=embed, ephemeral=True) + ticket: Ticket = ticket[0] + await self.client.db.execute( + "DELETE FROM tickets WHERE id=$1 AND guild_id=$2", + ticket.ticket_id, + ctx.guild.id, + ) + await ctx.channel.delete() + + @ticket.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def delete_all(self, ctx: discord.ApplicationContext): + # send a button to confirm to close or not + are_you_sure = ConfirmView(target=ctx.author) + em = discord.Embed( + title="Delete ALL tickets?", + description=( + f"{ctx.author.mention} are you sure you want to close and delete ALL tickets?\n\ + This action can not be reversed" + ), + color=discord.Color.random(), + ) + await ctx.respond(embed=em, view=are_you_sure, ephemeral=True) + await are_you_sure.wait() + if not are_you_sure.accepted: + return + + tickets = await self.__get_tickets(ctx.guild.id) + channels = map(self.client.get_channel, (i.channel_id for i in tickets)) + for channel in channels: + try: + await channel.delete() + except (AttributeError, discord.Forbidden): + continue + await self.client.db.execute( + "DELETE FROM tickets WHERE guild_id=$1", ctx.guild.id + ) + await ctx.send("All tickets have been deleted!") + + @ticket.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def add_ping_role(self, ctx: discord.ApplicationContext, role: discord.Role): + ticket_config = await self.__get_ticket_config(ctx.guild.id) + if role.id in ticket_config.ping_roles: + return await ctx.respond( + "Role is already in list for being pinged when tickets are made" + ) + await self.client.db.execute( + "UPDATE ticket_guild SET ping_roles = array_append(ping_roles, $1), \ + roles_allowed = array_append(roles_allowed, $1) WHERE guild_id=$2", + role.id, + ctx.guild.id, + ) + await ctx.respond( + embed=discord.Embed( + title="Ping Role Added", + description=f"{role.mention} will now be pinged when a new ticket is made", + color=discord.Color.random(), + ) + ) + + @ticket.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def remove_ping_role( + self, ctx: discord.ApplicationContext, role: discord.Role + ): + ticket_config = await self.__get_ticket_config(ctx.guild.id) + if role.id not in ticket_config.ping_roles: + return await ctx.respond( + "Role was already not in the list for being pinged when tickets are made" + ) + await self.client.db.execute( + "UPDATE ticket_guild SET ping_roles = array_remove(ping_roles, $1) WHERE guild_id=$2", + role.id, + ctx.guild.id, + ) + await ctx.respond( + embed=discord.Embed( + title="Ping Role Removed", + description=f"{role.mention} will no longer be pinged when a new ticket is made", + color=discord.Color.random(), + ) + ) + + @ticket.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def add_allowed_role( + self, ctx: discord.ApplicationContext, role: discord.Role + ): + ticket_config = await self.__get_ticket_config(ctx.guild.id) + if role.id in ticket_config.roles_allowed: + return await ctx.respond( + "Role is already in the list that have access to new tickets" + ) + await self.client.db.execute( + "UPDATE ticket_guild SET roles_allowed = array_append(roles_allowed, $1) WHERE guild_id=$2", + role.id, + ctx.guild.id, + ) + await ctx.respond( + embed=discord.Embed( + title="Role Access Added", + description=f"{role.mention} now has access to new tickets", + color=discord.Color.random(), + ) + ) + + @ticket.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def remove_allowed_role( + self, ctx: discord.ApplicationContext, role: discord.Role + ): + ticket_config = await self.__get_ticket_config(ctx.guild.id) + if role.id not in ticket_config.roles_allowed: + return await ctx.respond( + "Role is already not in the list that have access to new tickets" + ) + await self.client.db.execute( + "UPDATE ticket_guild SET roles_allowed = array_remove(roles_allowed, $1) WHERE guild_id=$2", + role.id, + ctx.guild.id, + ) + await ctx.respond( + embed=discord.Embed( + title="Role Access Removed", + description=f"{role.mention} no longer has access to new tickets", + color=discord.Color.random(), + ) + ) + + @ticket.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def ban_user(self, ctx: discord.ApplicationContext, member: discord.Member): + ticket_config = await self.__get_ticket_config(ctx.guild.id) + if member.id in ticket_config.banned_users: + return await ctx.respond("User is already banned from making tickets") + await self.client.db.execute( + "UPDATE ticket_guild SET banned_users = array_append(banned_users, $1) WHERE guild_id=$2", + member.id, + ctx.guild.id, + ) + await ctx.respond( + embed=discord.Embed( + title="User Banned", + description=f"{member.mention} is banned from making tickets", + color=discord.Color.random(), + ) + ) + + @ticket.command() + @default_permissions(administrator=True) + @commands.has_permissions(administrator=True) + async def unban_user(self, ctx: discord.ApplicationContext, member: discord.Member): + ticket_config = await self.__get_ticket_config(ctx.guild.id) + if member.id not in ticket_config.banned_users: + return await ctx.respond("User is already unbanned from making tickets") + await self.client.db.execute( + "UPDATE ticket_guild SET banned_users = array_remove(banned_users, $1) WHERE guild_id=$2", + member.id, + ctx.guild.id, + ) + await ctx.respond( + embed=discord.Embed( + title="User Unbanned", + description=f"{member.mention} is no longer banned from making tickets", + color=discord.Color.random(), + ) + ) + + +def setup(client): + client.add_cog(Tickets(client)) diff --git a/src/cogs/owner/blacklist.py b/src/cogs/owner/blacklist.py new file mode 100644 index 0000000..55a0013 --- /dev/null +++ b/src/cogs/owner/blacklist.py @@ -0,0 +1,229 @@ +import discord +from discord.ext import commands +from discord.commands import SlashCommandGroup + +from core import WhyBot +from core.helpers import UserAlreadyBlacklisted, UserAlreadyWhitelisted, GUILD_IDS + + +class Blacklist(commands.Cog): + def __init__(self, client): + self.client: WhyBot = client + + blacklisted = SlashCommandGroup( + "blacklist", "Commands for why bot blacklist management. OWNER ONLY" + ) + + @blacklisted.command( + name="blacklist", + description="ban a user from using whybot", + guild_ids=GUILD_IDS, + ) + @commands.is_owner() + async def blacklist( + self, + ctx: discord.ApplicationContext, + user_id: discord.Option(str, description="User id of user to ban"), + ): + """ + This command is used to ban a user from using Why Bot + + Help Info: + ---------- + Category: Owner + + Usage: blacklist + """ + if not user_id.isnumeric(): + return await ctx.respond( + embed=discord.Embed( + title="Invalid discord user id", + description="Please provide an integer", + color=ctx.author.color, + ), + ephemeral=True, + ) + + user_id = int(user_id) + + try: + await self.client.fetch_user(user_id) + except ( + discord.NotFound, + discord.HTTPException, + discord.ApplicationCommandInvokeError, + ): + return await ctx.respond( + embed=discord.Embed( + title="Something went wrong fetching the user", + description=( + "Most likely an invalid discord user id.\nPlease provide a real" + " user" + ), + color=ctx.author.color, + ), + ephemeral=True, + ) + + try: + await self.client.blacklist_user(user_id) + except UserAlreadyBlacklisted: + return await ctx.respond( + embed=discord.Embed( + title="User Already Blacklisted", + description=( + "The user you tried to blacklist was already blacklisted." + ), + color=ctx.author.color, + ), + ephemeral=True, + ) + + await ctx.respond( + embed=discord.Embed( + description="User blacklisted successfuly!", color=ctx.author.color + ), + ephemeral=True, + ) + + @blacklisted.command( + name="whitelist", + description="unban a user from using whybot", + guild_ids=GUILD_IDS, + ) + @commands.is_owner() + async def whitelist( + self, + ctx: discord.ApplicationContext, + user_id: discord.Option(str, description="User id of user to unban"), + ): + """ + This command is used to unban a user from using Why Bot + + Help Info: + ---------- + Category: Owner + + Usage: whitelist + """ + if not user_id.isnumeric(): + return await ctx.respond( + embed=discord.Embed( + title="Invalid discord user id", + description="Please provide an integer", + color=ctx.author.color, + ), + ephemeral=True, + ) + + user_id = int(user_id) + + try: + await self.client.fetch_user(user_id) + except ( + discord.NotFound, + discord.HTTPException, + discord.ApplicationCommandInvokeError, + ): + return await ctx.respond( + embed=discord.Embed( + title="Something went wrong fetching the user", + description=( + "Most likely an invalid discord user id.\nPlease provide a real" + " user" + ), + color=ctx.author.color, + ), + ephemeral=True, + ) + + try: + await self.client.whitelist_user(user_id) + except UserAlreadyWhitelisted: + return await ctx.respond( + embed=discord.Embed( + title="User Already Whitelisted", + description=( + "The user you tried to whitelist was already whitelisted." + ), + color=ctx.author.color, + ), + ephemeral=True, + ) + + await ctx.respond( + embed=discord.Embed( + description="User whitelisted successfuly!", color=ctx.author.color + ), + ephemeral=True, + ) + + @blacklisted.command( + guild_ids=GUILD_IDS, description="Check if a user is blacklisted" + ) + @commands.is_owner() + async def isblacklisted( + self, + ctx: discord.ApplicationContext, + user_id: discord.Option(str, description="User id of user to check") = None, + ): + users = await self.client.get_blacklisted_users(reasons=True) + if user_id is not None: + if not user_id.isnumeric(): + return await ctx.respond( + embed=discord.Embed( + title="Invalid discord user id", + description="Please provide an integer", + color=ctx.author.color, + ), + ephemeral=True, + ) + + user_id = int(user_id) + for userid, reason in users: + if userid == user_id: + try: + user = await self.client.fetch_user(user_id) + except ( + discord.NotFound, + discord.HTTPException, + discord.ApplicationCommandInvokeError, + ): + return await ctx.respond( + embed=discord.Embed( + title="Something went wrong fetching the user", + description=( + "Most likely an invalid discord user id.\nPlease" + " provide a real user" + ), + color=ctx.author.color, + ), + ephemeral=True, + ) + + return await ctx.respond( + embed=discord.Embed( + title=( + f"User: {user.name}#{user.discriminator} ({user.id}) is" + " blacklisted" + ), + description=f"Reason Provided: {reason}", + color=ctx.author.color, + ) + ) + embed = discord.Embed( + title=f"User with id {user_id} is NOT blacklisted", + color=ctx.author.color, + ) + return await ctx.respond(embed=embed) + + await ctx.respond( + embed=discord.Embed( + title="Blacklisted User IDs", + description="\n".join(str(i[0]) for i in users), + ) + ) + + +def setup(client): + client.add_cog(Blacklist(client)) diff --git a/src/cogs/owner/cog_tools.py b/src/cogs/owner/cog_tools.py new file mode 100644 index 0000000..8f26914 --- /dev/null +++ b/src/cogs/owner/cog_tools.py @@ -0,0 +1,120 @@ +import discord +from discord.ext import commands +from discord.commands import SlashCommandGroup + +from core import WhyBot +from core.helpers import GUILD_IDS + + +class CogTools(commands.Cog): + def __init__(self, client: WhyBot): + self.client = client + + cogtools = SlashCommandGroup( + "cogtools", "Commands for why bot cogs management. OWNER ONLY" + ) + + @cogtools.command(guild_ids=GUILD_IDS, description="Reload a cog") + @commands.is_owner() + async def reload(self, ctx: discord.ApplicationContext, extension): + """This command is used to reload a cog""" + + if extension not in self.client.cogs_list.keys(): + return await ctx.respond( + embed=discord.Embed( + title="Cog doesn't exist or was not loaded", + description="Use listcogs command to check cogs", + color=ctx.author.color, + ), + ephemeral=True, + ) + + self.client.reload_extension(self.client.cogs_list[extension]) + embed = discord.Embed( + title="Reload", + description=f"{extension} successfully reloaded", + color=ctx.author.color, + ) + await ctx.respond(embed=embed, ephemeral=True) + + @cogtools.command(guild_ids=GUILD_IDS, description="Load a cog") + @commands.is_owner() + async def load(self, ctx: discord.ApplicationContext, extension, name): + """This command is used to load a cog""" + + try: + self.client.load_extension(extension) + except discord.ApplicationCommandInvokeError: + await ctx.respond( + embed=discord.Embed( + title="Cog doesn't exist", + description="Please provied path properly", + color=ctx.author.color, + ), + ephemeral=True, + ) + self.client.cogs_list[name] = extension + + embed = discord.Embed( + title="Load", + description=f"{extension} successfully loaded", + color=ctx.author.color, + ) + await ctx.respond(embed=embed, ephemeral=True) + + @cogtools.command(guild_ids=GUILD_IDS, description="Unload a cog") + @commands.is_owner() + async def unload(self, ctx: discord.ApplicationContext, extension): + """This command is used to unload a cog""" + + if extension not in self.client.cogs_list.keys(): + await ctx.respond( + embed=discord.Embed( + title="Cog doesn't exist or was not loaded", + description="Use listcogs command to check cogs", + color=ctx.author.color, + ), + ephemeral=True, + ) + + self.client.unload_extension(self.client.cogs_list[extension]) + self.client.cogs_list.pop(extension) + + embed = discord.Embed( + title="Unload", + description=f"{extension} successfully unloaded", + color=ctx.author.color, + ) + await ctx.respond(embed=embed, ephemeral=True) + + @cogtools.command(guild_ids=GUILD_IDS, description="List all the cogs") + @commands.is_owner() + async def listcogs(self, ctx: discord.ApplicationContext): + """This command lists the cogs that the bot has""" + return await ctx.respond( + embed=discord.Embed( + title="Why Bot Cogs List", + description="\n".join(self.client.cogs_list.keys()), + color=ctx.author.color, + ), + ephemeral=True, + ) + + @cogtools.command(guild_ids=GUILD_IDS, description="Reload all the cogs") + @commands.is_owner() + async def reloadall(self, ctx: discord.ApplicationContext): + """This command is used to reload all the cogs""" + + cogs = self.client.cogs_list.values() + + for cog in cogs: + self.client.reload_extension(cog) + + await ctx.respond( + embed=discord.Embed(title="All Cogs Reloaded", color=ctx.author.color), + ephemeral=True, + ) + + +def setup(client: WhyBot): + client.add_cog(CogTools(client)) diff --git a/src/cogs/owner/dmreply.py b/src/cogs/owner/dmreply.py new file mode 100644 index 0000000..89421f6 --- /dev/null +++ b/src/cogs/owner/dmreply.py @@ -0,0 +1,225 @@ +import io +import time +import datetime + +import discord +from discord.ext import commands + +from core import WhyBot +from core.helpers import blacklist_check, GUILD_IDS +from core.utils import format_seconds + + +class DMReply(commands.Cog): + """ + This is the dmreply cog + It is used to provide support to the users of the bot + When a dm is sent to the bot it will be copied and sent to the dm reply channel + The bot owner will have the option to reply to the message + You can send images, videos messages etc + """ + + def __init__(self, client: WhyBot): + self.client = client + + dm_channel = self.client.config["dm_reply_channel"] + if dm_channel in (0, None): + self.dm_reply_channel = None + self.dm_reply_channel = dm_channel + + self.cooldown = commands.CooldownMapping.from_cooldown( + 10, 60, commands.BucketType.user + ) + + async def is_member_cooldown(self, message: discord.Message): + bucket = self.cooldown.get_bucket(message) + cooldown = bucket.update_rate_limit() + + if cooldown is None: + return False + + retry_after = await format_seconds(int(cooldown)) + em = discord.Embed( + title="Wow buddy, Slow it down\nYou are on cooldown from sending dms", + description=( + f"Try again {f'in **{retry_after}' if retry_after != '' else 'now'}**" + ), + color=discord.Color.red(), + ) + await message.reply(embed=em) + + return True + + @commands.Cog.listener() + async def on_message(self, message: discord.Message): + """The on message event that handles the dm's""" + + if message.author.bot: + return + + # this should only run once + if self.dm_reply_channel is not None and isinstance(self.dm_reply_channel, int): + try: + self.dm_reply_channel = await self.client.fetch_channel( + self.dm_reply_channel + ) + except discord.errors.NotFound: + self.dm_reply_channel = None + + if not await blacklist_check(message.author.id): + return + + # check if in dm / thread + if not isinstance(message.channel, discord.DMChannel): + if ( + not isinstance(message.channel, discord.Thread) + or message.author.id != self.client.owner_id + ): + return + + data = await self.client.db.fetch( + "SELECT * FROM dmreply WHERE thread_id=$1", message.channel.id + ) + if not data: + return + + person = await self.client.fetch_user(data[0][0]) + + if message.content is not None and message.content != "": + await person.send(message.content) + await message.add_reaction("āœ…") + + if message.attachments is not None: + for attachment in message.attachments: + return await person.send(attachment.url) + return + + # If user is on cooldown + if await self.is_member_cooldown(message): + return + + # if error getting dm reply channel or not set + if self.dm_reply_channel is None: + return await message.channel.send( + "The owner has disabled the dm reply feature from the bot" + ) + + channel = self.dm_reply_channel + author = message.author + thread_id = await self.client.db.fetch( + "SELECT * FROM dmreply WHERE user_id=$1", author.id + ) + + if not thread_id: + thread_id = None + + thread = None + + if thread_id is not None: + try: + thread = channel.get_thread(thread_id[0][1]) + except discord.NotFound: + thread = None + if thread is None: + await self.client.db.execute( + "DELETE FROM dmreply WHERE user_id=$1", author.id + ) + + if thread is None: + emb = discord.Embed( + title=author.name, + color=discord.Color.random(), + timestamp=datetime.datetime.now(), + ) + emb.set_thumbnail(url=author.avatar.url) + + emb.add_field(name="User ID:", value=author.id, inline=False) + emb.add_field( + name="Created Account:", + value=f"", + inline=False, + ) + + shared_guilds = [ + guild.name for guild in self.client.guilds if author in guild.members + ] + emb.add_field( + name=f"Shared Guilds: ({len(shared_guilds)})", + value=", ".join(shared_guilds), + ) + + message_to_create_thread = await channel.send(embed=emb) + thread = await message_to_create_thread.create_thread(name=author.name) + await self.client.db.execute( + "INSERT INTO dmreply (user_id, thread_id) VALUES ($1, $2)", + author.id, + thread.id, + ) + if message.content != "" and message.content is not None: + await thread.send(message.content) + + if message.attachments is not None: + for attachment in message.attachments: + await thread.send(attachment.url) + + @commands.slash_command( + guild_ids=GUILD_IDS, description="Ban someone from DMing the bot" + ) + @commands.is_owner() + async def dm_ban(self, ctx: discord.ApplicationContext, _id: int): + """TODO""" + + raise NotImplementedError + + @commands.slash_command( + guild_ids=GUILD_IDS, description="Unban someone from DMing the bot" + ) + @commands.is_owner() + async def dm_unban(self, ctx: discord.ApplicationContext, _id: int): + """TODO""" + + raise NotImplementedError + + @commands.slash_command(guild_ids=GUILD_IDS, description="Close a thread of dms") + @commands.is_owner() + async def close_thread( + self, ctx: discord.ApplicationContext, author_id: str, archive: bool = False + ): + try: + author_id = int(author_id) + except ValueError: + return await ctx.respond("Not found") + + thread_id = await self.client.db.fetch( + "SELECT * FROM dmreply WHERE user_id=$1", author_id + ) + + if not thread_id: + thread_id = None + + if thread_id is not None: + try: + thread: discord.Thread = self.dm_reply_channel.get_thread( + thread_id[0][1] + ) + except discord.NotFound: + return await ctx.respond("Not found") + + messages = "\n".join( + [ + f"{message.author.name}: {message.content}\n---" + async for message in thread.history(oldest_first=True) + if message.content is not None or message.content != "" + ] + ) + file = io.BytesIO(messages.encode()) + await ctx.respond(file=discord.File(file, "messages.txt"), ephemeral=True) + + if archive: + return await thread.archive() + + await thread.delete() + + +def setup(client: WhyBot): + client.add_cog(DMReply(client)) diff --git a/src/cogs/owner/errors.py b/src/cogs/owner/errors.py new file mode 100644 index 0000000..3fd129f --- /dev/null +++ b/src/cogs/owner/errors.py @@ -0,0 +1,87 @@ +import os +import datetime + +import discord +import aiofiles +from discord.commands import SlashCommandGroup +from discord.ext import commands + +import __main__ +from core import WhyBot +from core.helpers import ErrorView, get_last_errors, GUILD_IDS + +LOGFILE_PATH = os.path.join(os.path.dirname(__main__.__file__), "logfiles/main.log") + + +class ErrorLog(commands.Cog): + def __init__(self, client: WhyBot): + self.client = client + + error = SlashCommandGroup( + "errors", "Commands for why bot error management. OWNER ONLY" + ) + + @error.command(guild_ids=GUILD_IDS, description="Send the whole logs file") + @commands.is_owner() + async def logs_file(self, ctx: discord.ApplicationContext): + file = discord.File(LOGFILE_PATH, "main.log") + await ctx.respond(file=file, ephemeral=True) + + @error.command(guild_ids=GUILD_IDS, description="Clear the logs file") + @commands.is_owner() + async def clear_logs_file(self, ctx: discord.ApplicationContext): + async with aiofiles.open(LOGFILE_PATH, "r+") as f: + await f.truncate(0) + await ctx.respond("Logfile Cleared", ephemeral=True) + + @error.command( + guild_ids=GUILD_IDS, description="Get the last error from the log file" + ) + @commands.is_owner() + async def get_last_error(self, ctx: discord.ApplicationContext, limit: int = 1): + """This command is used to get the most recent errors/error that the bot logged to the log file""" + + await ctx.defer() + if limit >= 24: + return await ctx.respond( + "To big of a number", + ephemeral=True, + ) + + errors = await get_last_errors(count=limit) + + if errors is None: + return await ctx.respond( + "No recent error (you probably cleaned the file recently)", + ephemeral=True, + ) + + em = discord.Embed( + title=f"Last {str(limit)+' Errors' if limit > 1 else 'Error'}", + color=ctx.author.color, + timestamp=datetime.datetime.utcnow(), + ) + if limit == 1: + title = list(errors.keys())[0] + err = list(errors.values())[0] + em.description = f"**{title[:220]}**```py\n{err[:1900]}```" + + view = ErrorView(self.client.owner_id, f"{title}{err}") + return await ctx.respond(embed=em, ephemeral=True, view=view) + + for key, value in errors.items(): + em.add_field( + name=f"{key[:220]} **(read logfile for full)**" + if len(key) >= 220 + else key, + value=f"```py\n{value[:980]}```\n**(read logfile for full)**" + if len(value) >= 980 + else f"```py\n{value}```", + inline=False, + ) + + await ctx.respond(embed=em, ephemeral=True) + + +def setup(client: WhyBot): + client.add_cog(ErrorLog(client)) diff --git a/src/cogs/owner/ipc.py b/src/cogs/owner/ipc.py new file mode 100644 index 0000000..4478be5 --- /dev/null +++ b/src/cogs/owner/ipc.py @@ -0,0 +1,18 @@ +from pycord.ext import ipc +from discord.ext import commands + +from core.models import WhyBot + + +class IPCRoutes(commands.Cog): + def __init__(self, client: WhyBot): + self.client = client + + @ipc.server.route() + async def get_member_count(self, data: dict): + guild = await self.client.fetch_guild(data.guild_id) + return guild.member_count + + +def setup(client): + client.add_cog(IPCRoutes(client)) diff --git a/src/cogs/owner/server.py b/src/cogs/owner/server.py new file mode 100644 index 0000000..d709a45 --- /dev/null +++ b/src/cogs/owner/server.py @@ -0,0 +1,157 @@ +import time +import datetime + +import discord +from discord.ext import commands + +from core import WhyBot +from core.utils import chunkify +from core.helpers import GUILD_IDS + + +class Server(commands.Cog): + def __init__(self, client: WhyBot): + self.client = client + + @commands.slash_command( + guild_ids=GUILD_IDS, description="List the servers that the bot is in" + ) + @commands.is_owner() + async def server_list(self, ctx: discord.ApplicationContext): + """ + This command is used to list the servers the bots in + It makes an embed with a list of the servers + """ + em = discord.Embed( + title=f"Connected on {str(len(self.client.guilds))} servers:", + color=ctx.author.color, + timestamp=datetime.datetime.utcnow(), + ) + + if len(self.client.guilds) < 10: + for guild in self.client.guilds: + em.add_field( + name=guild.name, + value=( + f"Owner: {guild.owner.name}\nMembers: {guild.member_count}\nID:" + f" {guild.id}" + ), + inline=True, + ) + return await ctx.respond(embed=em) + + chunked_list = await chunkify(self.client.guilds) + + em = discord.Embed( + title=f"Connected on {str(len(self.client.guilds))} servers:", + color=ctx.author.color, + timestamp=datetime.datetime.utcnow(), + ) + + await ctx.send(embed=em) + for chunk in chunked_list: + em = discord.Embed( + title="** **", + color=ctx.author.color, + timestamp=datetime.datetime.utcnow(), + ) + for guild in chunk: + em.add_field( + name=guild.name, + value=( + f"Owner: {guild.owner.name}\nMembers: {guild.member_count}\nID:" + f" {guild.id}" + ), + inline=True, + ) + await ctx.respond(embed=em) + + @commands.slash_command(guild_ids=GUILD_IDS, description="Fetch info for a server") + @commands.is_owner() + async def fetch_server_info(self, ctx: discord.ApplicationContext, server_id: int): + """This command is used to get info on a server that the bot is in""" + + guild = self.client.get_guild(server_id) + + em = discord.Embed( + title="Server Info:", + description=f"For: {guild.name}", + color=ctx.author.color, + ) + em.add_field(name="Member Count:", value=guild.member_count, inline=False) + em.add_field( + name="Created: ", + value=f"", + inline=False, + ) + em.add_field(name="ID:", value=guild.id, inline=False) + + em.set_thumbnail(url=guild.icon.url) + em.set_author( + name=f"Guild Owner: {guild.owner.name}", icon_url=guild.owner.avatar.url + ) + + await ctx.respond(embed=em) + + @commands.slash_command( + description="fetch user info", + guild_ids=GUILD_IDS, + ) + @commands.is_owner() + async def fetch_user_info(self, ctx: discord.ApplicationContext, user: str): + """ + This command is used to fetch info a specific user. + It is useful if you are messaging someone in dmreply and want to know who you are messaging + """ + + try: + user = await self.client.fetch_user(int(user)) + except discord.NotFound: + return + + emb = discord.Embed( + title=user.name, + color=discord.Color.random(), + timestamp=datetime.datetime.now(), + ) + emb.set_thumbnail(url=user.avatar.url) + + emb.add_field(name="User ID:", value=user.id, inline=False) + emb.add_field( + name="Created Account:", + value=f"", + inline=False, + ) + + shared_guilds = [ + guild.name for guild in self.client.guilds if user in guild.members + ] + emb.add_field( + name=f"Shared Guilds: ({len(shared_guilds)})", + value=", ".join(shared_guilds), + ) + + emb.timestamp = datetime.datetime.now() + + data = await self.client.db.fetch( + "SELECT * FROM command_stats WHERE user_id=$1", user.id + ) + if data: + usage = sum(i[3] for i in data) + emb.add_field( + name="Command Usage", + value=f"This user has used the bot {usage} times", + inline=False, + ) + else: + emb.add_field( + name="Why Bot Usage", + value="This user has not used any why bot commands", + inline=False, + ) + + await ctx.respond(embed=emb) + + +def setup(client): + client.add_cog(Server(client)) diff --git a/src/cogs/programming/runcode.py b/src/cogs/programming/runcode.py new file mode 100644 index 0000000..721ead6 --- /dev/null +++ b/src/cogs/programming/runcode.py @@ -0,0 +1,140 @@ +import io +from contextlib import redirect_stdout + +import aiohttp +import discord +from aioconsole import aexec +from discord.ext import commands + +from core import BaseCog +from core.helpers import GUILD_IDS, post_request, InputModalView + + +class RunCode(BaseCog): + @commands.slash_command() + @commands.cooldown(1, 15, commands.BucketType.user) + async def zprol(self, ctx: discord.ApplicationContext): + modal = InputModalView(label="Please enter the code:", title="Code Input") + await ctx.send_modal(modal) + await modal.wait() + + if modal.value is None: + return await ctx.respond("Invalid Input", ephemeral=True) + + async with aiohttp.ClientSession() as session: + async with session.post( + "https://zprol.epicpix.ga/api/v1/run", + json={"code": modal.value}, + ) as resp: + try: + response = await resp.json() + except aiohttp.ContentTypeError: + response = None + + if response is None or response.get("compilation") is None: + em = discord.Embed( + title="zProl", + description="Something went wrong!\n(API probably had a skill issue)", + color=discord.Color.blue(), + ) + return await ctx.respond(embed=em) + + em = discord.Embed( + title="zProl Code Output", + color=discord.Color.blue(), + ) + + if response["compilation"]["stderr"] != "": + em.description = ( + f"**Compilation result:**```{response['compilation']['stderr']}```" + ) + em.color = discord.Color.red() + elif response["compilation"]["stdout"] != "": + em.description = ( + f"**Compilation result:**```{response['compilation']['stdout']}```" + ) + + # If the program produced output + if response.get("run") is not None and response.get("run") != "": + em.add_field(name="Program Output:", value=f"```\n{response['run']}\n```") + + await ctx.respond(embed=em) + + @commands.slash_command(description="Run code in the rickroll programming language") + @commands.cooldown(1, 15, commands.BucketType.user) + async def ricklang(self, ctx: discord.ApplicationContext): + modal = InputModalView(label="Please enter the code:", title="Code Input") + await ctx.send_modal(modal) + await modal.wait() + + if modal.value is None: + return await ctx.respond("Invalid Input", ephemeral=True) + + response = await post_request( + "https://api.fusionsid.xyz/api/runcode", + body={"code": modal.value, "language": "rickroll_lang"}, + ) + if response is None: + em = discord.Embed( + title="Rickroll-Lang", + description="Something went wrong!\n(API probably had a skill issue)", + color=discord.Color.blue(), + ) + return await ctx.respond(embed=em) + + em = discord.Embed( + title="Output", + color=discord.Color.blue(), + description=f"""```\n{response['stdout']}\n```""", + ) + + await ctx.respond(embed=em) + + @commands.slash_command( + guild_ids=GUILD_IDS, description="Run code in the rickroll programming language" + ) + @commands.is_owner() + @commands.cooldown(1, 15, commands.BucketType.user) + async def exec_code(self, ctx: discord.ApplicationContext): + # this command dangerous af so second check just to make sure: + if ctx.author.id != self.client.owner_id: + raise commands.NotOwner + + modal = InputModalView(label="Please enter the code:", title="Code Input") + await ctx.send_modal(modal) + await modal.wait() + + if modal.value is None: + return await ctx.respond("Invalid Input", ephemeral=True) + + locals = { + "ctx": ctx, + "client": self.client, + } + + # Run the code + stdout = io.StringIO() + stderr = None + with redirect_stdout(stdout): + try: + await aexec(modal.value, locals) + except Exception as err: + stderr = err + output = stdout.getvalue() + + em = discord.Embed( + title="Code Output:", + description=f"```bash\n{output if output else 'No Stdout'}\n```", + color=discord.Color.random(), + ) + if stderr is not None: + em.add_field( + name="Stderr:", + value="```bash\n{}: {}\n```".format(type(stderr).__name__, stderr), + ) + + await ctx.respond(embed=em) + + +def setup(client): + client.add_cog(RunCode(client)) diff --git a/src/cogs/programming/whybotdev.py b/src/cogs/programming/whybotdev.py new file mode 100644 index 0000000..46e9e5b --- /dev/null +++ b/src/cogs/programming/whybotdev.py @@ -0,0 +1,283 @@ +import io +import json +import time +import inspect +import datetime +from dateutil import parser + +import discord +from discord.commands import SlashCommandGroup +from discord.ext import commands + +from core import BaseCog +from core.helpers import LinkView +from core.utils import discord_timestamp +from core.helpers import get_request, post_request + + +class WhyBotDev(BaseCog): + + why_dev = SlashCommandGroup( + "why", "Why bot development info and programming commands" + ) + + @why_dev.command( + name="getcode", description="Get the code for a specific Why-Bot command" + ) + async def getcode(self, ctx: discord.ApplicationContext, name: str): + """ + This command is used to get the code for a specific command + It is useful if you want to quickly check the code for a command without opening the github + It also provides a link to the github link with the code highlighted + """ + commands_list = [] + for cmd in self.client.application_commands: + if isinstance(cmd, discord.SlashCommandGroup): + continue + commands_list.append(cmd) + + for command in commands_list: + if command.name.lower() == name.lower(): + func = command.callback + filename = inspect.getsourcefile(func).split("/src")[1] + function_code = inspect.getsource(func).replace("```", "'") + first_line = func.__code__.co_firstlineno + + function_length = len(function_code.replace("\\n", "").split("\n")[:-1]) + + last_line = function_length + first_line - 1 + + src_link = ( + "https://github.com/FusionSid/Why-Bot/blob/rewrite-the-rewrite/src" + ) + code_link = f"<{src_link}{filename}#L{first_line}-L{last_line}>" + + if len(function_code) > 1750: + file = io.BytesIO(function_code.encode()) + return await ctx.respond( + embed=discord.Embed( + title="Code to large to fit in a message", + description=code_link, + color=ctx.author.color, + ), + view=LinkView(["Code on Github", code_link]), + file=discord.File(file, "command.py"), + ) + + return await ctx.respond( + f"```py\n\t# Code for the: {func.__name__} function/command\n\n{function_code}\n```\n{code_link}", + view=LinkView(["Code on Github", code_link]), + ) + await ctx.respond( + embed=discord.Embed( + title="Get Code", + description="Command not found!", + color=ctx.author.color, + ), + ephemeral=True, + ) + + @why_dev.command(description="Show the bots uptime") + async def uptime(self, ctx: discord.ApplicationContext): + """This command is used to get the uptime for the bot""" + + em = discord.Embed( + title="Why bot uptime", + description=f"I has been online for: {await self.client.uptime}", + color=ctx.author.color, + ) + await ctx.respond(embed=em) + + @why_dev.command(name="ping", description="Shows the bot's ping") + async def ping(self, ctx: discord.ApplicationContext): + """ + This command is used to get the ping for the bot + """ + + await ctx.respond(f"Pong!\n{round(self.client.latency * 1000)}ms") + + @why_dev.command( + description="Suggest something that you would like why bot to have" + ) + @commands.cooldown(1, 15, commands.BucketType.user) + async def suggest(self, ctx: discord.ApplicationContext, suggestion): + """ + This command is used to make a suggestion for the bot + """ + + suggestion_channel = self.client.config["suggestion_channel"] + + error_embed = discord.Embed( + title="Error getting suggestion channel", + description="Most likely cause: Bot owner has disabled suggestions", + ) + + try: + channel = await self.client.fetch_channel(suggestion_channel) + except discord.errors.NotFound: + return await ctx.respond(embed=error_embed) + + if suggestion_channel == 0 or suggestion_channel is None: + return await ctx.respond(embed=error_embed) + + em = discord.Embed( + title="Suggestion", + description=suggestion, + color=ctx.author.color, + timestamp=datetime.datetime.now(), + ) + + em.add_field(name="Suggested By:", value=ctx.author.name, inline=False) + + message = await channel.send(content=ctx.author.id, embed=em) + + await message.add_reaction("āœ…") + await message.add_reaction("āŒ") + + await ctx.respond("Thank you for the suggestion :)") + + @why_dev.command(description="Report a bug with the bot to the devs") + @commands.cooldown(3, 300, commands.BucketType.user) + async def bug(self, ctx: discord.ApplicationContext, *, bug): + em = discord.Embed( + title="Bug Report", + color=ctx.author.color, + timestamp=datetime.datetime.now(), + description=bug, + ) + + em.add_field(name="Report By:", value=ctx.author.name) + + bug_channel = self.client.config["bug_report_channel"] + + error_embed = discord.Embed( + title="Error getting suggestion channel", + description="Most likely cause: Bot owner has disabled bug reports", + ) + + try: + channel = await self.client.fetch_channel(bug_channel) + except discord.errors.NotFound: + return await ctx.respond(embed=error_embed) + + if bug_channel == 0 or bug_channel is None: + return await ctx.respond(embed=error_embed) + + await channel.send(content=ctx.author.id, embed=em) + await ctx.respond("Thank you for the bug report :)") + + URL = "https://api.github.com/repos/FusionSid/Why-Bot/issues" + response = await post_request( + URL, + headers={ + "Authorization": "token " + self.client.config["GITHUB_ACCESS_TOKEN"] + }, + body=json.dumps( + { + # time is so the issue names can be unique + "title": f"Bug Report - {ctx.author.name} | {time.time()}", + "body": bug, + } + ), + json=False, + ) + if response is not None: + view = LinkView( + ["Github Issue Link", response.get("html_url")], + ["Why Bot Discord", "https://discord.gg/Jm8QPF6xbN"], + ) + await ctx.followup.send( + embed=discord.Embed( + title="Github Issue URL", + description="A github issue has been automatically made for this bug report.\n\ + If you would like you can discuss the issue there.\n\ + Alternatively you can make a thread on the Why Bot discord server", + color=discord.Color.random(), + ), + view=view, + ) + + @why_dev.command( + name="botinvite", description="Get a link to invite Why-Bot to the server" + ) + async def botinvite(self, ctx: discord.ApplicationContext): + """This command is used to get the invite link for the bot""" + view = LinkView( + [ + "Invite Link", + "https://discord.com/api/oauth2/authorize?\ + client_id=896932646846885898&permissions=8&scope=bot%20applications.commands", + ], + ) + interaction = await ctx.respond( + embed=discord.Embed( + title="Invite **Why?** to your server:", + description=( + "[Why Invite Link](https://discord.com/api/oauth2/authorize?\ + client_id=896932646846885898&permissions=8&scope=bot%20applications.commands)" + ), + color=ctx.author.color, + ), + view=view, + ) + message = await (await interaction.original_message()) + await message.add_reaction("šŸ”—") + react_check = ( + lambda reaction, user: user.id == ctx.author.id + and reaction.emoji == "šŸ”—" + and reaction.message.id == message.id + ) + await self.client.wait_for("reaction_add", check=react_check, timeout=30.0) + await ctx.respond( + "https://discord.com/api/oauth2/authorize?\ + client_id=896932646846885898&permissions=8&scope=bot%20applications.commands" + ) + + @why_dev.command(description="Show the most recent commit to the why bot repo") + async def recent_commit(self, ctx: discord.ApplicationContext): + # "https://api.github.com/repos/FusionSid/Why-Bot/commits/master" + URL = ( + "https://api.github.com/repos/FusionSid/Why-Bot/commits/rewrite-the-rewrite" + ) + response = await get_request(URL) + if response is None: + em = discord.Embed( + title="An error occured while trying to get the commit", + description=( + "API basically had a skill issue.\n\ + If this persists and you are able to, report this as a bug with :)" + ), + color=discord.Colour.red(), + ) + return await ctx.respond(embed=em, ephemeral=True) + + em = discord.Embed( + title="Why Bot - Most Recent Commit", + description=f"Commit Hash: {response.get('sha')}", + color=discord.Color.random(), + ) + if (commit_info := response.get("committer")) is not None: + em.set_author( + name=f"Author: {commit_info.get('login')}", + icon_url=commit_info.get("avatar_url"), + ) + if response.get("commit") is not None: + em.add_field( + name="Message:", value=response["commit"].get("message"), inline=False + ) + + date = parser.parse(response["commit"]["committer"].get("date")) + date = int(date.timestamp()) + + em.add_field( + name="When:", + value=f"{discord_timestamp(date, 'ts')} {discord_timestamp(date, 'md_yt')}", + inline=False, + ) + + view = LinkView(["Link to commit", response.get("html_url")]) + await ctx.respond(embed=em, view=view) + + +def setup(client): + client.add_cog(WhyBotDev(client)) diff --git a/src/cogs/roles/roles.py b/src/cogs/roles/roles.py new file mode 100644 index 0000000..a595aa5 --- /dev/null +++ b/src/cogs/roles/roles.py @@ -0,0 +1,137 @@ +import re + +import discord +from discord.ext import commands +from discord.commands import default_permissions + +from core import WhyBot, BaseCog + + +class Roles(BaseCog): + def __init__(self, client: WhyBot): + self.edit_role_mentions = discord.AllowedMentions( + users=False, everyone=False, roles=False, replied_user=False + ) + super().__init__(client) + + @commands.slash_command( + name="addrole", description="gives role / roles to a member" + ) + @default_permissions(manage_roles=True) + @commands.has_permissions(manage_roles=True) + @commands.bot_has_permissions(manage_roles=True) + async def addrole( + self, + ctx: discord.ApplicationContext, + member: discord.Member, + roles: str, + ): + + try: + roles = list(map(int, re.findall("\\d+", roles))) + except ValueError: + return await ctx.respond("Invalid input was provided", ephemeral=True) + + fetched_roles = [] + for role in roles: + fetched_role = ctx.guild.get_role(role) + if fetched_role is None: + continue + + conditions = [ + ctx.author.top_role.position <= member.top_role.position, + (ctx.guild.get_member(self.client.user.id)).top_role.position + <= member.top_role.position, + ctx.author.top_role.position <= fetched_role.position, + (ctx.guild.get_member(self.client.user.id)).top_role.position + <= fetched_role.position, + ] + if any(conditions): + continue + + fetched_roles.append(fetched_role) + + if len(fetched_roles) == 0: + return await ctx.respond( + "Unable to give any roles because of permissions", ephemeral=True + ) + + await member.add_roles(*fetched_roles) + + em = discord.Embed( + title="Role Given" if len(fetched_roles) == 1 else "Roles Given", + description=f"Member: {member.mention} has been given role: {fetched_roles[0].mention}" + if len(fetched_roles) == 1 + else f"Member: {member.mention} has been given roles: {''.join([role.mention for role in fetched_roles])}", + color=discord.Color.random(), + ) + em.set_footer( + text=f"Role given by {ctx.author.name}" + if len(roles) == 1 + else f"Roles given by {ctx.author.name}" + ) + + await ctx.respond(embed=em, allowed_mentions=self.edit_role_mentions) + + @commands.slash_command( + name="removerole", description="removes role / roles from a member" + ) + @default_permissions(manage_roles=True) + @commands.has_permissions(manage_roles=True) + @commands.bot_has_permissions(manage_roles=True) + async def removerole( + self, + ctx: discord.ApplicationContext, + member: discord.Member, + roles: str, + ): + try: + roles = list(map(int, re.findall("\\d+", roles))) + except ValueError: + return await ctx.respond("Invalid input was provided", ephemeral=True) + + fetched_roles = [] + for role in roles: + fetched_role = ctx.guild.get_role(role) + if fetched_role is None: + continue + + conditions = [ + ctx.author.top_role.position <= member.top_role.position, + (ctx.guild.get_member(self.client.user.id)).top_role.position + <= member.top_role.position, + ctx.author.top_role.position <= fetched_role.position, + (ctx.guild.get_member(self.client.user.id)).top_role.position + <= fetched_role.position, + ] + if any(conditions): + continue + + fetched_roles.append(fetched_role) + + if len(fetched_roles) == 0: + return await ctx.respond( + "Unable to remove any roles because of permissions", ephemeral=True + ) + + await member.remove_roles(*fetched_roles) + + em = discord.Embed( + title="Role Removed" if len(fetched_roles) == 1 else "Roles Removed", + description=f"Member: {member.mention} has had these role removed: {fetched_roles[0].mention}" + if len(fetched_roles) == 1 + else f"Member: {member.mention} has had these roles removed: \ + {''.join([role.mention for role in fetched_roles])}", + color=discord.Color.random(), + ) + em.set_footer( + text=f"Role removed by {ctx.author.name}" + if len(roles) == 1 + else f"Roles removed by {ctx.author.name}" + ) + + await ctx.respond(embed=em, allowed_mentions=self.edit_role_mentions) + + +def setup(client): + client.add_cog(Roles(client)) diff --git a/src/cogs/utilities/info.py b/src/cogs/utilities/info.py new file mode 100644 index 0000000..180bce0 --- /dev/null +++ b/src/cogs/utilities/info.py @@ -0,0 +1,314 @@ +import time +import datetime + +import psutil +import discord +import platform +from discord.ext import commands + +from core import WhyBot, BaseCog +from core.helpers.views import BotInfoView +from core.utils.count_lines import get_lines +from core.utils.formatters import discord_timestamp + + +class Info(BaseCog): + async def get_info( + self, ctx: discord.ApplicationContext, member: discord.Member = None + ): + if member is None: + member = ctx.author + + roles = list(member.roles) + em = discord.Embed( + title="User Info", + description=f"For: {member.name}{' [BOT]' if member.bot else ''}\nDisplay Name: {member.display_name}", + color=ctx.author.color, + ) + emojis = self.client.get_why_emojies + if str(member.status) == "online": + status = f"{emojis.get('online', '🟢')} Online" + elif str(member.status) == "offline": + status = f"{emojis.get('offline', '⚪')} Offline" + elif str(member.status) == "dnd": + status = f"{emojis.get('dnd', 'ā›”')} Do not disturb" + elif str(member.status) == "invisible": + status = f"{emojis.get('offline', '⚪')} Invisible" + elif str(member.status) == "idle": + status = f"{emojis.get('idle', '🟔')} Idle" + else: + status = f"{emojis.get('online', '🟢')} Online" + + em.add_field(name="Status:", value=status, inline=False) + em.add_field(name="ID:", value=member.id, inline=False) + em.add_field( + name="Created Account:", + value=f"", + inline=False, + ) + em.add_field( + name="Joined Server:", + value=f"", + inline=False, + ) + em.add_field(name="Highest Role:", value=member.top_role.mention, inline=False) + + if member.bot: + em.add_field( + name="Bot Status", + value=( + f"Bot is {'' if member.public_flags.verified_bot else 'not '}a" + " verified bot" + ), + ) + + if len(roles) > 15: + em.add_field(name="Roles:", value=f"{len(roles)}", inline=False) + else: + em.add_field( + name=f"Roles ({len(roles)}):", + value=" ".join(role.mention for role in roles), + inline=False, + ) + + em.set_thumbnail(url=member.avatar.url) + + await ctx.respond(embed=em) + + @commands.slash_command(name="info", description="Gets info on a member") + async def info( + self, ctx: discord.ApplicationContext, member: discord.Member = None + ): + """This command is used to get info on a member""" + + await self.get_info(ctx, member) + + @commands.user_command(name="Get User Info") + async def info_user_cmd( + self, ctx: discord.ApplicationContext, member: discord.Member + ): + if member.id == self.client.user.id: + return await self.botinfo(ctx) + + await self.get_info(ctx, member) + + @commands.slash_command(name="serverinfo", description="Shows server info") + @commands.guild_only() + async def serverinfo(self, ctx: discord.ApplicationContext): + """This command is used to get info on the server""" + + GUILD = ctx.guild + + text = len(GUILD.text_channels) + voice = len(GUILD.voice_channels) + total = len(GUILD.channels) + other = total - (text + voice) + categories = len(GUILD.categories) + channel_text = f"""**Channels:** + **{text}** Text channels + **{voice}** Voice channels + **{other}** Other channel types + **{total}** Total Channels + **{categories}** Total Categories""" + + members = GUILD.members + humans = len([m for m in members if not m.bot]) + bots = len(members) - humans + member_text = f"""**Members:** + **{humans}** Humans + **{bots}** Bots + **{len(members)}** Total Members""" + + emojis_text = f"**Emoji Count:**\n{len(GUILD.emojis)}" + role_text = f"**Role Count:**\n{len(GUILD.roles)}" + + created_at = int(time.mktime(GUILD.created_at.timetuple())) + created_text = ( + "**Server" + f' Created:**\n{discord_timestamp(created_at, "md_yt")} ({discord_timestamp(created_at, "ts")})' + ) + + server_id_text = f"**Server ID:** {GUILD.id}" + level_text = f"""\ + **Verification Level:** {GUILD.verification_level.name} + **2FA:** {'on' if bool(GUILD.mfa_level) else 'off'} + **NSFW Level:** {GUILD.nsfw_level.name}""" + + feature_text = "**Features:**\n" + ", ".join(GUILD.features) + + things = "\n\n".join( + [ + channel_text, + member_text, + emojis_text, + role_text, + created_text, + level_text, + feature_text, + ] + ) + description = f"{server_id_text}\n\n{things}" + + em = discord.Embed( + title=f"Server Info for {ctx.guild.name}", + description=description, + color=ctx.author.color, + ) + em.set_author( + name=f"Server Owner: {ctx.guild.owner.name}", + icon_url=ctx.guild.owner.avatar.url, + ) + em.set_thumbnail(url=ctx.guild.icon.url) + + await ctx.respond(embed=em) + + @commands.slash_command(name="botinfo", description="Gets info on Why Bot") + async def botinfo(self, ctx: discord.ApplicationContext): + """This command is used to get info on the bot""" + + em = discord.Embed( + title=f"Why Bot v{self.client.version}", + description="Just Why?", + color=discord.Color.random(), + ) + + em.add_field( + inline=True, name="Server Count", value=f"{len(self.client.guilds)}" + ) + em.add_field( + inline=True, + name="User Count", + value=len(list(self.client.get_all_members())), + ) + em.add_field( + inline=True, + name="Active User Count", + value=len( + await self.client.db.fetch("SELECT DISTINCT user_id FROM command_stats") + ), + ) + em.add_field( + inline=True, + name="Command Count", + value=f"{len(self.client.application_commands)} commands", + ) + em.add_field( + inline=True, name="Ping", value=f"{round(self.client.latency * 1000)}ms" + ) + em.add_field( + inline=True, + name="Uptime", + value=await self.client.uptime, + ) + em.add_field(inline=True, name="CPU Usage", value=f"{psutil.cpu_percent()}%") + em.add_field( + inline=True, + name="Memory Usage", + value=( + f"{psutil.virtual_memory().percent}% of" + f" ({round((psutil.virtual_memory().total/1073741824), 2)}GB)" + ), + ) + em.add_field( + inline=True, name="Python version", value=f"{platform.python_version()}" + ) + em.add_field( + inline=True, + name="Running on", + value=f"{platform.system()} {platform.release()}", + ) + em.add_field( + inline=True, + name="Lines of python code", + value=f"{(await get_lines(self.client.redis))} lines of code", + ) + em.add_field( + inline=True, + name="User ID:", + value=self.client.user.id, + ) + em.set_thumbnail(url=self.client.user.avatar.url) + em.set_footer( + text="Made by FusionSid#3645", + icon_url=self.client.get_user(self.client.owner_id).avatar.url, + ) + await ctx.respond(embed=em, view=BotInfoView()) + + @commands.slash_command(description="Show a users avatar") + async def avatar( + self, ctx: discord.ApplicationContext, member: discord.Member = None + ): + if member is None: + member = ctx.author + em = discord.Embed(title=f"{member.name}'s Avatar:") + em.set_image(url=member.avatar.url) + await ctx.respond(embed=em) + + @commands.user_command(name="Get user avatar") + async def avatar2(self, ctx: discord.ApplicationContext, member: discord.Member): + em = discord.Embed(title=f"{member.name}'s Avatar:") + em.set_image(url=member.avatar.url) + await ctx.respond(embed=em) + + @commands.command( + name="invites", + description="Get the amount of people that a member has invited to the server", + ) + async def invites( + self, ctx: discord.ApplicationContext, member: discord.Member = None + ): + """This command is used to get the amount of people that a member has invited to the server""" + + if member is None: + member = ctx.author + + total_invites = 0 + for invite in await ctx.guild.invites(): + if invite.inviter == member: + total_invites += invite.uses + + em = discord.Embed( + title="Invites", + # This line has been stolen from simplex bot + description=( + f"{member.mention} has invited" + f" {total_invites} member{'' if total_invites == 1 else 's'} to the" + " server!" + ), + color=ctx.author.color, + timestamp=datetime.datetime.now(), + ) + await ctx.respond(embed=em) + + @commands.command( + name="inviteslb", description="Get a leaderboard of the invites in the server" + ) + async def inviteslb(self, ctx: discord.ApplicationContext): + """ + This command is used to get a leaderboard of the invites in the server + """ + + em = discord.Embed( + title="Leaderboard", + color=ctx.author.color, + timestamp=datetime.datetime.now(), + ) + total_invites = {} + for invite in await ctx.guild.invites(): + try: + total_invites[invite.inviter.name] += invite.uses + except KeyError: + total_invites[invite.inviter.name] = invite.uses + total_invites = dict( + sorted(total_invites.items(), reverse=True, key=lambda item: item[1]) + ) + + for key, value in total_invites.items(): + if value != 0: + em.add_field(name=key, value=value, inline=False) + + await ctx.respond(embed=em) + + +def setup(client: WhyBot): + client.add_cog(Info(client)) diff --git a/src/cogs/utilities/tags.py b/src/cogs/utilities/tags.py new file mode 100644 index 0000000..2a8d6f5 --- /dev/null +++ b/src/cogs/utilities/tags.py @@ -0,0 +1,199 @@ +import time +from typing import Optional + +import discord +from discord.ext import commands +from discord.commands import SlashCommandGroup + +from core import BaseCog +from core.models import Tag +from core.helpers import InputModalView +from core.utils import discord_timestamp + + +class Tags(BaseCog): + tags = SlashCommandGroup("tags", "Command related to the tags plugin") + + async def __get_tag_by_name(self, tag_name: str, guild_id: int) -> Optional[Tag]: + tag = await self.client.db.fetch( + "SELECT * FROM tags WHERE guild_id=$1 AND tag_name=$2", + guild_id, + tag_name, + ) + if not tag: + return None + + return Tag(*tag[0][1:]) + + @tags.command(description="Create a new tag") + @commands.has_permissions(administrator=True) + async def create( + self, + ctx: discord.ApplicationContext, + name: str, + ): + tag_name = name.lower() + + # check + if await self.__get_tag_by_name(tag_name, ctx.guild.id) is not None: + return await ctx.respond( + embed=discord.Embed( + title="Name Conflict!", + description="Tag with this name already exists!\n\ + If you want you can edit the tag with the command", + color=discord.Color.red(), + ), + ephemeral=True, + ) + + # get tags value + input = InputModalView( + title="Tag Value", label="Please enter the value of the tag:" + ) + await ctx.send_modal(input) + await input.wait() + + if input.value is None: + return await ctx.respond( + "Not creating tag as input was either None or invalid", ephemeral=True + ) + + # create tag + await self.client.db.execute( + """INSERT INTO tags ( + guild_id, tag_name, tag_value, tag_author, time_created + ) VALUES ($1, $2, $3, $4, $5)""", + ctx.guild.id, + tag_name, + input.value, + ctx.author.name, + int(time.time()), + ) + + await ctx.respond( + "Tag Created Successfully! It can be viewed with the command\nIt will look like this:", + embed=discord.Embed( + title=tag_name, description=input.value, color=discord.Color.random() + ), + ) + + @tags.command(description="Delete an existing tag") + @commands.has_permissions(administrator=True) + async def delete(self, ctx: discord.ApplicationContext, name: str): + name = name.lower() + if await self.__get_tag_by_name(name, ctx.guild.id) is None: + return await ctx.respond( + embed=discord.Embed( + title="Tag doesn't exist!", + description="Tag with this name does not exist!\nYou can check tags on\ + this server with the command or create one with ", + color=discord.Color.red(), + ), + ephemeral=True, + ) + + await self.client.db.execute( + "DELETE FROM tags WHERE guild_id=$1 AND tag_name=$2", ctx.guild.id, name + ) + await ctx.respond( + embed=discord.Embed( + title="Tag Deleted", + description=f"Tag: `{name}` was successfuly deleted!", + color=discord.Color.green(), + ) + ) + + @tags.command(description="List the tags on this server") + async def list(self, ctx: discord.ApplicationContext): + tags = await self.client.db.fetch( + "SELECT * FROM tags WHERE guild_id=$1", ctx.guild.id + ) + if not tags: + return await ctx.respond( + embed=discord.Embed( + title="Tags", + description="This guild has no tags\nCreate one with ", + color=discord.Color.random(), + ) + ) + + return await ctx.respond( + embed=discord.Embed( + title="Tags", + description=", ".join( + map(lambda tag: f"`{tag[2]}`", tags) + ), # tag[2] = tag_name + color=discord.Color.random(), + ) + ) + + @tags.command(description="Edit the value of an existing tag") + @commands.has_permissions(administrator=True) + async def edit(self, ctx: discord.ApplicationContext, name: str): + tag_name = name.lower() + + if await self.__get_tag_by_name(tag_name, ctx.guild.id) is None: + return await ctx.respond( + embed=discord.Embed( + title="Tag doesnt exist!", + description="Tag with this name does not exist!\n\ + If you want you can create the tag with the command", + color=discord.Color.red(), + ), + ephemeral=True, + ) + + # get tags value + input = InputModalView( + title="Tag New Value", label="Please enter the new value of the tag:" + ) + await ctx.send_modal(input) + await input.wait() + + if input.value is None: + return await ctx.respond( + "Not editing tag as input was either None or invalid", ephemeral=True + ) + + # create tag + await self.client.db.execute( + "UPDATE tags SET tag_value=$1 WHERE guild_id=$2 AND tag_name=$3", + input.value, + ctx.guild.id, + tag_name, + ) + + await ctx.respond( + "Tag Modified! It will now look like this:", + embed=discord.Embed( + title=tag_name, description=input.value, color=discord.Color.random() + ), + ) + + @commands.slash_command(description="Show the value of a tag") + async def tag(self, ctx: discord.ApplicationContext, name: str): + name = name.lower() + tag = await self.__get_tag_by_name(name, ctx.guild.id) + if tag is None: + return await ctx.respond( + embed=discord.Embed( + title="Tag doesn't exist!", + description="Tag with this name does not exist!\nYou can check tags on this\ + server with the command or create one with ", + color=discord.Color.red(), + ), + ephemeral=True, + ) + + timestamp = discord_timestamp(tag.time_created, "ts") + em = discord.Embed( + title=tag.tag_name, + description=f"{tag.tag_value}\n\nTag created: {timestamp}", + color=discord.Color.random(), + ) + em.set_footer(text=f"Tag created by {tag.tag_author}") + await ctx.respond(embed=em) + + +def setup(client): + client.add_cog(Tags(client)) diff --git a/src/cogs/utilities/utilities.py b/src/cogs/utilities/utilities.py new file mode 100644 index 0000000..d9c69fb --- /dev/null +++ b/src/cogs/utilities/utilities.py @@ -0,0 +1,86 @@ +from io import BytesIO + +import qrcode +import discord +from discord.ext import commands +from discord.commands import SlashCommandGroup + +from core import BaseCog +from core.helpers import CalculatorView +from core.utils import slow_safe_calculate + + +class Utilities(BaseCog): + utilities = SlashCommandGroup("utilities", "Utility Commands") + + @utilities.command( + name="calculator", description="Open an interactive button calculator" + ) + async def calculator(self, ctx: discord.ApplicationContext): + """This command is used to show an interactive button calculator""" + + await ctx.defer() + + view = CalculatorView(ctx) + await ctx.respond("``` ```", view=view) + + @utilities.command(description="Calculate an expression and give the result") + async def calculate(self, ctx: discord.ApplicationContext, expression: str): + em = discord.Embed( + title="Calculation Result", + description=f"**Expression:**\n{expression}", + color=discord.Color.random(), + ) + + result = await slow_safe_calculate(expression) + em.add_field(name="Result", value=result) + + await ctx.respond(embed=em) + + @utilities.command(name="invite", description="Create an invite for the server") + @commands.has_permissions(create_instant_invite=True) + @commands.bot_has_permissions(create_instant_invite=True) + async def invite( + self, + ctx: discord.ApplicationContext, + expire_in: str = None, + max_uses: str = None, + ): + """This command is used to make an invite for the server""" + + expire_in = 0 if expire_in is not None else expire_in + max_uses = 0 if max_uses is not None else max_uses + + link = await ctx.channel.create_invite(max_age=expire_in, max_uses=max_uses) + await ctx.respond(link) + + @utilities.command(description="Creates a qrcode for a given URL") + async def qrcode( + self, + ctx: discord.ApplicationContext, + url: str, + color: str = "black", + background_color: str = "white", + ): + qr = qrcode.QRCode( + version=1, + error_correction=qrcode.constants.ERROR_CORRECT_H, + box_size=10, + border=4, + ) + qr.add_data(str(url)) + qr.make(fit=True) + try: + img = qr.make_image(fill_color=color, back_color=background_color).convert( + "RGB" + ) + except ValueError: + img = qr.make_image(fill_color="black", back_color="white").convert("RGB") + image = BytesIO() + img.save(image, "PNG") + image.seek(0) + await ctx.respond(file=discord.File(image, "qrcode.png")) + + +def setup(client): + client.add_cog(Utilities(client)) diff --git a/src/config.example.yaml b/src/config.example.yaml new file mode 100644 index 0000000..0c9da35 --- /dev/null +++ b/src/config.example.yaml @@ -0,0 +1,39 @@ +# PLEASE DO NOT MODIFY THIS FILE +# This file is not only an example but a template that will be used by setup.py + +# General +DEFAULT_PREFIX: "?" +BOT_OWNER_ID: 0 +BOT_TOKEN: "Token for the bot" +LOGGING: True + +# Guild Details +MAIN_GUILD: 0 # the bots main guild for emojis and owner commands +DEBUG_GUILDS: [] # for debug purposes, slash commands work instantly but only in selected guilds +DEBUG_GUILD_MODE: False + +# Channels +join_alert_channel: 0 +leave_alert_channel: 0 +online_alert_channel: 0 +bug_report_channel: 0 +suggestion_channel: 0 +dm_reply_channel: 0 + +# Database & Cache Auth Details +DATABASE_URL: "postgresql:///?user=&password=" +REDIS_URI: "redis://127.0.0.1" +REDIS_PASSWORD: "Password for redis auth" + +# IPC info +IPC_KEY: "whybot" +IPC_HOST: "0.0.0.0" + +# API Keys +GITHUB_ACCESS_TOKEN: "This api key is for bug reports" +HYPIXEL_API_KEY: "This api key for hypixel related commands" +PRAW_CLIENT_SECRET: "This key is for reddit commands" +PRAW_CLIENT_ID: "This key is for reddit commands" +GOOGLE_IMAGE_API: "This key is for google image search commands" +NASA_API_KEY: "This key is for nasa commands" +WEATHER_API_KEY: "This key is for weather commands" diff --git a/src/core/__init__.py b/src/core/__init__.py new file mode 100644 index 0000000..f41138a --- /dev/null +++ b/src/core/__init__.py @@ -0,0 +1,3 @@ +from .models import WhyBot, BaseCog + +__all__ = ["WhyBot", "BaseCog"] diff --git a/src/core/db/__init__.py b/src/core/db/__init__.py new file mode 100644 index 0000000..d03425f --- /dev/null +++ b/src/core/db/__init__.py @@ -0,0 +1,16 @@ +from .create_tables import create_tables, TABLES_TO_CREATE +from .setup_guild import ( + setup_counting, + setup_leveling_guild, + setup_tickets, + create_db_tables, +) + +__all__ = [ + "TABLES_TO_CREATE", + "create_tables", + "setup_counting", + "setup_leveling_guild", + "setup_tickets", + "create_db_tables", +] diff --git a/src/core/db/create_tables.py b/src/core/db/create_tables.py new file mode 100644 index 0000000..44ab2f6 --- /dev/null +++ b/src/core/db/create_tables.py @@ -0,0 +1,176 @@ +from typing import Final +import asyncio + +from core.helpers import create_connection_pool + + +blacklist_query: Final = """ +DROP TABLE IF EXISTS blacklist; CREATE TABLE blacklist +( + user_id integer NOT NULL, + reason text, + PRIMARY KEY (user_id) +); +""" + +command_stats_query: Final = """ +DROP TABLE IF EXISTS command_stats; CREATE TABLE command_stats +( + id SERIAL PRIMARY KEY, + user_id bigint NOT NULL, + command_name text NOT NULL, + usage_count integer NOT NULL DEFAULT 0 +); +""" + +counting_query: Final = """ +DROP TABLE IF EXISTS counting; CREATE TABLE counting +( + guild_id bigint NOT NULL PRIMARY KEY, + last_counter bigint, + current_number integer, + counting_channel bigint, + high_score integer, + plugin_enabled boolean, + auto_calculate boolean, + banned_users bigint[] +); +""" + +leveling_member_query: Final = """ +DROP TABLE IF EXISTS leveling_member; CREATE TABLE leveling_member +( + guild_id bigint NOT NULL, + member_id bigint NOT NULL, + member_name text, + member_xp integer, + member_level integer, + member_total_xp bigint +); +""" + +leveling_guild_query: Final = """ +DROP TABLE IF EXISTS leveling_guild; CREATE TABLE leveling_guild +( + guild_id bigint NOT NULL PRIMARY KEY, + plugin_enabled boolean, + text_font text, + text_color text, + background_image text, + background_color text, + progress_bar_color text, + no_xp_roles json, + no_xp_channels json, + level_up_text text, + level_up_enabled boolean, + per_minute text +); +""" + +leveling_rewards_query: Final = """ +DROP TABLE IF EXISTS leveling_rewards; CREATE TABLE leveling_rewards +( + guild_id bigint NOT NULL PRIMARY KEY, + level integer, + role bigint +); +""" + +counters_query: Final = """ +DROP TABLE IF EXISTS counters; CREATE TABLE counters +( + key text NOT NULL PRIMARY KEY, + value integer NOT NULL DEFAULT 0 +); +""" + +dmreply_query: Final = """ +DROP TABLE IF EXISTS dmreply; CREATE TABLE dmreply +( + user_id bigint NOT NULL PRIMARY KEY, + thread_id bigint NOT NULL +); +""" + +tags_query: Final = """ +DROP TABLE IF EXISTS tags; CREATE TABLE tags +( + id SERIAL NOT NULL PRIMARY KEY, + guild_id bigint NOT NULL, + tag_name TEXT NOT NULL, + tag_value TEXT NOT NULL, + tag_author TEXT NOT NULL, + time_created INTEGER NOT NULL +); +""" + +alerts_query: Final = """ +DROP TABLE IF EXISTS alerts; CREATE TABLE alerts +( + id serial NOT NULL PRIMARY KEY, + alert_title text NOT NULL, + alert_message text NOT NULL, + time_created INTEGER NOT NULL, + viewed integer NOT NULL DEFAULT 1 +); +""" + +alerts_user_query: Final = """ +DROP TABLE IF EXISTS alerts_users; CREATE TABLE alerts_users +( + user_id bigint NOT NULL PRIMARY KEY, + alert_viewed boolean NOT NULL DEFAULT false, + ignore_alerts boolean NOT NULL default false +); +""" + +ticket_guild_query: Final = """ +DROP TABLE IF EXISTS ticket_guild; CREATE TABLE ticket_guild +( + guild_id bigint NOT NULL PRIMARY KEY, + roles_allowed bigint[], + ping_roles bigint[], + create_button boolean DEFAULT false, + category bigint, + banned_users bigint[] +); +""" + +tickets_query: Final = """ +DROP TABLE IF EXISTS tickets; CREATE TABLE tickets +( + id serial NOT NULL PRIMARY KEY, + guild_id bigint NOT NULL, + channel_id bigint NOT NULL, + ticket_creator bigint NOT NULL, + time_created INTEGER NOT NULL +); +""" + +# If you wish not to create one of these tables in the setup process +# Simply just remove/comment that item from this list: +TABLES_TO_CREATE: Final = [ + blacklist_query, + command_stats_query, + counting_query, + leveling_member_query, + leveling_guild_query, + leveling_rewards_query, + counters_query, + dmreply_query, + tags_query, + alerts_query, + alerts_user_query, + ticket_guild_query, + tickets_query, +] + + +async def create_tables(): + """ + Runs all the table creation queries at once. + To choose which tables to create add / remove the queries from the tables_to_create list + """ + pool = await create_connection_pool() + tasks = [pool.execute(table) for table in TABLES_TO_CREATE] + await asyncio.gather(*tasks) diff --git a/src/core/db/setup_guild.py b/src/core/db/setup_guild.py new file mode 100644 index 0000000..f2ccbfc --- /dev/null +++ b/src/core/db/setup_guild.py @@ -0,0 +1,93 @@ +import asyncio + +import asyncpg + + +async def setup_counting(db: asyncpg.Pool, guild_id: int): + """ + Setup counting for the guild. basically adds to the counting table + + Parameters: + db (asyncpg.Pool): The database connection pool + guild_id (int): the id of the guild to create a row for + """ + try: + await db.execute( + "INSERT INTO counting (guild_id, high_score, banned_users) VALUES ($1, 0, $2)", + guild_id, + [], + ) + except asyncpg.UniqueViolationError: + return + + +async def setup_leveling_guild(db: asyncpg.Pool, guild_id: int): + """ + Setup leveling for the guild. Creates a row with the default values in the leveling table + + Parameters: + db (asyncpg.Pool): The database connection pool + guild_id (int): the id of the guild to create a row for + """ + default_data = [ + guild_id, + False, + "default", + "black", + None, + "black", + "green", + [], + [], + "GG {member.mention} you just leveled up to {level}", + True, + "20", + ] + query = """ + INSERT INTO leveling_guild ( + guild_id, plugin_enabled, + text_font, text_color, + background_image, background_color, progress_bar_color, + no_xp_roles, no_xp_channels, + level_up_text, level_up_enabled, per_minute + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) + """ + try: + await db.execute(query, *default_data) + except asyncpg.UniqueViolationError: + return + + +async def setup_tickets(db: asyncpg.Pool, guild_id: int): + """ + Setup ticketing for the guild. + + Parameters: + db (asyncpg.Pool): The database connection pool + guild_id (int): the id of the guild to create a row for + """ + default_data = [guild_id, [], [], False, None, []] + try: + await db.execute( + """INSERT INTO ticket_guild ( + guild_id, roles_allowed, ping_roles, create_button, category, banned_users + ) VALUES ($1, $2, $3, $4, $5, $6)""", + *default_data, + ) + except asyncpg.UniqueViolationError: + return + + +async def create_db_tables(db: asyncpg.Pool, guild_id: int): + """ + Function for running all the setup functions at once + This will be used when a new guild is joined and will help with not having to run these functions later + + Parameters: + db (asyncpg.Pool): The database connection pool + guild_id (int): the id of the guild to setup + """ + + things_to_setup = [setup_counting, setup_leveling_guild, setup_tickets] + tasks = [setup_function(db, guild_id) for setup_function in things_to_setup] + await asyncio.gather(*tasks) diff --git a/src/core/helpers/__init__.py b/src/core/helpers/__init__.py new file mode 100644 index 0000000..1f4cd93 --- /dev/null +++ b/src/core/helpers/__init__.py @@ -0,0 +1,85 @@ +from .checks import blacklist_check, plugin_enabled, update_stats, run_bot_checks +from .client_functions import ( + update_activity, + get_why_config, + create_connection_pool, + create_redis_connection, + GUILD_IDS, +) +from .exception import ( + BaseException, + RichBaseException, + ConfigNotFound, + InvalidDatabaseUrl, + UserAlreadyBlacklisted, + UserAlreadyWhitelisted, + ImageAPIFail, +) +from .http import get_request, get_request_bytes, post_request, post_request_bytes +from .log import ( + LOGFILE_PATH, + log_errors, + log_normal, + convert_to_dict, + get_last_errors, + on_error, +) +from .music import Player +from .views import ( + RickRollView, + BotInfoView, + LinkView, + CalculatorView, + ConfirmView, + InputModalView, + ErrorView, +) +from .why_leveling import ( + xp_needed, + get_level_data, + get_member_data, + update_member_data, + get_all_member_data, +) + +__all__ = [ + "blacklist_check", + "plugin_enabled", + "update_stats", + "run_bot_checks", + "update_activity", + "get_why_config", + "create_connection_pool", + "create_redis_connection", + "GUILD_IDS", + "BaseException", + "RichBaseException", + "ConfigNotFound", + "InvalidDatabaseUrl", + "UserAlreadyBlacklisted", + "UserAlreadyWhitelisted", + "ImageAPIFail", + "get_request", + "get_request_bytes", + "post_request", + "post_request_bytes", + "LOGFILE_PATH", + "log_errors", + "log_normal", + "convert_to_dict", + "get_last_errors", + "on_error", + "Player", + "RickRollView", + "BotInfoView", + "LinkView", + "CalculatorView", + "ConfirmView", + "InputModalView", + "ErrorView", + "xp_needed", + "get_level_data", + "get_member_data", + "update_member_data", + "get_all_member_data", +] diff --git a/src/core/helpers/checks.py b/src/core/helpers/checks.py new file mode 100644 index 0000000..a0e2800 --- /dev/null +++ b/src/core/helpers/checks.py @@ -0,0 +1,85 @@ +""" (module) checks +This module contains checks that will be run before most commands in the @commands.check() decorator +""" + +import asyncio +import datetime + +import aioredis +from discord.ext import commands +from discord import ApplicationContext # for the autocomplete + +from .client_functions import get_why_config +from core.utils import asyncpg_connect + + +async def blacklist_check(user_id: int) -> bool: + """returns true if the user is not blacklisted""" + + config = get_why_config() + + redis_url = config["REDIS_URI"] + redis_password = config["REDIS_PASSWORD"] + database_url = config["DATABASE_URL"] + + # Caching + redis = aioredis.from_url( + redis_url, decode_responses=True, password=redis_password, port=6379 + ) + + if await redis.exists("blacklisted"): # if the key exists then do this: + cached_blacklisted_users = await redis.lrange("blacklisted", 0, -1) + return not str(user_id) in cached_blacklisted_users + + async with asyncpg_connect(database_url) as conn: + data = await conn.fetch("SELECT * FROM blacklist;") + users = [int(user[0]) for user in data] + if users: + await redis.lpush("blacklisted", *users) + await redis.expire("blacklisted", datetime.timedelta(hours=12)) + return not str(user_id) in users + + +async def plugin_enabled(cog: commands.Cog) -> bool: + # TODO 𐐘 + # cog_name = cog.__cog_name__ + return True + + +async def update_stats(ctx: ApplicationContext): + config = get_why_config() + database_url = config["DATABASE_URL"] + + async with asyncpg_connect(database_url) as conn: + data = await conn.fetch( + "SELECT * FROM command_stats WHERE user_id=$1 AND command_name=$2", + ctx.author.id, + ctx.command.name, + ) + if data: + await conn.execute( + "UPDATE command_stats SET usage_count=$1 WHERE user_id=$2 AND" + " command_name=$3", + data[0][3] + 1, + ctx.author.id, + ctx.command.name, + ) + else: + await conn.execute( + "INSERT INTO command_stats (user_id, command_name, usage_count) VALUES" + " ($1, $2, $3)", + ctx.author.id, + ctx.command.name, + 1, + ) + + +async def run_bot_checks(ctx: ApplicationContext): + + blacklisted_check = await blacklist_check(ctx.author.id) + plugin_enabled_check = await plugin_enabled(ctx.cog) + all_checks_successful = all([blacklisted_check, plugin_enabled_check]) + + asyncio.get_event_loop().create_task(update_stats(ctx)) + + return all_checks_successful diff --git a/src/core/helpers/client_functions.py b/src/core/helpers/client_functions.py new file mode 100644 index 0000000..2a8aee9 --- /dev/null +++ b/src/core/helpers/client_functions.py @@ -0,0 +1,94 @@ +""" (module) client_functions +Useful functions for the WhyBot client +""" + +import os +import json + +import yaml +import discord +import asyncpg +import aioredis +from discord.ext import commands + +import __main__ +from .exception import ConfigNotFound + + +async def update_activity(client: commands.Bot): + """ + Updates the bot's activity with the amount of servers + + Parameters: + client (WhyBot): The bot to update presence for + """ + + await client.change_presence( + activity=discord.Game(f"On {len(client.guilds)} servers! | /help") + ) + + +def get_why_config() -> dict: + """ + Gets the why bot config + + Returns: + dict: The parsed result of config.yaml file + + Raises: + ConfigNotFound: If the config was not found + """ + + path = os.path.join(os.path.dirname(__main__.__file__), "config.yaml") + + if not os.path.exists(path): + raise ConfigNotFound + + with open(path) as f: + data = yaml.load(f, Loader=yaml.SafeLoader) + + return data + + +async def create_connection_pool() -> asyncpg.Pool: + """ + Creates a connection pool to the bots postgresql db + + Returns: + asyncpg.Pool: an asyncpg connection pool. + """ + + async def init(conn): + # Set up auto json encoder/decoder: + await conn.set_type_codec( + "json", encoder=json.dumps, decoder=json.loads, schema="pg_catalog" + ) + + config = get_why_config() + pool = await asyncpg.create_pool(dsn=config["DATABASE_URL"], init=init) + + return pool + + +async def create_redis_connection() -> aioredis.Redis: + """ + Creates a connection the the redis database + + Returns: + aioredis.Redis: the connection to the db + """ + config = get_why_config() + + redis = aioredis.from_url( + config["REDIS_URI"], + decode_responses=True, + password=config["REDIS_PASSWORD"], + port=6379, + ) + + return redis + + +GUILD_IDS = [ + get_why_config()["MAIN_GUILD"] +] # owner commands only work in the guilds in this array diff --git a/src/core/helpers/exception.py b/src/core/helpers/exception.py new file mode 100644 index 0000000..22bdf59 --- /dev/null +++ b/src/core/helpers/exception.py @@ -0,0 +1,63 @@ +""" (module) exception +This module contains exceptions to make development easier +""" + +import sys + +from rich.text import Text +from rich.panel import Panel +from rich.console import Console + + +class BaseException(Exception): + """Base class for other exceptions to inherit form""" + + pass + + +class RichBaseException(BaseException): + """ + Base rich class for other exceptions to inherit form + This one prints the error to console with rich + """ + + def __init__(self, title: str, message: str) -> None: + error_message = Panel( + Text.from_markup(f"[yellow]{message}"), + title=title, + border_style="red", + ) + Console().print(error_message, justify="left") + super().__init__() + + +class ConfigNotFound(RichBaseException): + def __init__(self) -> None: + super().__init__( + "CONFIG FILE NOT FOUND!!!", + "Config file (config.yaml) was not found.\nPlease run setup.py to create" + " config files", + ) + sys.exit(1) + + +class InvalidDatabaseUrl(RichBaseException): + def __init__(self) -> None: + super().__init__( + "INVALID DATABASE URL!!!", + "Invalid postgresql connection string was provided.\nPlease provide the" + " correct string in config", + ) + sys.exit(1) + + +class UserAlreadyBlacklisted(BaseException): + pass + + +class UserAlreadyWhitelisted(BaseException): + pass + + +class ImageAPIFail(BaseException): + pass diff --git a/src/core/helpers/http.py b/src/core/helpers/http.py new file mode 100644 index 0000000..7b38eb6 --- /dev/null +++ b/src/core/helpers/http.py @@ -0,0 +1,140 @@ +from io import BytesIO +from typing import Optional, Any +from urllib.parse import urlencode + +import aiohttp + + +async def get_request( + url: str, + data: Optional[dict] = None, + headers: Optional[dict] = None, + timeout: Optional[int] = None, +) -> Optional[aiohttp.ClientResponse | str | dict]: + """Makes a get request and returns the json or text result""" + + if data is not None: + url = f"{url}?{urlencode(data)}" + + kwargs = { + "headers": headers, + } + + if timeout is not None: + kwargs["timeout"] = aiohttp.ClientTimeout(total=timeout) + + async with aiohttp.ClientSession(**kwargs) as session: + async with session.get(url) as resp: + if resp.ok is False: + return None + + try: + return await resp.json() + except aiohttp.ContentTypeError: + try: + return await resp.text() + except aiohttp.ContentTypeError: + return None + + +async def post_request( + url: str, + data: Optional[dict] = None, + body: Optional[Any] = None, + json: Optional[bool] = True, + headers: Optional[dict] = None, + timeout: Optional[int] = None, +) -> Optional[aiohttp.ClientResponse | str | dict]: + """Makes a post request and returns the json or text result""" + + if data is not None: + url = f"{url}?{urlencode(data)}" + + kwargs = { + "headers": headers, + } + + if timeout is not None: + kwargs["timeout"] = aiohttp.ClientTimeout(total=timeout) + async with aiohttp.ClientSession(**kwargs) as session: + # decide if to do json=body or data=body in session.post() + json_or_data = "json" if json is True else "data" + async with session.post(url, **{json_or_data: body}) as resp: + if resp.ok is False: + return None + + try: + return await resp.json() + except aiohttp.ContentTypeError: + try: + return await resp.text() + except aiohttp.ContentTypeError: + return None + + +async def get_request_bytes( + url: str, + data: Optional[dict] = None, + headers: Optional[dict] = None, + timeout: Optional[int] = None, + bytes_io: Optional[bool] = False, +) -> Optional[aiohttp.ClientResponse | bytes | BytesIO]: + """Makes a get request and returns the byte result. This is useful for something like a file/image""" + + if data is not None: + url = f"{url}?{urlencode(data)}" + + kwargs = { + "headers": headers, + } + + if timeout is not None: + kwargs["timeout"] = aiohttp.ClientTimeout(total=timeout) + + async with aiohttp.ClientSession(**kwargs) as session: + async with session.get(url) as resp: + if resp.ok is False: + return None + + if bytes_io: + response_bytes = BytesIO(await resp.read()) + response_bytes.seek(0) + return response_bytes + + return await resp.read() + + +async def post_request_bytes( + url: str, + data: Optional[dict] = None, + body: Optional[Any] = None, + json: Optional[bool] = True, + headers: Optional[dict] = None, + timeout: Optional[int] = None, + bytes_io: Optional[bool] = False, +) -> Optional[aiohttp.ClientResponse | bytes | BytesIO]: + """Makes a post request and returns the byte result. This is useful for something like a file/image""" + + if data is not None: + url = f"{url}?{urlencode(data)}" + + kwargs = { + "headers": headers, + } + + if timeout is not None: + kwargs["timeout"] = aiohttp.ClientTimeout(total=timeout) + + async with aiohttp.ClientSession(**kwargs) as session: + # decide if to do json=body or data=body in session.post() + json_or_data = "json" if json is True else "data" + async with session.post(url, **{json_or_data: body}) as resp: + if resp.ok is False: + return None + + if bytes_io: + response_bytes = BytesIO(await resp.read()) + response_bytes.seek(0) + return response_bytes + + return await resp.read() diff --git a/src/core/helpers/log.py b/src/core/helpers/log.py new file mode 100644 index 0000000..17172cd --- /dev/null +++ b/src/core/helpers/log.py @@ -0,0 +1,143 @@ +""" (module) log: +This is for logging errors and exceptions +""" + +import os +import sys +import logging +import traceback +from datetime import datetime +from typing import Final, Optional + +import aiofiles +from rich.panel import Panel +from rich.console import Console + +import __main__ +from .client_functions import get_why_config + +rich_console = Console() + +path: Final = os.path.join(os.path.dirname(__main__.__file__), "logfiles") + +# check if log files dir exists +if not os.path.exists(path): + # and if not make it + os.makedirs(path) + + +# setup discord logger +logger = logging.getLogger("discord") +logger.setLevel(logging.INFO) +discord_logfile_path = os.path.join(path, "discord.log") +handler = logging.FileHandler(filename=discord_logfile_path, encoding="utf-8", mode="w") +handler.setFormatter( + logging.Formatter( + "[%(levelname)s] (%(asctime)s) - %(message)s", "%d-%b-%y %H:%M:%S" + ) +) +logger.addHandler(handler) + + +LOGFILE_PATH: Final = os.path.join(path, "main.log") + + +# Custom exeption handler +def log_errors(etype, value, tb) -> None: + """Logs errors to the file instead of terminal""" + + error = ( + f"{etype.__name__}:\n\tTraceback (most recent call" + f" last):\n\t{' '.join(traceback.format_tb(tb))}\n\t{value}" + ) + + # Pythons core module "logging" doesnt wanna work me very sad so me make this workaround: + config = get_why_config() + if config["LOGGING"]: + with open(LOGFILE_PATH, "a") as f: + f.write( + f"[ERROR] ({datetime.now().strftime('%d-%b-%Y %H:%M:%S')}) - {error}\n" + ) + + rich_console.print( + Panel( + "Traceback (most recent call" + f" last):\n\t{''.join(traceback.format_tb(tb))}\n{value}", + title=etype.__name__, + border_style="red", + ) + ) + + +async def log_normal(message: str) -> None: + """ + Logs an error + + Parameters + message (str): The message you want to log + """ + async with aiofiles.open(LOGFILE_PATH, "a") as f: + await f.write( + f"[INFO] ({datetime.now().strftime('%d-%b-%Y %H:%M:%S')}) - {message}\n" + ) + + +async def convert_to_dict() -> dict: + """ + Converts the log.txt file to a dict + + Returns + dict: The log file + """ + async with aiofiles.open(LOGFILE_PATH) as logs_data: + logs = {} + + async for line in logs_data: + if line.startswith("[INFO]"): + continue + if line.startswith("[ERROR]"): + logs[line] = "" + continue + try: + logs[(list(logs.keys())[-1])] += line + except (IndexError, TypeError): + break + + return logs + + +async def get_last_errors(count: int = 1) -> Optional[dict]: + """ + Gets the last x amount of errors from the logs file + + Parameters + count (Optional[int]): The amount of errors you want. (default = 1) + + Returns: + dict: The last errors in a dictionary + """ + logs: dict = await convert_to_dict() + + if len(logs) == 0: + return None + + last_errors = {} + + last_keys = [list(logs.keys())[-(i + 1)] for i in range(count)] + + for key in last_keys: + last_errors[key] = logs[key] + return last_errors + + +# client.event -> on_error +async def on_error(event_method, *args, **kwargs): + """ + This function is run when the client/bot encounters an error. + I will overwrite the default client.on_error method with this one + Basically it stops the bot from ignoring/printing error to terminal + instead logs the error to main.log and prints with rich + """ + # get error + ex_type, ex_value, tb = sys.exc_info() + log_errors(ex_type, ex_value, tb) diff --git a/src/core/helpers/music.py b/src/core/helpers/music.py new file mode 100644 index 0000000..973365a --- /dev/null +++ b/src/core/helpers/music.py @@ -0,0 +1,13 @@ +import discord +from discord.ext import commands +import pycord.wavelink as wavelink + + +class Player(wavelink.Player): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + +async def get_player(self, ctx: discord.ApplicationContext): + if isinstance(ctx, (discord.ApplicationContext, commands.Context)): + return diff --git a/src/core/helpers/views.py b/src/core/helpers/views.py new file mode 100644 index 0000000..d214552 --- /dev/null +++ b/src/core/helpers/views.py @@ -0,0 +1,347 @@ +import io + +import asyncpg +import discord + +from core.utils import number_suffix, slow_safe_calculate + + +class RickRollView(discord.ui.View): + """ + View with a button that says claim but after clicking the button you get rickrolled + It also updates a counter with the amount of people rickrolled from the view + + Parameter: + db (asyncpg.Pool): the connection to the psql database, used to update the counter + """ + + def __init__(self, db: asyncpg.Pool): + self.db = db + self.key = "rickroll_counter" + super().__init__(timeout=500) + + @discord.ui.button(style=discord.ButtonStyle.green, label="Claim") + async def claim_button( + self, button: discord.ui.Button, interaction: discord.Interaction + ): + button.style = discord.ButtonStyle.red + button.label = "Claimed" + button.disabled = True + + await interaction.response.edit_message(view=self) + await interaction.followup.send("https://imgur.com/NQinKJB", ephemeral=True) + + count = await self.db.fetch("SELECT value FROM counters WHERE key=$1", self.key) + count = count[0][0] + 1 if len(count) else None + + if count is None: + await self.db.execute( + "INSERT INTO counters (key, value) VALUES ($1, 1)", + self.key, + ) + count = 1 + + await interaction.followup.send( + f"You were the {number_suffix(count)} person to get rickrolled\nLMAO" + " imagine couldn't be me", + ephemeral=True, + ) + + await self.db.execute( + "UPDATE counters SET value=$1 WHERE key=$2", count, self.key + ) + + async def on_timeout(self) -> None: + self.children[0].label = ( + "Timed Out" + if self.children[0].label != "Claimed" + else self.children[0].label + ) + self.children[0].disabled = True + await self.message.edit(view=self) + + return await super().on_timeout() + + +class BotInfoView(discord.ui.View): + def __init__(self): + super().__init__(timeout=None) + self.add_item( + discord.ui.Button( + style=discord.ButtonStyle.grey, + label="Website", + url="https://why.fusionsid.xyz/", + ) + ) + self.add_item( + discord.ui.Button( + style=discord.ButtonStyle.grey, + label="Source Code", + url="https://github.com/FusionSid/Why-Bot", + ) + ) + self.add_item( + discord.ui.Button( + style=discord.ButtonStyle.grey, + label="Discord Server", + url="https://discord.gg/Jm8QPF6xbN", + ) + ) + self.add_item( + discord.ui.Button( + style=discord.ButtonStyle.grey, + label="Privacy", + url="https://github.com/FusionSid/Why-Bot/blob/rewrite-the-rewrite/PRIVACY.md", + ) + ) + + +class LinkView(discord.ui.View): + """ + This is a view that contains a bunch of buttons. + There can be as many buttons as you want (obviously not more than the limit) + Each button is in this format: [title, link] + """ + + def __init__(self, *links: tuple[list[str]]): + super().__init__(timeout=None) + for link in links: + self.add_item( + discord.ui.Button( + style=discord.ButtonStyle.grey, + label=link[0], + url=link[1], + ) + ) + + +class CalculatorView(discord.ui.View): + """This is a view for the calculator""" + + def __init__(self, ctx: discord.ApplicationContext): + self.expr = "" + self.ctx = ctx + super().__init__(timeout=100) + + @discord.ui.button(style=discord.ButtonStyle.blurple, label="1", row=0) + async def one(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "1" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.blurple, label="2", row=0) + async def two(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "2" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.blurple, label="3", row=0) + async def three(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "3" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.green, label="+", row=0) + async def plus(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "+" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.blurple, label="4", row=1) + async def last(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "4" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.blurple, label="5", row=1) + async def five(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "5" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.blurple, label="6", row=1) + async def six(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "6" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.green, label="/", row=1) + async def divide(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "/" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.blurple, label="7", row=2) + async def seven(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "7" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.blurple, label="8", row=2) + async def eight(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "8" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.blurple, label="9", row=2) + async def nine(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "9" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.green, label="*", row=2) + async def multiply( + self, button: discord.ui.Button, interaction: discord.Interaction + ): + self.expr += "*" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.blurple, label=".", row=3) + async def dot(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "." + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.blurple, label="0", row=3) + async def zero(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "0" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.green, label="=", row=3) + async def equal(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr = await slow_safe_calculate(self.expr) + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.green, label="-", row=3) + async def minus(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr += "-" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.green, label="(", row=4) + async def left_bracket( + self, button: discord.ui.Button, interaction: discord.Interaction + ): + self.expr += "(" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.green, label=")", row=4) + async def right_bracket( + self, button: discord.ui.Button, interaction: discord.Interaction + ): + self.expr += ")" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.red, label="C", row=4) + async def clear(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr = "" + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + @discord.ui.button(style=discord.ButtonStyle.red, label="<==", row=4) + async def back(self, button: discord.ui.Button, interaction: discord.Interaction): + self.expr = self.expr[:-1] + await interaction.response.edit_message(content=f"```\n{self.expr}\n```") + + async def interaction_check(self, interaction) -> bool: + if interaction.user != self.ctx.author: + await interaction.response.send_message( + "This calculator is not for you. Use /calculator to get your own.", + ephemeral=True, + ) + return False + return True + + async def on_timeout(self) -> None: + for button in self.children: + button.disabled = True + + await self.message.edit(view=self) + return await super().on_timeout() + + +class ConfirmView(discord.ui.View): + def __init__(self, target: discord.Member): + self.target = target + self.accepted = False + super().__init__(timeout=20) + + @discord.ui.button(label="Confirm", style=discord.ButtonStyle.green) + async def confirm_callback(self, button, interaction): + for button in self.children: + button.disabled = True + + await self.message.edit(view=self) + + await interaction.response.send_message("You accepted!", ephemeral=True) + self.accepted = True + self.stop() + + @discord.ui.button(label="Deny", style=discord.ButtonStyle.red) + async def deny_callback(self, button, interaction): + for button in self.children: + button.disabled = True + + await self.message.edit(view=self) + + await interaction.response.send_message("You Denied!", ephemeral=True) + self.accepted = False + self.stop() + + async def interaction_check(self, interaction) -> bool: + if interaction.user != self.target: + await interaction.response.send_message( + "This button is not for you!", + ephemeral=True, + ) + return False + return True + + async def on_timeout(self) -> None: + for button in self.children: + button.disabled = True + + await self.message.edit(view=self) + await super().on_timeout() + + self.stop() + + +class InputModalView(discord.ui.Modal): + def __init__(self, label: str, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.value = None + + self.add_item( + discord.ui.InputText( + label=label, + style=discord.InputTextStyle.long, + max_length=3999, + ) + ) + + async def callback(self, interaction: discord.Interaction): + self.value = self.children[0].value + await interaction.response.send_message("Processing input...", ephemeral=True) + + +class ErrorView(discord.ui.View): + def __init__(self, owner_id: int, data: str): + self.owner_id = owner_id + self.data = data + super().__init__(timeout=120) + + @discord.ui.button(label="Send as File", style=discord.ButtonStyle.green) + async def confirm_callback(self, button, interaction: discord.Interaction): + for button in self.children: + button.disabled = True + + await interaction.message.edit(view=self) + + file = io.BytesIO(self.data.encode()) + await interaction.response.send_message(file=discord.File(file, "error.txt")) + self.stop() + + async def interaction_check(self, interaction) -> bool: + if interaction.user.id != self.owner_id: + await interaction.response.send_message( + "This button is not for you!", + ephemeral=True, + ) + return False + return True + + async def on_timeout(self) -> None: + for button in self.children: + button.disabled = True + + await self.message.edit(view=self) + await super().on_timeout() + + self.stop() diff --git a/src/core/helpers/why_leveling.py b/src/core/helpers/why_leveling.py new file mode 100644 index 0000000..4a4c250 --- /dev/null +++ b/src/core/helpers/why_leveling.py @@ -0,0 +1,128 @@ +from typing import Final, Optional + +import discord +import asyncpg + +from core.models.level import LevelingDataGuild, LevelingDataMember + + +def xp_needed(level: int) -> int: + """ + Calculate the xp needed for any level + + Parameters: + level (int): The level to calculate xp needed + + Returns: + int: The amount of xp to reach the level provided + """ + + x, y = 0.125, 2 + return int((level / x) ** y) + + +async def get_level_data( + db: asyncpg.Pool, guild_id: int +) -> Optional[LevelingDataGuild]: + """ + This function gets the leveling data for a guild + + Parameters: + db (asyncpg.Pool): the connection pool to the database. This is found in client.db. + guild_id (int): The guild id of the guild to get leveling data for + + Returns: + Optional[LevelingDataGuild]: If data for the guild is not found it returns None else + it returns a LevelingDataGuild object with the data in it + """ + + data = await db.fetch("SELECT * FROM leveling_guild WHERE guild_id=$1", guild_id) + if not data: + return None + + return LevelingDataGuild(*data[0]) + + +async def get_member_data( + db: asyncpg.Pool, member: discord.Member, guild_id: int +) -> LevelingDataMember: + """ + This function gets the leveling data for a single member + + Parameters: + db (asyncpg.Pool): the connection pool to the database. This is found in client.db. + member (discord.Member): The member to get the data for + guild_id (int): The guild where to get the data for as members can be in multiple + guilds with different leveling data + + Returns: + LevelingDataMember: it returns a LevelingDataMember object with the mmeber's data in it + """ + + data = await db.fetch( + "SELECT * FROM leveling_member WHERE member_id=$1 AND guild_id=$2", + member.id, + guild_id, + ) + + if not data: + DEFAULT_MEMBER_DATA: Final[int] = [ + guild_id, + member.id, + f"{member.name}#{member.discriminator}", + 0, + 0, + 0, + ] + await db.execute( + "INSERT INTO leveling_member (guild_id, member_id, member_name, member_xp," + " member_level, member_total_xp) VALUES ($1, $2, $3, $4, $5, $6)", + *DEFAULT_MEMBER_DATA, + ) + return LevelingDataMember(*DEFAULT_MEMBER_DATA) + + return LevelingDataMember(*data[0]) + + +async def update_member_data( + db: asyncpg.Pool, message: discord.Message, member_data: LevelingDataMember +) -> None: + """ + Updates the data for a member + + Parameters: + db (asyncpg.Pool): the connection pool to the database. This is found in client.db. + message (discord.Message): The message that was sent. This is used to get things + like guild id and extra info + member_data (LevelingDataMember): The object with the new data + """ + + member = message.author + await db.execute( + """UPDATE leveling_member + SET member_name=$1, member_xp=$2, member_level=$3, member_total_xp=$4 + WHERE member_id=$5 AND guild_id=$6 + """, + f"{member.name}#{member.discriminator}", + member_data.member_xp, + member_data.member_level, + member_data.member_total_xp, + message.author.id, + message.guild.id, + ) + + +async def get_all_member_data(db: asyncpg.Pool, guild_id: int): + """ + Gets member data for all the members in a guild. This is used for leaderboards + + Parameters: + db (asyncpg.Pool) + """ + + data = await db.fetch( + "SELECT * FROM leveling_member WHERE guild_id=$1 ORDER BY member_total_xp", + guild_id, + ) + + return data[::-1] diff --git a/src/core/models/__init__.py b/src/core/models/__init__.py new file mode 100644 index 0000000..7f6db49 --- /dev/null +++ b/src/core/models/__init__.py @@ -0,0 +1,37 @@ +from discord.ext import commands + +from .client import WhyBot +from .counting import CountingData +from .level import ( + LevelingDataGuild, + LevelingDataMember, +) +from .rps import RockPaperScissorsView +from .tag import Tag +from .ticket import TicketGuild, Ticket, TicketView, NewTicketView, ClosedTicketView +from .ttt import TicTacToeAIView, TicTacToe2PlayerView +from core.helpers.checks import run_bot_checks + +__all__ = [ + "WhyBot", + "CountingData", + "LevelingDataGuild", + "LevelingDataMember", + "RockPaperScissorsView", + "Tag", + "TicketGuild", + "Ticket", + "TicketView", + "NewTicketView", + "ClosedTicketView", + "TicTacToeAIView", + "TicTacToe2PlayerView", +] + + +class BaseCog(commands.Cog): + """base class for cogs""" + + def __init__(self, client: WhyBot) -> None: + self.client = client + self.cog_check = run_bot_checks diff --git a/src/core/models/client.py b/src/core/models/client.py new file mode 100644 index 0000000..733b873 --- /dev/null +++ b/src/core/models/client.py @@ -0,0 +1,187 @@ +""" (module) whybot + +This contains the WhyBot commands.Bot client class and its class methods +Its also where most init tasks are done +""" + +__author__ = "FusionSid" +__licence__ = "MIT License" + +import datetime +from typing import Optional + +import discord +import aioredis +import asyncpg +from pycord.ext import ipc +from rich.console import Console +from discord.ext import commands + +from core.utils import format_seconds +from core.helpers import UserAlreadyBlacklisted, UserAlreadyWhitelisted + + +class WhyBot(commands.Bot): + """ + The Why Bot Class (subclass of: `discord.ext.commands.Bot`) + + Parameters: + config (dict): The parsed result of the config.yaml file + this is usualy obtained from the get_why_config function + + Attributes: + db Optional[asyncpg.Pool]: The connection to the postgres db + redis Optional[aioredis.Redis]: The connection to the redis db + config (dict): The bots config + version (str): the bots version + console (rich.console.Console): a Console object useful for rich printing + last_login_time (datetime.datetime.now()): The last time the bot started, used for uptime + + + all the ones inherited from `discord.ext.commands.Bot` + """ + + def __init__(self, config: dict, version: str): + + self.cogs_list = {} + self.db: asyncpg.Pool + self.redis: aioredis.Redis + + self.config = config + self.version = version + self.console = Console() + self.last_login_time = datetime.datetime.now() + + intents = discord.Intents.all() + allowed_mentions = discord.AllowedMentions(everyone=False) + + super().__init__( + intents=intents, + help_command=None, + case_insensitive=True, + command_prefix=config.get("DEFAULT_PREFIX"), + owner_id=config["BOT_OWNER_ID"], + debug_guilds=config.get("DEBUG_GUILDS") + if config.get("DEBUG_GUILD_MODE") + else None, + allowed_mentions=allowed_mentions, + ) + self.ipc = ipc.Server( + self, secret_key=config["IPC_KEY"], host=config["IPC_HOST"] + ) + + @property + async def uptime(self) -> str: + """ + This property returns the uptime for the bot. + + Returns: + str : Formated string with the uptime + """ + time_right_now = datetime.datetime.now() + seconds = int((time_right_now - self.last_login_time).total_seconds()) + + time = await format_seconds(seconds) + return time + + @property + def get_why_emojies(self) -> dict: + """ + This property returns the emojis for the bot + these emojis are the ones in the Why Bot guild + + Returns: + dict : A dictionary of emojis + """ + emojis_dict = {} + + for emoji in self.get_guild(self.config.get("MAIN_GUILD")).emojis: + emojis_dict[emoji.name] = str(emoji) + + return emojis_dict + + async def get_blacklisted_users( + self, reasons: Optional[bool] = False + ) -> list[int] | list[asyncpg.protocol.Record]: + """ + this function returns the blacklisted users for the bot from the db + this is used when the cache is empty or needs to be updated + + Parameters: + reasons (Optional[bool]): If this is True it will return a list + blacklisted users with their userids and reason for being banned + This default to false + + Returns: + list[int] | list[asyncpg.Record]: it will return a list with user ids of people blacklisted + but if reasons is True it will be a list of asyncpg.Records which will look like: list[list[int, str]] + """ + users = await self.db.fetch("SELECT * FROM blacklist;") + + if reasons: + return users + + return [int(user[0]) for user in users] + + async def reset_redis_blacklisted_cache(self): + """This function resets the blacklisted cache for the bot""" + + await self.redis.delete("blacklisted") + users = [int(user) for user in await self.get_blacklisted_users()] + if users: + await self.redis.lpush("blacklisted", *users) + await self.redis.expire("blacklisted", datetime.timedelta(days=5)) + + await self.redis.lpush("blacklisted", 0) + await self.redis.expire("blacklisted", datetime.timedelta(days=5)) + + async def blacklist_user(self, user_id: int, reason: Optional[str] = None): + """ + This function is used to black list a user from using the bot + + Parameters: + user_id (int): the user id to ban + reason (Optional[str]): the optional reason why the user was blacklisted + + Raises: + UserAlreadyBlacklisted: If the user is already blacklisted + """ + is_user_blacklisted = user_id in await self.get_blacklisted_users() + if is_user_blacklisted: # check if they are already blacklisted + raise UserAlreadyBlacklisted + + if reason is not None: + await self.db.execute( + "INSERT INTO public.blacklist (user_id) VALUES ($1)", user_id + ) + else: + await self.db.execute( + "INSERT INTO public.blacklist (user_id, reason) VALUES ($1, $2)", + user_id, + reason, + ) + + # Reset cache + await self.reset_redis_blacklisted_cache() + + async def whitelist_user(self, user_id: int): + """ + if a user was blacklisted from the bot it will whitelist them + + Parameters: + user_id (int): The user_id to unban + + Raises: + UserAlreadyWhitelisted: If the user is already whitelisted + """ + is_user_blacklisted = user_id in await self.get_blacklisted_users() + if not is_user_blacklisted: # check if they are already whitelisted + raise UserAlreadyWhitelisted + + await self.db.execute("DELETE FROM public.blacklist WHERE user_id=$1", user_id) + + # Reset cache + await self.reset_redis_blacklisted_cache() + + @staticmethod + async def on_ipc_error(endpoint, error): + print(endpoint, "raised", error) diff --git a/src/core/models/counting.py b/src/core/models/counting.py new file mode 100644 index 0000000..6394c19 --- /dev/null +++ b/src/core/models/counting.py @@ -0,0 +1,35 @@ +""" (module) counting +Model for making counting management easier +""" + +from typing import Optional +from dataclasses import dataclass + + +@dataclass +class CountingData: + """dataclass to help with counting data information from the counting table""" + + guild_id: int + last_counter: Optional[int] + current_number: Optional[int] + counting_channel: Optional[int] + + high_score: Optional[int] + plugin_enabled: Optional[bool] + auto_calculate: Optional[bool] + banned_counters: Optional[list[int]] + + @property + def next_number(self) -> int: + """ + This property returns the next number which is just one + more then the previous number (self.current_number + 1) + + Returns: + int: the next number + """ + if self.current_number is None: + return 1 + + return self.current_number + 1 diff --git a/src/core/models/level.py b/src/core/models/level.py new file mode 100644 index 0000000..e88d5cd --- /dev/null +++ b/src/core/models/level.py @@ -0,0 +1,55 @@ +from typing import Optional + +import random +from dataclasses import dataclass + + +@dataclass +class LevelingDataGuild: + """ + Dataclass to help with using the leveling data guild table + """ + + guild_id: int + plugin_enabled: Optional[bool] + + # card customization + text_font: Optional[str] + text_color: Optional[str] + background_image: Optional[str] + background_color: Optional[str] + progress_bar_color: Optional[str] + + # give xp execptions + no_xp_roles: Optional[list] + no_xp_channels: Optional[list] + + # level up announcment + level_up_text: Optional[str] + level_up_enabled: Optional[bool] + per_minute: Optional[str] + + def get_per_minute_xp(self) -> int: + if self.per_minute is None: + return random.randrange(15, 30) + + if self.per_minute.isnumeric(): + return int(self.per_minute) + + try: + left, right = self.per_minute.split("-") + return random.randrange(int(left), int(right)) + except ValueError: + return random.randrange(15, 30) + + +@dataclass +class LevelingDataMember: + """Dataclass to help with the leveling data member table""" + + guild_id: int + member_id: int + member_name: str + member_xp: int + member_level: int + member_total_xp: int diff --git a/src/core/models/rps.py b/src/core/models/rps.py new file mode 100644 index 0000000..ec0f132 --- /dev/null +++ b/src/core/models/rps.py @@ -0,0 +1,112 @@ +from typing import Literal + +import discord + + +class RockPaperScissorsView(discord.ui.View): + """Rock paper scissors game view""" + + def __init__(self, player1: discord.Member, player2: discord.Member): + self.results = {} + self.p1 = player1 + self.p2 = player2 + super().__init__(timeout=100) + + @discord.ui.button( + style=discord.ButtonStyle.green, label="Rock", emoji="šŸ—æ", custom_id="rock" + ) + async def rock(self, button: discord.ui.Button, interaction: discord.Interaction): + await interaction.response.send_message("You chose rock", ephemeral=True) + await self.handle_input(interaction.user, button.custom_id) + + @discord.ui.button( + style=discord.ButtonStyle.green, emoji="šŸ“„", label="Paper", custom_id="paper" + ) + async def paper(self, button: discord.ui.Button, interaction: discord.Interaction): + await interaction.response.send_message("You chose paper", ephemeral=True) + await self.handle_input(interaction.user, button.custom_id) + + @discord.ui.button( + style=discord.ButtonStyle.green, + label="Scissors", + emoji="āœ‚ļø", + custom_id="scissors", + ) + async def scissor( + self, button: discord.ui.Button, interaction: discord.Interaction + ): + await interaction.response.send_message("You chose scissors", ephemeral=True) + await self.handle_input(interaction.user, button.custom_id) + + async def handle_input( + self, user: discord.Member, input: Literal["rock", "paper", "scissors"] + ): + self.results[user.id] = input + + if self.results.get(self.p1.id) is None or self.results.get(self.p2.id) is None: + return + + for button in self.children: + button.disabled = True + + await self.message.edit(view=self) + await super().on_timeout() + + self.stop() + + if self.results[self.p1.id] == self.results[self.p2.id]: + await self.message.channel.send( + embed=discord.Embed( + title="Rock Paper Scissors", + description="It is a Draw!", + color=discord.Color.random(), + ) + ) + + p1_win = discord.Embed( + title="Rock Paper Scissors", + description=f"Yay {self.p1.mention} wins!", + color=discord.Color.random(), + ) + p2_win = discord.Embed( + title="Rock Paper Scissors", + description=f"Yay {self.p2.mention} wins!", + color=discord.Color.random(), + ) + + if self.results[self.p1.id] == "rock": + if self.results[self.p2.id] == "scissors": + await self.message.channel.send(embed=p1_win) + elif self.results[self.p2.id] == "paper": + await self.message.channel.send(embed=p2_win) + + elif self.results[self.p1.id] == "paper": + if self.results[self.p2.id] == "rock": + await self.message.channel.send(embed=p1_win) + elif self.results[self.p2.id] == "scissors": + await self.message.channel.send(embed=p2_win) + + elif self.results[self.p1.id] == "scissors": + if self.results[self.p2.id] == "paper": + await self.message.channel.send(embed=p1_win) + elif self.results[self.p2.id] == "rock": + await self.message.channel.send(embed=p2_win) + + async def interaction_check(self, interaction: discord.Interaction) -> bool: + if interaction.user not in [self.p1, self.p2]: + await interaction.response.send_message( + "This button is not for you!", + ephemeral=True, + ) + return False + return True + + async def on_timeout(self) -> None: + for button in self.children: + button.disabled = True + + await self.message.edit(view=self) + await super().on_timeout() + + self.stop() + await self.message.reply("Timed Out") diff --git a/src/core/models/tag.py b/src/core/models/tag.py new file mode 100644 index 0000000..4ea39f8 --- /dev/null +++ b/src/core/models/tag.py @@ -0,0 +1,12 @@ +from dataclasses import dataclass + + +@dataclass +class Tag: + """Dataclass for tags""" + + guild_id: int + tag_name: str + tag_value: str + tag_author: str + time_created: int diff --git a/src/core/models/ticket.py b/src/core/models/ticket.py new file mode 100644 index 0000000..094a786 --- /dev/null +++ b/src/core/models/ticket.py @@ -0,0 +1,282 @@ +import time +import io +from dataclasses import dataclass + +import discord +import chat_exporter + +from core.models import WhyBot +from core.utils import discord_timestamp +from core.db import setup_tickets +from core.helpers import ConfirmView, InputModalView + + +@dataclass +class TicketGuild: + guild_id: int + roles_allowed: list[int] + ping_roles: list[int] + create_button: bool + category: int + banned_users: list[int] + + +@dataclass +class Ticket: + ticket_id: int + guild_id: int + channel_id: int + ticket_creator: int + time_created: int + + +class TicketView(discord.ui.View): + def __init__(self, ticket_state: Ticket, member: discord.Member, client): + self.member = member + self.ticket = ticket_state + self.client = client + super().__init__(timeout=None) + + @discord.ui.button(label="Close", style=discord.ButtonStyle.gray, emoji="šŸ”’") + async def callback( + self, button: discord.ui.Button, interaction: discord.Interaction + ): + # send a button to confirm to close or not + are_you_sure = ConfirmView(target=interaction.user) + em = discord.Embed( + title="Close Ticket?", + description=( + f"{interaction.user.mention} are you sure you want to close this ticket?\nThis action removes \ + access for the person who made the ticket from the channel\nHowever this action can be reversed" + ), + color=discord.Color.random(), + ) + await interaction.response.send_message( + embed=em, view=are_you_sure, ephemeral=True + ) + await are_you_sure.wait() + + # if they hit no, do nothing + if not are_you_sure.accepted: + return + + # send the closed ticket view + view = ClosedTicketView(self.ticket, self.member, self.client) + embed = discord.Embed( + title="Ticket has been closed!", + description=f"Closed by: {interaction.user.mention}\nTo view a transcript of messages in this \ + channel hit the transcript button.\nNote that this will be an html file \ + so you'll need to download it and open in a webbrowser", + ) + await interaction.followup.send(embed=embed, view=view) + await interaction.channel.set_permissions( + self.member, send_messages=False, read_messages=False + ) + try: + await interaction.delete_original_message() + except discord.Forbidden: + return + self.stop() + + +class ClosedTicketView(discord.ui.View): + def __init__(self, ticket_state: Ticket, member: discord.Member, client): + self.member = member + self.ticket = ticket_state + self.transcript = None + self.client = client + super().__init__(timeout=None) + + @discord.ui.button(label="Transcript", style=discord.ButtonStyle.grey, emoji="šŸ“„") + async def transcript_button( + self, button: discord.ui.Button, interaction: discord.Interaction + ): + await interaction.response.defer() + if self.transcript is None: + transcript = await chat_exporter.export( + interaction.channel, + limit=1000, + tz_info="UTC", + military_time=True, + bot=self.client, + ) + + if transcript is None: + return + + self.transcript_file = io.BytesIO(transcript.encode()) + await interaction.followup.send( + file=discord.File( + self.transcript_file, + filename=f"transcript-{interaction.channel.name}.html", + ) + ) + + @discord.ui.button(label="Delete", style=discord.ButtonStyle.grey, emoji="šŸ—‘ļø") + async def delete_button( + self, button: discord.ui.Button, interaction: discord.Interaction + ): + # send a button to confirm to close or not + are_you_sure = ConfirmView(target=interaction.user) + em = discord.Embed( + title="Close Ticket?", + description=( + f"{interaction.user.mention} are you sure you want to DELETE this ticket?\n\ + This action is permanent and can NOT be reversed" + ), + color=discord.Color.random(), + ) + await interaction.response.send_message( + embed=em, view=are_you_sure, ephemeral=True + ) + await are_you_sure.wait() + # if they hit no, do nothing + if not are_you_sure.accepted: + return + + await interaction.channel.delete() + await self.client.db.execute( + "DELETE FROM tickets WHERE id=$1", self.ticket.ticket_id + ) + + @discord.ui.button(label="Reopen", style=discord.ButtonStyle.grey, emoji="šŸ”“") + async def reopen_button( + self, button: discord.ui.Button, interaction: discord.Interaction + ): + await interaction.response.defer() + + embed = discord.Embed( + title=f"Ticket from {self.member.name} has been reopened!", + description=f"Ticket was reopened by: {interaction.user}", + ) + embed.set_footer(text="To close this ticket click the close button") + view = TicketView(self.ticket, self.member, self.client) + await interaction.followup.send(embed=embed, view=view) + + perms = { + "send_messages": True, + "read_messages": True, + "add_reactions": True, + "embed_links": True, + "attach_files": True, + "read_message_history": True, + "external_emojis": True, + } + await interaction.channel.set_permissions(self.member, **perms) + + try: + await interaction.delete_original_message() + except discord.Forbidden: + return + self.stop() + + +class NewTicketView(discord.ui.View): + def __init__(self, guild_id: int, client: WhyBot): + self.client = client + self.guild_id = guild_id + super().__init__(timeout=None) + + button = discord.ui.Button( + label="New Ticket", + style=discord.ButtonStyle.gray, + emoji="šŸ“©", + custom_id=f"new-ticket-view-{guild_id}", + ) + button.callback = self.callback + self.add_item(button) + + async def callback(self, interaction: discord.Interaction): + # ik what ur thinking tHIs iS just COPy PaSTED codE fROm TIckEts.pY + # well stfu didnt ask im lazy ok + modal = InputModalView( + title="Ticket Reason", label="Enter the reason why you're making the ticket" + ) + modal.children[0].max_length = 2000 + await interaction.response.send_modal(modal) + await modal.wait() + + if modal.value is None: + return await interaction.followup.send("Invalid Input", ephemeral=True) + + data: list[list] = await self.client.db.fetch( + "SELECT * FROM ticket_guild WHERE guild_id=$1", self.guild_id + ) + if len(data) == 0: + await setup_tickets(self.client.db, self.guild_id) + default_data = [self.guild_id, [], [], False, None, []] + ticket_config = TicketGuild(*default_data) + else: + ticket_config = TicketGuild(*data[0]) + + if interaction.user.id in ticket_config.banned_users: + return await interaction.followup.send( + "You are banned from making tickets (imagine)", ephemeral=True + ) + + # figure out if to make it in a category or not + if ticket_config.category in (0, None): + category = None + else: + category = list( + filter( + lambda i: i.id == ticket_config.category, + interaction.guild.categories, + ) + ) + category = None if len(category) == 0 else category[0] + + # create channel + channel = await interaction.guild.create_text_channel( + f"ticket-{interaction.user.id}", category=category + ) + + # set channel perms + await channel.set_permissions( + interaction.guild.get_role(interaction.guild.id), + send_messages=False, + read_messages=False, + ) + perms = { + "send_messages": True, + "read_messages": True, + "add_reactions": True, + "embed_links": True, + "attach_files": True, + "read_message_history": True, + "external_emojis": True, + } + await channel.set_permissions(interaction.user, **perms) + for role in ticket_config.roles_allowed: + if (role := interaction.guild.get_role(role)) is not None: + await channel.set_permissions(role, **perms) + + ticket_data = [ + interaction.guild.id, + channel.id, + interaction.user.id, + int(time.time()), + ] + ticket_id = await self.client.db.fetch( + """INSERT INTO tickets ( + guild_id, channel_id, ticket_creator, time_created + ) VALUES ($1, $2, $3, $4) RETURNING id""", + *ticket_data, + ) + + ticket = Ticket(ticket_id[0][0], *ticket_data) + + embed = discord.Embed( + title=f"New ticket from {interaction.user.name}!", + description=f"**Please wait, support will be with you shortly!**\ + \n\nTicket Created: {discord_timestamp(ticket.time_created, 'ts')}", + color=discord.Color.random(), + ) + embed.add_field(name="Reason Provided:", value=str(modal.value)) + embed.set_footer(text="To close this ticket click the close button") + view = TicketView(ticket, interaction.user, self.client) + await channel.send(embed=embed, view=view) + + await interaction.followup.send( + f"A new ticket has been created for you: {channel.mention}", ephemeral=True + ) diff --git a/src/core/models/ttt.py b/src/core/models/ttt.py new file mode 100644 index 0000000..c56b090 --- /dev/null +++ b/src/core/models/ttt.py @@ -0,0 +1,427 @@ +from typing import Final, Literal + +import math +import discord + +# define what an empty space on the board will be +FREE_SPACE: Final[str] = " " + + +class TicTacToeGame: + """ + The tic tac toe game class + The handles the game logic and what happens during the game + """ + + def __init__(self): + self.reset_board() + + def reset_board(self): + self.board = [[FREE_SPACE for _ in range(3)] for _ in range(3)] + + def space_free(self, pos: int) -> bool: + """ + This function checks to see if a position on the board is free + + Parameters: + pos (int): The position to check for. Must be 1 - 9 or it will return False. + The first row is 0-3 second row 4-6 and third row is 7-9 + + Returns: + bool: True if the place is free if not it will return False + It will also return False if the position is not between (& inclduing) 1-9 + """ + if 0 < pos <= 3: + position = self.board[0][pos - 1] + elif 3 < pos <= 6: + position = self.board[1][pos - 4] + elif 6 < pos <= 9: + position = self.board[2][pos - 7] + else: + return False + + if position == FREE_SPACE: + return True + + return False + + def check_draw(self) -> bool: + """ + This checks to see if the game is over by a draw + + Returns: + bool: if draw it returns True else False + """ + return all(FREE_SPACE not in row for row in self.board) + + def check_win(self) -> bool: + """ + This function is used to check for a win + + Returns: + bool: If its a win True if not False + """ + # Horizontal Wins + for row in self.board: + if row[0] == row[1] and row[1] == row[2] and row[0] != FREE_SPACE: + return True + + # Vertical Wins + for i in range(3): + if ( + self.board[0][i] == self.board[1][i] + and self.board[1][i] == self.board[2][i] + and self.board[0][i] != FREE_SPACE + ): + return True + + # Check for diagonal wins + if ( + self.board[0][0] == self.board[1][1] + and self.board[1][1] == self.board[2][2] + and self.board[0][0] != FREE_SPACE + ): + return True + elif ( + self.board[0][2] == self.board[1][1] + and self.board[1][1] == self.board[2][0] + and self.board[0][2] != FREE_SPACE + ): + return True + + return False + + def update_postion(self, letter: Literal["X", "O"], pos: int) -> list[str]: + """ + This function is used to update a players position on the board + + Parameter: + letter (Literal[str]): The letter of the player usualy X or O. + pos (int): The position to move to + + Returns: + list[str]: There are 4 different types of list that will be returned + (For the sake of consistency i chose to use a list instead of a string as on of the values needs a list) + 1. ["invalid"]: this means the position is invalid + 2. ["draw"]: This means the game has ended in a draw + 3. ["win", letter: str]: This means that a player has won so it will return the player who won as well + eg if X wins it will return ["win", "X"] + 4. ["continue"]: This means that the move was played successfuly + and nothing else happened so the game can continue + """ + if not self.space_free(pos): + return ["invalid"] + + if 0 < pos <= 3: + self.board[0][pos - 1] = letter + elif 3 < pos <= 6: + self.board[1][pos - 4] = letter + elif 6 < pos <= 9: + self.board[2][pos - 7] = letter + + if self.check_draw(): + return ["draw"] + + if self.check_win(): + return ["win", letter] + + return ["continue"] + + +class TicTacToeAI(TicTacToeGame): + """ + Tic tac toe but with the minimax algorithm to always win or be a draw + The ai is just to smart cause why bot is massive brain + """ + + def check_mark_win(self, mark): + # Horizontal Wins + for row in self.board: + if row[0] == row[1] and row[1] == row[2] and row[0] == mark: + return True + + # Vertical Wins + for i in range(3): + if ( + self.board[0][i] == self.board[1][i] + and self.board[1][i] == self.board[2][i] + and self.board[0][i] == mark + ): + return True + + # Check for diagonal wins + if ( + self.board[0][0] == self.board[1][1] + and self.board[1][1] == self.board[2][2] + and self.board[0][0] == mark + ): + return True + + elif ( + self.board[0][2] == self.board[1][1] + and self.board[1][1] == self.board[2][0] + and self.board[0][2] == mark + ): + return True + + return False + + def bot_move(self, letter): + best_score, best_move = -100, None + + for row_id, row in enumerate(self.board): + for index, key in enumerate(row): + if key == FREE_SPACE: + self.board[row_id][index] = letter + score = self.minimax(letter, self.board, 0, False) + self.board[row_id][index] = FREE_SPACE + if score > best_score: + best_score = score + + index += 1 + if row_id == 0: + best_move = index + elif row_id == 1: + best_move = index + 3 + elif row_id == 2: + best_move = index + 6 + + return self.update_postion(letter, best_move) + + def minimax(self, bot, board, depth, isMaximizing): + player = "X" if bot == "O" else "O" + if self.check_mark_win(bot): + return 100 + + elif self.check_mark_win(player): + return -100 + + elif self.check_draw(): + return 0 + + if isMaximizing: + best_score = -100 + + for row_id, row in enumerate(board): + for index, key in enumerate(row): + if key == FREE_SPACE: + board[row_id][index] = bot + score = self.minimax(bot, board, 0, False) + board[row_id][index] = FREE_SPACE + if score > best_score: + best_score = score + + return best_score + + else: + best_score = 100 + + for row_id, row in enumerate(board): + for index, key in enumerate(row): + if key == FREE_SPACE: + board[row_id][index] = player + score = self.minimax(bot, board, 0, True) + board[row_id][index] = FREE_SPACE + if score < best_score: + best_score = score + + return best_score + + +class TicTacToe2PlayerButton(discord.ui.Button): + """ + Class for a single button (out of 9) on the member vs member mode for tic tac toe + This will be used by the TicTacToe2PlayerView + """ + + def __init__(self, pos): + self.pos = pos + row = math.ceil(pos / 3) + super().__init__(style=discord.ButtonStyle.secondary, label="\u200b", row=row) + + async def callback(self, interaction: discord.Interaction): + await interaction.response.defer() + if interaction.user != self.view.current_player: + return await interaction.followup.send( + "It is not your turn", + ephemeral=True, + ) + + letter = "X" if self.view.current_player == self.view.p2 else "O" + + result = self.view.game.update_postion(letter, self.pos) + + if result[0] == "invalid": + return await interaction.followup.send("Invalid move", ephemeral=True) + + elif result[0] == "win": + await interaction.followup.send( + embed=discord.Embed( + title="Game Over!", + description=f"Yay {self.view.current_player.mention} wins", + color=discord.Color.random(), + ) + ) + await self.view.on_timeout() + + elif result[0] == "draw": + await interaction.followup.send( + embed=discord.Embed( + title="Game Over!", + description="Its a draw gg", + color=discord.Color.random(), + ) + ) + await self.view.on_timeout() + + elif result[0] == "continue": + if self.view.current_player == self.view.p1: + self.view.current_player = self.view.p2 + else: + self.view.current_player = self.view.p1 + + await self.view.redraw_buttons() + + +class TicTacToe2PlayerView(discord.ui.View): + """View for tic tac toe member vs member mode""" + + def __init__(self, player1: discord.Member, player2: discord.Member): + self.game = TicTacToeGame() + self.p1 = player1 + self.p2 = player2 + + self.current_player = player2 + + super().__init__(timeout=500) + + for pos in range(1, 10): + self.add_item(TicTacToe2PlayerButton(pos)) + + async def redraw_buttons(self): + for idxa, row in enumerate(self.game.board): + for idxb, box in enumerate(row): + if box == FREE_SPACE: + continue + + index = (3 * idxa) + idxb + self.children[index].label = box + + await self.message.edit(view=self) + + async def interaction_check(self, interaction) -> bool: + if interaction.user not in [self.p1, self.p2]: + await interaction.response.send_message( + "You are not playing this game", + ephemeral=True, + ) + return False + + return True + + async def on_timeout(self) -> None: + for button in self.children: + button.disabled = True + + await self.message.edit(view=self) + await super().on_timeout() + + self.stop() + + +class TicTacToeAIView(discord.ui.View): + """View for tic tac toe member vs ai mode""" + + def __init__(self, player): + self.game = TicTacToeAI() + self.player = player + + self.current_player = player + + super().__init__(timeout=500) + + for pos in range(1, 10): + self.add_item(TicTacToeAIButton(pos)) + + async def redraw_buttons(self): + for idxa, row in enumerate(self.game.board): + for idxb, box in enumerate(row): + if box == FREE_SPACE: + continue + + index = (3 * idxa) + idxb + self.children[index].label = box + + await self.message.edit(view=self) + + async def interaction_check(self, interaction) -> bool: + if interaction.user != self.current_player: + await interaction.response.send_message( + "You are not playing this game", + ephemeral=True, + ) + return False + + return True + + async def on_timeout(self) -> None: + for button in self.children: + button.disabled = True + + await self.message.edit(view=self) + await super().on_timeout() + + self.stop() + + +class TicTacToeAIButton(TicTacToe2PlayerButton): + """ + Class for a single button (out of 9) on the member vs ai mode for tic tac toe + This will be used by the TicTacToeAIView + """ + + async def callback(self, interaction: discord.Interaction): + await interaction.response.defer() + if interaction.user != self.view.current_player: + return await interaction.followup.send( + "It is not your turn", + ephemeral=True, + ) + + if self.view.current_player == self.view.player: + result = self.view.game.update_postion("X", self.pos) + + if result[0] == "invalid": + return await interaction.followup.send("Invalid move", ephemeral=True) + + elif result[0] == "continue": + if self.view.current_player == "bot": + self.view.current_player = self.view.player + else: + self.view.current_player = "bot" + result = self.view.game.bot_move("O") + + if result[0] == "win": + await interaction.followup.send( + embed=discord.Embed( + title="Game Over!", + description=f"Yay {self.view.current_player} wins", + color=discord.Color.random(), + ) + ) + await self.view.on_timeout() + + elif result[0] == "draw": + await interaction.followup.send( + embed=discord.Embed( + title="Game Over!", + description="Its a draw gg", + color=discord.Color.random(), + ) + ) + await self.view.on_timeout() + + if self.view.current_player == "bot": + self.view.current_player = self.view.player + + await self.view.redraw_buttons() diff --git a/src/core/utils/__init__.py b/src/core/utils/__init__.py new file mode 100644 index 0000000..3cf5657 --- /dev/null +++ b/src/core/utils/__init__.py @@ -0,0 +1,18 @@ +from .asyncpg_context import asyncpg_connect +from .calc import slow_safe_calculate, calculate +from .count_lines import get_files, get_lines +from .formatters import format_seconds, number_suffix, discord_timestamp +from .other import chunkify, functime + +__all__ = [ + "asyncpg_connect", + "slow_safe_calculate", + "calculate", + "get_files", + "get_lines", + "format_seconds", + "number_suffix", + "discord_timestamp", + "chunkify", + "functime", +] diff --git a/src/core/utils/asyncpg_context.py b/src/core/utils/asyncpg_context.py new file mode 100644 index 0000000..514f88f --- /dev/null +++ b/src/core/utils/asyncpg_context.py @@ -0,0 +1,29 @@ +""" (module) asyncpg_context +Context manager for asyncpg +""" + +from contextlib import asynccontextmanager + +import asyncpg + + +@asynccontextmanager +async def asyncpg_connect(database_url: str) -> asyncpg.connection.Connection: + """ + Custom context manager to use asyncpg + Very useful to ensure that once I open a connection it will ALWAYS be closed even upon error + + Parameters: + database_url: str: The connection string + + Yeilds: + asyncpg.connection.Connection: The connection to the database + """ + # Connect to the database + connection = await asyncpg.connect(database_url) + + # Return connection for with statement + yield connection + + # close connection once context manager is closed + await connection.close() diff --git a/src/core/utils/calc.py b/src/core/utils/calc.py new file mode 100644 index 0000000..9b80306 --- /dev/null +++ b/src/core/utils/calc.py @@ -0,0 +1,68 @@ +""" (module) calc +Used to calculate an expression using the mathjs api +""" + +import urllib +from typing import Optional + +import numexpr +import aiohttp + + +async def slow_safe_calculate( + expr: str, only_int: Optional[bool] = False +) -> Optional[int | str]: + """ + Calculates a math expression using the mathjs API + + Parameters: + expr (str): The expression to evaluate + only_int (Optional[bool]): If this is True it will return the result only if its int + if its not an int it will return None. (By default this option is False) + + Returns: + int | str | None: It will return int if the result is already numeric or the result is int + it will return str if the result is something like a float + it will reutrn None if only_int is True and the result is not int + """ + if expr.isnumeric(): + return int(expr) + + expression = urllib.parse.quote(expr.replace("**", "^")) + async with aiohttp.ClientSession() as session: + async with session.get(f"http://api.mathjs.org/v4/?expr={expression}") as r: + result = await r.text() + + if only_int and result.isnumeric() is False: + return None + + if only_int: + return int(result) + + return result + + +async def calculate(expr: str) -> Optional[float]: + """ + Evaluates a math expression with numexpr + + Parameters: + expr (str): the expression to evaluate + + Returns: + Optional[float[]: It will return the result of the expression and if it + fails then it will return None + """ + try: + result = numexpr.evaluate(expr) + except ( + OverflowError, + AttributeError, + SyntaxError, + ZeroDivisionError, + KeyError, + ValueError, + ): + return None + + return result diff --git a/src/core/utils/count_lines.py b/src/core/utils/count_lines.py new file mode 100644 index 0000000..f60c610 --- /dev/null +++ b/src/core/utils/count_lines.py @@ -0,0 +1,61 @@ +""" (module) line_count +Used to get the amount of lines in the current project +""" + +import os +from datetime import timedelta + +import aiofiles +from aioredis import Redis + +import __main__ + + +async def get_files() -> list[str]: + """ + Gets a list of all the files in a python project + + Returns: + list[str]: List of file paths for the python files in the project + """ + file_list = [] + path = os.path.dirname(os.path.abspath(__main__.__file__)) + for root, _dirs, files in os.walk(path): + if "git" in root: + continue + + for file in files: + file_name = os.path.join(root, file) + + if file_name.endswith(".py"): + file_list.append(file_name) + + return file_list + + +async def get_lines(redis: Redis) -> int: + """ + Gets the amount of lines in a python project + + Parameters: + redis (Redis): an instance of redis.Redis which is connection to the redis database + this will be used to cache the line count to speed up performance + + Returns: + int: The amount of lines of code in the project + """ + if await redis.exists("python_line_count"): + # 6ms compute time, 0.3ms get from cache time + return int(await redis.get("python_line_count")) + + file_list = await get_files() + + lines = 0 + + for file in file_list: + async with aiofiles.open(file) as f: + lines += len(list(await f.readlines())) + + await redis.set("python_line_count", lines) + await redis.expire("python_line_count", timedelta(hours=6)) + return lines diff --git a/src/core/utils/formatters.py b/src/core/utils/formatters.py new file mode 100644 index 0000000..308d8fc --- /dev/null +++ b/src/core/utils/formatters.py @@ -0,0 +1,171 @@ +""" (module) formatters +This module contains formatting functions +""" + +from enum import Enum +from typing import Literal, Optional, Final + + +# Enum for amount of seconds per time period +class SecondIntervals(Enum): + second = 1 + minute = 60 + hour = 3600 + day = 86400 + week = 604800 + month = 2627424 + year = 31536000 + century = 3153600000 + millennium = 31536000000 + + +async def format_seconds(seconds: int, short: Optional[bool] = False) -> str: + """ + Takes in seconds and formats it into a more human readable format + + Parameters: + seconds (int): The time in seconds, this is the number that will be formated + short (Optional[bool]): This decides if to format the text into a shorter form. + Eg seconds -> sec, minutes -> min + This is off by default and also it is only supported for ms, sec, min & hrs + + Returns: + str: The formmated output + if it fails to format is will return an empty string ("") + """ + + if not isinstance(seconds, int) or seconds == 0: + return "" + + # milliseconds + elif 0 < seconds < SecondIntervals.second.value: + ms = int(round((seconds * 1000), 0)) + if short: + return f"{ms} ms" + return f"{ms} millisecond{'s' if ms != 1 else ''}" + + # convert to int (if not already) so we dont get shit like "7.0 minutes" + seconds = int(seconds) + + # seconds + if SecondIntervals.second.value <= seconds < SecondIntervals.minute.value: + if short: + return f"{seconds} sec" + return f"{seconds} second{'s' if seconds != 1 else ''}" + + # minutes + elif SecondIntervals.minute.value <= seconds < SecondIntervals.hour.value: + minutes, seconds = divmod(seconds, SecondIntervals.minute.value) + if short: + seconds = f"{seconds} sec" if seconds != 0 else "" + return f"{minutes} min {seconds}" + + seconds = ( + f"{seconds} second{'s' if seconds != 1 else ''}" if seconds != 0 else "" + ) + return f"{minutes} minute{'s' if minutes != 1 else ''} {seconds}" + + # hours + elif SecondIntervals.hour.value <= seconds < SecondIntervals.day.value: + hours, minutes = divmod(seconds, SecondIntervals.hour.value) + if short: + return f"{hours} hrs " + await format_seconds(minutes, short) + return f"{hours} hour{'s' if hours != 1 else ''} " + await format_seconds( + minutes + ) + + # days + elif SecondIntervals.day.value <= seconds < SecondIntervals.week.value: + days, hours = divmod(seconds, SecondIntervals.day.value) + return f"{days} day{'s' if days != 1 else ''} " + await format_seconds(hours) + + # weeks + elif SecondIntervals.week.value <= seconds < SecondIntervals.month.value: + weeks, days = divmod(seconds, SecondIntervals.week.value) + return f"{weeks} week{'s' if weeks != 1 else ''} " + await format_seconds(days) + + # months + elif SecondIntervals.month.value <= seconds < SecondIntervals.year.value: + months, weeks = divmod(seconds, SecondIntervals.month.value) + return f"{months} month{'s' if months != 1 else ''} " + await format_seconds( + weeks + ) + + # years + elif SecondIntervals.year.value <= seconds < SecondIntervals.century.value: + years, months = divmod(seconds, SecondIntervals.year.value) + return f"{years} year{'s' if years != 1 else ''} " + await format_seconds( + months + ) + + # centuries + elif SecondIntervals.century.value <= seconds < SecondIntervals.millennium.value: + century, years = divmod(seconds, SecondIntervals.century.value) + return ( + f"{century} {'centuries' if century != 1 else 'century'} " + + await format_seconds(years) + ) + + # millennia + elif SecondIntervals.millennium.value <= seconds: + millennium, century = divmod(seconds, SecondIntervals.millennium.value) + return ( + f"{millennium} {'millennia' if millennium != 1 else 'millennium'} " + + await format_seconds(century) + ) + + # if none + return "" + + +def number_suffix(number: int) -> str: + """ + This function adds the suffix / ordinal after a number provided + + Parameters: + number (int): The number that will be formatted + + Returns: + str: The formatted result + """ + SUFFIXES = {1: "st", 2: "nd", 3: "rd"} + if 10 <= number % 100 < 20: + suffix = "th" + else: + suffix = SUFFIXES.get(number % 10, "th") + return str(number) + suffix + + +def discord_timestamp( + time: int, + format_type: Literal["mdy", "md_yt", "t", "md_y", "w_md_yt", "ts", "h_m_s"], +) -> Optional[str]: + """ + This function takes in a timestamp and formats it into a discord timestamp + Discord timestamps look something like this: + + Parameters: + time (int): The unix timestamp + format_type: (Literal[str]): The type of timestamp you want + mdy = Month/Day/Year + md_yt = Month Day, Year Time + t = Time + md_y = Month Day, Year + w_md_yt = Weekday, Month Day, Year Time + ts = Time since + h_m_s = Hour:Minute:Second + + Returns: + Union[str, None]: str with the discord timestamp. If invalid code is provided it will return None + """ + formated_times: Final = { + "mdy": f"", + "md_yt": f"", + "t": f"", + "md_y": f"", + "w_md_yt": f"", + "ts": f"", + "h_m_s": f"", + } + + return formated_times.get(format_type) diff --git a/src/core/utils/other.py b/src/core/utils/other.py new file mode 100644 index 0000000..1c72dfe --- /dev/null +++ b/src/core/utils/other.py @@ -0,0 +1,44 @@ +import time +import inspect +import functools +from typing import Any, Optional, Callable + + +async def chunkify( + big_list: list[Any], chunk_size: Optional[int] = 10 +) -> list[list[Any]]: + """ + This function splits up a list into chunks of specified size + + Parameters: + big_list (list[Any]): The list that will be split into chunks + chunk_size (Optional[int]): The size of each chunk. The default size is 10 + + Returns: + list[list[Any]]: A list of lists. Each sub list is a chunk. + """ + return [big_list[i : i + chunk_size] for i in range(0, len(big_list), chunk_size)] + + +def functime(func: Callable, ns=False): + @functools.wraps(func) + async def wrapper(*args: tuple, **kwargs: dict): + if ns: + start = time.perf_counter_ns() + else: + start = time.perf_counter() + + # run function + if inspect.iscoroutinefunction(func): + await func(*args, **kwargs) + else: + func(*args, **kwargs) + + if ns: + stop = time.perf_counter_ns() + else: + stop = time.perf_counter() + + print("Function Execution Time:", stop - start) + + return wrapper diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..143140f --- /dev/null +++ b/src/main.py @@ -0,0 +1,89 @@ +""" +This is the main file for the bot +It contains functions to startup the bot + +If you are reading my code i'm sorry :moyai: +""" + +__version__ = "2.0.0" +__author__ = "FusionSid" +__licence__ = "MIT License" + + +import os +import sys +import time + +from rich.progress import Progress +from rich.traceback import install + +from core import WhyBot +from core.helpers import get_why_config, log_errors, on_error + + +def start_bot(client: WhyBot) -> None: + """ + Starts up the amazing Why Bot + + Parameters: + client (WhyBot): The instance of commands.Bot / subclasses. + This is the bot that will be started + """ + cogs = {} + + # get path of cogs directory + path = os.path.join(os.path.dirname(__file__), "cogs") + + for category in os.listdir(path): + if not os.path.isdir( + os.path.join(path, category) + ): # if its not a folder continue + continue + + # loop through the files in cogs/ + for filename in os.listdir(os.path.join(path, category)): + if os.path.isfile( + os.path.join(path, category, filename) + ) and filename.endswith( + ".py" + ): # check if the item is a python file + cog_name = filename[:-3] # remove .py from name + cogs[cog_name] = f"cogs.{category}.{cog_name}" + + # load the testing_cog (if it exists) + if os.path.exists(os.path.join(path, "testing_cog.py")): + cogs["testing"] = "cogs.testing_cog" + + print("\n") + client.cogs_list = cogs # for cog functions later like reload, load, unload + + # start all cogs with a spicy progress bar + with Progress() as progress: + loading_cogs = progress.add_task("[bold green]Loading Cogs", total=len(cogs)) + while not progress.finished: + for cog in cogs.values(): + client.load_extension(cog) + time.sleep(0.1) + progress.update( + loading_cogs, + advance=1, + description=f"[bold green]Loaded[/] [blue]{cog}[/]", + ) + progress.update(loading_cogs, description="[bold green]Loaded all cogs") + + time.sleep(1) + + client.event(on_error) # set event handler + client.ipc.start() + client.run(client.config.get("BOT_TOKEN")) + + +if __name__ == "__main__": + install(show_locals=True) + sys.__excepthook__ = log_errors + + config = get_why_config() + client = WhyBot(config, __version__) + + # Start + start_bot(client) diff --git a/src/requirements.txt b/src/requirements.txt new file mode 100644 index 0000000..aae34cf --- /dev/null +++ b/src/requirements.txt @@ -0,0 +1,23 @@ +aiofiles +Pillow +qrcode +simpcalci +discord_colorize +moviepy +numexpr +py-cord +pycord_ext_ipc +pyfiglet +black +rich +pre-commit +aiohttp +aioredis +asyncpg +psutil +PyYAML +validators +pycord.wavelink +python-dateutil +chat_exporter\ +aioconsole \ No newline at end of file diff --git a/src/scripts/backup.sh b/src/scripts/backup.sh new file mode 100644 index 0000000..ab47bfd --- /dev/null +++ b/src/scripts/backup.sh @@ -0,0 +1,5 @@ +path=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +export PGPASSWORD="Replace With PSQL Password" +name=`date +%d_%m_%Y` +mkdir -p $path/backups +pg_dump -Fc -h -U -f $path/backups/$name.dump \ No newline at end of file diff --git a/src/scripts/venv.sh b/src/scripts/venv.sh new file mode 100755 index 0000000..395f1d5 --- /dev/null +++ b/src/scripts/venv.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +rm -rf ./venv +python3 -m venv venv +source venv/bin/activate +pip install -r src/requirements.txt \ No newline at end of file diff --git a/src/setup.py b/src/setup.py new file mode 100644 index 0000000..43f0926 --- /dev/null +++ b/src/setup.py @@ -0,0 +1,196 @@ +import re +import os +import sys +import asyncio +import subprocess +from typing import Callable + +import yaml +from rich.console import Console +from rich.prompt import Prompt, Confirm + +from core.db import create_tables + + +console = Console() +PATH = os.path.dirname(__file__) + + +def clear(): + subprocess.call("clear" if os.name == "posix" else "cls") + + +def install_requirements() -> None: + """Install the required libraries from requirements.txt""" + + console.print("[bold blue]Requirements:") + section_text = [ + "In this section the script will ask you to install requirements", + "It is recomended that you use a virtual env when installing them but this is not required", + "Saying 'y' to the next prompt will install all the packages in src/requirements.txt", + ] + for text in section_text: + console.print(f"[blue]{text}") + + if Confirm.ask("[bold blue]\nWould you like to install requirements?"): + requirements_path = os.path.join(PATH, "requirements.txt") + try: + subprocess.check_call( + [sys.executable, "-m", "pip", "install", "-r", requirements_path] + ) + except (subprocess.CalledProcessError, FileNotFoundError): + console.print("[red bold]Failed to install requirements D:[/]") + + clear() + + +def create_db_tables() -> None: + """Create the database tables""" + + console.print("[bold blue]Database Tables:") + var_path = os.path.join(PATH, "core/db/create_tables.py") + section_text = [ + "In this section the script will ask you to create the db tables", + "Make sure you have already put the PostgreSQL database url in your config.yaml file", + "By default the script will create all the tables. If the table exists it will DELETE it and remake it", + "If you don't want that to happen or just want to make a specific table please edit TABLES_TO_CREATE", + f'TABLES_TO_CREATE is located at: "{var_path}", line 152', + ] + for text in section_text: + console.print(f"[blue]{text}") + + if Confirm.ask( + "[bold blue]\nWould you like to create the tables in TABLES_TO_CREATE?" + ): + asyncio.run(create_tables()) + + clear() + + +def create_config_file() -> None: + """Create the config.yaml file""" + + console.print("[bold blue]Config File:") + selfhost_path = os.path.join(PATH, "..", "docs/SELFHOSTING.md") + section_text = [ + "In this section the script will ask you to create the config.yaml file", + "There will be a series of prompts asking for the data needed.", + f"If you are not sure how to get the values or keys for a prompt read the selfhosting guide: {selfhost_path}", + "Also not that answering 'y' to the next prompt will overwrite the current config.yaml if there is one", + "So if you don't want that happening and just want to edit one value modify it directly in the file", + ] + for text in section_text: + console.print(f"[blue]{text}") + + create_file = Confirm.ask( + "[bold blue]\nWould you like to create the config.yaml file?" + ) + if not create_file: + return clear() + + config_path = os.path.join(PATH, "config.example.yaml") + + with open(config_path, "r") as f: + data: dict = yaml.load(f, Loader=yaml.SafeLoader) + + new_config_file_data = {key: None for key in data} + + for key, val in data.items(): + value = None + + if type(val) == str: + value = Prompt.ask( + f"[bold yellow]Enter the value for '{key}' (type=string)", + default=None, + ) + elif type(val) == int: + value = Prompt.ask( + f"[bold yellow]Enter the value for '{key}' (type=integer)", + default=None, + ) + value = int(value) if value is not None and value.isnumeric() else 0 + elif type(val) == bool: + value = Prompt.ask( + f"[bold yellow]Enter the value for '{key}' (type=list)", + choices=["true", "false"], + default="false", + ) + value = True if value.lower() == "true" else False + elif type(val) == list: + console.print( + "[bold yellow]Enter each item sperated by a comma or space eg: 123, 42 123" + ) + value = Prompt.ask( + f"[bold yellow]Enter the value for '{key}' (type=list)", + default=None, + ) + value = ( + [int(x) for x in re.findall("\\d+", value) if x.isnumeric()] + if value is not None + else [] + ) + + if value is not None: + new_config_file_data[key] = value + + clear() + + with open(os.path.join(PATH, "config.aml"), "w") as f: + yaml.dump(new_config_file_data, f) + + clear() + + +def is_first_time() -> bool: + """Ask user if its their first time""" + + console.print("[red bold]Why Bot Setup\n") + + section_text = [ + "Welcome to the why bot setup script", + "Please make sure you have the prerequisites that are found in the README", + "Also if this is your first time running the script please answer 'y' to most of the prompts", + ] + for text in section_text: + console.print(f"[red]{text}") + + is_first_time = Confirm.ask( + "[bold red]\nIs this your first time running this script?" + ) + clear() + return is_first_time + + +def first_time() -> None: + install_requirements() + create_config_file() + create_db_tables() + + +def main() -> None: + if is_first_time(): + return first_time() + + options: dict[str, list[str | Callable[[], None]]] = { + "req": ["Goes to the install requirements section", install_requirements], + "cfg": ["Goes to the create new config file section", create_config_file], + "dbt": ["Goes to the create db tables section", create_db_tables], + "exit": ["Exits the script"], + } + while True: + console.print("[b u green]Options:") + for key, value in options.items(): + console.print(f"[b green]{key}:[/] [green]{value[0]}") + option = options.get(Prompt.ask("[b green]What would you like to do?"), None) + if option is None: + clear() + continue + if len(option) == 1: + break + + option[1]() + + +if __name__ == "__main__": + clear() + main() diff --git a/tests/async_redis.py b/tests/async_redis.py new file mode 100644 index 0000000..8c211b1 --- /dev/null +++ b/tests/async_redis.py @@ -0,0 +1,19 @@ +import asyncio +import aioredis +from datetime import timedelta + + +async def main(): + redis = aioredis.from_url("", decode_responses=True, password="", port=6379) + + name = await redis.get("name") + if name is None: + name = input("What is your name? ") + await redis.set("name", name) + await redis.expire("name", timedelta(minutes=1)) + return print("Hello", name) + print("Hello", name) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/c_plus_py.py b/tests/c_plus_py.py new file mode 100644 index 0000000..cd4e9d5 --- /dev/null +++ b/tests/c_plus_py.py @@ -0,0 +1,8 @@ +import ctypes + +lib = ctypes.CDLL("e.so") +lib.run() + +print(5 + 5) + +# `gcc -fPIC -shared` diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..a2893e4 --- /dev/null +++ b/tests/main.c @@ -0,0 +1,17 @@ +#include + +int fib(n) { + if (n <= 1) { + return n; + }; + return fib(n - 1) + fib(n - 2); +} + +void run() { + for (int i = 0; i < 42; i++) + { + int res = fib(i); + printf("%i\n", res); + } +} + diff --git a/tests/rankimage_test.py b/tests/rankimage_test.py new file mode 100644 index 0000000..c41edd1 --- /dev/null +++ b/tests/rankimage_test.py @@ -0,0 +1,61 @@ +from PIL import Image, ImageDraw, ImageFont + +img = Image.new("RGBA", (900, 300), "#24272a") +profile = Image.open("test.png").resize((250, 250)) + + +def round_corners(image: Image.Image): + background = Image.new("RGBA", size=image.size, color=(255, 255, 255, 0)) + holder = Image.new("RGBA", size=image.size, color=(255, 255, 255, 0)) + mask = Image.new("RGBA", size=image.size, color=(255, 255, 255, 0)) + mask_draw = ImageDraw.Draw(mask) + mask_draw.rounded_rectangle( + (2, 2) + (image.size[0] - 2, image.size[1] - 2), + radius=10, + fill="black", + ) + holder.paste(image, (0, 0)) + return Image.composite(holder, background, mask) + + +img = round_corners(img) + +blank = Image.new("RGBA", size=img.size, color=(255, 255, 255, 0)) +blank.paste(profile, (25, 25)) +img = Image.alpha_composite(img, blank) + +draw = ImageDraw.Draw(img) +font = ImageFont.truetype(f"f.ttf", 40) +draw.text((300, 50), "FusionSid#3645", fill="white", font=font, align="center") +font = ImageFont.truetype(f"f.ttf", 35) +draw.text((700, 50), "#1", fill="white", font=font, align="center") + + +position = (300, 225) +ratio = 600 / 100 +to_width = (ratio * 55 + position[0]) / 1.05 + +height = 40 + position[1] + +draw.rounded_rectangle( + position + (to_width, height), + fill="#ffffff", + outline=None, + width=1, +) + +draw.rounded_rectangle( + (position[0] - 3, position[1] - 3) + + ((ratio * 100 + position[0] + 3) / 1.05, height + 3), + outline="#ffffff", + width=3, +) + +img.save("e.png") +# draw.rounded_rectangle( +# position + (to_width, height), +# radius=10, +# fill="#00fa81", +# outline=outline, +# width=stroke_width, +# ) diff --git a/utils/__init__.py b/utils/__init__.py deleted file mode 100644 index fed74a0..0000000 --- a/utils/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from utils.buttons.paginator import Paginator -from utils.buttons.linkview import LinkView - -from utils.log import Log - -from utils.checks import is_it_me, notblacklisted, plugin_enabled - -from utils.client import update_activity - -from utils.database_utils.guild import get_log_channel - -from utils.get_data import get_url_image, get_url_json, post_get_json, return_url_image - -from utils.embed_kwarg import kwarg_to_embed \ No newline at end of file diff --git a/utils/buttons/linkview.py b/utils/buttons/linkview.py deleted file mode 100644 index 4f6b274..0000000 --- a/utils/buttons/linkview.py +++ /dev/null @@ -1,16 +0,0 @@ -import discord - -class LinkView(discord.ui.View): - def __init__(self, link:str, label:str): - """ - This function creates a view with a single button. The button has a link in it. - - Args: - link (str) : The link the button will take you to - label (str) : The label of the button - """ - super().__init__(timeout=180) - - button = discord.ui.Button(style=discord.ButtonStyle.grey, label=label, url=link) - - self.add_item(button) diff --git a/utils/buttons/paginator.py b/utils/buttons/paginator.py deleted file mode 100644 index eda444a..0000000 --- a/utils/buttons/paginator.py +++ /dev/null @@ -1,45 +0,0 @@ -import discord -from discord.ext import commands -from discord.ui import Button, View -from discord import Option - -class Paginator(View): - def __init__(self, ctx, ems): - super().__init__(timeout=10) - self.ctx = ctx - self.em = ems - self.index = 0 - - def add(self, embed): - self.em.append(embed) - - @discord.ui.button(style=discord.ButtonStyle.green, emoji="⬅", custom_id="left") - async def left(self, button, interaction): - if self.index == 0: - button = [x for x in self.children if x.custom_id=="left"][0] - button.disabled = True - else: - button = [x for x in self.children if x.custom_id=="right"][0] - button.disabled = False - self.index -= 1 - em = self.em[self.index] - await interaction.response.edit_message(view=self,embed=em) - - @discord.ui.button(style=discord.ButtonStyle.green, emoji="āž”ļø", custom_id="right") - async def right(self, button, interaction): - if self.index == (len(self.em)-1): - button = [x for x in self.children if x.custom_id=="right"][0] - button.disabled = True - else: - button = [x for x in self.children if x.custom_id=="left"][0] - button.disabled = False - self.index += 1 - em = self.em[self.index] - await interaction.response.edit_message(view=self,embed=em) - - async def interaction_check(self, interaction) -> bool: - if interaction.user != self.ctx.author: - await interaction.response.send_message("This isnt for you",ephemeral=True) - return False - else: - return True \ No newline at end of file diff --git a/utils/checks.py b/utils/checks.py deleted file mode 100644 index 64e44fa..0000000 --- a/utils/checks.py +++ /dev/null @@ -1,32 +0,0 @@ -import json -import datetime -import discord - -def is_it_me(ctx): - return ctx.author.id == 624076054969188363 - -def notblacklisted(ctx): - with open("./database/blacklisted.json") as f: - data = json.load(f) - if ctx.author.id not in data: - return True - -async def plugin_enabled(ctx): - with open("./database/db.json") as f: - data = json.load(f) - - data = data[str(ctx.guild.id)] - - try: - category = ctx.command.extras['category'] - except Exception: - return True - - if data["settings"]['plugins'][category]: - return True - - em = discord.Embed(title="This command has been disabled for your server", description=f"Ask the admins to do `?plugins enable {category}`", color=ctx.author.color) - em.timestamp = datetime.datetime.now() - - await ctx.send(embed=em) - return False \ No newline at end of file diff --git a/utils/client.py b/utils/client.py deleted file mode 100644 index dc8b376..0000000 --- a/utils/client.py +++ /dev/null @@ -1,19 +0,0 @@ -import discord - -async def update_activity(client): - """ - Used to updates the bots discord.Game activity - to show the current server count - - Args: - - client (discord.ext.commands.Bot) : The bot, This will be used to update the guild count - - """ - await client.change_presence( - activity=discord.Game( - f"On {len(client.guilds)} servers! | ?help" - ) - ) - - diff --git a/utils/database_utils/guild.py b/utils/database_utils/guild.py deleted file mode 100644 index 798ac9a..0000000 --- a/utils/database_utils/guild.py +++ /dev/null @@ -1,21 +0,0 @@ -import discord - -async def get_log_channel(client, guild): - """ - This function is used to find the guilds log channel - This channel is used to log things like message edits or kicks - - Args: - client (discord.ext.commands.Bot) : This is the discord bot - guild (discord.Guild) : This is the guild - - Returns: - discord.TextChannel : This is the log channel - """ - data = await client.get_db() - - if data[str(guild.id)]['log_channel'] is None: - return None - - channel = data[str(guild.id)]['log_channel'] - return await client.fetch_channel(channel) \ No newline at end of file diff --git a/utils/db_todo.py b/utils/db_todo.py deleted file mode 100644 index 0835c24..0000000 --- a/utils/db_todo.py +++ /dev/null @@ -1,44 +0,0 @@ -import asyncio -import aiosqlite - -def convertToBinaryData(filename): - with open(filename, 'rb') as file: - blobData = file.read() - return blobData - -def convertToImage(binary_data): - with open("image.png", 'wb') as file: - file.write(binary_data) - -async def insert_into_db(id, time, name, desc = None, image = None): - async with aiosqlite.connect("todo.db") as db: - if desc is None: - sqlite_insert_query = """INSERT INTO "123" (id, time, name, image) VALUES (?, ?, ?, ?)""" - elif image is None: - sqlite_insert_query = """INSERT INTO "123" (id, time, name, desc) VALUES (?, ?, ?, ?)""" - elif image is None and desc is None: - sqlite_insert_query = """INSERT INTO "123" (id, time, name) VALUES (?, ?, ?)""" - else: - sqlite_insert_query = """INSERT INTO "123" (id, time, name, desc, image) VALUES (?, ?, ?, ?, ?)""" - - photo = await convertToBinaryData(photo) - data_tuple = (id, time, name, desc, image) - await db.execute(sqlite_insert_query, data_tuple) - await db.commit() - - -async def main(): - async with aiosqlite.connect("todo.db") as db: - await db.execute("""CREATE TABLE "123" ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - time TEXT NOT NULL, - name TEXT NOT NULL, - description TEXT, - image BLOB, - );""") - await db.commit() - -loop = asyncio.new_event_loop() -loop.create_task(main()) -loop.run_forever() - diff --git a/utils/embed_kwarg.py b/utils/embed_kwarg.py deleted file mode 100644 index 2c1882c..0000000 --- a/utils/embed_kwarg.py +++ /dev/null @@ -1,134 +0,0 @@ -import discord -import datetime -import shlex -import aiohttp - -colors = { - "none" : None, - "blue": discord.Color.blue(), - "blurple": discord.Color.blurple(), - "brand_green": discord.Color.brand_green(), - "brand_red": discord.Color.brand_red(), - "dark_blue": discord.Color.dark_blue(), - "dark_gold": discord.Color.dark_gold(), - "dark_gray": discord.Color.dark_gray(), - "dark_green": discord.Color.dark_green(), - "dark_grey": discord.Color.dark_grey(), - "dark_magenta": discord.Color.dark_magenta(), - "dark_orange": discord.Color.dark_orange(), - "dark_purple": discord.Color.dark_purple(), - "dark_red": discord.Color.dark_red(), - "dark_teal": discord.Color.dark_teal(), - "dark_theme": discord.Color.dark_theme(), - "darker_gray": discord.Color.darker_gray(), - "darker_grey": discord.Color.darker_grey(), - "fuchsia": discord.Color.fuchsia(), - "gold": discord.Color.gold(), - "green": discord.Color.green(), - "greyple": discord.Color.greyple(), - "light_gray": discord.Color.light_gray(), - "light_grey": discord.Color.light_grey(), - "lighter_gray": discord.Color.lighter_gray(), - "lighter_grey": discord.Color.lighter_grey(), - "magenta": discord.Color.magenta(), - "nitro_pink": discord.Color.nitro_pink(), - "og_blurple": discord.Color.og_blurple(), - "orange": discord.Color.orange(), - "purple": discord.Color.purple(), - "random": discord.Color.random(), - "red": discord.Color.red(), - "teal": discord.Color.teal(), -} - -async def kwarg_to_embed(client, ctx, kwargs): - - colorlist = [] - for c in colors: - colorlist.append(c) - - def wait_for_check(m): - return m.author == ctx.author and m.channel == ctx.message.channel - - em = discord.Embed() - em.timestamp = datetime.datetime.utcnow() - - kwargs = shlex.split(kwargs) - args = {} - - for index in range(len(kwargs)): - if index % 2 == 0: - args[kwargs[index].lstrip("--")] = kwargs[index+1] - index += 0 - - channel = ctx.message.channel - webhook_dict = { - "name" : None, - "avatar" : None, - } - - for key, value in args.items(): - if key.lower() == "title": - em.title = value - elif key.lower() == "description" or key.lower() == "desc": - em.description = value - elif key.lower() == "channel": - channel = await client.fetch_channel(int(value)) - elif key.lower() == "img" or key.lower() == "image": - em.set_image(url=value) - elif key.lower() == "color" or key.lower() == "colour": - if value.lower() == "list" or value.lower() == "help": - return await ctx.send(", ".join(colorlist)) - if value.lower() not in colorlist: - await ctx.send("Color not found", delete_after=2) - em.color = ctx.author.color - else: - em.color = colors[value.lower()] - elif key.lower() == "fields": - vint = False - try: - int(value) - vint= True - except: - vint = False - - if vint is True: - for i in range(int(value)): - entername = await ctx.send("Enter Name:") - name = await client.wait_for("message", check=wait_for_check, timeout=300) - await name.delete() - - entervalue = await ctx.send("Enter Value:") - value = await client.wait_for("message", check=wait_for_check, timeout=300) - await entername.delete() - await entervalue.delete() - await value.delete() - - em.add_field(name=name.content, value=value.content, inline=False) - - elif key.lower() in ["timestamp", "time"] and value.lower() in ["true", "yes"]: - em.timestamp = datetime.datetime.now() - - elif key.lower() == "webhook_name": - webhook_dict['name'] = value - - elif key.lower() == "webhook_avatar": - webhook_dict['avatar'] = value - - if ctx.author.id != client.owner_id: - em.set_footer(text=f"Message sent by {ctx.author.name}") - - name, avatar = None, None - if webhook_dict["name"] is not None: - name = webhook_dict["name"] - - if webhook_dict["avatar"] is not None: - async with aiohttp.ClientSession() as session: - async with session.get(webhook_dict["avatar"]) as resp: - avatar = bytes(await resp.read()) - - webhook = await channel.create_webhook(name=name, avatar=avatar) - await webhook.send(embed=em) - await webhook.delete() - return None - - return [em, channel] diff --git a/utils/get_data.py b/utils/get_data.py deleted file mode 100644 index 5cc4856..0000000 --- a/utils/get_data.py +++ /dev/null @@ -1,81 +0,0 @@ -import aiohttp -import aiofiles - - -async def get_url_json(url, data=None): - """ - This function makes a GET request to a url and returns the json - - Args: - url (str) : The url to make a request to - data (Dict, optional) : This is a dictionary of any extra params to send the request - - Returns: - Dict : The json response - """ - async with aiohttp.ClientSession() as session: - async with session.get(url, data=data) as resp: - try: - response = await resp.json() - except Exception as e: - print(e) - response = resp - return response - - -async def get_url_image(url, name, data = None): - """ - This function makes a get request to a url and returns the image and saves - it to a file in the `tempstorage` directory - - Args: - url (str) : The url to make a request to - data (Dict, optional) : This is a dictionary of any extra params to send the request - - Returns: - str : The file path for the image returned - """ - async with aiohttp.ClientSession() as session: - async with session.get(url, data=data) as resp: - f = await aiofiles.open(f'./tempstorage/{name}.png', mode='wb') - await f.write(await resp.read()) - await f.close() - - return f'./tempstorage/{name}.png' - - -async def post_get_json(url, data=None): - """ - This function makes a POST request to a url and returns the json - - Args: - url (str) : The url to make a request to - data (Dict, optional) : This is a dictionary of any extra params to send the request - - Returns: - Dict : The json response - """ - async with aiohttp.ClientSession() as session: - async with session.post(url, data=data) as resp: - try: - response = await resp.json() - except Exception as e: - print(e) - response = resp - return response - - -async def return_url_image(url, data = None): - """ - This function makes a get request to a url and returns the image - - Args: - url (str) : The url to make a request to - data (Dict, optional) : This is a dictionary of any extra params to send the request - - """ - async with aiohttp.ClientSession() as session: - async with session.get(url=url, data=data) as resp: - response = await resp.read() - - return response \ No newline at end of file diff --git a/utils/log.py b/utils/log.py deleted file mode 100644 index 80d1eb2..0000000 --- a/utils/log.py +++ /dev/null @@ -1,80 +0,0 @@ -from datetime import datetime - -class Log(): - def __init__(self, path:str="./database/log.txt", timestamp:bool=True): - self.path = path - self.timestamp = timestamp - - - def log_error(self, error): - if self.timestamp: - now = datetime.now() - timern = now.strftime("%d/%m/%Y %H:%M:%S") - log_string = f"{timern} | ERROR: {error}" - else: - log_string = f"ERROR: {error}" - - with open(self.path, 'a') as f: - f.write('\n') - f.write(log_string) - - - def log_command_error(self, error): - if self.timestamp: - now = datetime.now() - timern = now.strftime("%d/%m/%Y %H:%M:%S") - log_string = f"{timern} | {error[0]}" - else: - log_string = f"{error[0]}" - - with open(self.path, 'a') as f: - f.write(log_string) - for i in error[1]: - f.write(f"{timern} | {i}") - - - def log_message(self, message): - if self.timestamp: - now = datetime.now() - timern = now.strftime("%d/%m/%Y %H:%M:%S") - log_string = f"{timern} | MESSAGE: {message}" - else: - log_string = f"ERROR: {message}" - - with open(self.path, 'a') as f: - f.write('\n') - f.write(log_string) - - - @property - def file(self): - data = [] - with open(self.path, 'r') as f: - for i in f: - data.append(i) - return data - - - @property - def today(self): - data = [] - - todays_date = datetime.now().strftime("%d/%m/%Y") - - with open(self.path, 'r') as f: - for line in f: - if todays_date in line: - data.append(line) - - return data - - - def get_date(self, date): - data = [] - - with open(self.path, 'r') as f: - for line in f: - if date in line: - data.append(line) - - return data