From 04645ec6394969143b43de37740bd9de62cea23a Mon Sep 17 00:00:00 2001 From: Romain J Date: Sun, 24 May 2020 01:16:08 +0200 Subject: [PATCH] first commit --- .github/issue_template.md | 31 - .gitignore | 136 +---- .idea/discord.xml | 6 + .idea/inspectionProfiles/Project_Default.xml | 15 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/tuxbot-bot-rewrite.iml | 10 + .idea/vcs.xml | 11 + .idea/workspace.xml | 199 +++++++ LICENSE | 437 -------------- README.md | 86 --- app.py | 151 +++++ bot.py | 250 -------- cogs/API.py | 56 -- cogs/Admin.py | 534 ------------------ cogs/Help.py | 227 -------- cogs/Images.py | 180 ++++++ cogs/Logs.py | 76 ++- cogs/Monitoring.py | 110 ---- cogs/Poll.py | 222 -------- cogs/Useful.py | 410 -------------- cogs/User.py | 64 --- configs/bot/blacklist.json | 5 + configs/bot/protected.py | 8 + configs/bot/settings.py.example | 44 ++ configs/bot/whitelist.json | 3 + configs/config.cfg.example | 24 - configs/fallbacks.cfg.example | 18 - configs/prefixes.cfg | 18 - database.py | 19 - generate_locales.sh | 17 - configs/blacklist.cfg => models/__init__.py | 0 requirements.txt | 40 +- todo | 2 - utils/__init__.py | 6 - utils/fonts/credit_card.ttf | Bin 70136 -> 0 bytes utils/functions/__init__.py | 6 - utils/functions/config.py | 36 -- utils/functions/database.py | 16 - utils/functions/emotes.py | 10 - utils/functions/extra.py | 82 ++- utils/functions/lang.py | 34 -- utils/functions/paginator.py | 343 ----------- utils/functions/version.py | 12 - utils/images/blank_credit_card.png | Bin 71861 -> 0 bytes utils/images/gnous.png | Bin 42812 -> 0 bytes utils/locales/en/LC_MESSAGES/admin.po | 61 -- utils/locales/en/LC_MESSAGES/admin_help.po | 252 --------- utils/locales/en/LC_MESSAGES/base.po | 62 -- utils/locales/en/LC_MESSAGES/help.po | 43 -- utils/locales/en/LC_MESSAGES/logs.po | 17 - utils/locales/en/LC_MESSAGES/poll.po | 20 - utils/locales/en/LC_MESSAGES/poll_help.po | 17 - utils/locales/en/LC_MESSAGES/useful.po | 83 --- utils/locales/en/LC_MESSAGES/useful_help.po | 17 - utils/locales/en/LC_MESSAGES/user.po | 0 utils/locales/en/LC_MESSAGES/user_help.po | 17 - utils/locales/en/LC_MESSAGES/utils.po | 22 - utils/locales/fr/LC_MESSAGES/admin.po | 61 -- utils/locales/fr/LC_MESSAGES/admin_help.po | 263 --------- utils/locales/fr/LC_MESSAGES/base.po | 62 -- utils/locales/fr/LC_MESSAGES/help.po | 43 -- utils/locales/fr/LC_MESSAGES/logs.po | 17 - utils/locales/fr/LC_MESSAGES/logs_help.po | 19 - utils/locales/fr/LC_MESSAGES/poll.po | 20 - utils/locales/fr/LC_MESSAGES/poll_help.po | 40 -- utils/locales/fr/LC_MESSAGES/useful.po | 82 --- utils/locales/fr/LC_MESSAGES/useful_help.po | 17 - utils/locales/fr/LC_MESSAGES/user.po | 0 utils/locales/fr/LC_MESSAGES/user_help.po | 17 - utils/locales/fr/LC_MESSAGES/utils.po | 22 - utils/models/__init__.py | 18 - utils/models/alias.py | 14 - utils/models/poll.py | 29 - utils/models/warn.py | 14 - 76 files changed, 792 insertions(+), 4532 deletions(-) delete mode 100644 .github/issue_template.md create mode 100644 .idea/discord.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/tuxbot-bot-rewrite.iml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml delete mode 100755 LICENSE delete mode 100644 README.md create mode 100644 app.py delete mode 100755 bot.py delete mode 100644 cogs/API.py delete mode 100644 cogs/Admin.py delete mode 100644 cogs/Help.py create mode 100644 cogs/Images.py delete mode 100644 cogs/Monitoring.py delete mode 100644 cogs/Poll.py delete mode 100644 cogs/Useful.py delete mode 100644 cogs/User.py create mode 100644 configs/bot/blacklist.json create mode 100644 configs/bot/protected.py create mode 100644 configs/bot/settings.py.example create mode 100644 configs/bot/whitelist.json delete mode 100644 configs/config.cfg.example delete mode 100644 configs/fallbacks.cfg.example delete mode 100644 configs/prefixes.cfg delete mode 100644 database.py delete mode 100755 generate_locales.sh rename configs/blacklist.cfg => models/__init__.py (100%) delete mode 100644 todo delete mode 100755 utils/__init__.py delete mode 100644 utils/fonts/credit_card.ttf delete mode 100644 utils/functions/__init__.py delete mode 100644 utils/functions/config.py delete mode 100644 utils/functions/database.py delete mode 100644 utils/functions/emotes.py delete mode 100644 utils/functions/lang.py delete mode 100644 utils/functions/paginator.py delete mode 100644 utils/functions/version.py delete mode 100644 utils/images/blank_credit_card.png delete mode 100644 utils/images/gnous.png delete mode 100644 utils/locales/en/LC_MESSAGES/admin.po delete mode 100644 utils/locales/en/LC_MESSAGES/admin_help.po delete mode 100644 utils/locales/en/LC_MESSAGES/base.po delete mode 100644 utils/locales/en/LC_MESSAGES/help.po delete mode 100644 utils/locales/en/LC_MESSAGES/logs.po delete mode 100644 utils/locales/en/LC_MESSAGES/poll.po delete mode 100644 utils/locales/en/LC_MESSAGES/poll_help.po delete mode 100644 utils/locales/en/LC_MESSAGES/useful.po delete mode 100644 utils/locales/en/LC_MESSAGES/useful_help.po delete mode 100644 utils/locales/en/LC_MESSAGES/user.po delete mode 100644 utils/locales/en/LC_MESSAGES/user_help.po delete mode 100644 utils/locales/en/LC_MESSAGES/utils.po delete mode 100644 utils/locales/fr/LC_MESSAGES/admin.po delete mode 100644 utils/locales/fr/LC_MESSAGES/admin_help.po delete mode 100644 utils/locales/fr/LC_MESSAGES/base.po delete mode 100644 utils/locales/fr/LC_MESSAGES/help.po delete mode 100644 utils/locales/fr/LC_MESSAGES/logs.po delete mode 100644 utils/locales/fr/LC_MESSAGES/logs_help.po delete mode 100644 utils/locales/fr/LC_MESSAGES/poll.po delete mode 100644 utils/locales/fr/LC_MESSAGES/poll_help.po delete mode 100644 utils/locales/fr/LC_MESSAGES/useful.po delete mode 100644 utils/locales/fr/LC_MESSAGES/useful_help.po delete mode 100644 utils/locales/fr/LC_MESSAGES/user.po delete mode 100644 utils/locales/fr/LC_MESSAGES/user_help.po delete mode 100644 utils/locales/fr/LC_MESSAGES/utils.po delete mode 100644 utils/models/__init__.py delete mode 100644 utils/models/alias.py delete mode 100644 utils/models/poll.py delete mode 100644 utils/models/warn.py diff --git a/.github/issue_template.md b/.github/issue_template.md deleted file mode 100644 index a2f9aff..0000000 --- a/.github/issue_template.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Launch bot -3. Type command '...' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. Debian] - - Python Version [e.g. 3.7.4] - -**Additional context** -Add any other context about the problem here. \ No newline at end of file diff --git a/.gitignore b/.gitignore index 45b11a6..02ae0be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,146 +1,12 @@ -#Python -__pycache__/ -*.pyc -.env -configs/config.cfg -configs/prefixes.cfg -configs/fallbacks.cfg -.DS_Store -private.py - -#jetbrains -.idea/ - -# other -logs/tuxbot.log -utils/images/tmp/* - -# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: *.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ venv/ -ENV/ -env.bak/ -venv.bak/ -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ +settings.py \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..368d7c1 --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..f031c4e --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..4941e09 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..dd59735 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/tuxbot-bot-rewrite.iml b/.idea/tuxbot-bot-rewrite.iml new file mode 100644 index 0000000..74d515a --- /dev/null +++ b/.idea/tuxbot-bot-rewrite.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..78cface --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..5210e9e --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1589922546510 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100755 index d60efe4..0000000 --- a/LICENSE +++ /dev/null @@ -1,437 +0,0 @@ -Attribution-NonCommercial-ShareAlike 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More_considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International -Public License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution-NonCommercial-ShareAlike 4.0 International Public License -("Public License"). To the extent this Public License may be -interpreted as a contract, You are granted the Licensed Rights in -consideration of Your acceptance of these terms and conditions, and the -Licensor grants You such rights in consideration of benefits the -Licensor receives from making the Licensed Material available under -these terms and conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. BY-NC-SA Compatible License means a license listed at - creativecommons.org/compatiblelicenses, approved by Creative - Commons as essentially the equivalent of this Public License. - - d. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - e. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - f. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - g. License Elements means the license attributes listed in the name - of a Creative Commons Public License. The License Elements of this - Public License are Attribution, NonCommercial, and ShareAlike. - - h. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - i. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - j. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - k. NonCommercial means not primarily intended for or directed towards - commercial advantage or monetary compensation. For purposes of - this Public License, the exchange of the Licensed Material for - other material subject to Copyright and Similar Rights by digital - file-sharing or similar means is NonCommercial provided there is - no payment of monetary compensation in connection with the - exchange. - - l. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - m. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - n. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part, for NonCommercial purposes only; and - - b. produce, reproduce, and Share Adapted Material for - NonCommercial purposes only. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. Additional offer from the Licensor -- Adapted Material. - Every recipient of Adapted Material from You - automatically receives an offer from the Licensor to - exercise the Licensed Rights in the Adapted Material - under the conditions of the Adapter's License You apply. - - c. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties, including when - the Licensed Material is used other than for NonCommercial - purposes. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - b. ShareAlike. - - In addition to the conditions in Section 3(a), if You Share - Adapted Material You produce, the following conditions also apply. - - 1. The Adapter's License You apply must be a Creative Commons - license with the same License Elements, this version or - later, or a BY-NC-SA Compatible License. - - 2. You must include the text of, or the URI or hyperlink to, the - Adapter's License You apply. You may satisfy this condition - in any reasonable manner based on the medium, means, and - context in which You Share Adapted Material. - - 3. You may not offer or impose any additional or different terms - or conditions on, or apply any Effective Technological - Measures to, Adapted Material that restrict exercise of the - rights granted under the Adapter's License You apply. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database for NonCommercial purposes - only; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material, - including for purposes of Section 3(b); and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - -======================================================================= - -Creative Commons is not a party to its public -licenses. Notwithstanding, Creative Commons may elect to apply one of -its public licenses to material it publishes and in those instances -will be considered the “Licensor.” The text of the Creative Commons -public licenses is dedicated to the public domain under the CC0 Public -Domain Dedication. Except for the limited purpose of indicating that -material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the -public licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/README.md b/README.md deleted file mode 100644 index b543613..0000000 --- a/README.md +++ /dev/null @@ -1,86 +0,0 @@ -# News - - - [ ] i18n for messages - - [x] Custom prefixes - - [ ] Better help command - - [ ] Alias system for commands (e.g. `.alias .ci show .cs`) - - [x] Migrate MySQL to postgresql - - [x] Prepare bot for python 3.8 and discord.py 1.3.0 - - [ ] Create launcher - - [ ] Create documentation - -## Launcher requirements : - - - [ ] Can install the bot - - [ ] Can launch the bot - - [ ] Can propose updates - -## New commands : - - - [x] `.sondage --anonyme <...>` (create à sondage with the possibility of answering anonymously) - - [ ] `.sondage --edit ` (edit a sondage if we are the author or an admin) - -## Documentation: - - [ ] How to use ? - - [ ] How to add more commands ? - -## Ultimate : - - - [ ] Send email or Telegram's message when something is wrong on the bot - - [ ] Create skynet (group of multiple commands about sky (planes, meteo, AI,...)) - - --- - - # Cogs.admin commands - - - [x] upload `removed`, cause : `never used` - - [x] ban - - [x] kick - - [x] clear - - [x] say - - [x] sayto `removed`, now : `say to` - - [x] sayto_dm `removed`, now : `say to` - - [x] editsay `removed`, now : `say edit` - - [x] addreaction `renamed`, now `react add` - - [x] delete - - [x] deletefrom `removed`, now `delete (from|to|in)` - - [x] embed `removed`, cause : `never used` - - [x] warn `new command` - - --- - - # Cogs.basics commands - - [x] ping - - [x] info - - [ ] help - - [x] credits `new command` - - --- - - # Cogs.ci commands `canceled until the frontend development` - - [ ] ci (help?) - - [ ] ci show - - [ ] ci register - - [ ] ci delete - - [ ] ci update - - [ ] ci setconfig - - [ ] ci setos - - [ ] ci setcountry - - [ ] ci online_edit `renamed`, cause : `website down` - - [ ] ci list - - --- - - # Cogs.utility commands - - [ ] clock `removed` ? - - [ ] clock * `removed` ? - - [ ] ytdiscover `removed` ? - - [x] iplocalise - - [x] getheaders - - [x] git - - [x] quote - - --- - - # Cogs.sondage commands `(renamed as cogs.poll)` `canceled until the frontend development` - - [ ] sondage (help?) diff --git a/app.py b/app.py new file mode 100644 index 0000000..97376fc --- /dev/null +++ b/app.py @@ -0,0 +1,151 @@ +import contextlib +import datetime +import logging +from collections import Counter +from typing import List + +import aiohttp +import discord +from discord.ext import commands +from tortoise import Tortoise + +from configs.bot import settings +from utils.functions.extra import ContextPlus, get_prefix, \ + get_owners, get_blacklist + +log = logging.getLogger(__name__) + +l_extensions: List[str] = [ + "jishaku", + "cogs.Logs", + "cogs.Images", +] + + +class TuxBot(commands.AutoShardedBot): + logs_channels: dict + session: aiohttp.ClientSession + command_stats: Counter = Counter() + socket_stats: Counter = Counter() + + def __init__(self): + self.uptime = datetime.datetime.utcnow() + self.config = settings + super().__init__( + command_prefix=get_prefix, + case_insensitive=True + ) + + self.logs_channels = { + "dm": self.config.logs["dm"], + "mentions": self.config.logs["mentions"], + "guilds": self.config.logs["guilds"], + "errors": self.config.logs["errors"], + } + + print("\n"*2) + + for extension in l_extensions: + try: + self.load_extension(extension) + print(f"{extension} loaded !") + except Exception as e: + print(f"{type(e).__name__}: {e}") + + print("\n"*2) + + async def is_owner(self, user: discord.User): + return user.id in get_owners() + + async def on_ready(self): + print(f"Connected !\n" + f"\n" + f"==> info: bot username {self.user}\n" + f" info: bot id {self.user.id}\n" + f" info: bot prefix {self.command_prefix}\n" + f"==> info: guild count {len(self.guilds)}\n" + f" info: member count {len(list(self.get_all_members()))}\n" + f" info: channel count {len(list(self.get_all_channels()))}") + + print(f"\n{'='*118}\n\n") + + @staticmethod + async def on_resumed(): + print("resumed...") + + async def get_context(self, message: discord.Message, *, cls=None): + return await super().get_context(message, cls=ContextPlus) + + async def on_message(self, message: discord.Message): + if message.author.bot: + return + + if message.author.id in get_blacklist()['users'] \ + or message.channel.id in get_blacklist()['channels'] \ + or (message.channel.guild + and message.channel.guild.id in get_blacklist()['guilds']): + return + + try: + await self.process_commands(message) + except Exception as e: + print(f"{type(e).__name__}: {e}") + + async def bot_logout(self): + await super().logout() + await self.session.close() + + async def bot_start(self): + self.session = aiohttp.ClientSession(loop=self.loop) + await self.login(self.config.token, bot=True) + await self.connect() + + def run(self): + loop = self.loop + + loop.run_until_complete(Tortoise.init( + db_url=self.config.postgresql, + modules={ + "models": [ + "models.__init__" + ] + } + )) + loop.run_until_complete(Tortoise.generate_schemas()) + + try: + loop.run_until_complete(self.bot_start()) + except KeyboardInterrupt: + loop.run_until_complete(self.bot_logout()) + + +@contextlib.contextmanager +def setup_logging(): + logging.getLogger('discord').setLevel(logging.INFO) + logging.getLogger('discord.http').setLevel(logging.WARNING) + + logger = logging.getLogger() + logger.setLevel(logging.INFO) + + try: + handler = logging.FileHandler(filename='logs/tuxbot.log', + encoding='utf-8', mode='w') + fmt = logging.Formatter('[{levelname:<7}] [{asctime}]' + ' {name}: {message}', + '%Y-%m-%d %H:%M:%S', style='{') + + handler.setFormatter(fmt) + logger.addHandler(handler) + + yield + finally: + handlers = logger.handlers[:] + for handler in handlers: + handler.close() + logger.removeHandler(handler) + + +if __name__ == "__main__": + tutux = TuxBot() + with setup_logging(): + tutux.run() diff --git a/bot.py b/bot.py deleted file mode 100755 index 74e18ba..0000000 --- a/bot.py +++ /dev/null @@ -1,250 +0,0 @@ -import contextlib -import datetime -import json -import logging -import sys -from collections import deque, Counter -from typing import List - -import aiohttp -import discord -import git -import sqlalchemy -from discord.ext import commands - -from utils.functions import Config -from utils.functions import Texts -from utils.functions import Version -from utils.functions import ContextPlus - -from utils.models import metadata, database - -description = """ -Je suis TuxBot, le bot qui vit de l'OpenSource ! ;) -""" - -build = git.Repo(search_parent_directories=True).head.object.hexsha -version = (2, 0, 0) - -log = logging.getLogger(__name__) - -l_extensions: List[str] = [ - 'cogs.Admin', - 'cogs.API', - 'cogs.Help', - 'cogs.Logs', - # 'cogs.Monitoring', - 'cogs.Poll', - 'cogs.Useful', - 'cogs.User', - 'jishaku', -] - - -async def _prefix_callable(bot, message: discord.message) -> list: -<<<<<<< HEAD - extras = [bot.cluster.get('Name') + '.'] - if message.guild is not None: - if str(message.guild.id) in bot.prefixes: - extras.extend( - bot.prefixes.get(str(message.guild.id), "prefixes").split( - bot.config.get("misc", "Separator") - ) - ) -======= - try: - with open(f'./configs/guilds/{message.guild.id}.json', 'r') as f: - data = json.load(f) - - custom_prefix = data['prefixes'] - except FileNotFoundError: - custom_prefix = [''] - - extras = [bot.cluster.get('Name') + '.'] - extras.extend(custom_prefix) ->>>>>>> cce7bb409303e9ad27ef4e5617d0bc9068810f13 - - return commands.when_mentioned_or(*extras)(bot, message) - - -class TuxBot(commands.AutoShardedBot): - - def __init__(self, ): - super().__init__(command_prefix=_prefix_callable, pm_help=None, - help_command=None, description=description, - help_attrs=dict(hidden=True), - activity=discord.Game( - name=Texts().get('Starting...')) - ) - - self.socket_stats = Counter() - self.command_stats = Counter() - - self.config = Config('./configs/config.cfg') - self.blacklist = Config('./configs/blacklist.cfg') - self.fallbacks = Config('./configs/fallbacks.cfg') - self.cluster = self.fallbacks.find('True', key='This', first=True) - - self.uptime: datetime = datetime.datetime.utcnow() - self._prev_events = deque(maxlen=10) - self.session = aiohttp.ClientSession(loop=self.loop) - - self.database, self.metadata = database, metadata - self.engine = sqlalchemy.create_engine(str(self.database.url)) - self.metadata.create_all(self.engine) - - self.version = Version(*version, pre_release='rc2', build=build) - self.owners_id = [int(owner_id) for owner_id in self.config.get('permissions', 'Owners').split(', ')] - self.owner_id = int(self.owners_id[0]) - - for extension in l_extensions: - try: - self.load_extension(extension) - print(Texts().get("Extension loaded successfully : ") - + extension) - log.info(Texts().get("Extension loaded successfully : ") - + extension) - except Exception as e: - print(Texts().get("Failed to load extension : ") - + extension, file=sys.stderr) - print(e) - log.error(Texts().get("Failed to load extension : ") - + extension, exc_info=e) - - @property - def owner(self): - return self.get_user(self.owner_id) - - @property - def owners(self): - return [self.get_user(owner_id) for owner_id in self.owners_id] - - async def is_owner(self, user: discord.User) -> bool: - return user in self.owners - - async def get_context(self, message, *, cls=None): - return await super().get_context(message, cls=cls or ContextPlus) - - async def on_socket_response(self, msg): - self._prev_events.append(msg) - - async def on_command_error(self, ctx: discord.ext.commands.Context, error): - if isinstance(error, commands.NoPrivateMessage): - await ctx.author.send( - Texts().get("This command cannot be used in private messages.") - ) - - elif isinstance(error, commands.DisabledCommand): - await ctx.author.send( - Texts().get( - "Sorry. This command is disabled and cannot be used." - ) - ) - elif isinstance(error, commands.CommandOnCooldown): - await ctx.send(str(error)) - - async def process_commands(self, message: discord.message): - ctx: commands.Context = await self.get_context(message) - - if ctx.command is None: - return - - await self.invoke(ctx) - - async def on_message(self, message: discord.message): - if message.author.id in self.blacklist \ - or (message.guild is not None - and message.guild.id in self.blacklist): - return - - if message.author.bot and message.author.id != int( - self.config.get('bot', 'Tester')): - return - - await self.process_commands(message) - - async def on_ready(self): - if not hasattr(self, 'uptime'): - self.uptime = datetime.datetime.utcnow() - - print('-' * 60) - print(Texts().get("Ready:") + f' {self.user} (ID: {self.user.id})') - print(self.version) - - presence: dict = dict(status=discord.Status.dnd) - if self.config.get("bot", "Activity", fallback=None) is not None: - presence.update( - activity=discord.Game( - name=self.config.get("bot", "Activity") - ) - ) - print(f"Discord.py: {discord.__version__}") - print(f"Server: {self.cluster.get('Name')}") - print('-' * 60) - - await self.change_presence(**presence) - - @staticmethod - async def on_resumed(): - print('resumed...') - - @property - def logs_webhook(self) -> discord.Webhook: - webhook_config = self.config["webhook"] - webhook = discord.Webhook.partial( - id=webhook_config.get('ID'), - token=webhook_config.get('Token'), - adapter=discord.AsyncWebhookAdapter( - self.session - ) - ) - - return webhook - - async def close(self): - extensions = self.extensions.copy() - for extension in extensions: - self.unload_extension(extension) - await super().close() - await self.session.close() - - def run(self): - super().run(self.config.get("bot", "Token"), reconnect=True) - - -@contextlib.contextmanager -def setup_logging(): - logging.getLogger('discord').setLevel(logging.INFO) - logging.getLogger('discord.http').setLevel(logging.WARNING) - - log = logging.getLogger() - log.setLevel(logging.INFO) - - try: - handler = logging.FileHandler(filename='logs/tuxbot.log', - encoding='utf-8', mode='w') - fmt = logging.Formatter('[{levelname:<7}] [{asctime}]' - ' {name}: {message}', - '%Y-%m-%d %H:%M:%S', style='{') - - handler.setFormatter(fmt) - log.addHandler(handler) - - yield - finally: - handlers = log.handlers[:] - for handler in handlers: - handler.close() - log.removeHandler(handler) - - -if __name__ == "__main__": - print(Texts().get('Starting...')) - - app = TuxBot() - - try: - with setup_logging(): - app.run() - except KeyboardInterrupt: - app.close() diff --git a/cogs/API.py b/cogs/API.py deleted file mode 100644 index a483b25..0000000 --- a/cogs/API.py +++ /dev/null @@ -1,56 +0,0 @@ -import logging - -import discord -from aiohttp import web -from discord.ext import commands - -from bot import TuxBot - -log = logging.getLogger(__name__) - - -class API(commands.Cog): - - def __init__(self, bot: TuxBot): - self.bot = bot - self.site = web.TCPSite - - app = web.Application() - app.add_routes([web.get('/users/{user_id}', self.users)]) - - self.runner = web.AppRunner(app) - self.bot.loop.create_task(self.start_HTTPMonitoring_server()) - - async def start_HTTPMonitoring_server(self): - host = self.bot.config.get('API', 'Host') - port = self.bot.config.get('API', 'Port') - - print(f"Starting API server on {host}:{port}") - - await self.runner.setup() - self.site = web.TCPSite(self.runner, host, port) - await self.site.start() - - async def users(self, request): - try: - user = await self.bot.fetch_user(request.match_info['user_id']) - except discord.NotFound: - return web.Response(status=404) - - json = { - 'id': user.id, - 'username': user.name, - 'discriminator': user.discriminator, - 'avatar': user.avatar, - 'default_avatar': user.default_avatar.value, - 'bot': user.bot, - 'system': user.system, - } - - return web.json_response( - json - ) - - -def setup(bot: TuxBot): - bot.add_cog(API(bot)) diff --git a/cogs/Admin.py b/cogs/Admin.py deleted file mode 100644 index a3bc330..0000000 --- a/cogs/Admin.py +++ /dev/null @@ -1,534 +0,0 @@ -import asyncio -import datetime -import logging -from typing import Union - -import discord -import humanize -from discord.ext import commands - -from bot import TuxBot -from utils import Texts -from utils.models import WarnModel -from utils import command_extra, group_extra - -log = logging.getLogger(__name__) - - -class Admin(commands.Cog): - - def __init__(self, bot: TuxBot): - self.bot = bot - self.icon = ":shield:" - self.big_icon = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/twitter/233/shield_1f6e1.png" - - async def cog_check(self, ctx: commands.Context) -> bool: - permissions: discord.Permissions = ctx.channel.permissions_for( - ctx.author) - - has_permission = permissions.administrator - is_owner = await self.bot.is_owner(ctx.author) - - return has_permission or is_owner - - @staticmethod - async def kick_ban_message(ctx: commands.Context, - **kwargs) -> discord.Embed: - member: discord.Member = kwargs.get('member') - reason = kwargs.get( - 'reason', - Texts('admin', ctx).get("Please enter a reason") - ) - - if kwargs.get('type') == 'ban': - title = '**Ban** ' + str(len(await ctx.guild.bans())) - color = discord.Color.dark_red() - else: - title = '**Kick**' - color = discord.Color.red() - e = discord.Embed( - title=title, - description=reason, - timestamp=datetime.datetime.utcnow(), - color=color - ) - e.set_author( - name=f'{member.name}#{member.discriminator} ({member.id})', - icon_url=member.avatar_url_as(format='jpg') - ) - e.set_footer( - text=f'{ctx.author.name}#{ctx.author.discriminator}', - icon_url=ctx.author.avatar_url_as(format='png') - ) - - return e - - ########################################################################### - - @group_extra(name='say', invoke_without_command=True, category='text') - async def _say(self, ctx: commands.Context, *, content: str): - if ctx.invoked_subcommand is None: - try: - await ctx.message.delete() - except discord.errors.Forbidden: - pass - - await ctx.send(content) - - @_say.command(name='edit') - async def _say_edit(self, ctx: commands.Context, message_id: int, *, - content: str): - try: - await ctx.message.delete() - except discord.errors.Forbidden: - pass - - try: - message: discord.Message = await ctx.channel.fetch_message( - message_id) - await message.edit(content=content) - except (discord.errors.NotFound, discord.errors.Forbidden): - await ctx.send( - Texts('utils', ctx).get("Unable to find the message"), - delete_after=5) - - @_say.command(name='to') - async def _say_to(self, ctx: commands.Context, - channel: Union[discord.TextChannel, discord.User], *, - content): - try: - await ctx.message.delete() - except discord.errors.Forbidden: - pass - - await channel.send(content) - - ########################################################################### - - @command_extra(name='ban', category='administration') - async def _ban(self, ctx: commands.Context, user: discord.Member, *, - reason=""): - try: - member: discord.Member = await ctx.guild.fetch_member(user.id) - - try: - await member.ban(reason=reason) - e: discord.Embed = await self.kick_ban_message( - ctx, - member=member, - type='ban', - reason=reason - ) - - await ctx.send(embed=e) - except discord.Forbidden: - await ctx.send( - Texts('admin', ctx).get("Unable to ban this user"), - delete_after=5) - except discord.errors.NotFound: - await ctx.send( - Texts('utils', ctx).get("Unable to find the user..."), - delete_after=5) - - ########################################################################### - - @command_extra(name='kick', category='administration') - async def _kick(self, ctx: commands.Context, user: discord.Member, *, - reason=""): - try: - member: discord.Member = await ctx.guild.fetch_member(user.id) - - try: - await member.kick(reason=reason) - e: discord.Embed = await self.kick_ban_message( - ctx, - member=member, - type='kick', - reason=reason - ) - - await ctx.send(embed=e) - except discord.Forbidden: - await ctx.send( - Texts('admin', ctx).get("Unable to kick this user"), - delete_after=5) - except discord.errors.NotFound: - await ctx.send( - Texts('utils', ctx).get("Unable to find the user..."), - delete_after=5) - - ########################################################################### - - @command_extra(name='clear', category='text') - async def _clear(self, ctx: commands.Context, count: int): - try: - await ctx.message.delete() - await ctx.channel.purge(limit=count) - except discord.errors.Forbidden: - pass - - ########################################################################### - - @group_extra(name='react', category='text') - async def _react(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.send_help('react') - - @_react.command(name='add') - async def _react_add(self, ctx: commands.Context, message_id: int, *, - emojis: str): - emojis: list = emojis.split(' ') - - try: - message: discord.Message = await ctx.channel.fetch_message( - message_id) - - for emoji in emojis: - await message.add_reaction(emoji) - except discord.errors.NotFound: - await ctx.send( - Texts('utils', ctx).get("Unable to find the message"), - delete_after=5) - - @_react.command(name='remove', aliases=['clear']) - async def _react_remove(self, ctx: commands.Context, message_id: int): - try: - message: discord.Message = await ctx.channel.fetch_message( - message_id) - await message.clear_reactions() - except discord.errors.NotFound: - await ctx.send( - Texts('utils', ctx).get("Unable to find the message"), - delete_after=5) - - ########################################################################### - - @group_extra(name='delete', invoke_without_command=True, category='text') - async def _delete(self, ctx: commands.Context, message_id: int): - try: - await ctx.message.delete() - except discord.errors.Forbidden: - pass - - try: - message: discord.Message = await ctx.channel.fetch_message( - message_id) - await message.delete() - except (discord.errors.NotFound, discord.errors.Forbidden): - await ctx.send( - Texts('utils', ctx).get("Unable to find the message"), - delete_after=5) - - @_delete.command(name='from', aliases=['to', 'in']) - async def _delete_from(self, ctx: commands.Context, - channel: discord.TextChannel, message_id: int): - try: - await ctx.message.delete() - except discord.errors.Forbidden: - pass - - try: - message: discord.Message = await channel.fetch_message( - message_id - ) - await message.delete() - except (discord.errors.NotFound, discord.errors.Forbidden): - await ctx.send( - Texts('utils', ctx).get("Unable to find the message"), - delete_after=5 - ) - - ########################################################################### - - async def get_warn(self, ctx: commands.Context, - member: discord.Member = False): - await ctx.trigger_typing() - - if member: - warns = WarnModel.objects.filter( - server_id=str(ctx.guild.id), - user_id=member.id - ) - else: - warns = WarnModel.objects.filter( - server_id=str(ctx.guild.id) - ) - warns_list = '' - - for warn in await warns.all(): - row_id = warn.id - user_id = warn.user_id - user = await self.bot.fetch_user(user_id) - reason = warn.reason - ago = humanize.naturaldelta( - datetime.datetime.now() - warn.created_at - ) - - warns_list += f"[{row_id}] **{user}**: `{reason}` *({ago} ago)*\n" - - return warns_list, warns - - async def add_warn(self, ctx: commands.Context, member: discord.Member, - reason): - - now = datetime.datetime.now() - warn = WarnModel(server_id=ctx.guild.id, user_id=member.id, - reason=reason, - created_at=now) - - self.bot.database.session.add(warn) - self.bot.database.session.commit() - - @group_extra(name='warn', aliases=['warns'], category='administration') - async def _warn(self, ctx: commands.Context): - await ctx.trigger_typing() - if ctx.invoked_subcommand is None: - warns_list, warns = await self.get_warn(ctx) - e = discord.Embed( - title=f"{warns.count()} {Texts('admin', ctx).get('last warns')}: ", - description=warns_list - ) - - await ctx.send(embed=e) - - @_warn.command(name='add', aliases=['new']) - async def _warn_add(self, ctx: commands.Context, member: discord.Member, - *, reason="N/A"): - if not member: - return await ctx.send( - Texts('utils', ctx).get("Unable to find the user...") - ) - - def check(pld: discord.RawReactionActionEvent): - if pld.message_id != choice.id \ - or pld.user_id != ctx.author.id: - return False - return pld.emoji.name in ('1⃣', '2⃣', '3⃣') - - warns_list, warns = await self.get_warn(ctx, member) - - if warns.count() >= 2: - e = discord.Embed( - title=Texts('admin', ctx).get('More than 2 warns'), - description=f"{member.mention} " - + Texts('admin', ctx).get('has more than 2 warns') - ) - e.add_field( - name='__Actions__', - value=':one: kick\n' - ':two: ban\n' - ':three: ' + Texts('admin', ctx).get('ignore') - ) - - choice = await ctx.send(embed=e) - - for reaction in ('1⃣', '2⃣', '3⃣'): - await choice.add_reaction(reaction) - - try: - payload = await self.bot.wait_for( - 'raw_reaction_add', - check=check, - timeout=50.0 - ) - except asyncio.TimeoutError: - return await ctx.send( - Texts('admin', ctx).get('Took too long. Aborting.') - ) - finally: - await choice.delete() - - if payload.emoji.name == '1⃣': - from jishaku.models import copy_context_with - - alt_ctx = await copy_context_with( - ctx, - content=f"{ctx.prefix}" - f"kick " - f"{member} " - f"{Texts('admin', ctx).get('More than 2 warns')}" - ) - return await alt_ctx.command.invoke(alt_ctx) - - elif payload.emoji.name == '2⃣': - from jishaku.models import copy_context_with - - alt_ctx = await copy_context_with( - ctx, - content=f"{ctx.prefix}" - f"ban " - f"{member} " - f"{Texts('admin', ctx).get('More than 2 warns')}" - ) - return await alt_ctx.command.invoke(alt_ctx) - - await self.add_warn(ctx, member, reason) - await ctx.send( - content=f"{member.mention} " - f"**{Texts('admin', ctx).get('got a warn')}**" - f"\n**{Texts('admin', ctx).get('Reason')}:** `{reason}`" - ) - - @_warn.command(name='remove', aliases=['revoke', 'del', 'delete']) - async def _warn_remove(self, ctx: commands.Context, warn_id: int): - warn = self.bot.database.session \ - .query(WarnModel) \ - .filter(WarnModel.id == warn_id) \ - .one() - - self.bot.database.session.delete(warn) - - await ctx.send(f"{Texts('admin', ctx).get('Warn with id')} `{warn_id}`" - f" {Texts('admin', ctx).get('successfully removed')}") - - @_warn.command(name='show', aliases=['list', 'all']) - async def _warn_show(self, ctx: commands.Context, member: discord.Member): - warns_list, warns = await self.get_warn(ctx, member) - - e = discord.Embed( - title=f"{warns.count()} {Texts('admin', ctx).get('last warns')}: ", - description=warns_list - ) - - await ctx.send(embed=e) - - @_warn.command(name='edit', aliases=['change', 'modify']) - async def _warn_edit(self, ctx: commands.Context, warn_id: int, *, reason): - warn = self.bot.database.session \ - .query(WarnModel) \ - .filter(WarnModel.id == warn_id) \ - .one() - warn.reason = reason - - self.bot.database.session.commit() - - await ctx.send(f"{Texts('admin', ctx).get('Warn with id')} `{warn_id}`" - f" {Texts('admin', ctx).get('successfully edited')}") - - ########################################################################### - - @command_extra(name='language', aliases=['lang', 'langue', 'langage'], category='server') - async def _language(self, ctx: commands.Context, locale: str): - available = self.bot.database.session \ - .query(LangModel.value) \ - .filter(LangModel.key == 'available') \ - .first()[0] \ - .split(',') - - if locale.lower() not in available: - await ctx.send( - Texts('admin', ctx).get('Unable to find this language')) - else: - current = self.bot.database.session \ - .query(LangModel) \ - .filter(LangModel.key == str(ctx.guild.id)) - - if current.count() > 0: - current = current.one() - current.value = locale.lower() - self.bot.database.session.commit() - else: - new_row = LangModel(key=str(ctx.guild.id), - value=locale.lower()) - self.bot.database.session.add(new_row) - self.bot.database.session.commit() - - await ctx.send( - Texts('admin', ctx).get('Language changed successfully')) - - ########################################################################### - - @group_extra(name='prefix', aliases=['prefixes'], category='server') - async def _prefix(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.send_help('prefix') - - @_prefix.command(name='add', aliases=['set', 'new']) - async def _prefix_add(self, ctx: commands.Context, prefix: str): - if str(ctx.guild.id) in self.bot.prefixes: - prefixes = self.bot.prefixes.get( - str(ctx.guild.id), "prefixes" - ).split( - self.bot.config.get("misc", "separator") - ) - - if prefix in prefixes: - return await ctx.send( - Texts('admin', ctx).get('This prefix already exists') - ) - else: - prefixes.append(prefix) - self.bot.prefixes.set( - str(ctx.guild.id), - "prefixes", - self.bot.config.get("misc", "separator") - .join(prefixes) - ) - with open('./configs/prefixes.cfg', 'w') as configfile: - self.bot.prefixes.write(configfile) - else: - self.bot.prefixes.add_section(str(ctx.guild.id)) - self.bot.prefixes.set(str(ctx.guild.id), "prefixes", prefix) - with open('./configs/prefixes.cfg', 'w') as configfile: - self.bot.prefixes.write(configfile) - - await ctx.send( - Texts('admin', ctx).get('Prefix added successfully') - ) - - @_prefix.command(name='remove', aliases=['drop', 'del', 'delete']) - async def _prefix_remove(self, ctx: commands.Context, prefix: str): - if str(ctx.guild.id) in self.bot.prefixes: - prefixes = self.bot.prefixes.get( - str(ctx.guild.id), "prefixes" - ).split( - self.bot.config.get("misc", "separator") - ) - - if prefix in prefixes: - prefixes.remove(prefix) - self.bot.prefixes.set( - str(ctx.guild.id), - "prefixes", - self.bot.config.get("misc", "separator") - .join(prefixes) - ) - with open('./configs/prefixes.cfg', 'w') as configfile: - self.bot.prefixes.write(configfile) - - return await ctx.send( - Texts('admin', ctx).get('Prefix removed successfully') - ) - - await ctx.send( - Texts('admin', ctx).get('This prefix does not exist') - ) - - @_prefix.command(name='list', aliases=['show', 'all']) - async def _prefix_list(self, ctx: commands.Context): - extras = ['.'] - if ctx.message.guild is not None: - extras = [] - if str(ctx.message.guild.id) in self.bot.prefixes: - extras.extend( - self.bot.prefixes.get(str(ctx.message.guild.id), - "prefixes").split( - self.bot.config.get("misc", "separator") - ) - ) - - prefixes = [self.bot.user.mention] - prefixes.extend(extras) - - if len(prefixes) <= 1: - text = Texts('admin', ctx) \ - .get('The only prefix for this guild is :\n') - else: - text = Texts('admin', ctx) \ - .get('Available prefixes for this guild are :\n') - - await ctx.send(text + "\n • ".join(prefixes)) - - -def setup(bot: TuxBot): - bot.add_cog(Admin(bot)) diff --git a/cogs/Help.py b/cogs/Help.py deleted file mode 100644 index 396c23c..0000000 --- a/cogs/Help.py +++ /dev/null @@ -1,227 +0,0 @@ -# Created by romain at 04/01/2020 - -import logging - -import discord -from discord.ext import commands - -from bot import TuxBot -from utils import Texts, GroupPlus -from utils import FieldPages - -log = logging.getLogger(__name__) - - -class HelpCommand(commands.HelpCommand): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.ignore_cogs = ["Monitoring", "Help", "Jishaku"] - self.owner_cogs = [] - self.admin_cogs = ["Admin"] - - def command_formatting(self, e, command): - prefix = self.context.prefix \ - if str(self.context.bot.user.id) not in self.context.prefix \ - else f"@{self.context.bot.user.name}" - file = Texts(command.cog.qualified_name.lower() + '_help', self.context) - - if command.parent is not None: - description = file.get(f"_{command.parent}_{command.name}") - usage = file.get(f"_{command.parent}_{command.name}__usage") - else: - description = file.get(f"_{command.name}") - usage = file.get(f"_{command.name}__usage") - - e.title = self.get_command_signature(command) + usage - e.description = description - - e.add_field( - name=Texts( - 'help', self.context - ).get( - 'command_help.params' - ), - value=usage - ) - e.add_field( - name=Texts( - 'help', self.context - ).get( - 'command_help.usage' - ), - value=f"{prefix}{command.qualified_name} " + usage - ) - - aliases = "`" + '`, `'.join(command.aliases) + "`" - if aliases == "``": - aliases = Texts( - 'help', self.context - ).get( - 'command_help.no_aliases' - ) - e.add_field( - name=Texts( - 'help', self.context - ).get( - 'command_help.aliases' - ), - value=aliases - ) - - return e - - async def send_bot_help(self, mapping): - owners = self.context.bot.owners - prefix = self.context.prefix \ - if str(self.context.bot.user.id) not in self.context.prefix \ - else f"@{self.context.bot.user.name} " - - e = discord.Embed( - color=discord.Color.blue(), - description=Texts( - 'help', self.context - ).get( - 'main_page.description' - ) - ) - e.set_author( - icon_url=self.context.author.avatar_url_as(format='png'), - name=self.context.author - ) - e.set_footer( - text=Texts( - 'help', self.context - ).get( - 'main_page.footer' - ).format( - prefix - ) - ) - - for extension in self.context.bot.cogs.values(): - if self.context.author not in owners \ - and extension.__class__.__name__ in self.owner_cogs: - continue - if extension.__class__.__name__ in self.ignore_cogs: - continue - - count = len(extension.get_commands()) - text = Texts('help', self.context).get('main_page.commands') - - if count <= 1: - text = text[:-1] - - e.add_field( - name=f"__{extension.icon} **{extension.qualified_name}**__", - value=f"{count} {text}" - ) - - await self.context.send(embed=e) - - async def send_cog_help(self, cog): - pages = {} - prefix = self.context.prefix \ - if str(self.context.bot.user.id) not in self.context.prefix \ - else f"@{self.context.bot.user.name}" - file = Texts(cog.qualified_name.lower() + '_help', self.context) - - if cog.__class__.__name__ in self.owner_cogs \ - and self.context.author not in self.context.bot.owners: - return self.command_not_found(cog.qualified_name) - - for cmd in cog.get_commands(): - if self.context.author not in self.context.bot.owners \ - and (cmd.hidden or cmd.category == "Hidden"): - continue - - if cmd.category not in pages: - pages[cmd.category] = "```asciidoc\n" - - pages[cmd.category] \ - += f"{cmd.name}" \ - + ' ' * int(13 - len(cmd.name)) \ - + f":: {file.get(f'_{cmd.name}__short')}\n" - - if isinstance(cmd, GroupPlus): - for group_command in cmd.commands: - pages[cmd.category] \ - += f"└> {group_command.name}" \ - + ' ' * int(10 - len(group_command.name)) \ - + f":: {file.get(f'_{group_command.parent}_{group_command.name}__short')}\n" - for e in pages: - pages[e] += "```" - formatted = [] - for name, cont in pages.items(): - formatted.append((name, cont)) - footer_text = Texts('help', self.context) \ - .get('main_page.footer') \ - .format(prefix) - - pages = FieldPages( - self.context, - embed_color=discord.Color.blue(), - entries=formatted, - title=cog.qualified_name.upper(), - thumbnail=cog.big_icon, - footericon=self.context.bot.user.avatar_url, - footertext=footer_text, - per_page=1 - ) - await pages.paginate() - - async def send_group_help(self, group): - if group.cog_name in self.ignore_cogs: - return await self.send_error_message( - self.command_not_found(group.name) - ) - file = Texts(group.qualified_name.lower() + '_help', self.context) - - formatted = self.command_formatting( - discord.Embed(color=discord.Color.blue()), - group - ) - sub_command_list = "⠀" # this is braille, please don't touch unless you know what you're doing - for group_command in group.commands: - sub_command_list += f"└> **{group_command.name}** - {file.get(f'_{group_command.parent}_{group_command.name}')}\n" - - subcommands = Texts( - 'help', self.context - ).get( - 'command_help.subcommands' - ) - - formatted.add_field(name=subcommands, value=sub_command_list, inline=False) - await self.context.send(embed=formatted) - - async def send_command_help(self, command): - if isinstance(command, commands.Group): - return await self.send_group_help(command) - - if command.cog_name in self.ignore_cogs: - return await self.send_error_message( - self.command_not_found(command.name)) - - formatted = self.command_formatting( - discord.Embed(color=discord.Color.blue()), - command - ) - - await self.context.send(embed=formatted) - - def command_not_found(self, command): - return Texts( - 'help', self.context - ).get( - 'main_page.not_found' - ).format( - command - ) - - -class Help(commands.Cog): - def __init__(self, bot: TuxBot): - bot.help_command = HelpCommand() - - -def setup(bot: TuxBot): - bot.add_cog(Help(bot)) diff --git a/cogs/Images.py b/cogs/Images.py new file mode 100644 index 0000000..957eece --- /dev/null +++ b/cogs/Images.py @@ -0,0 +1,180 @@ +import logging +from io import BytesIO + +import discord +from discord.ext import commands, flags + +from app import TuxBot +from utils.functions.extra import ContextPlus + +log = logging.getLogger(__name__) + + +class Images(commands.Cog, name="Images"): + def __init__(self, bot): + self.bot = bot + self.image_api = "http://0.0.0.0:8080" + + async def _send_meme(self, ctx: ContextPlus, endpoint: str, **passed_flags): + async with ctx.typing(): + url = f"{self.image_api}/{endpoint}?" + for key, val in passed_flags.items(): + if val: + url += f"{key}={val}&" + + async with self.bot.session.get(url) as r: + if r.status != 200: + return await ctx.send("Failed...") + + data = BytesIO(await r.read()) + + await ctx.send( + file=discord.File(data, "output.png") + ) + + @commands.command(name="phcomment") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _phcomment(self, ctx: ContextPlus, user: discord.User = None, *, message: commands.clean_content(fix_channel_mentions=True, escape_markdown=True)): + async with ctx.typing(): + message = message.replace("&", "%26") + if user is None: + avatar = ctx.author.avatar_url_as(format='png') + username = ctx.author.name + else: + avatar = user.avatar_url_as(format='png') + username = user.name + + url = f"{self.image_api}/ph/comment" \ + f"?image={avatar}" \ + f"&username={username}" \ + f"&message={message}" + + async with self.bot.session.get(url) as r: + if r.status != 200: + return await ctx.send("Failed...") + + data = BytesIO(await r.read()) + + await ctx.send( + file=discord.File(data, "output.png") + ) + + @commands.command(name="phvideo") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _phvideo(self, ctx: ContextPlus, image: str, author: discord.User, *, title: commands.clean_content(fix_channel_mentions=True, escape_markdown=True)): + async with ctx.typing(): + url = f"{self.image_api}/ph/video" \ + f"?image={image}" \ + f"&username={author.name}" \ + f"&title={title}" + + async with self.bot.session.get(url) as r: + if r.status != 200: + return await ctx.send("Failed...") + + data = BytesIO(await r.read()) + + await ctx.send( + file=discord.File(data, "output.png") + ) + + @flags.add_flag("--text1", type=str) + @flags.add_flag("--text2", type=str) + @flags.add_flag("--text3", type=str) + @flags.command(name="balloon") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _balloon(self, ctx: ContextPlus, **passed_flags): + passed_flags["text3"] = passed_flags.get("text3") + passed_flags["text4"] = passed_flags.get("text1") + passed_flags["text5"] = passed_flags.get("text2") + + await self._send_meme(ctx, 'balloon', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.add_flag("--text2", type=str) + @flags.add_flag("--text3", type=str) + @flags.command(name="butterfly") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _butterfly(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'butterfly', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.add_flag("--text2", type=str) + @flags.command(name="buttons") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _buttons(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'buttons', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.command(name="cmm") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _cmm(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'change_my_mind', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.add_flag("--text2", type=str) + @flags.command(name="drake") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _drake(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'drake', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.add_flag("--text2", type=str, default=False) + @flags.command(name="fry") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _fry(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'fry', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.add_flag("--text2", type=str, default=False) + @flags.command(name="imagination") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _imagination(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'imagination', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.add_flag("--text2", type=str, default=False) + @flags.command(name="everywhere") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _everywhere(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'everywhere', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.add_flag("--text2", type=str) + @flags.add_flag("--text3", type=str) + @flags.command(name="choice") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _choice(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'choice', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.command(name="pika") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _pika(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'pika', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.add_flag("--text2", type=str) + @flags.add_flag("--text3", type=str) + @flags.command(name="pkp") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _pkp(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'pkp', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.add_flag("--text2", type=str) + @flags.command(name="puppet") + @commands.cooldown(1, 5, commands.BucketType.user) + async def _puppet(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'puppet', **passed_flags) + + @flags.add_flag("--text1", type=str) + @flags.command(name="scroll_of_truth", alias=['sot']) + @commands.cooldown(1, 5, commands.BucketType.user) + async def _sot(self, ctx: ContextPlus, **passed_flags): + await self._send_meme(ctx, 'scroll_of_truth', **passed_flags) + + +def setup(bot: TuxBot): + cog = Images(bot) + bot.add_cog(cog) diff --git a/cogs/Logs.py b/cogs/Logs.py index 7caa4ec..a238d7b 100644 --- a/cogs/Logs.py +++ b/cogs/Logs.py @@ -18,9 +18,7 @@ import humanize import psutil from discord.ext import commands, tasks -from bot import TuxBot -from utils import Texts -from utils import command_extra +from app import TuxBot log = logging.getLogger(__name__) @@ -52,9 +50,6 @@ class Logs(commands.Cog): self._resumes = [] self._identifies = defaultdict(list) - self.icon = ":newspaper:" - self.big_icon = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/twitter/233/newspaper_1f4f0.png" - def _clear_gateway_data(self): one_week_ago = datetime.datetime.utcnow() - datetime.timedelta(days=7) to_remove = [ @@ -112,8 +107,19 @@ class Logs(commands.Cog): self.bot.socket_stats[msg.get('t')] += 1 @property - def webhook(self): - return self.bot.logs_webhook + def logs(self): + webhooks = {} + + for key, value in self.bot.logs_channels.items(): + webhooks[key] = discord.Webhook.partial( + id=value.get('webhook')['id'], + token=value.get('webhook')['token'], + adapter=discord.AsyncWebhookAdapter( + self.bot.session + ) + ) + + return webhooks async def log_error(self, *, ctx=None, extra=None): e = discord.Embed(title='Error', colour=0xdd5f53) @@ -131,7 +137,7 @@ class Logs(commands.Cog): e.add_field(name='Channel', value=channel) e.add_field(name='Guild', value=guild) - await self.webhook.send(embed=e) + await self.logs.get('errors').send(embed=e) async def send_guild_stats(self, e, guild): e.add_field(name='Name', value=guild.name) @@ -155,7 +161,7 @@ class Logs(commands.Cog): if guild.me: e.timestamp = guild.me.joined_at - await self.webhook.send(embed=e) + await self.logs.get('guilds').send(embed=e) @commands.Cog.listener() async def on_guild_join(self, guild: discord.guild): @@ -169,17 +175,37 @@ class Logs(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.message): - if message.guild is None: - e = discord.Embed(colour=0x0a97f5, title='New DM') # blue colour + ctx = await self.bot.get_context(message) + if ctx.valid: + return + + if isinstance(message.channel, discord.DMChannel): + if message.author is self.bot.user: + e = discord.Embed( + title=f"DM to: {message.channel.recipient}", + description=message.content, + color=0x39e326 + ) + else: + e = discord.Embed( + title="New DM:", + description=message.content, + color=0x0A97F5 + ) e.set_author( - name=message.author, - icon_url=message.author.avatar_url_as(format='png') + name=message.channel.recipient, + icon_url=message.channel.recipient.avatar_url_as(format="png") ) - e.description = message.content - if len(message.attachments) > 0: - e.set_image(url=message.attachments[0].url) - e.set_footer(text=f"User ID: {message.author.id}") - await self.webhook.send(embed=e) + + if message.attachments: + attachment_url = message.attachments[0].url + e.set_image(url=attachment_url) + + e.set_footer( + text=f"User ID: {message.channel.recipient.id}" + ) + + await self.logs["dm"].send(embed=e) @commands.Cog.listener() async def on_command_error(self, ctx, error): @@ -212,7 +238,7 @@ class Logs(commands.Cog): ) e.description = f'```py\n{exc}\n```' e.timestamp = datetime.datetime.utcnow() - await self.webhook.send(embed=e) + await self.logs.get('errors').send(embed=e) @commands.Cog.listener() async def on_socket_raw_send(self, data): @@ -241,9 +267,9 @@ class Logs(commands.Cog): emoji = types.get(record.levelname, ':heavy_multiplication_x:') dt = datetime.datetime.utcfromtimestamp(record.created) msg = f'{emoji} `[{dt:%Y-%m-%d %H:%M:%S}] {record.message}`' - await self.webhook.send(msg) + await self.logs.get('gateway').send(msg) - @command_extra(name='commandstats', hidden=True, category='misc') + @commands.command('commandstats') @commands.is_owner() async def _commandstats(self, ctx, limit=20): counter = self.bot.command_stats @@ -258,7 +284,7 @@ class Logs(commands.Cog): await ctx.send(f'```\n{output}\n```') - @command_extra(name='socketstats', hidden=True, category='misc') + @commands.command('socketstats') @commands.is_owner() async def _socketstats(self, ctx): delta = datetime.datetime.utcnow() - self.bot.uptime @@ -268,7 +294,7 @@ class Logs(commands.Cog): await ctx.send( f'{total} socket events observed ({cpm:.2f}/minute):\n{self.bot.socket_stats}') - @command_extra(name='uptime', category='misc') + @commands.command('uptime') async def _uptime(self, ctx): uptime = humanize.naturaltime( datetime.datetime.utcnow() - self.bot.uptime) @@ -287,7 +313,7 @@ async def on_error(self, event, *args): args_str.append('```') e.add_field(name='Args', value='\n'.join(args_str), inline=False) - hook = self.get_cog('Logs').webhook + hook = self.get_cog('Logs').logs.get('errors') try: await hook.send(embed=e) except (discord.HTTPException, discord.NotFound, diff --git a/cogs/Monitoring.py b/cogs/Monitoring.py deleted file mode 100644 index 5827299..0000000 --- a/cogs/Monitoring.py +++ /dev/null @@ -1,110 +0,0 @@ -import logging -import urllib.request -from datetime import datetime - -import discord -from aiohttp import web -from discord.ext import tasks, commands - -from bot import TuxBot - -log = logging.getLogger(__name__) - - -class Monitoring(commands.Cog): - - def __init__(self, bot: TuxBot): - self.bot = bot - self.site = web.TCPSite - - self.ping_clusters.start() - - app = web.Application() - app.add_routes([web.get('/', self.handle)]) - - self.runner = web.AppRunner(app) - self.bot.loop.create_task(self.start_HTTPMonitoring_server()) - - def cog_unload(self): - self.ping_clusters.stop() - - @tasks.loop(seconds=10.0) - async def ping_clusters(self): - for cluster in self.bot.fallbacks: - if cluster == 'DEFAULT': - pass - else: - cluster = self.bot.fallbacks[cluster] - if not cluster.get('This', False): - host = cluster.get('Host') - port = cluster.get('Port') - - try: - req = urllib.request.urlopen( - f"http://{host}:{port}", - timeout=2 - ) - except Exception: - global_channel = await self.bot.fetch_channel( - 661347412463321098 - ) - - e = discord.Embed( - title=f"Server `{cluster.get('Name')}`", - color=discord.colour.Color.red(), - description=f"Server **`{cluster.get('Name')}`** with address **`http://{host}:{port}`** is down ! ", - timestamp=datetime.now() - ) - e.set_thumbnail( - url='https://upload.wikimedia.org/wikipedia/commons/7/75/Erroricon404.PNG' - ) - - await global_channel.send(embed=e) - else: - print(req.read().decode()) - - @ping_clusters.before_loop - async def before_pinging(self): - await self.bot.wait_until_ready() - - cluster = self.bot.cluster - host = cluster.get('Host') - port = cluster.get('Port') - - global_channel = await self.bot.fetch_channel( - 661347412463321098 - ) - - e = discord.Embed( - title=f"Server `{cluster.get('Name')}`", - color=discord.colour.Color.green(), - description=f"Server **`{cluster.get('Name')}`** with address **`http://{host}:{port}`** is started ! ", - timestamp=datetime.now() - ) - e.set_thumbnail( - url='https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/MW-Icon-CheckMark.svg/1024px-MW-Icon-CheckMark.svg.png' - ) - - await global_channel.send(embed=e) - - async def start_HTTPMonitoring_server(self): - host = self.bot.cluster.get('WebPage') - port = self.bot.cluster.get('Port') - - print(f"Starting HTTP Monitoring server on {host}:{port}") - - await self.runner.setup() - self.site = web.TCPSite(self.runner, host, port) - await self.site.start() - - async def handle(self, _): - return web.json_response( - { - 'message': "I'm alive !", - 'ws': self.bot.latency * 1000 - } - ) - - -def setup(bot: TuxBot): - bot.add_cog(Monitoring(bot)) diff --git a/cogs/Poll.py b/cogs/Poll.py deleted file mode 100644 index 44dd1eb..0000000 --- a/cogs/Poll.py +++ /dev/null @@ -1,222 +0,0 @@ -import json -import logging -from typing import Union - -import discord -from discord.ext import commands -from yarl import URL - -from bot import TuxBot -from utils import PollModel, ResponsesModel -from utils import Texts -from utils.functions import emotes as utils_emotes -from utils import group_extra - -log = logging.getLogger(__name__) - - -class Poll(commands.Cog): - - def __init__(self, bot: TuxBot): - self.bot = bot - self.icon = ":bar_chart:" - self.big_icon = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/twitter/233/bar-chart_1f4ca.png:" - - def get_poll(self, pld) -> Union[bool, PollModel]: - if pld.user_id != self.bot.user.id: - poll = self.bot.database.session \ - .query(PollModel) \ - .filter(PollModel.message_id == pld.message_id) - - if poll.count() > 0: - poll = poll.one() - emotes = utils_emotes.get(poll.available_choices) - if pld.emoji.name in emotes: - return poll - - return False - - async def remove_reaction(self, pld): - channel: discord.TextChannel = self.bot.get_channel(pld.channel_id) - message: discord.Message = await channel.fetch_message(pld.message_id) - user: discord.User = await self.bot.fetch_user(pld.user_id) - - await message.remove_reaction(pld.emoji.name, user) - - @commands.Cog.listener() - async def on_raw_reaction_add(self, pld: discord.RawReactionActionEvent): - poll = self.get_poll(pld) - - if poll: - if poll.is_anonymous: - try: - await self.remove_reaction(pld) - except discord.errors.Forbidden: - pass - choice = utils_emotes.get_index(pld.emoji.name) - - responses = self.bot.database.session.query(ResponsesModel) \ - .filter( - ResponsesModel.poll_id == poll.id, - ResponsesModel.user == pld.user_id, - ResponsesModel.choice == choice - ) - - if responses.count() != 0: - response = responses.first() - self.bot.database.session.delete(response) - self.bot.database.session.commit() - else: - response = ResponsesModel( - user=pld.user_id, - poll_id=poll.id, - choice=choice - ) - self.bot.database.session.add(response) - self.bot.database.session.commit() - - await self.update_poll(poll.id) - - @commands.Cog.listener() - async def on_raw_reaction_remove(self, - pld: discord.RawReactionActionEvent): - poll = self.get_poll(pld) - - if poll: - choice = utils_emotes.get_index(pld.emoji.name) - - responses = self.bot.database.session.query(ResponsesModel) \ - .filter( - ResponsesModel.poll_id == poll.id, - ResponsesModel.user == pld.user_id, - ResponsesModel.choice == choice - ) - - if responses.count() != 0: - response = responses.first() - self.bot.database.session.delete(response) - self.bot.database.session.commit() - await self.update_poll(poll.id) - - ########################################################################### - - async def create_poll(self, ctx: commands.Context, poll: str, anonymous): - question = (poll.split('|')[0]).strip() - responses = [response.strip() for response in poll.split('|')[1:]] - emotes = utils_emotes.get(len(responses)) - - stmt = await ctx.send(Texts('poll', ctx).get('**Preparation...**')) - - poll_row = PollModel() - self.bot.database.session.add(poll_row) - self.bot.database.session.flush() - - e = discord.Embed(description=f"**{question}**") - e.set_author( - name=ctx.author, - icon_url="https://cdn.gnous.eu/tuxbot/survey1.png" - ) - for i, response in enumerate(responses): - e.add_field( - name=f"__{emotes[i]}` - {response.capitalize()}`__", - value="**0** vote" - ) - e.set_footer(text=f"ID: #{poll_row.id}") - - poll_row.channel_id = stmt.channel.id - poll_row.message_id = stmt.id - poll_row.content = e.to_dict() - poll_row.is_anonymous = anonymous - poll_row.available_choices = len(responses) - - self.bot.database.session.commit() - - await stmt.edit(content='', embed=e) - for emote in range(len(responses)): - await stmt.add_reaction(emotes[emote]) - - async def update_poll(self, poll_id: int): - poll = self.bot.database.session \ - .query(PollModel) \ - .filter(PollModel.id == poll_id) \ - .one() - channel: discord.TextChannel = self.bot.get_channel(poll.channel_id) - message: discord.Message = await channel.fetch_message(poll.message_id) - - chart_base_url = "https://quickchart.io/chart?backgroundColor=white&c=" - chart_options = { - 'type': 'pie', - 'data': { - 'labels': [], - 'datasets': [ - { - 'data': [] - } - ] - } - } - - content = json.loads(poll.content) \ - if isinstance(poll.content, str) \ - else poll.content - raw_responses = self.bot.database.session \ - .query(ResponsesModel) \ - .filter(ResponsesModel.poll_id == poll_id) - responses = {} - - for response in raw_responses.all(): - if responses.get(response.choice): - responses[response.choice] += 1 - else: - responses[response.choice] = 1 - - for i, field in enumerate(content.get('fields')): - responders = responses.get(i, 0) - chart_options.get('data') \ - .get('labels') \ - .append(field.get('name')[5:].replace('__', '')) - chart_options.get('data') \ - .get('datasets')[0] \ - .get('data') \ - .append(responders) - - if responders <= 1: - field['value'] = f"**{responders}** vote" - else: - field['value'] = f"**{responders}** votes" - - e = discord.Embed(description=content.get('description')) - e.set_author( - name=content.get('author').get('name'), - icon_url=content.get('author').get('icon_url') - ) - chart_url = URL(chart_base_url + json.dumps(chart_options)) - e.set_thumbnail(url=str(chart_url)) - for field in content.get('fields'): - e.add_field( - name=field.get('name'), - value=field.get('value'), - inline=True - ) - e.set_footer(text=content.get('footer').get('text')) - - await message.edit(embed=e) - - poll.content = json.dumps(content) - self.bot.database.session.commit() - - @group_extra(name='poll', aliases=['sondage'], category='poll') - async def _poll(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.send_help('poll') - - @_poll.group(name='create', aliases=['new', 'nouveau']) - async def _poll_create(self, ctx: commands.Context, *, poll: str): - is_anonymous = '--anonyme' in poll - poll = poll.replace('--anonyme', '') - - await self.create_poll(ctx, poll, anonymous=is_anonymous) - - -def setup(bot: TuxBot): - bot.add_cog(Poll(bot)) diff --git a/cogs/Useful.py b/cogs/Useful.py deleted file mode 100644 index f4344c0..0000000 --- a/cogs/Useful.py +++ /dev/null @@ -1,410 +0,0 @@ -# Created by romain at 04/01/2020 -import logging -import os -import pathlib -import platform -import random -import re -import socket -import time -from socket import AF_INET6 -from io import BytesIO -from PIL import Image -from PIL import ImageFont -from PIL import ImageDraw -from PIL import ImageOps - -import aiohttp -import discord -import humanize -import psutil -from discord.ext import commands -from tcp_latency import measure_latency - -from bot import TuxBot -from utils import Texts -from utils import command_extra, group_extra - -log = logging.getLogger(__name__) - - -class Useful(commands.Cog): - - def __init__(self, bot: TuxBot): - self.bot = bot - self.icon = ":toolbox:" - self.big_icon = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/twitter/233/toolbox_1f9f0.png" - - @staticmethod - def _latest_commits(): - cmd = 'git log -n 3 -s --format="[\`%h\`](https://git.gnous.eu/gnouseu/tuxbot-bot/commits/%H) %s (%cr)"' - - return os.popen(cmd).read().strip() - - @staticmethod - def fetch_info(): - total_lines = 0 - total_python_lines = 0 - file_amount = 0 - python_file_amount = 0 - ENV = "env" - - for path, _, files in os.walk("."): - for name in files: - file_dir = str(pathlib.PurePath(path, name)) - if ( - not name.endswith(".py") - and not name.endswith(".po") - and not name.endswith(".json") - ) or ENV in file_dir: - continue - file_amount += 1 - python_file_amount += 1 if name.endswith(".py") else 0 - with open(file_dir, "r", encoding="utf-8") as file: - for line in file: - if not line.strip().startswith("#") \ - or not line.strip(): - total_lines += 1 - total_python_lines += 1 if name.endswith(".py") \ - else 0 - - return (file_amount, total_lines), ( - python_file_amount, total_python_lines) - - @staticmethod - def luhn_checker(number: int): - digits = [int(x) for x in reversed(str(number))] - - for index, digit in enumerate(digits, start=1): - digit = digit * 2 if index % 2 == 0 else digit - if digit >= 10: - digit = sum(int(x) for x in list(str(digit))) - - digits[index - 1] = digit - - return sum(digits) % 10 == 0 - - ########################################################################### - - @command_extra(name='iplocalise', category='network') - async def _iplocalise(self, ctx: commands.Context, addr, ip_type=''): - addr = re.sub(r'http(s?)://', '', addr) - addr = addr[:-1] if addr.endswith('/') else addr - - await ctx.trigger_typing() - - try: - if 'v6' in ip_type: - try: - ip = socket.getaddrinfo(addr, None, AF_INET6)[1][4][0] - except socket.gaierror: - return await ctx.send( - Texts('useful', ctx).get('ipv6 not available')) - else: - ip = socket.gethostbyname(addr) - - async with self.bot.session.get(f"http://ip-api.com/json/{ip}") \ - as s: - response: dict = await s.json() - if response.get('status') == 'success': - e = discord.Embed( - title=f"{Texts('useful', ctx).get('Information for')}" - f" ``{addr}`` *`({response.get('query')})`*", - color=0x5858d7 - ) - - e.add_field( - name=Texts('useful', ctx).get('Belongs to :'), - value=response['org'] if response['org'] else 'N/A', - inline=False - ) - - e.add_field( - name=Texts('useful', ctx).get('Is located at :'), - value=response['city'] if response['city'] else 'N/A', - inline=True - ) - - e.add_field( - name="Region :", - value=f"{response['regionName'] if response['regionName'] else 'N/A'} " - f"({response['country'] if response['country'] else 'N/A'})", - inline=True - ) - - e.set_thumbnail( - url=f"https://www.countryflags.io/" - f"{response.get('countryCode')}/flat/64.png") - - await ctx.send(embed=e) - else: - await ctx.send( - content=f"{Texts('useful', ctx).get('info not available')}" - f"``{response['query'] if response['query'] else 'N/A'}``") - - except Exception as e: - await ctx.send(e) - await ctx.send( - f"{Texts('useful', ctx).get('Cannot connect to host')} {addr}" - ) - - ########################################################################### - - @command_extra(name='getheaders', category='network') - async def _getheaders(self, ctx: commands.Context, addr: str): - if (addr.startswith('http') or addr.startswith('ftp')) is not True: - addr = f"http://{addr}" - - await ctx.trigger_typing() - - try: - async with self.bot.session.get(addr) as s: - e = discord.Embed( - title=f"{Texts('useful', ctx).get('Headers of')} {addr}", - color=0xd75858 - ) - e.add_field(name="Status", value=s.status, inline=True) - e.set_thumbnail(url=f"https://http.cat/{s.status}") - - headers = dict(s.headers.items()) - headers.pop('Set-Cookie', headers) - - for key, value in headers.items(): - e.add_field(name=key, value=value, inline=True) - await ctx.send(embed=e) - - except aiohttp.ClientError: - await ctx.send( - f"{Texts('useful', ctx).get('Cannot connect to host')} {addr}" - ) - - ########################################################################### - - @command_extra(name='git', aliases=['sources', 'source', 'github'], category='misc') - async def _git(self, ctx): - e = discord.Embed( - title=Texts('useful', ctx).get('git repo'), - description=Texts('useful', ctx).get('git text'), - colour=0xE9D460 - ) - e.set_author( - name='Gnous', - icon_url="https://cdn.gnous.eu/logo1.png" - ) - await ctx.send(embed=e) - - ########################################################################### - - @command_extra(name='quote', category='misc') - async def _quote(self, ctx, message_id: discord.Message): - e = discord.Embed( - colour=message_id.author.colour, - description=message_id.clean_content, - timestamp=message_id.created_at - ) - e.set_author( - name=message_id.author.display_name, - icon_url=message_id.author.avatar_url_as(format="jpg") - ) - if len(message_id.attachments) >= 1: - e.set_image(url=message_id.attachments[0].url) - - e.add_field(name="**Original**", - value=f"[Go!]({message_id.jump_url})") - e.set_footer(text="#" + message_id.channel.name) - - await ctx.send(embed=e) - - ########################################################################### - - @command_extra(name='ping', category='network') - async def _ping(self, ctx: commands.Context): - start = time.perf_counter() - await ctx.trigger_typing() - end = time.perf_counter() - - latency = round(self.bot.latency * 1000, 2) - typing = round((end - start) * 1000, 2) - discordapp = measure_latency(host='discordapp.com', wait=0)[0] - - e = discord.Embed(title='Ping', color=discord.Color.teal()) - e.add_field(name='Websocket', value=f'{latency}ms') - e.add_field(name='Typing', value=f'{typing}ms') - e.add_field(name='discordapp.com', value=f'{discordapp}ms') - await ctx.send(embed=e) - - ########################################################################### - - @command_extra(name='info', aliases=['about'], category='misc') - async def _info(self, ctx: commands.Context): - proc = psutil.Process() - total, python = self.fetch_info() - - with proc.oneshot(): - mem = proc.memory_full_info() - e = discord.Embed( - title=Texts('useful', ctx).get('Information about TuxBot'), - color=0x89C4F9) - - e.add_field( - name=f"__:busts_in_silhouette: " - f"{Texts('useful', ctx).get('Development')}__", - value=f"**Romain#5117:** [git](https://git.gnous.eu/Romain)\n" - f"**Outout#4039:** [git](https://git.gnous.eu/mael)\n", - inline=True - ) - e.add_field( - name="__<:python:596577462335307777> Python__", - value=f"**python** `{platform.python_version()}`\n" - f"**discord.py** `{discord.__version__}`", - inline=True - ) - e.add_field( - name="__:gear: Usage__", - value=f"**{humanize.naturalsize(mem.rss)}** " - f"{Texts('useful', ctx).get('physical memory')}\n" - f"**{humanize.naturalsize(mem.vms)}** " - f"{Texts('useful', ctx).get('virtual memory')}\n", - inline=True - ) - - e.add_field( - name=f"__{Texts('useful', ctx).get('Servers count')}__", - value=str(len(self.bot.guilds)), - inline=True - ) - e.add_field( - name=f"__{Texts('useful', ctx).get('Channels count')}__", - value=str(len([_ for _ in self.bot.get_all_channels()])), - inline=True - ) - e.add_field( - name=f"__{Texts('useful', ctx).get('Members count')}__", - value=str(len([_ for _ in self.bot.get_all_members()])), - inline=True - ) - - e.add_field( - name=f"__:file_folder: {Texts('useful', ctx).get('Files')}__", - value=f"{total[0]} *({python[0]} <:python:596577462335307777>)*", - inline=True - ) - e.add_field( - name=f"__¶ {Texts('useful', ctx).get('Lines')}__", - value=f"{total[1]} *({python[1]} <:python:596577462335307777>)*", - inline=True - ) - - e.add_field( - name=f"__{Texts('useful', ctx).get('Latest changes')}__", - value=self._latest_commits(), - inline=False - ) - - e.add_field( - name=f"__:link: {Texts('useful', ctx).get('Links')}__", - value="[tuxbot.gnous.eu](https://tuxbot.gnous.eu/) " - "| [gnous.eu](https://gnous.eu/) " - "| [git](https://git.gnous.eu/gnouseu/tuxbot-bot) " - "| [status](https://status.gnous.eu/check/154250) " - f"| [{Texts('useful', ctx).get('Invite')}](https://discordapp.com/oauth2/authorize?client_id=301062143942590465&scope=bot&permissions=268749888)", - inline=False - ) - - e.set_footer(text=f'version: {self.bot.version} ' - f'• prefix: {ctx.prefix}') - - await ctx.send(embed=e) - - ########################################################################### - - @command_extra(name='credits', aliases=['contributors', 'authors'], category='misc') - async def _credits(self, ctx: commands.Context): - e = discord.Embed( - title=Texts('useful', ctx).get('Contributors'), - color=0x36393f - ) - - e.add_field( - name="**Outout#4039** ", - value="• https://git.gnous.eu/mael ⠀\n" - "• mael@gnous.eu\n" - "• [@outoutxyz](https://twitter.com/outouxyz)", - inline=True - ) - e.add_field( - name="**Romain#5117** ", - value="• https://git.gnous.eu/Romain\n" - "• romain@gnous.eu", - inline=True - ) - - await ctx.send(embed=e) - - ########################################################################### - @group_extra(name='cb', aliases=['cc'], category='misc') - @commands.cooldown(1, 5, type=commands.BucketType.user) - async def _cb(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.send_help('cb') - - @_cb.command(name='validate', aliases=['valid', 'correct'], category='misc') - @commands.cooldown(1, 5, type=commands.BucketType.user) - async def _cb_validate(self, ctx: commands.Context, *, number: int): - valid = self.luhn_checker(number) - - await ctx.send( - Texts( - 'useful', ctx - ).get( - 'valid_credit_card' - if valid - else 'invalid_credit_card' - ) - ) - - @_cb.command(name='generate', aliases=['new', 'get'], category='misc') - @commands.cooldown(1, 5, type=commands.BucketType.user) - async def _cb_generate(self, ctx: commands.Context): - await ctx.channel.trigger_typing() - - number = random.randint(4000_0000_0000_0000, 5999_9999_9999_9999) - while not self.luhn_checker(number): - number = random.randint(4000_0000_0000_0000, 5999_9999_9999_9999) - number = str(number) - cvv = ''.join(random.choice("abcdefghij") for _ in range(3)) - - with Image.open("utils/images/blank_credit_card.png") as blank: - cc_font = ImageFont.truetype('utils/fonts/credit_card.ttf', 26) - user_font = ImageFont.truetype('utils/fonts/credit_card.ttf', 20) - draw = ImageDraw.Draw(blank) - - cvv_text = Image.new('L', (500, 50)) - cvv_draw = ImageDraw.Draw(cvv_text) - cvv_draw.text((0, 0), cvv, font=user_font, fill=255) - cvv_rotated = cvv_text.rotate(23, expand=1) - - draw.text( - (69, 510), - ' '.join([number[i:i+4] for i in range(0, len(number), 4)]), - (210, 210, 210), - font=cc_font - ) - - draw.text( - (69, 550), - ctx.author.name.upper(), - (210, 210, 210), - font=user_font - ) - blank.paste(ImageOps.colorize(cvv_rotated, (0, 0, 0), (0, 0, 0)), (470, 0), cvv_rotated) - - output = BytesIO() - blank.save(output, 'png') - output.seek(0) - - await ctx.send(file=discord.File(fp=output, filename="credit_card.png")) - - -def setup(bot: TuxBot): - bot.add_cog(Useful(bot)) diff --git a/cogs/User.py b/cogs/User.py deleted file mode 100644 index ac0c830..0000000 --- a/cogs/User.py +++ /dev/null @@ -1,64 +0,0 @@ -import logging - -from discord.ext import commands - -from bot import TuxBot -from utils import AliasesModel -from utils import Texts -from utils import group_extra - -log = logging.getLogger(__name__) - - -class User(commands.Cog): - - def __init__(self, bot: TuxBot): - self.bot = bot - self.icon = ":bust_in_silhouette:" - self.big_icon = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/twitter/233/bust-in-silhouette_1f464.png" - - ########################################################################### - - @group_extra(name='alias', aliases=['aliases'], category='alias') - async def _alias(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.send_help('alias') - - @_alias.command(name='add', aliases=['set', 'new']) - async def _alias_add(self, ctx: commands.Context, *, user_alias: str): - is_global = False - if '--global' in user_alias: - is_global = True - user_alias.replace('--global', '') - - user_alias = user_alias.split(' -> ') - if len(user_alias) != 2: - return await ctx.send_help('alias') - - command = user_alias[1] - user_alias = user_alias[0] - - if self.bot.get_command(command) is None: - return await ctx.send(Texts('user').get('Command not found')) - - alias = AliasesModel( - user_id=ctx.author.id, - alias=user_alias, - command=command, - guild="global" if is_global else str(ctx.guild.id) - ) - - self.bot.database.session.add(alias) - self.bot.database.session.commit() - - @_alias.command(name='remove', aliases=['drop', 'del', 'delete']) - async def _alias_remove(self, ctx: commands.Context, prefix: str): - ... - - @_alias.command(name='list', aliases=['show', 'all']) - async def _alias_list(self, ctx: commands.Context): - ... - - -def setup(bot: TuxBot): - bot.add_cog(User(bot)) diff --git a/configs/bot/blacklist.json b/configs/bot/blacklist.json new file mode 100644 index 0000000..cbbe594 --- /dev/null +++ b/configs/bot/blacklist.json @@ -0,0 +1,5 @@ +{ + "channels": [], + "guilds": [], + "users": [] +} \ No newline at end of file diff --git a/configs/bot/protected.py b/configs/bot/protected.py new file mode 100644 index 0000000..073705f --- /dev/null +++ b/configs/bot/protected.py @@ -0,0 +1,8 @@ +from .settings import token, postgresql, logs + +protected = [ + token, str(list(token)), + postgresql, str(list(postgresql)), + *[channel.get('webhook').get('token') for channel in logs.values()] +] + diff --git a/configs/bot/settings.py.example b/configs/bot/settings.py.example new file mode 100644 index 0000000..626785d --- /dev/null +++ b/configs/bot/settings.py.example @@ -0,0 +1,44 @@ +token = "" +prefix = "drw." + +main_guild = int + +logs = { + "gateway": { + 'channel': int, + 'webhook': { + 'id': int, + 'token': '' + } + }, + "dm": { + 'channel': int, + 'webhook': { + 'id': int, + 'token': '' + } + }, + "mentions": { + 'channel': int, + 'webhook': { + 'id': int, + 'token': '' + } + }, + "guilds": { + 'channel': int, + 'webhook': { + 'id': int, + 'token': '' + } + }, + "errors": { + 'channel': int, + 'webhook': { + 'id': int, + 'token': '' + } + }, +} + +postgresql = 'postgres://tuxbot:tuxbot@localhost:5432/tuxbot-rewrite' diff --git a/configs/bot/whitelist.json b/configs/bot/whitelist.json new file mode 100644 index 0000000..83434f3 --- /dev/null +++ b/configs/bot/whitelist.json @@ -0,0 +1,3 @@ +{ + "owners": [269156684155453451] +} \ No newline at end of file diff --git a/configs/config.cfg.example b/configs/config.cfg.example deleted file mode 100644 index e156f5d..0000000 --- a/configs/config.cfg.example +++ /dev/null @@ -1,24 +0,0 @@ -[bot] -Token = -Tester = -Activity = - -[postgresql] -Username = -Password = -Host = -DBName = - -[permissions] -Owners = - -[webhook] -ID = -Token = - -[misc] -Separator = - -[API] -Host = -Port = \ No newline at end of file diff --git a/configs/fallbacks.cfg.example b/configs/fallbacks.cfg.example deleted file mode 100644 index ea9bc7b..0000000 --- a/configs/fallbacks.cfg.example +++ /dev/null @@ -1,18 +0,0 @@ -[fr-srv01] -Host = -Name = fr-srv01 -WebPage = 0.0.0.0 -Port = - -[rm-dev01] -This = True -Host = 127.0.0.1 -Name = rm-dev01 -WebPage = 0.0.0.0 -Port = 3389 - -[rm-srv01] -Host = 127.0.0.1 -Name = rm-srv01 -WebPage = 0.0.0.0 -Port = 3390 diff --git a/configs/prefixes.cfg b/configs/prefixes.cfg deleted file mode 100644 index 49d6637..0000000 --- a/configs/prefixes.cfg +++ /dev/null @@ -1,18 +0,0 @@ -[280805240977227776] -prefixes = b1.[301062143942590465]* - -[303633056944881686] -prefixes = b1.[301062143942590465]* - -[373881878471770112] -prefixes = b1. - -[336642139381301249] -prefixes = ba. - -[274247231534792704] -prefixes = test. - -[528679953399676938] -prefixes = test. - diff --git a/database.py b/database.py deleted file mode 100644 index 49127d5..0000000 --- a/database.py +++ /dev/null @@ -1,19 +0,0 @@ -import sqlalchemy -from utils.models import database, metadata -import argparse - -parser = argparse.ArgumentParser() -parser.add_argument("-m", "--migrate", action="store_true") -parser.add_argument("-s", "--seed", action="store_true") -args = parser.parse_args() - -if args.migrate: - print("Migrate...") - engine = sqlalchemy.create_engine(str(database.url)) - metadata.create_all(engine) - print("Done!") - -if args.seed: - print('Seeding...') - # todo: add seeding - print("Done!") diff --git a/generate_locales.sh b/generate_locales.sh deleted file mode 100755 index 78be2cd..0000000 --- a/generate_locales.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -BASEDIR=$(pwd) - -cd "$BASEDIR/utils/locales/en/LC_MESSAGES" - -for i in *.po ; do - [[ -f "$i" ]] || continue - /usr/lib/python3.8/Tools/i18n/msgfmt.py -o "${i%.po}.mo" "${i%.po}" -done - -cd "$BASEDIR/utils/locales/fr/LC_MESSAGES" - -for i in *.po ; do - [[ -f "$i" ]] || continue - /usr/lib/python3.8/Tools/i18n/msgfmt.py -o "${i%.po}.mo" "${i%.po}" -done \ No newline at end of file diff --git a/configs/blacklist.cfg b/models/__init__.py similarity index 100% rename from configs/blacklist.cfg rename to models/__init__.py diff --git a/requirements.txt b/requirements.txt index 442102d..d5a26eb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,27 @@ -requests -humanize -git+https://github.com/Rapptz/discord.py@master -jishaku -gitpython -orm -asyncpg -psycopg2 -configparser -psutil -tcp_latency -yarl -pillow \ No newline at end of file +aiofiles==0.5.0 +aiohttp==3.6.2 +aiosqlite==0.13.0 +astunparse==1.6.3 +async-timeout==3.0.1 +asyncpg==0.20.1 +attrs==19.3.0 +braceexpand==0.1.5 +chardet==3.0.4 +ciso8601==2.1.3 +discord-flags==2.1.1 +discord.py==1.3.3 +humanize==2.4.0 +idna==2.9 +import-expression==1.1.2 +jishaku==1.18.2.188 +mpmath==1.1.0 +multidict==4.7.6 +Pillow==7.1.2 +psutil==5.7.0 +PyPika==0.37.6 +six==1.14.0 +sympy==1.5.1 +tortoise-orm==0.16.11 +typing-extensions==3.7.4.2 +websockets==8.1 +yarl==1.4.2 diff --git a/todo b/todo deleted file mode 100644 index 5ac368b..0000000 --- a/todo +++ /dev/null @@ -1,2 +0,0 @@ -reconnaissance d'image -commande d'archivage pour les salons vocaux avec output mp4 dans lequel on voit le pseudo de celui qui parle diff --git a/utils/__init__.py b/utils/__init__.py deleted file mode 100755 index ff23e97..0000000 --- a/utils/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from utils.functions.config import * -from utils.functions.lang import * -from utils.functions.version import * - -from utils.functions.extra import * -from utils.functions.paginator import * diff --git a/utils/fonts/credit_card.ttf b/utils/fonts/credit_card.ttf deleted file mode 100644 index 00abec8468d1f53d3f51a6da0f086334f517fdab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70136 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 diff --git a/utils/functions/__init__.py b/utils/functions/__init__.py deleted file mode 100644 index 171e8cc..0000000 --- a/utils/functions/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .config import Config -from .database import Database -from .extra import * -from .lang import Texts -from .paginator import * -from .version import Version diff --git a/utils/functions/config.py b/utils/functions/config.py deleted file mode 100644 index 64e3f69..0000000 --- a/utils/functions/config.py +++ /dev/null @@ -1,36 +0,0 @@ -from typing import List, Union - -import configparser - - -class Config(configparser.ConfigParser): - __slots__ = ('name', '_db') - - def __init__(self, name): - super().__init__() - - self._db = super() - self._db.read(name) - - def find(self, value: str, **kwargs) \ - -> Union[ - List[configparser.SectionProxy], configparser.SectionProxy - ]: - key = kwargs.get('key', None) - first = kwargs.get('first', False) - - results = [] - - for name, section in self._db.items(): - if key is None: - for k in section.keys(): - if section.get(k) == value: - results.append(section) - if first and len(results) == 1: - return results[0] - else: - if section.get(key) == value: - results.append(section) - if first and len(results) == 1: - return results[0] - return results diff --git a/utils/functions/database.py b/utils/functions/database.py deleted file mode 100644 index 20388f0..0000000 --- a/utils/functions/database.py +++ /dev/null @@ -1,16 +0,0 @@ -from .config import Config - -import sqlalchemy -import databases - - -class Database: - def __init__(self, config: Config): - conf_postgresql = config["postgresql"] - postgresql = 'postgresql://{}:{}@{}/{}'.format( - conf_postgresql.get("Username"), conf_postgresql.get("Password"), - conf_postgresql.get("Host"), conf_postgresql.get("DBName")) - - self.database = databases.Database(postgresql) - self.metadata = sqlalchemy.MetaData() - self.engine = sqlalchemy.create_engine(str(self.database.url)) diff --git a/utils/functions/emotes.py b/utils/functions/emotes.py deleted file mode 100644 index d296718..0000000 --- a/utils/functions/emotes.py +++ /dev/null @@ -1,10 +0,0 @@ -emotes = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟', '0⃣', - '🇦', '🇧', '🇨', '🇩', '🇪', '🇫', '🇬', '🇭', '🇮'] - - -def get(count): - return emotes[:count] - - -def get_index(emote): - return emotes.index(emote) diff --git a/utils/functions/extra.py b/utils/functions/extra.py index b07ad88..a186dd4 100644 --- a/utils/functions/extra.py +++ b/utils/functions/extra.py @@ -1,32 +1,70 @@ +import ast +import json +import os + +import discord from discord.ext import commands -from utils.functions import Config - - -class CommandsPlus(commands.Command): - def __init__(self, func, **kwargs): - super().__init__(func, **kwargs) - self.category = kwargs.get("category", 'other') - - -class GroupPlus(commands.Group): - def __init__(self, func, **kwargs): - super().__init__(func, **kwargs) - self.category = kwargs.get("category", 'other') +from configs.bot.protected import protected +from configs.bot.settings import prefix class ContextPlus(commands.Context): - async def send(self, content=None, **kwargs): - config = Config('./configs/config.cfg') + async def send(self, content=None, *args, **kwargs): + if content is not None: + for value in protected: + content = content.replace( + str(value), + '[Deleted]' + ) - content = content.replace(config.get("bot", "Token"), 'Whoops! leaked token') - content = content.replace(config.get("webhook", "Token"), 'Whoops! leaked token') + if kwargs.get('content') is not None: + for value in protected: + kwargs['content'] = kwargs['content'].replace( + str(value), + '[Deleted]' + ) - return await super().send(content, **kwargs) + if kwargs.get('embeds') is not None and len(kwargs.get('embeds')) > 0: + for i, embed in enumerate(kwargs.get('embeds')): + embed = str(kwargs.get('embed').to_dict()) + for value in protected: + embed = embed.replace(str(value), '[Deleted]') + kwargs['embeds'][i] = discord.Embed.from_dict( + ast.literal_eval(embed) + ) + + if kwargs.get('embed') is not None: + embed = str(kwargs.get('embed').to_dict()) + for value in protected: + embed = embed.replace(str(value), '[Deleted]') + kwargs['embed'] = discord.Embed.from_dict( + ast.literal_eval(embed) + ) + + return await super().send(content, *args, **kwargs) -def command_extra(*args, **kwargs): - return commands.command(*args, **kwargs, cls=CommandsPlus) +async def get_prefix(bot, message): + custom_prefix = [prefix] + if message.guild: + path = f"configs/guilds/{str(message.guild.id)}.json" + + if os.path.exists(path): + with open(path) as f: + datas = json.load(f) + + custom_prefix = datas["Prefix"] + + return commands.when_mentioned_or(*custom_prefix)(bot, message) -def group_extra(*args, **kwargs): - return commands.group(*args, **kwargs, cls=GroupPlus) +def get_owners() -> list: + with open("configs/bot/whitelist.json") as f: + datas = json.load(f) + + return datas['owners'] + + +def get_blacklist() -> dict: + with open("configs/bot/blacklist.json") as f: + return json.load(f) diff --git a/utils/functions/lang.py b/utils/functions/lang.py deleted file mode 100644 index fd3330b..0000000 --- a/utils/functions/lang.py +++ /dev/null @@ -1,34 +0,0 @@ -import gettext -import json - -from discord.ext import commands - - -class Texts: - def __init__(self, base: str = 'base', ctx: commands.Context = None): - self.locale = self.get_locale(ctx) - self.base = base - - def get(self, text: str) -> str: - texts = gettext.translation(self.base, localedir='utils/locales', - languages=[self.locale]) - texts.install() - return texts.gettext(text) - - def set(self, lang: str): - self.locale = lang - - @staticmethod - def get_locale(ctx: commands.Context): - lang = 'fr' - if ctx is not None: - try: - with open(f'./configs/guilds/{ctx.guild.id}.json', 'r') as f: - data = json.load(f) - - lang = data['lang'] - - except FileNotFoundError: - pass - - return lang diff --git a/utils/functions/paginator.py b/utils/functions/paginator.py deleted file mode 100644 index b18bd84..0000000 --- a/utils/functions/paginator.py +++ /dev/null @@ -1,343 +0,0 @@ -""" - -Based on https://github.com/Rapptz/RoboDanny/blob/3ec71c4c4031f868caff3027d71aecdebc3c5cec/cogs/utils/paginator.py -Adapted by Romain J. - -""" - -import asyncio - -import discord -from discord.ext import commands -from discord.ext.commands import Paginator as CommandPaginator - - -class CannotPaginate(Exception): - pass - - -class Pages: - """Implements a paginator that queries the user for the - pagination interface. - Pages are 1-index based, not 0-index based. - If the user does not reply within 2 minutes then the pagination - interface exits automatically. - Parameters - ------------ - ctx: Context - The context of the command. - entries: List[str] - A list of entries to paginate. - per_page: int - How many entries show up per page. - show_entry_count: bool - Whether to show an entry count in the footer. - Attributes - ----------- - embed: discord.Embed - The embed object that is being used to send pagination info. - Feel free to modify this externally. Only the description, - footer fields, and colour are internally modified. - permissions: discord.Permissions - Our permissions for the channel. - """ - - def __init__(self, ctx, *, entries, per_page=12, show_entry_count=True, - embed_color=discord.Color.blurple(), title=None, - thumbnail=None, footericon=None, footertext=None, author=None, - delete_after=None): - self.bot = ctx.bot - self.entries = entries - self.message = ctx.message - self.channel = ctx.channel - self.author = author if author else ctx.author - self.thumbnail = thumbnail - self.footericon = footericon - self.footertext = footertext - self.title = title - self.delete_after = delete_after - self.per_page = per_page - pages, left_over = divmod(len(self.entries), self.per_page) - if left_over: - pages += 1 - self.maximum_pages = pages - self.embed = discord.Embed(colour=embed_color) - self.paginating = len(entries) > per_page - self.show_entry_count = show_entry_count - self.reaction_emojis = [ - ('\U000023ee\U0000fe0f', self.first_page), - ('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page), - ('\U000023f9', self.stop_pages), - ('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page), - ('\U000023ed\U0000fe0f', self.last_page) - ] - - if ctx.guild is not None: - self.permissions = self.channel.permissions_for(ctx.guild.me) - else: - self.permissions = self.channel.permissions_for(ctx.bot.user) - - if not self.permissions.embed_links: - raise commands.BotMissingPermissions( - 'I do not have permissions to : Embed links.' - ) - - if not self.permissions.send_messages: - raise commands.BotMissingPermissions('Bot cannot send messages.') - - if self.paginating: - # verify we can actually use the pagination session - if not self.permissions.add_reactions: - raise commands.BotMissingPermissions( - 'I do not have permissions to : Add Reactions.' - ) - - if not self.permissions.read_message_history: - raise commands.BotMissingPermissions( - 'I do not have permissions to : Read Message History.' - ) - - def get_page(self, page): - base = (page - 1) * self.per_page - return self.entries[base:base + self.per_page] - - def get_content(self, entries, page, *, first=False): - return None - - def get_embed(self, entries, page, *, first=False): - self.prepare_embed(entries, page, first=first) - return self.embed - - def prepare_embed(self, entries, page, *, first=False): - p = [] - for index, entry in enumerate(entries, - 1 + ((page - 1) * self.per_page)): - p.append(f'`{index}.` {entry}') - - if self.maximum_pages > 1: - if self.show_entry_count: - text = f'Showing page {page}/{self.maximum_pages} ({len(self.entries)} entries)' - else: - text = f'Showing page {page}/{self.maximum_pages}' - - self.embed.set_footer(text=text) - - if self.paginating and first: - p.append('') - - self.embed.description = '\n'.join(p) - self.embed.title = self.title or discord.Embed.Empty - self.embed.set_author(icon_url=self.author.avatar_url, - name=str(self.author)) - - async def show_page(self, page, *, first=False): - self.current_page = page - entries = self.get_page(page) - content = self.get_content(entries, page, first=first) - embed = self.get_embed(entries, page, first=first) - - if not self.paginating: - return await self.channel.send(content=content, embed=embed) - - if not first: - await self.message.edit(content=content, embed=embed) - return - - self.message = await self.channel.send(content=content, embed=embed) - for (reaction, _) in self.reaction_emojis: - if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'): - # no |<< or >>| buttons if we only have two pages - # we can't forbid it if someone ends up using it but remove - # it from the default set - continue - - await self.message.add_reaction(reaction) - - async def checked_show_page(self, page): - if page != 0 and page <= self.maximum_pages: - await self.show_page(page) - - async def first_page(self): - """goes to the first page""" - await self.show_page(1) - - async def last_page(self): - """goes to the last page""" - await self.show_page(self.maximum_pages) - - async def next_page(self): - """goes to the next page""" - await self.checked_show_page(self.current_page + 1) - - async def previous_page(self): - """goes to the previous page""" - await self.checked_show_page(self.current_page - 1) - - async def show_current_page(self): - if self.paginating: - await self.show_page(self.current_page) - - async def numbered_page(self): - """lets you type a page number to go to""" - to_delete = [] - to_delete.append( - await self.channel.send('What page do you want to go to?')) - - def message_check(m): - return m.author == self.author and \ - self.channel == m.channel and \ - m.content.isdigit() - - try: - msg = await self.bot.wait_for( - 'message', - check=message_check, - timeout=30.0 - ) - except asyncio.TimeoutError: - to_delete.append(await self.channel.send('Took too long.')) - await asyncio.sleep(5) - else: - page = int(msg.content) - to_delete.append(msg) - if page != 0 and page <= self.maximum_pages: - await self.show_page(page) - else: - to_delete.append(await self.channel.send( - f'Invalid page given. ({page}/{self.maximum_pages})')) - await asyncio.sleep(5) - - try: - await self.channel.delete_messages(to_delete) - except Exception: - pass - - async def show_help(self): - """shows this message""" - messages = ['Welcome to the interactive paginator!\n'] - messages.append( - 'This interactively allows you to see pages of text by navigating with ' \ - 'reactions. They are as follows:\n') - - for (emoji, func) in self.reaction_emojis: - messages.append(f'{emoji} {func.__doc__}') - - embed = self.embed.copy() - embed.clear_fields() - embed.description = '\n'.join(messages) - embed.set_footer( - text=f'We were on page {self.current_page} before this message.') - await self.message.edit(content=None, embed=embed) - - async def go_back_to_current_page(): - await asyncio.sleep(60.0) - await self.show_current_page() - - self.bot.loop.create_task(go_back_to_current_page()) - - async def stop_pages(self): - """stops the interactive pagination session""" - await self.message.delete() - self.paginating = False - - def react_check(self, reaction, user): - if user is None or user.id != self.author.id: - return False - - if reaction.message.id != self.message.id: - return False - - for (emoji, func) in self.reaction_emojis: - if reaction.emoji == emoji: - self.match = func - return True - return False - - async def paginate(self): - """Actually paginate the entries and run the interactive loop if necessary.""" - first_page = self.show_page(1, first=True) - if not self.paginating: - await first_page - else: - # allow us to react to reactions right away if we're paginating - self.bot.loop.create_task(first_page) - - while self.paginating: - try: - reaction, user = await self.bot.wait_for( - 'reaction_add', - check=self.react_check, - timeout=self.delete_after - ) - except asyncio.TimeoutError: - self.paginating = False - try: - await self.message.delete() - except: - pass - finally: - break - - try: - await self.message.remove_reaction(reaction, user) - except: - pass # can't remove it so don't bother doing so - - await self.match() - - -class FieldPages(Pages): - """Similar to Pages except entries should be a list of - tuples having (key, value) to show as embed fields instead. - """ - - def __init__(self, ctx, *, entries, per_page=12, show_entry_count=True, - title, thumbnail, footericon, footertext, - embed_color=discord.Color.blurple()): - super().__init__(ctx, entries=entries, per_page=per_page, - show_entry_count=show_entry_count, title=title, - thumbnail=thumbnail, footericon=footericon, - footertext=footertext, embed_color=embed_color) - - def prepare_embed(self, entries, page, *, first=False): - self.embed.clear_fields() - - for key, value in entries: - self.embed.add_field(name=key, value=value, inline=False) - - self.embed.title = self.title - - if self.maximum_pages > 1: - if self.show_entry_count: - text = f' [{page}/{self.maximum_pages}]' - else: - text = f' [{page}/{self.maximum_pages}]' - self.embed.title = self.title + text - - self.embed.set_footer(icon_url=self.footericon, text=self.footertext) - self.embed.set_thumbnail(url=self.thumbnail) - - -class TextPages(Pages): - """Uses a commands.Paginator internally to paginate some text.""" - - def __init__(self, ctx, text, *, prefix='```', suffix='```', - max_size=2000): - paginator = CommandPaginator(prefix=prefix, suffix=suffix, - max_size=max_size - 200) - for line in text.split('\n'): - paginator.add_line(line) - - super().__init__(ctx, entries=paginator.pages, per_page=1, - show_entry_count=False) - - def get_page(self, page): - return self.entries[page - 1] - - def get_embed(self, entries, page, *, first=False): - return None - - def get_content(self, entry, page, *, first=False): - if self.maximum_pages > 1: - return f'{entry}\nPage {page}/{self.maximum_pages}' - return entry diff --git a/utils/functions/version.py b/utils/functions/version.py deleted file mode 100644 index 9aaf3ec..0000000 --- a/utils/functions/version.py +++ /dev/null @@ -1,12 +0,0 @@ -class Version: - def __init__(self, major: int, minor: int, patch: int, **kwargs): - self.major: int = major - self.minor: int = minor - self.patch: int = patch - - self.pre_release = kwargs.get('pre_release', '') - self.build = kwargs.get('build', '') - - def __str__(self) -> str: - build = self.build[:10] - return f'v{self.major}.{self.minor}.{self.patch}{self.pre_release}+{build}' diff --git a/utils/images/blank_credit_card.png b/utils/images/blank_credit_card.png deleted file mode 100644 index b0e5a5724483665047adace1d5b271592b748144..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71861 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 diff --git a/utils/locales/en/LC_MESSAGES/admin.po b/utils/locales/en/LC_MESSAGES/admin.po deleted file mode 100644 index 93677bd..0000000 --- a/utils/locales/en/LC_MESSAGES/admin.po +++ /dev/null @@ -1,61 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -msgid "Please enter a reason" -msgstr "" - -msgid "Unable to ban this user" -msgstr "" - -msgid "Unable to kick this user" -msgstr "" - -msgid "last warns" -msgstr "" - -msgid "More than 2 warns" -msgstr "" - -msgid "has more than 2 warns" -msgstr "has more than 2 warns, what do you want to do ?" - -msgid "ignore" -msgstr "" - -msgid "Took too long. Aborting." -msgstr "" - -msgid "got a warn" -msgstr "" - -msgid "Reason" -msgstr "" - -msgid "WarnModel with id" -msgstr "" - -msgid "successfully removed" -msgstr "" - -msgid "successfully edited" -msgstr "" - -msgid "Unable to find this language" -msgstr "" - -msgid "Language changed successfully" -msgstr "" diff --git a/utils/locales/en/LC_MESSAGES/admin_help.po b/utils/locales/en/LC_MESSAGES/admin_help.po deleted file mode 100644 index 94d24bd..0000000 --- a/utils/locales/en/LC_MESSAGES/admin_help.po +++ /dev/null @@ -1,252 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - -########################################################################### -########################## SAY ######################################## -########################################################################### - -msgid '_say' -msgstr '' - -msgid '_say_short' -msgstr '_say_short' - -msgid '_say_usage' -msgstr '_say_usage' - -########################################################################### - -msgid '_say_edit' -msgstr '' - -msgid '_say_edit__help' -msgstr '' - -msgid '_say_edit__usage' -msgstr '' - -########################################################################### - -msgid '_say_to' -msgstr '' - -msgid '_say_to__help' -msgstr '' - -msgid '_say_to__usage' -msgstr '' - -########################################################################### -########################## BAN ######################################## -########################################################################### -msgid '_ban' -msgstr '' - -msgid '_ban__help' -msgstr '' - -msgid '_ban__usage' -msgstr '' - -########################################################################### -########################## KICK ####################################### -########################################################################### - -msgid '_kick' -msgstr '' - -msgid '_kick__help' -msgstr '' - -msgid '_kick__usage' -msgstr '' - -########################################################################### -########################## CLEAR ###################################### -########################################################################### - -msgid '_clear' -msgstr '' - -msgid '_clear__help' -msgstr '' - -msgid '_clear__usage' -msgstr '' - -########################################################################### -########################## REACT ###################################### -########################################################################### - -msgid '_react' -msgstr '' - -msgid '_react__help' -msgstr '' - -msgid '_react__usage' -msgstr '' - -########################################################################### - -msgid '_react_remove' -msgstr '' - -msgid '_react_remove__help' -msgstr '' - -msgid '_react_remove__usage' -msgstr '' - -########################################################################### -########################## DELETE ##################################### -########################################################################### - -msgid '_delete' -msgstr '' - -msgid '_delete__help' -msgstr '' - -msgid '_delete__usage' -msgstr '' - -########################################################################### - -msgid '_delete_from' -msgstr '' - -msgid '_delete_from__help' -msgstr '' - -msgid '_delete_from__usage' -msgstr '' - -########################################################################### -########################## WARN ####################################### -########################################################################### - -msgid '_warn' -msgstr '' - -msgid '_warn__help' -msgstr '' - -msgid '_warn__usage' -msgstr '' - -########################################################################### - -msgid '_warn_new' -msgstr '' - -msgid '_warn_new__help' -msgstr '' - -msgid '_warn_new__usage' -msgstr '' - -########################################################################### - -msgid '_warn_remove' -msgstr '' - -msgid '_warn_remove__help' -msgstr '' - -msgid '_warn_remove__usage' -msgstr '' - -########################################################################### - -msgid '_warn_show' -msgstr '' - -msgid '_warn_show__help' -msgstr '' - -msgid '_warn_show__usage' -msgstr '' - -########################################################################### - -msgid '_warn_edit' -msgstr '' - -msgid '_warn_edit__help' -msgstr '' - -msgid '_warn_edit__usage' -msgstr '' - -########################################################################### -########################## LANGUAGE ################################### -########################################################################### - -msgid '_language' -msgstr '' - -msgid '_language__help' -msgstr '' - -msgid '_language__usage' -msgstr '' - -########################################################################### -########################## PREFIX ##################################### -########################################################################### - -msgid '_prefix' -msgstr '' - -msgid '_prefix__help' -msgstr '' - -msgid '_prefix__usage' -msgstr '' - -########################################################################### - -msgid '_prefix_add' -msgstr '' - -msgid '_prefix_add__help' -msgstr '' - -msgid '_prefix_add__usage' -msgstr '' - -########################################################################### - -msgid '_prefix_remove' -msgstr '' - -msgid '_prefix_remove__help' -msgstr '' - -msgid '_prefix_remove__usage' -msgstr '' - -########################################################################### - -msgid '_prefix_list' -msgstr '' - -msgid '_prefix_list__help' -msgstr '' - -msgid '_prefix_list__usage' -msgstr '' diff --git a/utils/locales/en/LC_MESSAGES/base.po b/utils/locales/en/LC_MESSAGES/base.po deleted file mode 100644 index 3ecd6d4..0000000 --- a/utils/locales/en/LC_MESSAGES/base.po +++ /dev/null @@ -1,62 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -msgid "Starting..." -msgstr "" - -msgid "Could not set up PostgreSQL..." -msgstr "" - -msgid "Launch without loading the module" -msgstr "" - -msgid "Search for update" -msgstr "" - -msgid "Checking for update..." -msgstr "" - -msgid "A new version is available !" -msgstr "" - -msgid "Update ? [Y/n] " -msgstr "" - -msgid "Downloading..." -msgstr "" - -msgid "Tuxbot is up to date" -msgstr "" - -msgid "Failed to load extension : " -msgstr "" - -msgid "Extension loaded successfully : " -msgstr "" - -msgid "This command cannot be used in private messages." -msgstr "" - -msgid "Sorry. This command is disabled and cannot be used." -msgstr "" - -msgid "In " -msgstr "" - -msgid "Ready:" -msgstr "" - diff --git a/utils/locales/en/LC_MESSAGES/help.po b/utils/locales/en/LC_MESSAGES/help.po deleted file mode 100644 index 6e63ec3..0000000 --- a/utils/locales/en/LC_MESSAGES/help.po +++ /dev/null @@ -1,43 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -msgid 'main_page.description' -msgstr "When using commands, **<>** means a **required argument** and **[]** means an **optional** argument.\n***(Don't type these symbols!)***" - -msgid 'main_page.commands' -msgstr 'Commands' - -msgid 'main_page.footer' -msgstr '- Send {}help to see more help about a command.' - -msgid 'main_page.not_found' -msgstr 'No command called "{}" found.' - -msgid 'command_help.subcommands' -msgstr 'Subcommands' - -msgid 'command_help.aliases' -msgstr 'Aliases' - -msgid 'command_help.no_aliases' -msgstr 'No aliases' - -msgid 'command_help.params' -msgstr 'Parameters' - -msgid 'command_help.usage' -msgstr 'Usage' \ No newline at end of file diff --git a/utils/locales/en/LC_MESSAGES/logs.po b/utils/locales/en/LC_MESSAGES/logs.po deleted file mode 100644 index cdc6fcd..0000000 --- a/utils/locales/en/LC_MESSAGES/logs.po +++ /dev/null @@ -1,17 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - diff --git a/utils/locales/en/LC_MESSAGES/poll.po b/utils/locales/en/LC_MESSAGES/poll.po deleted file mode 100644 index 320f5d4..0000000 --- a/utils/locales/en/LC_MESSAGES/poll.po +++ /dev/null @@ -1,20 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -#: launcher.py:51 -msgid "**Preparation...**" -msgstr "" diff --git a/utils/locales/en/LC_MESSAGES/poll_help.po b/utils/locales/en/LC_MESSAGES/poll_help.po deleted file mode 100644 index cdc6fcd..0000000 --- a/utils/locales/en/LC_MESSAGES/poll_help.po +++ /dev/null @@ -1,17 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - diff --git a/utils/locales/en/LC_MESSAGES/useful.po b/utils/locales/en/LC_MESSAGES/useful.po deleted file mode 100644 index 904dd0b..0000000 --- a/utils/locales/en/LC_MESSAGES/useful.po +++ /dev/null @@ -1,83 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -msgid "Information about TuxBot" -msgstr "" - -msgid "Latest changes" -msgstr "" - -msgid "Development" -msgstr "" - -msgid "physical memory" -msgstr "" - -msgid "virtual memory" -msgstr "" - -msgid "Servers count" -msgstr "" - -msgid "Channels count" -msgstr "" - -msgid "Members count" -msgstr "" - -msgid "Links" -msgstr "" - -msgid "Files" -msgstr "" - -msgid "Lines" -msgstr "" - -msgid "Invite" -msgstr "" - -msgid "Contributors" -msgstr "" - -msgid "ipv6 not available" -msgstr "Error, this address is not available in IPv6." - -msgid "Information for" -msgstr "" - -msgid "Belongs to :" -msgstr "" - -msgid "Is located at :" -msgstr "" - -msgid "info not available" -msgstr "" - -msgid "Headers of" -msgstr "" - -msgid "Cannot connect to host" -msgstr "" - -msgid "git repo" -msgstr "TuxBot-Bot's repository" - -msgid "git text" -msgstr "Whoa, do you want to see my Gitea repository to dissect me? No problem ! I am a Bot, I do not feel the pain! \n https://git.gnous.eu/gnouseu/tuxbot-bot" - diff --git a/utils/locales/en/LC_MESSAGES/useful_help.po b/utils/locales/en/LC_MESSAGES/useful_help.po deleted file mode 100644 index cdc6fcd..0000000 --- a/utils/locales/en/LC_MESSAGES/useful_help.po +++ /dev/null @@ -1,17 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - diff --git a/utils/locales/en/LC_MESSAGES/user.po b/utils/locales/en/LC_MESSAGES/user.po deleted file mode 100644 index e69de29..0000000 diff --git a/utils/locales/en/LC_MESSAGES/user_help.po b/utils/locales/en/LC_MESSAGES/user_help.po deleted file mode 100644 index cdc6fcd..0000000 --- a/utils/locales/en/LC_MESSAGES/user_help.po +++ /dev/null @@ -1,17 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - diff --git a/utils/locales/en/LC_MESSAGES/utils.po b/utils/locales/en/LC_MESSAGES/utils.po deleted file mode 100644 index 01482bf..0000000 --- a/utils/locales/en/LC_MESSAGES/utils.po +++ /dev/null @@ -1,22 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -msgid "Unable to find the user..." -msgstr "" - -msgid "Unable to find the message" -msgstr "" \ No newline at end of file diff --git a/utils/locales/fr/LC_MESSAGES/admin.po b/utils/locales/fr/LC_MESSAGES/admin.po deleted file mode 100644 index 83de253..0000000 --- a/utils/locales/fr/LC_MESSAGES/admin.po +++ /dev/null @@ -1,61 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -msgid "Please enter a reason" -msgstr "Merci d'entrer une raison" - -msgid "Unable to ban this user" -msgstr "Impossible de bannir cet utilisateur" - -msgid "Unable to kick this user" -msgstr "Impossible d'expulser cet utilisateur" - -msgid "last warns" -msgstr "derniers avertissements" - -msgid "More than 2 warns" -msgstr "Plus de 2 avertissements" - -msgid "has more than 2 warns" -msgstr "a plus de 2 avertissements, que voulez-vous faire?" - -msgid "ignore" -msgstr "ignorer" - -msgid "Took too long. Aborting." -msgstr "Temps expiré. Abandons." - -msgid "got a warn" -msgstr "a recu un avertissement" - -msgid "Reason" -msgstr "Raison" - -msgid "WarnModel with id" -msgstr "L'avertissement avec l'id" - -msgid "successfully removed" -msgstr "a été enlevé avec succes" - -msgid "successfully edited" -msgstr "a été édité avec succes" - -msgid "Unable to find this language" -msgstr "Impossible de trouver cette langue" - -msgid "Language changed successfully" -msgstr "Langue changée avec succès" diff --git a/utils/locales/fr/LC_MESSAGES/admin_help.po b/utils/locales/fr/LC_MESSAGES/admin_help.po deleted file mode 100644 index 0df3a5b..0000000 --- a/utils/locales/fr/LC_MESSAGES/admin_help.po +++ /dev/null @@ -1,263 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - -########################################################################### -########################## SAY ######################################## -########################################################################### - -msgid '_say' -msgstr 'Permet de faire en sorte que le TuxBot envoie votre message' - -msgid '_say__short' -msgstr 'Faire parler TuxBot' - -msgid '_say_usage' -msgstr '[sous-commande] ' - -########################################################################### - -msgid '_say_edit' -msgstr "Permet de modifier le contenu d'un message envoyé par TuxBot" - -msgid '_say_edit__short' -msgstr 'Editer un message envoyé' - -msgid '_say_edit__usage' -msgstr ' ' - -########################################################################### - -msgid '_say_to' -msgstr "Permet de faire en sorte que le TuxBot envoi votre message dans un autre salon ou en MP à quelqu'un" - -msgid '_say_to__short' -msgstr 'Faire parler TuxBot dans un autre salon' - -msgid '_say_to__usage' -msgstr " " - -########################################################################### -########################## BAN ######################################## -########################################################################### -msgid '_ban' -msgstr 'Permet de bannir un membre' - -msgid '_ban__short' -msgstr 'Bannir un membre' - -msgid '_ban__usage' -msgstr '' - -########################################################################### -########################## KICK ####################################### -########################################################################### - -msgid '_kick' -msgstr "Permet d'expulser un membre" - -msgid '_kick__short' -msgstr 'Expulser un membre' - -msgid '_kick__usage' -msgstr '' - -########################################################################### -########################## CLEAR ###################################### -########################################################################### - -msgid '_clear' -msgstr 'Permet de supprimer un nombre donné de message' - -msgid '_clear__short' -msgstr 'Supprime X messages' - -msgid '_clear__usage' -msgstr '' - -########################################################################### -########################## REACT ###################################### -########################################################################### - -msgid '_react' -msgstr "Affiche l'aide relative a la commande `react`" - -msgid '_react__short' -msgstr "Afficher l'aide pour `react`" - -msgid '_react__usage' -msgstr '[sous-commande]' - -########################################################################### - -msgid '_react_add' -msgstr "Permet d'ajouter une réaction à un message de la part de TuxBot" - -msgid '_react_add__short' -msgstr 'Ajouter une réaction' - -msgid '_react_add__usage' -msgstr ' <émoji>' - -########################################################################### - -msgid '_react_remove' -msgstr "Permet d'enlever toutes les réactions d'un message" - -msgid '_react_remove__short' -msgstr "Enlever toutes les réactions d'un message" - -msgid '_react_remove__usage' -msgstr '' - -########################################################################### -########################## DELETE ##################################### -########################################################################### - -msgid '_delete' -msgstr 'Permet de supprimer un message' - -msgid '_delete__short' -msgstr 'Supprimer un message' - -msgid '_delete__usage' -msgstr '[sous-commande] ' - -########################################################################### - -msgid '_delete_from' -msgstr 'Permet de supprimer un message dans un autre salon' - -msgid '_delete_from__short' -msgstr 'Supprimer un message dans un autre salon' - -msgid '_delete_from__usage' -msgstr ' ' - -########################################################################### -########################## WARN ####################################### -########################################################################### - -msgid '_warn' -msgstr "Permet d'afficher les derniers avertissements donnés sur ce serveur" - -msgid '_warn__short' -msgstr 'Lister les derniers avertissements' - -msgid '_warn__usage' -msgstr '[sous-commande]' - -########################################################################### - -msgid '_warn_add' -msgstr "Permet d'ajouter un avertissement à un membre en donnant une raison" - -msgid '_warn_add__short' -msgstr 'Ajouter un avertissement a un membre' - -msgid '_warn_add__usage' -msgstr ' ' - -########################################################################### - -msgid '_warn_remove' -msgstr "Permet de supprimer un avertissement qui a été donné à un membre" - -msgid '_warn_remove__short' -msgstr "Retirer l'avertissement d'un membre" - -msgid '_warn_remove__usage' -msgstr "" - -########################################################################### - -msgid '_warn_show' -msgstr "Permet d'afficher les derniers avertissements donnés à un membre" - -msgid '_warn_show__short' -msgstr "Lister les derniers avertissements d'un membre" - -msgid '_warn_show__usage' -msgstr '' - -########################################################################### - -msgid '_warn_edit' -msgstr "Permet de modifier la raison de l'avertissement donné à un membre" - -msgid '_warn_edit__short' -msgstr "Modifier la raison d'un avertissement" - -msgid '_warn_edit__usage' -msgstr " " - -########################################################################### -########################## LANGUAGE ################################### -########################################################################### - -msgid '_language' -msgstr 'Permet de définir la langue utilisée sur ce serveur' - -msgid '_language__short' -msgstr 'Définir la langue de Tuxbot' - -msgid '_language__usage' -msgstr '' - -########################################################################### -########################## PREFIX ##################################### -########################################################################### - -msgid '_prefix' -msgstr "Affiche l'aide relative a la commande `prefix`" - -msgid '_prefix__short' -msgstr "Afficher l'aide pour `prefix`" - -msgid '_prefix__usage' -msgstr '[sous-commande]' - -########################################################################### - -msgid '_prefix_add' -msgstr "Permet d'ajouter un nouveau préfixe pour ce serveur" - -msgid '_prefix_add__short' -msgstr 'Ajouter un préfixe pour ce serveur' - -msgid '_prefix_add__usage' -msgstr '' - -########################################################################### - -msgid '_prefix_remove' -msgstr "Permet retirer un préfixe pour ce serveur" - -msgid '_prefix_remove__short' -msgstr 'Retirer un préfixe pour ce serveur' - -msgid '_prefix_remove__usage' -msgstr '' - -########################################################################### - -msgid '_prefix_list' -msgstr "Permet d'afficher tous les prefixes définis pour ce serveur" - -msgid '_prefix_list__short' -msgstr 'Lister les prefixes pour ce serveur' - -msgid '_prefix_list__usage' -msgstr '⠀' diff --git a/utils/locales/fr/LC_MESSAGES/base.po b/utils/locales/fr/LC_MESSAGES/base.po deleted file mode 100644 index 61a4d25..0000000 --- a/utils/locales/fr/LC_MESSAGES/base.po +++ /dev/null @@ -1,62 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -msgid "Starting..." -msgstr "Démarrage..." - -msgid "Could not set up PostgreSQL..." -msgstr "Impossible de lancer PostgreSQL..." - -msgid "Launch without loading the module" -msgstr "Lancer sans charger le module " - -msgid "Search for update" -msgstr "Rechercher les mises à jour" - -msgid "Checking for update..." -msgstr "Recherche de mise à jour..." - -msgid "A new version is available !" -msgstr "Une nouvelle version est disponible !" - -msgid "Update ? [Y/n] " -msgstr "Mettre à jour ? [O/n]" - -msgid "Downloading..." -msgstr "Téléchargement..." - -msgid "Tuxbot is up to date" -msgstr "Tuxbot est à jour" - -msgid "Failed to load extension : " -msgstr "Impossible de charger l'extension : " - -msgid "Extension loaded successfully : " -msgstr "Extension chargée avec succes : " - -msgid "This command cannot be used in private messages." -msgstr "Cette commande ne peut pas être utilisée en message privé." - -msgid "Sorry. This command is disabled and cannot be used." -msgstr "Désolé mais cette commande est désactivée." - -msgid "In " -msgstr "Dans " - -msgid "Ready:" -msgstr "Prêt :" - diff --git a/utils/locales/fr/LC_MESSAGES/help.po b/utils/locales/fr/LC_MESSAGES/help.po deleted file mode 100644 index 232f3a3..0000000 --- a/utils/locales/fr/LC_MESSAGES/help.po +++ /dev/null @@ -1,43 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -msgid 'main_page.description' -msgstr "Lorsque vous utilisez les commandes, **<>** correspond à un argument **obligatoire** et **[]** à un argument **optionnel**.\n***(N'écrivez pas ces symboles!)***" - -msgid 'main_page.commands' -msgstr 'Commandes' - -msgid 'main_page.footer' -msgstr "- Envoyez {}help pour avoir plus d'aide sur la commande." - -msgid 'main_page.not_found' -msgstr 'Impossible de trouver la commande {}.' - -msgid 'command_help.subcommands' -msgstr 'Sous-commandes' - -msgid 'command_help.aliases' -msgstr 'Alias' - -msgid 'command_help.no_aliases' -msgstr 'Aucun alias' - -msgid 'command_help.params' -msgstr 'Parametres' - -msgid 'command_help.usage' -msgstr 'Utilisation' \ No newline at end of file diff --git a/utils/locales/fr/LC_MESSAGES/logs.po b/utils/locales/fr/LC_MESSAGES/logs.po deleted file mode 100644 index cdc6fcd..0000000 --- a/utils/locales/fr/LC_MESSAGES/logs.po +++ /dev/null @@ -1,17 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - diff --git a/utils/locales/fr/LC_MESSAGES/logs_help.po b/utils/locales/fr/LC_MESSAGES/logs_help.po deleted file mode 100644 index 12ddb52..0000000 --- a/utils/locales/fr/LC_MESSAGES/logs_help.po +++ /dev/null @@ -1,19 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -msgid "_uptime__short" -msgstr "Retourne depuis quand TuxBot est en ligne." \ No newline at end of file diff --git a/utils/locales/fr/LC_MESSAGES/poll.po b/utils/locales/fr/LC_MESSAGES/poll.po deleted file mode 100644 index ca29875..0000000 --- a/utils/locales/fr/LC_MESSAGES/poll.po +++ /dev/null @@ -1,20 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -#: launcher.py:51 -msgid "**Preparation...**" -msgstr "**Préparation...**" diff --git a/utils/locales/fr/LC_MESSAGES/poll_help.po b/utils/locales/fr/LC_MESSAGES/poll_help.po deleted file mode 100644 index f379622..0000000 --- a/utils/locales/fr/LC_MESSAGES/poll_help.po +++ /dev/null @@ -1,40 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -########################################################################### -########################## POLLS ##################################### -########################################################################### - -msgid '_poll' -msgstr "Affiche l'aide relative a la commande `sondage`" - -msgid '_poll__short' -msgstr "Afficher l'aide pour `sondage`" - -msgid '_poll__usage' -msgstr '[sous-commande]' - -########################################################################### - -msgid '_poll_create' -msgstr "Créez un sondage basé sur les réactions ! *(**BETA!** ajoutez `--anonyme` pour rendre les réponses anonymes)*" - -msgid '_poll_create__short' -msgstr 'Créer un sondage' - -msgid '_poll_create__usage' -msgstr ' | | | [réponse C] | [réponse D] | [...]' diff --git a/utils/locales/fr/LC_MESSAGES/useful.po b/utils/locales/fr/LC_MESSAGES/useful.po deleted file mode 100644 index 89525c0..0000000 --- a/utils/locales/fr/LC_MESSAGES/useful.po +++ /dev/null @@ -1,82 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -msgid "Information about TuxBot" -msgstr "Informations sur TuxBot" - -msgid "Latest changes" -msgstr "Derniers changements" - -msgid "Development" -msgstr "Développement" - -msgid "physical memory" -msgstr "mémoire physique" - -msgid "virtual memory" -msgstr "mémoire virtuelle" - -msgid "Servers count" -msgstr "Nombre de serveurs" - -msgid "Channels count" -msgstr "Nombre de salons" - -msgid "Members count" -msgstr "Nombre de membres" - -msgid "Links" -msgstr "Liens" - -msgid "Files" -msgstr "Fichiers" - -msgid "Lines" -msgstr "Lignes" - -msgid "Invite" -msgstr "Invitation" - -msgid "Contributors" -msgstr "Contributeurs" - -msgid "ipv6 not available" -msgstr "Erreur, cette adresse n'est pas disponible en IPv6." - -msgid "Information for" -msgstr "Informations pour" - -msgid "Belongs to :" -msgstr "Appartient à :" - -msgid "Is located at :" -msgstr "Se situe à :" - -msgid "info not available" -msgstr "Erreur, impossible d'obtenir des informations sur cette adresse IP" - -msgid "Headers of" -msgstr "Entêtes de" - -msgid "Cannot connect to host" -msgstr "Impossible de se connecter à l'hôte" - -msgid "git repo" -msgstr "Repos TuxBot-Bot" - -msgid "git text" -msgstr "Whoa tu veux voir mon repos Gitea pour me disséquer ? Pas de soucis ! Je suis un Bot, je ne ressens pas la douleur! \n https://git.gnous.eu/gnouseu/tuxbot-bot" diff --git a/utils/locales/fr/LC_MESSAGES/useful_help.po b/utils/locales/fr/LC_MESSAGES/useful_help.po deleted file mode 100644 index cdc6fcd..0000000 --- a/utils/locales/fr/LC_MESSAGES/useful_help.po +++ /dev/null @@ -1,17 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - diff --git a/utils/locales/fr/LC_MESSAGES/user.po b/utils/locales/fr/LC_MESSAGES/user.po deleted file mode 100644 index e69de29..0000000 diff --git a/utils/locales/fr/LC_MESSAGES/user_help.po b/utils/locales/fr/LC_MESSAGES/user_help.po deleted file mode 100644 index cdc6fcd..0000000 --- a/utils/locales/fr/LC_MESSAGES/user_help.po +++ /dev/null @@ -1,17 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - diff --git a/utils/locales/fr/LC_MESSAGES/utils.po b/utils/locales/fr/LC_MESSAGES/utils.po deleted file mode 100644 index 9a8d3e6..0000000 --- a/utils/locales/fr/LC_MESSAGES/utils.po +++ /dev/null @@ -1,22 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-09-08 19:04+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" - - -msgid "Unable to find the user..." -msgstr "Impossibe de trouver l'utilisateur..." - -msgid "Unable to find the message" -msgstr "Impossible de trouver le message" \ No newline at end of file diff --git a/utils/models/__init__.py b/utils/models/__init__.py deleted file mode 100644 index 36ba5cc..0000000 --- a/utils/models/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -import databases -import sqlalchemy -from utils.functions import Config - -conf_postgresql = Config('./configs/config.cfg')["postgresql"] -postgresql = 'postgresql://{}:{}@{}/{}'.format( - conf_postgresql.get("Username"), conf_postgresql.get("Password"), - conf_postgresql.get("Host"), conf_postgresql.get("DBName")) - -database = databases.Database(postgresql) -metadata = sqlalchemy.MetaData() - -engine = sqlalchemy.create_engine(str(database.url)) -metadata.create_all(engine) - -from .warn import WarnModel -from .poll import PollModel, ResponsesModel -from .alias import AliasesModel diff --git a/utils/models/alias.py b/utils/models/alias.py deleted file mode 100644 index 4a1a96c..0000000 --- a/utils/models/alias.py +++ /dev/null @@ -1,14 +0,0 @@ -import orm -from . import database, metadata - - -class AliasesModel(orm.Model): - __tablename__ = 'aliases' - __database__ = database - __metadata__ = metadata - - id = orm.Integer(primary_key=True) - user_id = orm.String(max_length=18) - alias = orm.String(max_length=255) - command = orm.String(max_length=255) - guild = orm.String(max_length=255) diff --git a/utils/models/poll.py b/utils/models/poll.py deleted file mode 100644 index a2f793e..0000000 --- a/utils/models/poll.py +++ /dev/null @@ -1,29 +0,0 @@ -import orm -from . import database, metadata - - -class ResponsesModel(orm.Model): - __tablename__ = 'responses' - __database__ = database - __metadata__ = metadata - - id = orm.Integer(primary_key=True) - user = orm.String(max_length=18) - - choice = orm.Integer() - - -class PollModel(orm.Model): - __tablename__ = 'polls' - __database__ = database - __metadata__ = metadata - - id = orm.Integer(primary_key=True) - channel_id = orm.String(max_length=18) - message_id = orm.String(max_length=18) - - content = orm.JSON() - is_anonymous = orm.Boolean() - - available_choices = orm.Integer() - choice = orm.ForeignKey(ResponsesModel) diff --git a/utils/models/warn.py b/utils/models/warn.py deleted file mode 100644 index 77f8833..0000000 --- a/utils/models/warn.py +++ /dev/null @@ -1,14 +0,0 @@ -import orm -from . import database, metadata - - -class WarnModel(orm.Model): - __tablename__ = 'warns' - __database__ = database - __metadata__ = metadata - - id = orm.Integer(primary_key=True) - server_id = orm.String(max_length=18) - user_id = orm.String(max_length=18) - reason = orm.String(max_length=255) - created_at = orm.DateTime()