Iplocalise update

This commit is contained in:
Mael G. 2020-04-27 11:21:56 -04:00
parent 083d14e056
commit 534a78e447
39 changed files with 4354 additions and 4258 deletions

23
.gitignore vendored
View File

@ -1,10 +1,13 @@
#Python
__pycache__/
*.pyc
.env
config.py
.DS_Store
private.py
#jetbrains
.idea/
#Python
__pycache__/
*.pyc
.env
config.py
.DS_Store
private.py
#jetbrains
.idea/
#Ipinfo key
ipinfoio.key

874
LICENSE
View File

@ -1,437 +1,437 @@
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.
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.

View File

@ -1,33 +1,35 @@
TuxBot, un bot discord écrit en Python.
Ici ce trouve le code source du bot provenant du serveur Discord [Aide GNU/Linux-Fr"](https://gnous.eu/discord "Rejoindre le serveur"), il à été créé spécialement pour ce discord, si vous souhaitez l'utiliser il vous faudra modifier ``params.json`` et ``cogs/utils/checks.py`` ;)
### Pré-requis
Il vous faut :
- Un ordinateur sous **GNU/Linux** avec une connexion à internet;
- Python3.7 ou + ;
- Installer ``requirements.txt`` (avec ``pip install -r requirements.txt`` par ex)
### Installation
Une fois les pré-requis installés et ``config.py.example`` renommé en ``config.py`` éditez le. Ensuite placez vous dans le repertoire de tuxbot et lancez ``bot.py`` avec python3 (ex: ``python3 bot.py``)
## Démarrage
Placez vous dans le repertoire de tuxbot et exécutez ``bot.py`` avec python3 (ex: ``python3 bot.py``)
## Fabriqué avec
* [PyCharm](https://www.jetbrains.com/pycharm/) - Editeur de texte payant :3
* [discord.py](https://github.com/Rapptz/discord.py) - API Python pour discord
## Versions
Liste des versions : [Cliquer pour afficher](https://github.com/outout14/tuxbot-bot/tags)
## Auteurs
* **Maël** _alias_ [@outout14](https://github.com/outout14)
* **Romain** _alias_ [Romain le malchanceux](https://github.com/Rom194)
## License
Ce projet est sous licence ``Creative Commons BY-NC-SA 4.0`` - voir le fichier [LICENSE.md](LICENSE.md) pour plus d'informations
TuxBot, un bot discord écrit en Python.
Ici ce trouve le code source du bot provenant du serveur Discord [Aide GNU/Linux-Fr"](https://gnous.eu/discord "Rejoindre le serveur"), il à été créé spécialement pour ce discord, si vous souhaitez l'utiliser il vous faudra modifier ``params.json`` et ``cogs/utils/checks.py`` ;)
### Pré-requis
Il vous faut :
- Un ordinateur sous **GNU/Linux** avec une connexion à internet;
- Python3.7 ou + ;
- Installer ``requirements.txt`` (avec ``pip install -r requirements.txt`` par ex)
### Installation
Une fois les pré-requis installés et ``config.py.example`` renommé en ``config.py`` éditez le. Ensuite placez vous dans le repertoire de tuxbot et lancez ``bot.py`` avec python3 (ex: ``python3 bot.py``)
Tuxbot recommande l'utilisation de l'API https://ipinfo.io/ pour des résultats plus précis. Placez votre token d'accès dans un fichier nommé ``ipinfoio.key`` à la racine du dossier du bot. Si vous ne souhaitez pas utiliser leur API le bot rollback automatiquement sur une librairie moins précise.
## Démarrage
Placez vous dans le repertoire de tuxbot et exécutez ``bot.py`` avec python3 (ex: ``python3 bot.py``)
## Fabriqué avec
* [PyCharm](https://www.jetbrains.com/pycharm/) - Editeur de texte payant :3
* [discord.py](https://github.com/Rapptz/discord.py) - API Python pour discord
## Versions
Liste des versions : [Cliquer pour afficher](https://github.com/outout14/tuxbot-bot/tags)
## Auteurs
* **Maël** _alias_ [@outout14](https://github.com/outout14)
* **Romain** _alias_ [Romain le malchanceux](https://github.com/Rom194)
## License
Ce projet est sous licence ``Creative Commons BY-NC-SA 4.0`` - voir le fichier [LICENSE.md](LICENSE.md) pour plus d'informations

View File

@ -1,146 +1,146 @@
#!/bin/bash
# This script is meant for easy install Tuxbot using curl/wget
printf "Welcome to Tuxbot's installation guide.\n"
printf "\nLog file is in ~/.tuxinstall.log\n"
# Command checking
if (( $EUID != 0 )); then
printf "\n\nError : Please run this script as ROOT"
exit 0
fi
if ! [ -x "$(command -v git)" ]; then
printf "\n\nError : Git is not installed"
exit 0
fi
if ! [ -x "$(command -v pip3.7)" ]; then
printf "\n\nError : pip3.7 is not installed (using pip3.7 command)\nPlease install it to continue"
exit 0
fi
# Tuxbot directory answer
read -p "In which directory Tuxbot should be installed ? : [/srv/]" na
na=${na:-"/srv/"}
# Cloning tuxbot USING GNOUS'S GIT MIRROR
printf "Cloning git repository, please wait... \n" &
git clone https://git.gnous.eu/gnouseu/tuxbot-bot $na/tuxbot-bot &> ~/.tuxinstall.log
sleep 1
printf "Tuxbot has been cloned to $na.\n" 5 50
sleep 1
printf "Installing pip modules, please wait...\n" 5 50 &
sleep 1
# Downloading PIP modules using pip3.7 cmd
pip3.7 install -U discord.py[voice] &> ~/.tuxinstall.log
cd $na/tuxbot-bot
pip3.7 install -r requirements.txt &> ~/.tuxinstall.log
sleep 1
printf "Tuxbot's python dependencies have been downloaded\n"
sleep 1
# Answers to generate config
function generateConfig {
DATE=`date +%Y-%m-%d`
read -p "Enter your Discord API Token : " cToken
read -p "Enter the bot client ID : " cID
read -p "Enter the log channel ID : " cLogID
read -p "Enter the main channel of your server : " cSrvID
read -p "What game tuxbot should display as playing (eg : 'Eat potatoes') : " cGame
read -p "What is you're discord user ID (for admin cmd) : " cAdmin
echo "------------"
read -p "MySQL's tuxbot user : " mSQLuser
read -p "MySQL's tuxbot password : " mSQLpass
read -p "MySQL's tuxbot database name : " mSQLdb
echo """
#Generated by Tuxbot install script
#$DATE
token = \"$cToken\"
client_id = \"$cID\"
log_channel_id = \"$cLogID\"
main_server_id = \"$cSrvID\"
game = \"$cGame\"
authorized_id = [\"$cAdmin\"]
prefix = [\".\"]
description = '.'
mysql = {
\"host\": \"localhost\",
\"username\": \"$mSQLuser\",
\"password\": \"$mSQLpass\",
\"dbname\": \"$mSQLdb\"
}
fonts = {
\"normal\": \"NotoSansCJK-Regular.ttc\",
\"bold\": \"NotoSansCJK-Bold.ttc\"
}
""" &> $na/tuxbot-bot/config.py
}
printf "Do you want to generate config file ?\n1 - Yes (selected)\n2 - No\n"
read -p "(1-2) : " initConf
initConf=${initConf:-"1"}
case $initConf in
1) generateConfig;;
esac
#Non login user
echo "Adding tuxbot non-login user..."
useradd -M tuxbot
sleep 1
#Chown all perms to the non login user
echo "Fixing permissions..."
chown tuxbot:tuxbot -R $na/tuxbot-bot/
sleep 1
#Create the service file
echo "Adding Tuxbot service & start it..."
echo """[Unit]
Description=Tuxbot, a discord bot
#After=network.target
[Service]
Type=simple
User=tuxbot
Restart=on-failure
Restart=always
RestartSec=1
WorkingDirectory=$na/tuxbot-bot/
ExecStart=/usr/bin/env python3.7 $na/tuxbot-bot/bot.py
StandardOutput=file:/var/log/tuxbot.log
[Install]
WantedBy=multi-user.target
""" &> /lib/systemd/system/tuxbot.service
systemctl daemon-reload
systemctl start tuxbot
sleep 1
echo "Activation of tuxbot at startup..."
sleep 1
systemctl enable tuxbot
#End message
echo """
Tuxbot should be correctly installed.
Please check if all is good by execute :
systemctl status tuxbot
And .ping command in discord.
Configuration file is $na/tuxbot-bot/config.py
Main tuxbot directory is $na/tuxbot-bot/
Any question ? => Make an issue on github
https://git.gnous.eu/gnouseu/tuxbot-bot
https://github.com/outout14/tuxbot-bot
"""
#!/bin/bash
# This script is meant for easy install Tuxbot using curl/wget
printf "Welcome to Tuxbot's installation guide.\n"
printf "\nLog file is in ~/.tuxinstall.log\n"
# Command checking
if (( $EUID != 0 )); then
printf "\n\nError : Please run this script as ROOT"
exit 0
fi
if ! [ -x "$(command -v git)" ]; then
printf "\n\nError : Git is not installed"
exit 0
fi
if ! [ -x "$(command -v pip3.7)" ]; then
printf "\n\nError : pip3.7 is not installed (using pip3.7 command)\nPlease install it to continue"
exit 0
fi
# Tuxbot directory answer
read -p "In which directory Tuxbot should be installed ? : [/srv/]" na
na=${na:-"/srv/"}
# Cloning tuxbot USING GNOUS'S GIT MIRROR
printf "Cloning git repository, please wait... \n" &
git clone https://git.gnous.eu/gnouseu/tuxbot-bot $na/tuxbot-bot &> ~/.tuxinstall.log
sleep 1
printf "Tuxbot has been cloned to $na.\n" 5 50
sleep 1
printf "Installing pip modules, please wait...\n" 5 50 &
sleep 1
# Downloading PIP modules using pip3.7 cmd
pip3.7 install -U discord.py[voice] &> ~/.tuxinstall.log
cd $na/tuxbot-bot
pip3.7 install -r requirements.txt &> ~/.tuxinstall.log
sleep 1
printf "Tuxbot's python dependencies have been downloaded\n"
sleep 1
# Answers to generate config
function generateConfig {
DATE=`date +%Y-%m-%d`
read -p "Enter your Discord API Token : " cToken
read -p "Enter the bot client ID : " cID
read -p "Enter the log channel ID : " cLogID
read -p "Enter the main channel of your server : " cSrvID
read -p "What game tuxbot should display as playing (eg : 'Eat potatoes') : " cGame
read -p "What is you're discord user ID (for admin cmd) : " cAdmin
echo "------------"
read -p "MySQL's tuxbot user : " mSQLuser
read -p "MySQL's tuxbot password : " mSQLpass
read -p "MySQL's tuxbot database name : " mSQLdb
echo """
#Generated by Tuxbot install script
#$DATE
token = \"$cToken\"
client_id = \"$cID\"
log_channel_id = \"$cLogID\"
main_server_id = \"$cSrvID\"
game = \"$cGame\"
authorized_id = [\"$cAdmin\"]
prefix = [\".\"]
description = '.'
mysql = {
\"host\": \"localhost\",
\"username\": \"$mSQLuser\",
\"password\": \"$mSQLpass\",
\"dbname\": \"$mSQLdb\"
}
fonts = {
\"normal\": \"NotoSansCJK-Regular.ttc\",
\"bold\": \"NotoSansCJK-Bold.ttc\"
}
""" &> $na/tuxbot-bot/config.py
}
printf "Do you want to generate config file ?\n1 - Yes (selected)\n2 - No\n"
read -p "(1-2) : " initConf
initConf=${initConf:-"1"}
case $initConf in
1) generateConfig;;
esac
#Non login user
echo "Adding tuxbot non-login user..."
useradd -M tuxbot
sleep 1
#Chown all perms to the non login user
echo "Fixing permissions..."
chown tuxbot:tuxbot -R $na/tuxbot-bot/
sleep 1
#Create the service file
echo "Adding Tuxbot service & start it..."
echo """[Unit]
Description=Tuxbot, a discord bot
#After=network.target
[Service]
Type=simple
User=tuxbot
Restart=on-failure
Restart=always
RestartSec=1
WorkingDirectory=$na/tuxbot-bot/
ExecStart=/usr/bin/env python3.7 $na/tuxbot-bot/bot.py
StandardOutput=file:/var/log/tuxbot.log
[Install]
WantedBy=multi-user.target
""" &> /lib/systemd/system/tuxbot.service
systemctl daemon-reload
systemctl start tuxbot
sleep 1
echo "Activation of tuxbot at startup..."
sleep 1
systemctl enable tuxbot
#End message
echo """
Tuxbot should be correctly installed.
Please check if all is good by execute :
systemctl status tuxbot
And .ping command in discord.
Configuration file is $na/tuxbot-bot/config.py
Main tuxbot directory is $na/tuxbot-bot/
Any question ? => Make an issue on github
https://git.gnous.eu/gnouseu/tuxbot-bot
https://github.com/outout14/tuxbot-bot
"""

282
bot.py
View File

@ -1,141 +1,141 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = "Maël / Outout | Romain"
__licence__ = "WTFPL Licence 2.0"
import copy
import datetime
import os
import sys
import traceback
import aiohttp
import discord
from discord.ext import commands
import cogs.utils.cli_colors as colors
import config
from cogs.utils import checks
if sys.version_info[1] < 7 or sys.version_info[0] < 3:
print(f"{colors.text_colors.RED}[ERROR] Python 3.7 or + is required.{colors.ENDC}")
exit()
l_extensions = (
'cogs.admin',
'cogs.afk',
'cogs.atc',
'cogs.basics',
'cogs.ci',
'cogs.filter_messages',
'cogs.funs',
'cogs.role',
'cogs.search',
'cogs.send_logs',
'cogs.sondage',
'cogs.utility',
'cogs.vocal',
'cogs.private',
'cogs.monitoring',
'jishaku'
)
help_attrs = dict(hidden=True, in_help=True, name="DONOTUSE")
class TuxBot(commands.Bot):
def __init__(self):
self.uptime = datetime.datetime.utcnow()
self.config = config
super().__init__(command_prefix=self.config.prefix[0],
description=self.config.description,
pm_help=None,
help_command=None)
self.client_id = self.config.client_id
self.session = aiohttp.ClientSession(loop=self.loop)
self._events = []
self.add_command(self.do)
for extension in l_extensions:
try:
self.load_extension(extension)
print(f"{colors.text_colors.GREEN}\"{extension}\""
f" chargé !{colors.ENDC}")
except Exception as e:
print(f"{colors.text_colors.RED}"
f"Impossible de charger l'extension {extension}\n"
f"{type(e).__name__}: {e}{colors.ENDC}", file=sys.stderr)
async def is_owner(self, user: discord.User):
return str(user.id) in config.authorized_id
async def on_command_error(self, ctx, error):
if isinstance(error, commands.NoPrivateMessage):
await ctx.author.send('Cette commande ne peut pas être utilisee '
'en message privee.')
elif isinstance(error, commands.DisabledCommand):
await ctx.author.send('Desoler mais cette commande est desactive, '
'elle ne peut donc pas être utilisée.')
elif isinstance(error, commands.CommandInvokeError):
print(f'In {ctx.command.qualified_name}:', file=sys.stderr)
traceback.print_tb(error.original.__traceback__)
print(f'{error.original.__class__.__name__}: {error.original}',
file=sys.stderr)
async def on_ready(self):
log_channel_id = await self.fetch_channel(self.config.log_channel_id)
print('\n\n---------------------')
print('CONNECTÉ :')
print(f'Nom d\'utilisateur: {self.user} {colors.text_style.DIM}'
f'(ID: {self.user.id}){colors.ENDC}')
print(f'Channel de log: {log_channel_id} {colors.text_style.DIM}'
f'(ID: {log_channel_id.id}){colors.ENDC}')
print(f'Prefix: {self.config.prefix[0]}')
print('Merci d\'utiliser TuxBot')
print('---------------------\n\n')
await self.change_presence(status=discord.Status.dnd,
activity=discord.Game(
name=self.config.game)
)
@staticmethod
async def on_resumed():
print('resumed...')
async def on_message(self, message):
if message.author.bot:
return
try:
await self.process_commands(message)
except Exception as e:
print(f'{colors.text_colors.RED}Erreur rencontré : \n'
f' {type(e).__name__}: {e}{colors.ENDC} \n \n')
def run(self):
super().run(self.config.token, reconnect=True)
@checks.has_permissions(administrator=True)
@commands.command(pass_context=True, hidden=True)
async def do(self, ctx, times: int, *, command):
"""Repeats a command a specified number of times."""
msg = copy.copy(ctx.message)
msg.content = command
for i in range(times):
await self.process_commands(msg)
if __name__ == '__main__':
if os.path.exists('config.py') is not True:
print(f"{colors.text_colors.RED}"
f"Veuillez créer le fichier config.py{colors.ENDC}")
exit()
tuxbot = TuxBot()
tuxbot.run()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = "Maël / Outout | Romain"
__licence__ = "WTFPL Licence 2.0"
import copy
import datetime
import os
import sys
import traceback
import aiohttp
import discord
from discord.ext import commands
import cogs.utils.cli_colors as colors
import config
from cogs.utils import checks
if sys.version_info[1] < 7 or sys.version_info[0] < 3:
print(f"{colors.text_colors.RED}[ERROR] Python 3.7 or + is required.{colors.ENDC}")
exit()
l_extensions = (
'cogs.admin',
'cogs.afk',
'cogs.atc',
'cogs.basics',
'cogs.ci',
'cogs.filter_messages',
'cogs.funs',
'cogs.role',
'cogs.search',
'cogs.send_logs',
'cogs.sondage',
'cogs.utility',
'cogs.vocal',
'cogs.private',
'cogs.monitoring',
'jishaku'
)
help_attrs = dict(hidden=True, in_help=True, name="DONOTUSE")
class TuxBot(commands.Bot):
def __init__(self):
self.uptime = datetime.datetime.utcnow()
self.config = config
super().__init__(command_prefix=self.config.prefix[0],
description=self.config.description,
pm_help=None,
help_command=None)
self.client_id = self.config.client_id
self.session = aiohttp.ClientSession(loop=self.loop)
self._events = []
self.add_command(self.do)
for extension in l_extensions:
try:
self.load_extension(extension)
print(f"{colors.text_colors.GREEN}\"{extension}\""
f" chargé !{colors.ENDC}")
except Exception as e:
print(f"{colors.text_colors.RED}"
f"Impossible de charger l'extension {extension}\n"
f"{type(e).__name__}: {e}{colors.ENDC}", file=sys.stderr)
async def is_owner(self, user: discord.User):
return str(user.id) in config.authorized_id
async def on_command_error(self, ctx, error):
if isinstance(error, commands.NoPrivateMessage):
await ctx.author.send('Cette commande ne peut pas être utilisee '
'en message privee.')
elif isinstance(error, commands.DisabledCommand):
await ctx.author.send('Desoler mais cette commande est desactive, '
'elle ne peut donc pas être utilisée.')
elif isinstance(error, commands.CommandInvokeError):
print(f'In {ctx.command.qualified_name}:', file=sys.stderr)
traceback.print_tb(error.original.__traceback__)
print(f'{error.original.__class__.__name__}: {error.original}',
file=sys.stderr)
async def on_ready(self):
log_channel_id = await self.fetch_channel(self.config.log_channel_id)
print('\n\n---------------------')
print('CONNECTÉ :')
print(f'Nom d\'utilisateur: {self.user} {colors.text_style.DIM}'
f'(ID: {self.user.id}){colors.ENDC}')
print(f'Channel de log: {log_channel_id} {colors.text_style.DIM}'
f'(ID: {log_channel_id.id}){colors.ENDC}')
print(f'Prefix: {self.config.prefix[0]}')
print('Merci d\'utiliser TuxBot')
print('---------------------\n\n')
await self.change_presence(status=discord.Status.dnd,
activity=discord.Game(
name=self.config.game)
)
@staticmethod
async def on_resumed():
print('resumed...')
async def on_message(self, message):
if message.author.bot:
return
try:
await self.process_commands(message)
except Exception as e:
print(f'{colors.text_colors.RED}Erreur rencontré : \n'
f' {type(e).__name__}: {e}{colors.ENDC} \n \n')
def run(self):
super().run(self.config.token, reconnect=True)
@checks.has_permissions(administrator=True)
@commands.command(pass_context=True, hidden=True)
async def do(self, ctx, times: int, *, command):
"""Repeats a command a specified number of times."""
msg = copy.copy(ctx.message)
msg.content = command
for i in range(times):
await self.process_commands(msg)
if __name__ == '__main__':
if os.path.exists('config.py') is not True:
print(f"{colors.text_colors.RED}"
f"Veuillez créer le fichier config.py{colors.ENDC}")
exit()
tuxbot = TuxBot()
tuxbot.run()

View File

@ -1,407 +1,407 @@
import aiohttp
from discord.ext import commands
import discord
from .utils import checks
from .utils.checks import get_user
class Admin(commands.Cog):
"""Commandes secrètes d'administration."""
def __init__(self, bot):
self.bot = bot
self._last_result = None
self.sessions = set()
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name="upload", pass_context=True)
async def _upload(self, ctx, *, url=""):
if len(ctx.message.attachments) >= 1:
file = ctx.message.attachments[0].url
elif url != "":
file = url
else:
em = discord.Embed(title='Une erreur est survenue',
description="Fichier introuvable.",
colour=0xDC3546)
await ctx.send(embed=em)
return
async with aiohttp.ClientSession() as session:
async with session.get(file) as r:
image = await r.content.read()
with open(f"data/tmp/{str(ctx.author.id)}.png", 'wb') as f:
f.write(image)
f.close()
await ctx.send(file=discord.File(f"data/tmp/{str(ctx.author.id)}.png"))
@checks.has_permissions(administrator=True)
@commands.command(name="ban", pass_context=True)
async def _ban(self, ctx, user, *, reason=""):
"""Ban user"""
user = get_user(ctx.message, user)
if user and str(user.id) not in self.bot.config.unkickable_id:
try:
await user.ban(reason=reason)
return_msg = f"`{user.mention}` a été banni\n"
if reason:
return_msg += f"raison : `{reason}`"
return_msg += "."
await ctx.send(return_msg)
except discord.Forbidden:
await ctx.send('Impossible de bannir cet user,'
' probleme de permission.')
else:
return await ctx.send('Impossible de trouver l\'user.')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name="kick", pass_context=True)
async def _kick(self, ctx, user, *, reason=""):
"""Kick a user"""
user = get_user(ctx.message, user)
if user and str(user.id) not in self.bot.config.unkickable_id:
try:
await user.kick(reason=reason)
return_msg = f"`{user.mention}` a été kické\n"
if reason:
return_msg += f"raison : `{reason}`"
return_msg += "."
await ctx.send(return_msg)
except discord.Forbidden:
await ctx.send('Impossible de kicker cet user,'
' probleme de permission.')
else:
return await ctx.send('Impossible de trouver l\'user.')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='clear', pass_context=True)
async def _clear(self, ctx, number: int, silent: str = True):
"""Clear <number> of message(s)"""
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
if number < 1000:
try:
await ctx.message.channel.purge(limit=number)
except Exception as e: # TODO : A virer dans l'event on_error
if silent is not True:
await ctx.send(f':sob: Une erreur est survenue : \n'
f' {type(e).__name__}: {e}')
if silent is not True:
await ctx.send("Hop voila j'ai viré des messages! Hello World")
print(f"{str(number)} messages ont été supprimés")
else:
await ctx.send('Trop de messages, entre un nombre < 1000')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='say', pass_context=True)
async def _say(self, ctx, *, tosay: str):
"""Say a message in the current channel"""
try:
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
await ctx.send(tosay)
except Exception as e: # TODO : A virer dans l'event on_error
await ctx.send(f':sob: Une erreur est survenue : \n'
f' {type(e).__name__}: {e}')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='sayto', pass_context=True)
async def _sayto(self, ctx, channel_id: int, *, tosay: str):
"""Say a message in the <channel_id> channel"""
try:
chan = self.bot.get_channel(channel_id)
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
try:
await chan.send(tosay)
except Exception:
print(f"Impossible d'envoyer le message dans {str(channel_id)}")
except Exception as e: # TODO : A virer dans l'event on_error
await ctx.send(f':sob: Une erreur est survenue : \n'
f' {type(e).__name__}: {e}')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='sayto_dm', pass_context=True)
async def _sayto_dm(self, ctx, user_id: int, *, tosay: str):
"""Say a message to the <user_id> user"""
try:
user = self.bot.get_user(user_id)
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
try:
await user.send(tosay)
except Exception:
print(f"Impossible d'envoyer le message dans {str(user_id)}")
except Exception as e: # TODO : A virer dans l'event on_error
await ctx.send(f':sob: Une erreur est survenue : \n'
f' {type(e).__name__}: {e}')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='editsay', pass_context=True)
async def _editsay(self, ctx, message_id: int, *, new_content: str):
"""Edit a bot's message"""
try:
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
toedit = await ctx.channel.fetch_message(message_id)
except discord.errors.NotFound:
await ctx.send(f"Impossible de trouver le message avec l'id "
f"`{message_id}` sur ce salon")
return
try:
await toedit.edit(content=str(new_content))
except discord.errors.Forbidden:
await ctx.send("J'ai po les perms pour editer mes messages :(")
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='addreaction', pass_context=True)
async def _addreaction(self, ctx, message_id: int, reaction: str):
"""Add reactions to a message"""
try:
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
toadd = await ctx.channel.fetch_message(message_id)
except discord.errors.NotFound:
await ctx.send(f"Impossible de trouver le message avec l'id "
f"`{message_id}` sur ce salon")
return
try:
await toadd.add_reaction(reaction)
except discord.errors.Forbidden:
await ctx.send("J'ai po les perms pour ajouter des réactions :(")
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='delete', pass_context=True)
async def _delete(self, ctx, message_id: int):
"""Delete message in current channel"""
try:
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
todelete = await ctx.channel.fetch_message(message_id)
except discord.errors.NotFound:
await ctx.send(f"Impossible de trouver le message avec l'id "
f"`{message_id}` sur ce salon")
return
try:
await todelete.delete()
except discord.errors.Forbidden:
await ctx.send("J'ai po les perms pour supprimer des messages :(")
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='deletefrom', pass_context=True)
async def _deletefrom(self, ctx, chan_id: int, *, message_id: int):
"""Delete message in <chan_id> channel"""
try:
chan = self.bot.get_channel(chan_id)
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
todelete = await chan.fetch_message(message_id)
except discord.errors.NotFound:
await ctx.send(f"Impossible de trouver le message avec l'id "
f"`{id}` sur le salon")
return
try:
await todelete.delete()
except discord.errors.Forbidden:
await ctx.send("J'ai po les perms pour supprimer le message :(")
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='embed', pass_context=True)
async def _embed(self, ctx, *, msg: str = "help"):
"""Send an embed"""
if msg != "help":
ptext = title \
= description \
= image \
= thumbnail \
= color \
= footer \
= author \
= None
timestamp = discord.Embed.Empty
embed_values = msg.split('|')
for i in embed_values:
if i.strip().lower().startswith('ptext='):
ptext = i.strip()[6:].strip()
elif i.strip().lower().startswith('title='):
title = i.strip()[6:].strip()
elif i.strip().lower().startswith('description='):
description = i.strip()[12:].strip()
elif i.strip().lower().startswith('desc='):
description = i.strip()[5:].strip()
elif i.strip().lower().startswith('image='):
image = i.strip()[6:].strip()
elif i.strip().lower().startswith('thumbnail='):
thumbnail = i.strip()[10:].strip()
elif i.strip().lower().startswith('colour='):
color = i.strip()[7:].strip()
elif i.strip().lower().startswith('color='):
color = i.strip()[6:].strip()
elif i.strip().lower().startswith('footer='):
footer = i.strip()[7:].strip()
elif i.strip().lower().startswith('author='):
author = i.strip()[7:].strip()
elif i.strip().lower().startswith('timestamp'):
timestamp = ctx.message.created_at
else:
if description is None and not i.strip()\
.lower().startswith('field='):
description = i.strip()
if color:
if color.startswith('#'):
color = color[1:]
if not color.startswith('0x'):
color = '0x' + color
if ptext \
is title \
is description \
is image \
is thumbnail \
is color \
is footer \
is author \
is None \
and 'field=' not in msg:
try:
await ctx.message.delete()
except Exception:
print("Impossible de supprimer le message \"" + str(
ctx.message.content) + "\"")
return await ctx.send(content=None,
embed=discord.Embed(description=msg))
if color:
em = discord.Embed(timestamp=timestamp, title=title,
description=description,
color=int(color, 16))
else:
em = discord.Embed(timestamp=timestamp, title=title,
description=description)
for i in embed_values:
if i.strip().lower().startswith('field='):
field_inline = True
field = i.strip().lstrip('field=')
field_name, field_value = field.split('value=')
if 'inline=' in field_value:
field_value, field_inline = field_value.split(
'inline=')
if 'false' in field_inline.lower() \
or 'no' in field_inline.lower():
field_inline = False
field_name = field_name.strip().lstrip('name=')
em.add_field(name=field_name, value=field_value.strip(),
inline=field_inline)
if author:
if 'icon=' in author:
text, icon = author.split('icon=')
if 'url=' in icon:
em.set_author(name=text.strip()[5:],
icon_url=icon.split('url=')[0].strip(),
url=icon.split('url=')[1].strip())
else:
em.set_author(name=text.strip()[5:], icon_url=icon)
else:
if 'url=' in author:
em.set_author(name=author.split('url=')[0].strip()[5:],
url=author.split('url=')[1].strip())
else:
em.set_author(name=author)
if image:
em.set_image(url=image)
if thumbnail:
em.set_thumbnail(url=thumbnail)
if footer:
if 'icon=' in footer:
text, icon = footer.split('icon=')
em.set_footer(text=text.strip()[5:], icon_url=icon)
else:
em.set_footer(text=footer)
try:
await ctx.message.delete()
except Exception:
print("Impossible de supprimer le message \"" + str(
ctx.message.content) + "\"")
await ctx.send(content=ptext, embed=em)
else:
embed = discord.Embed(
title="Aide sur l'utilisation de la commande .embed:")
embed.add_field(name="Titre:", value="title=<le titre>",
inline=True)
embed.add_field(name="Description:",
value="description=<la description>", inline=True)
embed.add_field(name="Couleur:", value="color=<couleur en hexa>",
inline=True)
embed.add_field(name="Image:",
value="image=<url de l'image (en https)>",
inline=True)
embed.add_field(name="Thumbnail:",
value="thumbnail=<url de l'image>", inline=True)
embed.add_field(name="Auteur:", value="author=<nom de l'auteur>",
inline=True)
embed.add_field(name="Icon", value="icon=<url de l'image>",
inline=True)
embed.add_field(name="Footer", value="footer=<le footer>",
inline=True)
embed.set_footer(text="Exemple: .embed title=Un titre |"
" description=Une description |"
" color=3AB35E |"
" field=name=test value=test")
await ctx.send(embed=embed)
def setup(bot):
bot.add_cog(Admin(bot))
import aiohttp
from discord.ext import commands
import discord
from .utils import checks
from .utils.checks import get_user
class Admin(commands.Cog):
"""Commandes secrètes d'administration."""
def __init__(self, bot):
self.bot = bot
self._last_result = None
self.sessions = set()
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name="upload", pass_context=True)
async def _upload(self, ctx, *, url=""):
if len(ctx.message.attachments) >= 1:
file = ctx.message.attachments[0].url
elif url != "":
file = url
else:
em = discord.Embed(title='Une erreur est survenue',
description="Fichier introuvable.",
colour=0xDC3546)
await ctx.send(embed=em)
return
async with aiohttp.ClientSession() as session:
async with session.get(file) as r:
image = await r.content.read()
with open(f"data/tmp/{str(ctx.author.id)}.png", 'wb') as f:
f.write(image)
f.close()
await ctx.send(file=discord.File(f"data/tmp/{str(ctx.author.id)}.png"))
@checks.has_permissions(administrator=True)
@commands.command(name="ban", pass_context=True)
async def _ban(self, ctx, user, *, reason=""):
"""Ban user"""
user = get_user(ctx.message, user)
if user and str(user.id) not in self.bot.config.unkickable_id:
try:
await user.ban(reason=reason)
return_msg = f"`{user.mention}` a été banni\n"
if reason:
return_msg += f"raison : `{reason}`"
return_msg += "."
await ctx.send(return_msg)
except discord.Forbidden:
await ctx.send('Impossible de bannir cet user,'
' probleme de permission.')
else:
return await ctx.send('Impossible de trouver l\'user.')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name="kick", pass_context=True)
async def _kick(self, ctx, user, *, reason=""):
"""Kick a user"""
user = get_user(ctx.message, user)
if user and str(user.id) not in self.bot.config.unkickable_id:
try:
await user.kick(reason=reason)
return_msg = f"`{user.mention}` a été kické\n"
if reason:
return_msg += f"raison : `{reason}`"
return_msg += "."
await ctx.send(return_msg)
except discord.Forbidden:
await ctx.send('Impossible de kicker cet user,'
' probleme de permission.')
else:
return await ctx.send('Impossible de trouver l\'user.')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='clear', pass_context=True)
async def _clear(self, ctx, number: int, silent: str = True):
"""Clear <number> of message(s)"""
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
if number < 1000:
try:
await ctx.message.channel.purge(limit=number)
except Exception as e: # TODO : A virer dans l'event on_error
if silent is not True:
await ctx.send(f':sob: Une erreur est survenue : \n'
f' {type(e).__name__}: {e}')
if silent is not True:
await ctx.send("Hop voila j'ai viré des messages! Hello World")
print(f"{str(number)} messages ont été supprimés")
else:
await ctx.send('Trop de messages, entre un nombre < 1000')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='say', pass_context=True)
async def _say(self, ctx, *, tosay: str):
"""Say a message in the current channel"""
try:
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
await ctx.send(tosay)
except Exception as e: # TODO : A virer dans l'event on_error
await ctx.send(f':sob: Une erreur est survenue : \n'
f' {type(e).__name__}: {e}')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='sayto', pass_context=True)
async def _sayto(self, ctx, channel_id: int, *, tosay: str):
"""Say a message in the <channel_id> channel"""
try:
chan = self.bot.get_channel(channel_id)
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
try:
await chan.send(tosay)
except Exception:
print(f"Impossible d'envoyer le message dans {str(channel_id)}")
except Exception as e: # TODO : A virer dans l'event on_error
await ctx.send(f':sob: Une erreur est survenue : \n'
f' {type(e).__name__}: {e}')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='sayto_dm', pass_context=True)
async def _sayto_dm(self, ctx, user_id: int, *, tosay: str):
"""Say a message to the <user_id> user"""
try:
user = self.bot.get_user(user_id)
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
try:
await user.send(tosay)
except Exception:
print(f"Impossible d'envoyer le message dans {str(user_id)}")
except Exception as e: # TODO : A virer dans l'event on_error
await ctx.send(f':sob: Une erreur est survenue : \n'
f' {type(e).__name__}: {e}')
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='editsay', pass_context=True)
async def _editsay(self, ctx, message_id: int, *, new_content: str):
"""Edit a bot's message"""
try:
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
toedit = await ctx.channel.fetch_message(message_id)
except discord.errors.NotFound:
await ctx.send(f"Impossible de trouver le message avec l'id "
f"`{message_id}` sur ce salon")
return
try:
await toedit.edit(content=str(new_content))
except discord.errors.Forbidden:
await ctx.send("J'ai po les perms pour editer mes messages :(")
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='addreaction', pass_context=True)
async def _addreaction(self, ctx, message_id: int, reaction: str):
"""Add reactions to a message"""
try:
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
toadd = await ctx.channel.fetch_message(message_id)
except discord.errors.NotFound:
await ctx.send(f"Impossible de trouver le message avec l'id "
f"`{message_id}` sur ce salon")
return
try:
await toadd.add_reaction(reaction)
except discord.errors.Forbidden:
await ctx.send("J'ai po les perms pour ajouter des réactions :(")
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='delete', pass_context=True)
async def _delete(self, ctx, message_id: int):
"""Delete message in current channel"""
try:
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
todelete = await ctx.channel.fetch_message(message_id)
except discord.errors.NotFound:
await ctx.send(f"Impossible de trouver le message avec l'id "
f"`{message_id}` sur ce salon")
return
try:
await todelete.delete()
except discord.errors.Forbidden:
await ctx.send("J'ai po les perms pour supprimer des messages :(")
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='deletefrom', pass_context=True)
async def _deletefrom(self, ctx, chan_id: int, *, message_id: int):
"""Delete message in <chan_id> channel"""
try:
chan = self.bot.get_channel(chan_id)
try:
await ctx.message.delete()
except Exception:
print(f"Impossible de supprimer le message "
f"\"{str(ctx.message.content)}\"")
todelete = await chan.fetch_message(message_id)
except discord.errors.NotFound:
await ctx.send(f"Impossible de trouver le message avec l'id "
f"`{id}` sur le salon")
return
try:
await todelete.delete()
except discord.errors.Forbidden:
await ctx.send("J'ai po les perms pour supprimer le message :(")
"""---------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@commands.command(name='embed', pass_context=True)
async def _embed(self, ctx, *, msg: str = "help"):
"""Send an embed"""
if msg != "help":
ptext = title \
= description \
= image \
= thumbnail \
= color \
= footer \
= author \
= None
timestamp = discord.Embed.Empty
embed_values = msg.split('|')
for i in embed_values:
if i.strip().lower().startswith('ptext='):
ptext = i.strip()[6:].strip()
elif i.strip().lower().startswith('title='):
title = i.strip()[6:].strip()
elif i.strip().lower().startswith('description='):
description = i.strip()[12:].strip()
elif i.strip().lower().startswith('desc='):
description = i.strip()[5:].strip()
elif i.strip().lower().startswith('image='):
image = i.strip()[6:].strip()
elif i.strip().lower().startswith('thumbnail='):
thumbnail = i.strip()[10:].strip()
elif i.strip().lower().startswith('colour='):
color = i.strip()[7:].strip()
elif i.strip().lower().startswith('color='):
color = i.strip()[6:].strip()
elif i.strip().lower().startswith('footer='):
footer = i.strip()[7:].strip()
elif i.strip().lower().startswith('author='):
author = i.strip()[7:].strip()
elif i.strip().lower().startswith('timestamp'):
timestamp = ctx.message.created_at
else:
if description is None and not i.strip()\
.lower().startswith('field='):
description = i.strip()
if color:
if color.startswith('#'):
color = color[1:]
if not color.startswith('0x'):
color = '0x' + color
if ptext \
is title \
is description \
is image \
is thumbnail \
is color \
is footer \
is author \
is None \
and 'field=' not in msg:
try:
await ctx.message.delete()
except Exception:
print("Impossible de supprimer le message \"" + str(
ctx.message.content) + "\"")
return await ctx.send(content=None,
embed=discord.Embed(description=msg))
if color:
em = discord.Embed(timestamp=timestamp, title=title,
description=description,
color=int(color, 16))
else:
em = discord.Embed(timestamp=timestamp, title=title,
description=description)
for i in embed_values:
if i.strip().lower().startswith('field='):
field_inline = True
field = i.strip().lstrip('field=')
field_name, field_value = field.split('value=')
if 'inline=' in field_value:
field_value, field_inline = field_value.split(
'inline=')
if 'false' in field_inline.lower() \
or 'no' in field_inline.lower():
field_inline = False
field_name = field_name.strip().lstrip('name=')
em.add_field(name=field_name, value=field_value.strip(),
inline=field_inline)
if author:
if 'icon=' in author:
text, icon = author.split('icon=')
if 'url=' in icon:
em.set_author(name=text.strip()[5:],
icon_url=icon.split('url=')[0].strip(),
url=icon.split('url=')[1].strip())
else:
em.set_author(name=text.strip()[5:], icon_url=icon)
else:
if 'url=' in author:
em.set_author(name=author.split('url=')[0].strip()[5:],
url=author.split('url=')[1].strip())
else:
em.set_author(name=author)
if image:
em.set_image(url=image)
if thumbnail:
em.set_thumbnail(url=thumbnail)
if footer:
if 'icon=' in footer:
text, icon = footer.split('icon=')
em.set_footer(text=text.strip()[5:], icon_url=icon)
else:
em.set_footer(text=footer)
try:
await ctx.message.delete()
except Exception:
print("Impossible de supprimer le message \"" + str(
ctx.message.content) + "\"")
await ctx.send(content=ptext, embed=em)
else:
embed = discord.Embed(
title="Aide sur l'utilisation de la commande .embed:")
embed.add_field(name="Titre:", value="title=<le titre>",
inline=True)
embed.add_field(name="Description:",
value="description=<la description>", inline=True)
embed.add_field(name="Couleur:", value="color=<couleur en hexa>",
inline=True)
embed.add_field(name="Image:",
value="image=<url de l'image (en https)>",
inline=True)
embed.add_field(name="Thumbnail:",
value="thumbnail=<url de l'image>", inline=True)
embed.add_field(name="Auteur:", value="author=<nom de l'auteur>",
inline=True)
embed.add_field(name="Icon", value="icon=<url de l'image>",
inline=True)
embed.add_field(name="Footer", value="footer=<le footer>",
inline=True)
embed.set_footer(text="Exemple: .embed title=Un titre |"
" description=Une description |"
" color=3AB35E |"
" field=name=test value=test")
await ctx.send(embed=embed)
def setup(bot):
bot.add_cog(Admin(bot))

View File

@ -1,68 +1,68 @@
from discord.ext import commands
import discord
import random
class AFK(commands.Cog):
"""Commandes utilitaires."""
def __init__(self, bot):
self.bot = bot
self.afk_users = []
"""---------------------------------------------------------------------"""
@commands.command(pass_context=True)
async def afk(self, ctx, action: str = ""):
if action.lower() == "list":
try:
await ctx.send(*self.afk_users)
except discord.HTTPException:
await ctx.send("Il n'y a personne d'afk...")
else:
user = ctx.author
self.afk_users.append(user)
msgs = ["s'absente de discord quelques instants",
"se casse de son pc",
"va sortir son chien",
"reviens bientôt",
"va nourrir son cochon",
"va manger des cookies",
"va manger de la poutine",
"va faire caca",
"va faire pipi"]
await ctx.send(f"**{user.mention}** {random.choice(msgs)}...")
"""---------------------------------------------------------------------"""
@commands.Cog.listener()
async def on_message(self, message):
if message.author.bot \
or message.guild.id != int(self.bot.config.main_server_id):
return
user = message.author
if user in self.afk_users \
and message.content != self.bot.config.prefix[0] + "afk":
self.afk_users.remove(user)
msgs = ["a réssuscité",
"est de nouveau parmi nous",
"a fini de faire caca",
"a fini d'uriner",
"n'est plus mort",
"est de nouveau sur son PC",
"a fini de manger sa poutine",
"a fini de danser",
"s'est réveillé",
"est de retour dans ce monde cruel"]
await message.channel.send(f"**{user.mention}**"
f" {random.choice(msgs)}...")
def setup(bot):
bot.add_cog(AFK(bot))
from discord.ext import commands
import discord
import random
class AFK(commands.Cog):
"""Commandes utilitaires."""
def __init__(self, bot):
self.bot = bot
self.afk_users = []
"""---------------------------------------------------------------------"""
@commands.command(pass_context=True)
async def afk(self, ctx, action: str = ""):
if action.lower() == "list":
try:
await ctx.send(*self.afk_users)
except discord.HTTPException:
await ctx.send("Il n'y a personne d'afk...")
else:
user = ctx.author
self.afk_users.append(user)
msgs = ["s'absente de discord quelques instants",
"se casse de son pc",
"va sortir son chien",
"reviens bientôt",
"va nourrir son cochon",
"va manger des cookies",
"va manger de la poutine",
"va faire caca",
"va faire pipi"]
await ctx.send(f"**{user.mention}** {random.choice(msgs)}...")
"""---------------------------------------------------------------------"""
@commands.Cog.listener()
async def on_message(self, message):
if message.author.bot \
or message.guild.id != int(self.bot.config.main_server_id):
return
user = message.author
if user in self.afk_users \
and message.content != self.bot.config.prefix[0] + "afk":
self.afk_users.remove(user)
msgs = ["a réssuscité",
"est de nouveau parmi nous",
"a fini de faire caca",
"a fini d'uriner",
"n'est plus mort",
"est de nouveau sur son PC",
"a fini de manger sa poutine",
"a fini de danser",
"s'est réveillé",
"est de retour dans ce monde cruel"]
await message.channel.send(f"**{user.mention}**"
f" {random.choice(msgs)}...")
def setup(bot):
bot.add_cog(AFK(bot))

View File

@ -1,124 +1,124 @@
import asyncio
from bs4 import BeautifulSoup
import requests
import re
import discord
from discord.ext import commands
class ATC(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.playing = False
self.author = None
self.voice = None
@staticmethod
async def extra(self, ctx, icao):
if icao == "stop_playing":
if self.playing and (
ctx.author.id == self.author.id
or ctx.message.channel.permissions_for(ctx.message.author).administrator is True
):
await self.voice.disconnect()
self.playing = False
await ctx.send("Écoute terminée !")
return "quit"
else:
await ctx.send("Veuillez specifier un icao")
return "quit"
if icao == "info":
em = discord.Embed(title=f"Infos sur les services utilisés par {self.bot.config.prefix[0]}atc")
em.add_field(name="Service pour les communications:",
value="[liveatc.net](https://www.liveatc.net/)",
inline=False)
em.add_field(name="Service pour les plans des aéroports:",
value="[universalweather.com](http://www.universalweather.com/)",
inline=False)
await ctx.send(embed=em)
return "quit"
"""---------------------------------------------------------------------"""
@commands.command(name="atc", no_pm=True, pass_context=True)
async def _atc(self, ctx, icao="stop_playing"):
user = ctx.author
if not user.voice:
await ctx.send('Veuillez aller dans un channel vocal.')
return
if await self.extra(self, ctx, icao) == "quit":
return
if self.playing:
await ctx.send(f"Une écoute est déja en court, "
f"demandez à {self.author.mention} de faire "
f"`.atc stop_playing` pour l'arreter")
return
self.author = user
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.35 Safari/537.36',
}
req = requests.post("https://www.liveatc.net/search/",
data={'icao': icao},
headers=headers)
html = BeautifulSoup(req.text, features="lxml")
regex = r"(javascript: pageTracker\._trackPageview\('\/listen\/)(.*)(\'\)\;)"
possibilities = {}
emojis = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟',
'0⃣', '🇦', '🇧', '🇨', '🇩', '🇪', '🇫', '🇬', '🇭', '🇮']
to_react = []
idx = 0
for a in html.findAll("a", onclick=True):
val = a.get('onclick')
for match in re.finditer(regex, val):
possibilities[idx] = f"{emojis[idx]} - {match.groups()[1]}\n"
to_react.append(emojis[idx])
idx += 1
em = discord.Embed(title='Résultats pour : ' + icao,
description=str(''.join(possibilities.values())),
colour=0x4ECDC4)
em.set_image(
url=f"http://www.universalweather.com/regusers/mod-bin/uvtp_airport_image?icao={icao}")
poll_msg = await ctx.send(embed=em)
for emote in to_react:
await poll_msg.add_reaction(emote)
def check(reaction, user):
return user == ctx.author and reaction.emoji in to_react and \
reaction.message.id == poll_msg.id
async def waiter(future: asyncio.Future):
reaction, user = await self.bot.wait_for('reaction_add',
check=check)
future.set_result(emojis.index(reaction.emoji))
added_emoji = asyncio.Future()
self.bot.loop.create_task(waiter(added_emoji))
while not added_emoji.done():
await asyncio.sleep(0.1)
freq = possibilities[added_emoji.result()].split('- ')[1]
if possibilities:
self.playing = True
self.voice = await user.voice.channel.connect()
self.voice.play(
discord.FFmpegPCMAudio(f"http://yyz.liveatc.net/{freq}"))
await poll_msg.delete()
await ctx.send(f"Écoute de {freq}")
else:
await ctx.send(f"Aucun résultat trouvé pour {icao} {freq}")
def setup(bot):
bot.add_cog(ATC(bot))
import asyncio
from bs4 import BeautifulSoup
import requests
import re
import discord
from discord.ext import commands
class ATC(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.playing = False
self.author = None
self.voice = None
@staticmethod
async def extra(self, ctx, icao):
if icao == "stop_playing":
if self.playing and (
ctx.author.id == self.author.id
or ctx.message.channel.permissions_for(ctx.message.author).administrator is True
):
await self.voice.disconnect()
self.playing = False
await ctx.send("Écoute terminée !")
return "quit"
else:
await ctx.send("Veuillez specifier un icao")
return "quit"
if icao == "info":
em = discord.Embed(title=f"Infos sur les services utilisés par {self.bot.config.prefix[0]}atc")
em.add_field(name="Service pour les communications:",
value="[liveatc.net](https://www.liveatc.net/)",
inline=False)
em.add_field(name="Service pour les plans des aéroports:",
value="[universalweather.com](http://www.universalweather.com/)",
inline=False)
await ctx.send(embed=em)
return "quit"
"""---------------------------------------------------------------------"""
@commands.command(name="atc", no_pm=True, pass_context=True)
async def _atc(self, ctx, icao="stop_playing"):
user = ctx.author
if not user.voice:
await ctx.send('Veuillez aller dans un channel vocal.')
return
if await self.extra(self, ctx, icao) == "quit":
return
if self.playing:
await ctx.send(f"Une écoute est déja en court, "
f"demandez à {self.author.mention} de faire "
f"`.atc stop_playing` pour l'arreter")
return
self.author = user
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.35 Safari/537.36',
}
req = requests.post("https://www.liveatc.net/search/",
data={'icao': icao},
headers=headers)
html = BeautifulSoup(req.text, features="lxml")
regex = r"(javascript: pageTracker\._trackPageview\('\/listen\/)(.*)(\'\)\;)"
possibilities = {}
emojis = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟',
'0⃣', '🇦', '🇧', '🇨', '🇩', '🇪', '🇫', '🇬', '🇭', '🇮']
to_react = []
idx = 0
for a in html.findAll("a", onclick=True):
val = a.get('onclick')
for match in re.finditer(regex, val):
possibilities[idx] = f"{emojis[idx]} - {match.groups()[1]}\n"
to_react.append(emojis[idx])
idx += 1
em = discord.Embed(title='Résultats pour : ' + icao,
description=str(''.join(possibilities.values())),
colour=0x4ECDC4)
em.set_image(
url=f"http://www.universalweather.com/regusers/mod-bin/uvtp_airport_image?icao={icao}")
poll_msg = await ctx.send(embed=em)
for emote in to_react:
await poll_msg.add_reaction(emote)
def check(reaction, user):
return user == ctx.author and reaction.emoji in to_react and \
reaction.message.id == poll_msg.id
async def waiter(future: asyncio.Future):
reaction, user = await self.bot.wait_for('reaction_add',
check=check)
future.set_result(emojis.index(reaction.emoji))
added_emoji = asyncio.Future()
self.bot.loop.create_task(waiter(added_emoji))
while not added_emoji.done():
await asyncio.sleep(0.1)
freq = possibilities[added_emoji.result()].split('- ')[1]
if possibilities:
self.playing = True
self.voice = await user.voice.channel.connect()
self.voice.play(
discord.FFmpegPCMAudio(f"http://yyz.liveatc.net/{freq}"))
await poll_msg.delete()
await ctx.send(f"Écoute de {freq}")
else:
await ctx.send(f"Aucun résultat trouvé pour {icao} {freq}")
def setup(bot):
bot.add_cog(ATC(bot))

View File

@ -1,71 +1,71 @@
import platform
import socket
import subprocess
import discord
from discord.ext import commands
from discord.http import Route
class Basics(commands.Cog):
"""Commandes générales."""
def __init__(self, bot):
self.bot = bot
@commands.command()
async def ping(self, ctx):
ping_res = str(subprocess.Popen(["/bin/ping", "-c1", "discordapp.com"],
stdout=subprocess.PIPE).stdout.read())
formated_res = [item for item in ping_res.split() if 'time=' in item]
result = self.bot.latency * 1000 # str(formated_res[0])[5:]
if float(result) >= 200:
em = discord.Embed(title="Ping : " + str(result) + "ms",
description="... c'est quoi ce ping !",
colour=0xFF1111)
await ctx.send(embed=em)
elif float(result) > 100 < 200:
em = discord.Embed(title="Ping : " + str(result) + "ms",
description="Ca va, ça peut aller, mais j'ai "
"l'impression d'avoir 40 ans !",
colour=0xFFA500)
await ctx.send(embed=em)
else:
em = discord.Embed(title="Ping : " + str(result) + "ms",
description="Wow c'te vitesse de réaction, "
"je m'épate moi-même !",
colour=0x11FF11)
await ctx.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.command()
async def info(self, ctx):
"""Affiches des informations sur le bot"""
text = open('texts/info.md').read()
os_info = str(platform.system()) + " / " + str(platform.release())
em = discord.Embed(title='Informations sur TuxBot',
description=text.format(os_info,
platform.python_version(),
socket.gethostname(),
discord.__version__,
Route.BASE),
colour=0x89C4F9)
em.set_footer(text="/home/****/bot.py")
await ctx.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.command()
async def help(self, ctx, page: int = 1):
"""Affiches l'aide du bot"""
page = int(page) if 0 < int(page) < 5 else 1
text = open('texts/help.md').read().split("[split]")
em = discord.Embed(title='Commandes de TuxBot', description=text[page - 1],
colour=0x89C4F9)
await ctx.send(content=f"page {page}/{len(text)}", embed=em)
def setup(bot):
bot.add_cog(Basics(bot))
import platform
import socket
import subprocess
import discord
from discord.ext import commands
from discord.http import Route
class Basics(commands.Cog):
"""Commandes générales."""
def __init__(self, bot):
self.bot = bot
@commands.command()
async def ping(self, ctx):
ping_res = str(subprocess.Popen(["/bin/ping", "-c1", "discordapp.com"],
stdout=subprocess.PIPE).stdout.read())
formated_res = [item for item in ping_res.split() if 'time=' in item]
result = self.bot.latency * 1000 # str(formated_res[0])[5:]
if float(result) >= 200:
em = discord.Embed(title="Ping : " + str(result) + "ms",
description="... c'est quoi ce ping !",
colour=0xFF1111)
await ctx.send(embed=em)
elif float(result) > 100 < 200:
em = discord.Embed(title="Ping : " + str(result) + "ms",
description="Ca va, ça peut aller, mais j'ai "
"l'impression d'avoir 40 ans !",
colour=0xFFA500)
await ctx.send(embed=em)
else:
em = discord.Embed(title="Ping : " + str(result) + "ms",
description="Wow c'te vitesse de réaction, "
"je m'épate moi-même !",
colour=0x11FF11)
await ctx.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.command()
async def info(self, ctx):
"""Affiches des informations sur le bot"""
text = open('texts/info.md').read()
os_info = str(platform.system()) + " / " + str(platform.release())
em = discord.Embed(title='Informations sur TuxBot',
description=text.format(os_info,
platform.python_version(),
socket.gethostname(),
discord.__version__,
Route.BASE),
colour=0x89C4F9)
em.set_footer(text="/home/****/bot.py")
await ctx.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.command()
async def help(self, ctx, page: int = 1):
"""Affiches l'aide du bot"""
page = int(page) if 0 < int(page) < 5 else 1
text = open('texts/help.md').read().split("[split]")
em = discord.Embed(title='Commandes de TuxBot', description=text[page - 1],
colour=0x89C4F9)
await ctx.send(content=f"page {page}/{len(text)}", embed=em)
def setup(bot):
bot.add_cog(Basics(bot))

View File

@ -1,311 +1,311 @@
import datetime
import random
import discord
import requests
from discord.ext import commands
from .utils import checks
from .utils import db
from .utils.checks import get_user, check_date
class Identity(commands.Cog):
"""Commandes des cartes d'identité ."""
def __init__(self, bot):
self.bot = bot
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
self.cursor.execute("""SHOW TABLES LIKE 'users'""")
result = self.cursor.fetchone()
if not result:
# Creation table Utilisateur si premiere fois
sql = "CREATE TABLE users ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, userid TEXT null, username TEXT null, os TEXT null, config TEXT null, useravatar TEXT null, userbirth TEXT null, pays TEXT null, cidate TEXT null, cibureau TEXT null);"
self.cursor.execute(sql)
"""--------------------------------------------------------------------------------------------------------------------------"""
@commands.group(name="ci", no_pm=True, pass_context=True)
async def _ci(self, ctx):
"""Cartes d'identité"""
if ctx.invoked_subcommand is None:
text = open('texts/ci-info.md').read()
em = discord.Embed(title='Commandes de carte d\'identité de TuxBot', description=text, colour=0x89C4F9)
await ctx.send(embed=em)
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="show")
async def ci_show(self, ctx, args: str = None):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
if args == None:
user = get_user(ctx.message, ctx.author.name)
else:
user = get_user(ctx.message, args)
if user:
self.cursor.execute("""SELECT userid, username, useravatar, userbirth, cidate, cibureau, os, config, pays, id FROM users WHERE userid=%s""",(str(user.id)))
result = self.cursor.fetchone()
def isexist(var):
if not var:
return "Non renseigné."
else:
return var
if not result:
await ctx.send(f"{ctx.author.mention}> :x: Désolé mais {user.mention} est sans papier !")
else:
try:
user_birth = datetime.datetime.fromisoformat(result[3])
user_birth_day = check_date(str(user_birth.day))
user_birth_month = check_date(str(user_birth.month))
formated_user_birth = str(user_birth_day) + "/" + str(user_birth_month) + "/" + str(user_birth.year)
try: ## a virer une fois le patch appliqué pour tout le monde
cidate = datetime.datetime.fromisoformat(result[4])
cidate_day = check_date(str(cidate.day)) ## a garder
cidate_month = check_date(str(cidate.month)) ## a garder
formated_cidate = str(cidate_day) + "/" + str(cidate_month) + "/" + str(cidate.year) ## a garder
except ValueError: ## a virer une fois le patch appliqué pour tout le monde
formated_cidate = str(result[4]).replace('-', '/') ## a virer une fois le patch appliqué pour tout le monde
await ctx.send(f"{user.mention} vous êtes prié(e) de faire la commande `.ci update` afin de regler un probleme de date coté bdd") ## a virer une fois le patch appliqué pour tout le monde
embed=discord.Embed(title="Carte d'identité | Communisme Linuxien")
embed.set_author(name=result[1], icon_url=result[2])
embed.set_thumbnail(url = result[2])
embed.add_field(name="Nom :", value=result[1], inline=True)
embed.add_field(name="Système d'exploitation :", value=isexist(result[6]), inline=True)
embed.add_field(name="Configuration Système : ", value=isexist(result[7]), inline=False)
embed.add_field(name="Date de naissance sur discord : ", value=formated_user_birth, inline=True)
embed.add_field(name="Pays : ", value=isexist(result[8]), inline=True)
embed.add_field(name="Profil sur le web : ", value="*indisponible*") # value=f"https://tuxbot.gnous.eu/users/{result[9]}", inline=True)
embed.set_footer(text=f"Enregistré dans le bureau {result[5]} le {formated_cidate}.")
await ctx.send(embed=embed)
except Exception as e:
await ctx.send(f"{ctx.author.mention}> :x: Désolé mais la carte d'identité de {user.mention} est trop longue de ce fait je ne peux te l'envoyer, essaye de l'aléger, {user.mention} :wink: !")
await ctx.send(f':sob: Une erreur est survenue : \n {type(e).__name__}: {e}')
else:
return await ctx.send('Impossible de trouver l\'user.')
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="register")
async def ci_register(self, ctx):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
await ctx.send("Mais tu as déja une carte d'identité ! u_u")
else:
now = datetime.datetime.now()
self.cursor.execute("""INSERT INTO users(userid, username, useravatar, userbirth, cidate, cibureau) VALUES(%s, %s, %s, %s, %s, %s)""", (str(ctx.author.id), str(ctx.author), str(ctx.author.avatar_url_as(format="jpg", size=512)), str(ctx.author.created_at), now, str(ctx.message.guild.name)))
self.conn.commit()
await ctx.send(f":clap: Bievenue à toi {ctx.author.name} dans le communisme {ctx.message.guild.name} ! Fait ``.ci`` pour plus d'informations !")
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="delete")
async def ci_delete(self, ctx):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
self.cursor.execute("""DELETE FROM users WHERE userid =%s""", (str(ctx.author.id)))
self.conn.commit()
await ctx.send("Tu es maintenant sans papiers !")
else:
await ctx.send("Déja enregistre ta carte d'identité avant de la supprimer u_u (après c'est pas logique...)")
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="update")
async def ci_update(self, ctx):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
try:
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
self.cursor.execute("""SELECT cidate FROM users WHERE userid=%s""",(str(ctx.author.id)))
old_ci_date = self.cursor.fetchone()
try:
new_ci_date = datetime.datetime.fromisoformat(old_ci_date[0])
except ValueError:
old_ci_date = datetime.datetime.strptime(old_ci_date[0].replace('/', '-'), '%d-%m-%Y')
old_ci_date_day = check_date(str(old_ci_date.day))
old_ci_date_month = check_date(str(old_ci_date.month))
new_ci_date = f"{str(old_ci_date.year)}-{str(old_ci_date_month)}-{str(old_ci_date_day)} 00:00:00.000000"
await ctx.send("succes update")
self.cursor.execute("""UPDATE users SET cidate = %s WHERE userid = %s""", (str(new_ci_date), str(ctx.author.id)))
self.conn.commit()
self.cursor.execute("""UPDATE users SET useravatar = %s, username = %s, cibureau = %s WHERE userid = %s""", (str(ctx.author.avatar_url_as(format="jpg", size=512)), str(ctx.author), str(ctx.message.guild), str(ctx.author.id)))
self.conn.commit()
await ctx.send(f"{ctx.author.mention}> Tu viens, en quelques sortes, de renaitre !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Veuillez enregistrer votre carte d'identité pour commencer !")
except Exception as e: #TODO : A virer dans l'event on_error
await ctx.send(':( Erreur veuillez contacter votre administrateur :')
await ctx.send(f'{type(e).__name__}: {e}')
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="setconfig")
async def ci_setconfig(self, ctx, *, conf: str = None):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
if conf:
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
self.cursor.execute("""UPDATE users SET config = %s WHERE userid = %s""", (str(conf), str(ctx.author.id)))
self.conn.commit()
await ctx.send(f"{ctx.author.mention}> :ok_hand: Carte d'identité mise à jour !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Veuillez enregistrer votre carte d'identité pour commencer !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Il manque un paramètre !")
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="setos")
async def ci_setos(self, ctx, *, conf: str = None):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
if conf:
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
self.cursor.execute("""UPDATE users SET os = %s WHERE userid = %s""", (str(conf), str(ctx.author.id)))
self.conn.commit()
await ctx.send(f"{ctx.author.mention}> :ok_hand: Carte d'identité mise à jour !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Veuillez enregistrer votre carte d'identité pour commencer !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Il manque un paramètre !")
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="setcountry")
async def ci_setcountry(self, ctx, *, country: str = None):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
if country:
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
self.cursor.execute("""UPDATE users SET pays = %s WHERE userid = %s""", (str(country), str(ctx.author.id)))
self.conn.commit()
await ctx.send(f"{ctx.author.mention}> :ok_hand: Carte d'identité mise à jour !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Veuillez enregistrer votre carte d'identité pour commencer !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Il manque un paramètre !")
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="online_edit")
async def ci_online_edit(self, ctx):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
self.cursor.execute("""SELECT id FROM users WHERE userid=%s""",(str(ctx.author.id)))
result = self.cursor.fetchone()
if not result:
return await ctx.send(f"Déja enregistre ta carte d'identité avant de l'éditer u_u (après c'est pas logique...)")
dm = await ctx.author.create_dm()
try:
def is_exist(key, value):
self.cursor.execute("""SELECT * FROM sessions WHERE {}=%s""".format(str(key)), (str(value)))
return self.cursor.fetchone()
user_id = result[0]
is_admin = '1' if str(ctx.author.id) in self.bot.config.authorized_id else '0'
token = ''.join(random.sample('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'*25, 25))
created_at = datetime.datetime.utcnow()
while is_exist('token', token):
token = ''.join(random.sample('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'*25, 25))
if is_exist('user_id', user_id):
self.cursor.execute("""UPDATE sessions SET is_admin = %s, token = %s, updated_at = %s WHERE user_id = %s""", (str(is_admin), str(token), str(created_at), str(user_id)))
self.conn.commit()
else:
self.cursor.execute("""INSERT INTO sessions(user_id, is_admin, token, created_at, updated_at) VALUES(%s, %s, %s, %s, %s)""", (str(user_id), str(is_admin), str(token), str(created_at), str(created_at)))
self.conn.commit()
embed=discord.Embed(title="Clé d'édition pour tuxweb", description=f"Voici ta clé d'édition, vas sur [https://tuxbot.gnous.eu/fr/users/{user_id}](https://tuxbot.gnous.eu/fr/users/{user_id}) puis cliques sur `editer` et entre la clé afin de pouvoir modifier ta ci", colour=0x89C4F9)
embed.set_footer(text=f"Cette clé sera valide durant les 10 prochaines minutes, ne la communiques à personne !")
await dm.send(embed=embed)
await dm.send(token)
await ctx.send(f"{ctx.author.mention} ta clé d'édition t'a été envoyée en message privé")
except Exception as e:
await ctx.send(f"{ctx.author.mention}, je ne peux pas t'envoyer de message privé :(. Penses à autoriser les messages privés provenant des membres du serveur pour que je puisse te donner ta clef d'édition")
"""--------------------------------------------------------------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@_ci.command(pass_context=True, name="list")
async def ci_list(self, ctx):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
self.cursor.execute("""SELECT id, username FROM users""")
rows = self.cursor.fetchall()
msg = ""
try:
for row in rows:
row_id = row[0]
row_name = row[1].encode('utf-8')
msg += f"{str(row_id)} : {str(row_name)} \n"
post = requests.post("https://hastebin.com/documents", data=msg)
await ctx.send(f"{ctx.author.mention} liste posté avec succès sur :\nhttps://hastebin.com/{post.json()['key']}.txt")
with open('ci_list.txt', 'w', encoding='utf-8') as fp:
for row in rows:
row_id = row[0]
row_name = row[1]
fp.write(f"{str(row_id)} : {str(row_name)} \n")
except Exception as e:
await ctx.send(f':sob: Une erreur est survenue : \n {type(e).__name__}: {e}')
def setup(bot):
bot.add_cog(Identity(bot))
import datetime
import random
import discord
import requests
from discord.ext import commands
from .utils import checks
from .utils import db
from .utils.checks import get_user, check_date
class Identity(commands.Cog):
"""Commandes des cartes d'identité ."""
def __init__(self, bot):
self.bot = bot
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
self.cursor.execute("""SHOW TABLES LIKE 'users'""")
result = self.cursor.fetchone()
if not result:
# Creation table Utilisateur si premiere fois
sql = "CREATE TABLE users ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, userid TEXT null, username TEXT null, os TEXT null, config TEXT null, useravatar TEXT null, userbirth TEXT null, pays TEXT null, cidate TEXT null, cibureau TEXT null);"
self.cursor.execute(sql)
"""--------------------------------------------------------------------------------------------------------------------------"""
@commands.group(name="ci", no_pm=True, pass_context=True)
async def _ci(self, ctx):
"""Cartes d'identité"""
if ctx.invoked_subcommand is None:
text = open('texts/ci-info.md').read()
em = discord.Embed(title='Commandes de carte d\'identité de TuxBot', description=text, colour=0x89C4F9)
await ctx.send(embed=em)
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="show")
async def ci_show(self, ctx, args: str = None):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
if args == None:
user = get_user(ctx.message, ctx.author.name)
else:
user = get_user(ctx.message, args)
if user:
self.cursor.execute("""SELECT userid, username, useravatar, userbirth, cidate, cibureau, os, config, pays, id FROM users WHERE userid=%s""",(str(user.id)))
result = self.cursor.fetchone()
def isexist(var):
if not var:
return "Non renseigné."
else:
return var
if not result:
await ctx.send(f"{ctx.author.mention}> :x: Désolé mais {user.mention} est sans papier !")
else:
try:
user_birth = datetime.datetime.fromisoformat(result[3])
user_birth_day = check_date(str(user_birth.day))
user_birth_month = check_date(str(user_birth.month))
formated_user_birth = str(user_birth_day) + "/" + str(user_birth_month) + "/" + str(user_birth.year)
try: ## a virer une fois le patch appliqué pour tout le monde
cidate = datetime.datetime.fromisoformat(result[4])
cidate_day = check_date(str(cidate.day)) ## a garder
cidate_month = check_date(str(cidate.month)) ## a garder
formated_cidate = str(cidate_day) + "/" + str(cidate_month) + "/" + str(cidate.year) ## a garder
except ValueError: ## a virer une fois le patch appliqué pour tout le monde
formated_cidate = str(result[4]).replace('-', '/') ## a virer une fois le patch appliqué pour tout le monde
await ctx.send(f"{user.mention} vous êtes prié(e) de faire la commande `.ci update` afin de regler un probleme de date coté bdd") ## a virer une fois le patch appliqué pour tout le monde
embed=discord.Embed(title="Carte d'identité | Communisme Linuxien")
embed.set_author(name=result[1], icon_url=result[2])
embed.set_thumbnail(url = result[2])
embed.add_field(name="Nom :", value=result[1], inline=True)
embed.add_field(name="Système d'exploitation :", value=isexist(result[6]), inline=True)
embed.add_field(name="Configuration Système : ", value=isexist(result[7]), inline=False)
embed.add_field(name="Date de naissance sur discord : ", value=formated_user_birth, inline=True)
embed.add_field(name="Pays : ", value=isexist(result[8]), inline=True)
embed.add_field(name="Profil sur le web : ", value="*indisponible*") # value=f"https://tuxbot.gnous.eu/users/{result[9]}", inline=True)
embed.set_footer(text=f"Enregistré dans le bureau {result[5]} le {formated_cidate}.")
await ctx.send(embed=embed)
except Exception as e:
await ctx.send(f"{ctx.author.mention}> :x: Désolé mais la carte d'identité de {user.mention} est trop longue de ce fait je ne peux te l'envoyer, essaye de l'aléger, {user.mention} :wink: !")
await ctx.send(f':sob: Une erreur est survenue : \n {type(e).__name__}: {e}')
else:
return await ctx.send('Impossible de trouver l\'user.')
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="register")
async def ci_register(self, ctx):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
await ctx.send("Mais tu as déja une carte d'identité ! u_u")
else:
now = datetime.datetime.now()
self.cursor.execute("""INSERT INTO users(userid, username, useravatar, userbirth, cidate, cibureau) VALUES(%s, %s, %s, %s, %s, %s)""", (str(ctx.author.id), str(ctx.author), str(ctx.author.avatar_url_as(format="jpg", size=512)), str(ctx.author.created_at), now, str(ctx.message.guild.name)))
self.conn.commit()
await ctx.send(f":clap: Bievenue à toi {ctx.author.name} dans le communisme {ctx.message.guild.name} ! Fait ``.ci`` pour plus d'informations !")
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="delete")
async def ci_delete(self, ctx):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
self.cursor.execute("""DELETE FROM users WHERE userid =%s""", (str(ctx.author.id)))
self.conn.commit()
await ctx.send("Tu es maintenant sans papiers !")
else:
await ctx.send("Déja enregistre ta carte d'identité avant de la supprimer u_u (après c'est pas logique...)")
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="update")
async def ci_update(self, ctx):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
try:
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
self.cursor.execute("""SELECT cidate FROM users WHERE userid=%s""",(str(ctx.author.id)))
old_ci_date = self.cursor.fetchone()
try:
new_ci_date = datetime.datetime.fromisoformat(old_ci_date[0])
except ValueError:
old_ci_date = datetime.datetime.strptime(old_ci_date[0].replace('/', '-'), '%d-%m-%Y')
old_ci_date_day = check_date(str(old_ci_date.day))
old_ci_date_month = check_date(str(old_ci_date.month))
new_ci_date = f"{str(old_ci_date.year)}-{str(old_ci_date_month)}-{str(old_ci_date_day)} 00:00:00.000000"
await ctx.send("succes update")
self.cursor.execute("""UPDATE users SET cidate = %s WHERE userid = %s""", (str(new_ci_date), str(ctx.author.id)))
self.conn.commit()
self.cursor.execute("""UPDATE users SET useravatar = %s, username = %s, cibureau = %s WHERE userid = %s""", (str(ctx.author.avatar_url_as(format="jpg", size=512)), str(ctx.author), str(ctx.message.guild), str(ctx.author.id)))
self.conn.commit()
await ctx.send(f"{ctx.author.mention}> Tu viens, en quelques sortes, de renaitre !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Veuillez enregistrer votre carte d'identité pour commencer !")
except Exception as e: #TODO : A virer dans l'event on_error
await ctx.send(':( Erreur veuillez contacter votre administrateur :')
await ctx.send(f'{type(e).__name__}: {e}')
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="setconfig")
async def ci_setconfig(self, ctx, *, conf: str = None):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
if conf:
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
self.cursor.execute("""UPDATE users SET config = %s WHERE userid = %s""", (str(conf), str(ctx.author.id)))
self.conn.commit()
await ctx.send(f"{ctx.author.mention}> :ok_hand: Carte d'identité mise à jour !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Veuillez enregistrer votre carte d'identité pour commencer !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Il manque un paramètre !")
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="setos")
async def ci_setos(self, ctx, *, conf: str = None):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
if conf:
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
self.cursor.execute("""UPDATE users SET os = %s WHERE userid = %s""", (str(conf), str(ctx.author.id)))
self.conn.commit()
await ctx.send(f"{ctx.author.mention}> :ok_hand: Carte d'identité mise à jour !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Veuillez enregistrer votre carte d'identité pour commencer !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Il manque un paramètre !")
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="setcountry")
async def ci_setcountry(self, ctx, *, country: str = None):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
if country:
self.cursor.execute("""SELECT id, userid FROM users WHERE userid=%s""", (str(ctx.author.id)))
result = self.cursor.fetchone()
if result:
self.cursor.execute("""UPDATE users SET pays = %s WHERE userid = %s""", (str(country), str(ctx.author.id)))
self.conn.commit()
await ctx.send(f"{ctx.author.mention}> :ok_hand: Carte d'identité mise à jour !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Veuillez enregistrer votre carte d'identité pour commencer !")
else:
await ctx.send(f"{ctx.author.mention}> :x: Il manque un paramètre !")
"""--------------------------------------------------------------------------------------------------------------------------"""
@_ci.command(pass_context=True, name="online_edit")
async def ci_online_edit(self, ctx):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
self.cursor.execute("""SELECT id FROM users WHERE userid=%s""",(str(ctx.author.id)))
result = self.cursor.fetchone()
if not result:
return await ctx.send(f"Déja enregistre ta carte d'identité avant de l'éditer u_u (après c'est pas logique...)")
dm = await ctx.author.create_dm()
try:
def is_exist(key, value):
self.cursor.execute("""SELECT * FROM sessions WHERE {}=%s""".format(str(key)), (str(value)))
return self.cursor.fetchone()
user_id = result[0]
is_admin = '1' if str(ctx.author.id) in self.bot.config.authorized_id else '0'
token = ''.join(random.sample('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'*25, 25))
created_at = datetime.datetime.utcnow()
while is_exist('token', token):
token = ''.join(random.sample('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'*25, 25))
if is_exist('user_id', user_id):
self.cursor.execute("""UPDATE sessions SET is_admin = %s, token = %s, updated_at = %s WHERE user_id = %s""", (str(is_admin), str(token), str(created_at), str(user_id)))
self.conn.commit()
else:
self.cursor.execute("""INSERT INTO sessions(user_id, is_admin, token, created_at, updated_at) VALUES(%s, %s, %s, %s, %s)""", (str(user_id), str(is_admin), str(token), str(created_at), str(created_at)))
self.conn.commit()
embed=discord.Embed(title="Clé d'édition pour tuxweb", description=f"Voici ta clé d'édition, vas sur [https://tuxbot.gnous.eu/fr/users/{user_id}](https://tuxbot.gnous.eu/fr/users/{user_id}) puis cliques sur `editer` et entre la clé afin de pouvoir modifier ta ci", colour=0x89C4F9)
embed.set_footer(text=f"Cette clé sera valide durant les 10 prochaines minutes, ne la communiques à personne !")
await dm.send(embed=embed)
await dm.send(token)
await ctx.send(f"{ctx.author.mention} ta clé d'édition t'a été envoyée en message privé")
except Exception as e:
await ctx.send(f"{ctx.author.mention}, je ne peux pas t'envoyer de message privé :(. Penses à autoriser les messages privés provenant des membres du serveur pour que je puisse te donner ta clef d'édition")
"""--------------------------------------------------------------------------------------------------------------------------"""
@checks.has_permissions(administrator=True)
@_ci.command(pass_context=True, name="list")
async def ci_list(self, ctx):
self.conn = db.connect_to_db(self)
self.cursor = self.conn.cursor()
self.cursor.execute("""SELECT id, username FROM users""")
rows = self.cursor.fetchall()
msg = ""
try:
for row in rows:
row_id = row[0]
row_name = row[1].encode('utf-8')
msg += f"{str(row_id)} : {str(row_name)} \n"
post = requests.post("https://hastebin.com/documents", data=msg)
await ctx.send(f"{ctx.author.mention} liste posté avec succès sur :\nhttps://hastebin.com/{post.json()['key']}.txt")
with open('ci_list.txt', 'w', encoding='utf-8') as fp:
for row in rows:
row_id = row[0]
row_name = row[1]
fp.write(f"{str(row_id)} : {str(row_name)} \n")
except Exception as e:
await ctx.send(f':sob: Une erreur est survenue : \n {type(e).__name__}: {e}')
def setup(bot):
bot.add_cog(Identity(bot))

View File

@ -1,60 +1,60 @@
from discord.ext import commands
import re
class FilterMessages(commands.Cog):
"""Flitre des messages"""
def __init__(self, bot):
self.bot = bot
@commands.Cog.listener()
async def on_message(self, message):
no_pub_guild = [280805240977227776, 303633056944881686,
274247231534792704]
lien_channel = [280805783795662848, 508794201509593088,
516017286948061204]
sondage_channel = [394146769107419146, 477147964393914388]
if message.author.bot \
or str(message.author.id) in self.bot.config.authorized_id \
or message.channel.permissions_for(message.author).administrator is True:
return
discord_invite_regex = re.compile(r"(discord\.(gg|io|me|li)|discordapp\.com\/invite)\/[0-9A-Za-z]*", re.IGNORECASE)
invalid_link_regex = re.compile(r"^(\[[^\]]+\]|<\:[a-z0-9]+\:[0-9]+>) .+ https?:\/\/\S*$", re.IGNORECASE)
try:
if message.guild.id in no_pub_guild:
if isinstance(discord_invite_regex.search(message.content), re.Match):
author = self.bot.get_user(message.author.id)
await message.delete()
await author.send("La pub pour les serveurs discord n'est pas autorisée ici")
if message.channel.id in lien_channel \
and not isinstance(invalid_link_regex.search(message.content), re.Match):
author = self.bot.get_user(message.author.id)
await message.delete()
await author.send(f"Votre message `{message.content}` a été "
f"supprimé du channel `liens` ou `projets` "
f"car il ne respecte pas la structure "
f"définie. Pour partager un lien veuillez "
f"suivre la structure suivante :"
f" ` [Sujet] Descirption http(s)://....`")
await author.send("Si vous voulez commenter ou discuter à "
"propos d'un lien ou d'un projet, veuillez "
"le faire dans le channel"
" `#discussion-des-liens` ou"
" `#discussion-projets`.")
if message.channel.id in sondage_channel:
prefix_lenght = len(await self.bot.get_prefix(message))
command = (message.content.split()[0])[prefix_lenght:]
if command != "sondage":
await message.delete()
except AttributeError:
pass
def setup(bot):
bot.add_cog(FilterMessages(bot))
from discord.ext import commands
import re
class FilterMessages(commands.Cog):
"""Flitre des messages"""
def __init__(self, bot):
self.bot = bot
@commands.Cog.listener()
async def on_message(self, message):
no_pub_guild = [280805240977227776, 303633056944881686,
274247231534792704]
lien_channel = [280805783795662848, 508794201509593088,
516017286948061204]
sondage_channel = [394146769107419146, 477147964393914388]
if message.author.bot \
or str(message.author.id) in self.bot.config.authorized_id \
or message.channel.permissions_for(message.author).administrator is True:
return
discord_invite_regex = re.compile(r"(discord\.(gg|io|me|li)|discordapp\.com\/invite)\/[0-9A-Za-z]*", re.IGNORECASE)
invalid_link_regex = re.compile(r"^(\[[^\]]+\]|<\:[a-z0-9]+\:[0-9]+>) .+ https?:\/\/\S*$", re.IGNORECASE)
try:
if message.guild.id in no_pub_guild:
if isinstance(discord_invite_regex.search(message.content), re.Match):
author = self.bot.get_user(message.author.id)
await message.delete()
await author.send("La pub pour les serveurs discord n'est pas autorisée ici")
if message.channel.id in lien_channel \
and not isinstance(invalid_link_regex.search(message.content), re.Match):
author = self.bot.get_user(message.author.id)
await message.delete()
await author.send(f"Votre message `{message.content}` a été "
f"supprimé du channel `liens` ou `projets` "
f"car il ne respecte pas la structure "
f"définie. Pour partager un lien veuillez "
f"suivre la structure suivante :"
f" ` [Sujet] Descirption http(s)://....`")
await author.send("Si vous voulez commenter ou discuter à "
"propos d'un lien ou d'un projet, veuillez "
"le faire dans le channel"
" `#discussion-des-liens` ou"
" `#discussion-projets`.")
if message.channel.id in sondage_channel:
prefix_lenght = len(await self.bot.get_prefix(message))
command = (message.content.split()[0])[prefix_lenght:]
if command != "sondage":
await message.delete()
except AttributeError:
pass
def setup(bot):
bot.add_cog(FilterMessages(bot))

View File

@ -1,190 +1,190 @@
from discord.ext import commands
import asyncio
import discord
import urllib.request
import json
import random
import requests
class Funs(commands.Cog):
"""Commandes funs."""
def __init__(self, bot):
self.bot = bot
"""---------------------------------------------------------------------"""
@commands.command()
async def avatar(self, ctx, user: discord.Member = None):
"""Récuperer l'avatar de ..."""
if user is None:
user = ctx.message.author
embed = discord.Embed(title="Avatar de " + user.name,
description=f"[Ouvrir dans mon navigateur]"
f"({user.avatar_url_as(format='png')})")
embed.set_image(url=user.avatar_url_as(format='png'))
await ctx.send(embed=embed)
"""---------------------------------------------------------------------"""
@commands.command(pass_context=True)
async def poke(self, ctx, user: discord.Member):
"""Poke quelqu'un"""
await ctx.send(f":clap: Hey {user.mention} tu t'es fait poker par"
f" {ctx.message.author} !")
await ctx.message.delete()
"""---------------------------------------------------------------------"""
@commands.command()
async def btcprice(self, ctx):
"""Le prix du BTC"""
loading = await ctx.send("_réfléchis..._")
try:
url = urllib.request.urlopen("https://blockchain.info/fr/ticker")
btc = json.loads(url.read().decode())
except KeyError:
btc = 1
if btc == 1:
await loading.edit(content="Impossible d'accèder à l'API"
" blockchain.info, veuillez réessayer"
" ultérieurment ! :c")
else:
frbtc = str(btc["EUR"]["last"]).replace(".", ",")
usbtc = str(btc["USD"]["last"]).replace(".", ",")
await loading.edit(content=f"Un bitcoin est égal à :"
f" {usbtc}$US soit {frbtc}€.")
"""---------------------------------------------------------------------"""
@commands.command()
async def joke(self, ctx, number: str = 0):
"""Print a random joke in a json file"""
with open('texts/jokes.json') as js:
jk = json.load(js)
try:
if 15 >= int(number) > 0:
clef = str(number)
else:
clef = str(random.randint(1, 15))
except Exception:
clef = str(random.randint(1, 15))
joke = jk["{}".format(clef)]
embed = discord.Embed(title="Blague _{}_ : ".format(clef),
description=joke['content'], colour=0x03C9A9)
embed.set_footer(text="Par " + joke['author'])
embed.set_thumbnail(url='https://outout.tech/tuxbot/blobjoy.png')
await ctx.send(embed=embed)
"""---------------------------------------------------------------------"""
@commands.command()
async def ethylotest(self, ctx):
"""Ethylotest simulator 2018"""
results_poulet = ["Désolé mais mon ethylotest est sous Windows Vista, "
"merci de patienter...",
"_(ethylotest)_ : Une erreur est survenue. Windows "
"cherche une solution à se problème.",
"Mais j'l'ai foutu où ce p\\*\\*\\* d'ethylotest de m\\*\\*\\* "
"bordel fait ch\\*\\*\\*",
"C'est pas possible z'avez cassé l'ethylotest !"]
results_client = ["D'accord, il n'y a pas de problème à cela je suis "
"complètement clean",
"Bien sur si c'est votre devoir !", "Suce bi\\*e !",
"J'ai l'air d'être bourré ?",
"_laissez moi prendre un bonbon à la menthe..._"]
result_p = random.choice(results_poulet)
result_c = random.choice(results_client)
await ctx.send(":oncoming_police_car: Bonjour bonjour, contrôle "
"d'alcoolémie !")
await asyncio.sleep(0.5)
await ctx.send(":man: " + result_c)
await asyncio.sleep(1)
await ctx.send(":police_car: " + result_p)
"""---------------------------------------------------------------------"""
@commands.command()
async def coin(self, ctx):
"""Coin flip simulator 2025"""
starts_msg = ["Je lance la pièce !", "C'est parti !", "C'est une pièce"
" d'un cent faut"
" pas la perdre",
"C'est une pièce d'un euro faut pas la perdre",
"Je lance !"]
results_coin = ["{0} pile", "{0} face", "{1} Heu c'est quoi pile c'est"
" quoi face enfaite ?",
"{1} Oh shit, je crois que je l'ai perdue",
"{1} Et bim je te vol ta pièce !",
"{0} Oh une erreur d'impression il n'y a ni pile ni"
" face !"]
start = random.choice(starts_msg)
result = random.choice(results_coin)
await ctx.send(start)
await asyncio.sleep(0.6)
await ctx.send(result.format(":moneybag: Et la pièce retombe sur ...",
":robot:"))
"""---------------------------------------------------------------------"""
@commands.command()
async def pokemon(self, ctx):
"""Random pokemon fight"""
with open('texts/pokemons.json') as js:
jk = json.load(js)
poke1 = jk[random.randint(1, 150)]
poke2 = jk[random.randint(1, 150)]
try:
if poke1['MaxHP'] > poke2['MaxHP']:
winer = poke1
else:
winer = poke2
except KeyError:
winer = poke1
await ctx.send(":flag_white: **Le combat commence !**")
await asyncio.sleep(1)
await ctx.send(":loudspeaker: Les concurants sont {} contre {} ! Bonne"
" chance à eux !".format(poke1["Name"], poke2["Name"]))
await asyncio.sleep(0.5)
await ctx.send(":boom: {} commence et utilise {}".format(
poke1["Name"], poke1["Fast Attack(s)"][0]["Name"]))
await asyncio.sleep(1)
await ctx.send(":dash: {} réplique avec {}".format(
poke2["Name"], poke2["Fast Attack(s)"][0]["Name"]))
await asyncio.sleep(1.2)
await ctx.send("_le combat continue de se dérouler..._")
await asyncio.sleep(1.5)
await ctx.send(":trophy: Le gagnant est **{}** !".format(
winer["Name"]))
"""---------------------------------------------------------------------"""
@commands.command()
async def randomcat(self, ctx):
"""Display a random cat"""
r = requests.get('http://aws.random.cat/meow')
cat = str(r.json()['file'])
embed = discord.Embed(title="Meow",
description="[Voir le chat plus grand]({})".
format(cat), colour=0x03C9A9)
embed.set_thumbnail(url=cat)
embed.set_author(name="Random.cat", url='https://random.cat/')
await ctx.send(embed=embed)
def setup(bot):
bot.add_cog(Funs(bot))
from discord.ext import commands
import asyncio
import discord
import urllib.request
import json
import random
import requests
class Funs(commands.Cog):
"""Commandes funs."""
def __init__(self, bot):
self.bot = bot
"""---------------------------------------------------------------------"""
@commands.command()
async def avatar(self, ctx, user: discord.Member = None):
"""Récuperer l'avatar de ..."""
if user is None:
user = ctx.message.author
embed = discord.Embed(title="Avatar de " + user.name,
description=f"[Ouvrir dans mon navigateur]"
f"({user.avatar_url_as(format='png')})")
embed.set_image(url=user.avatar_url_as(format='png'))
await ctx.send(embed=embed)
"""---------------------------------------------------------------------"""
@commands.command(pass_context=True)
async def poke(self, ctx, user: discord.Member):
"""Poke quelqu'un"""
await ctx.send(f":clap: Hey {user.mention} tu t'es fait poker par"
f" {ctx.message.author} !")
await ctx.message.delete()
"""---------------------------------------------------------------------"""
@commands.command()
async def btcprice(self, ctx):
"""Le prix du BTC"""
loading = await ctx.send("_réfléchis..._")
try:
url = urllib.request.urlopen("https://blockchain.info/fr/ticker")
btc = json.loads(url.read().decode())
except KeyError:
btc = 1
if btc == 1:
await loading.edit(content="Impossible d'accèder à l'API"
" blockchain.info, veuillez réessayer"
" ultérieurment ! :c")
else:
frbtc = str(btc["EUR"]["last"]).replace(".", ",")
usbtc = str(btc["USD"]["last"]).replace(".", ",")
await loading.edit(content=f"Un bitcoin est égal à :"
f" {usbtc}$US soit {frbtc}€.")
"""---------------------------------------------------------------------"""
@commands.command()
async def joke(self, ctx, number: str = 0):
"""Print a random joke in a json file"""
with open('texts/jokes.json') as js:
jk = json.load(js)
try:
if 15 >= int(number) > 0:
clef = str(number)
else:
clef = str(random.randint(1, 15))
except Exception:
clef = str(random.randint(1, 15))
joke = jk["{}".format(clef)]
embed = discord.Embed(title="Blague _{}_ : ".format(clef),
description=joke['content'], colour=0x03C9A9)
embed.set_footer(text="Par " + joke['author'])
embed.set_thumbnail(url='https://outout.tech/tuxbot/blobjoy.png')
await ctx.send(embed=embed)
"""---------------------------------------------------------------------"""
@commands.command()
async def ethylotest(self, ctx):
"""Ethylotest simulator 2018"""
results_poulet = ["Désolé mais mon ethylotest est sous Windows Vista, "
"merci de patienter...",
"_(ethylotest)_ : Une erreur est survenue. Windows "
"cherche une solution à se problème.",
"Mais j'l'ai foutu où ce p\\*\\*\\* d'ethylotest de m\\*\\*\\* "
"bordel fait ch\\*\\*\\*",
"C'est pas possible z'avez cassé l'ethylotest !"]
results_client = ["D'accord, il n'y a pas de problème à cela je suis "
"complètement clean",
"Bien sur si c'est votre devoir !", "Suce bi\\*e !",
"J'ai l'air d'être bourré ?",
"_laissez moi prendre un bonbon à la menthe..._"]
result_p = random.choice(results_poulet)
result_c = random.choice(results_client)
await ctx.send(":oncoming_police_car: Bonjour bonjour, contrôle "
"d'alcoolémie !")
await asyncio.sleep(0.5)
await ctx.send(":man: " + result_c)
await asyncio.sleep(1)
await ctx.send(":police_car: " + result_p)
"""---------------------------------------------------------------------"""
@commands.command()
async def coin(self, ctx):
"""Coin flip simulator 2025"""
starts_msg = ["Je lance la pièce !", "C'est parti !", "C'est une pièce"
" d'un cent faut"
" pas la perdre",
"C'est une pièce d'un euro faut pas la perdre",
"Je lance !"]
results_coin = ["{0} pile", "{0} face", "{1} Heu c'est quoi pile c'est"
" quoi face enfaite ?",
"{1} Oh shit, je crois que je l'ai perdue",
"{1} Et bim je te vol ta pièce !",
"{0} Oh une erreur d'impression il n'y a ni pile ni"
" face !"]
start = random.choice(starts_msg)
result = random.choice(results_coin)
await ctx.send(start)
await asyncio.sleep(0.6)
await ctx.send(result.format(":moneybag: Et la pièce retombe sur ...",
":robot:"))
"""---------------------------------------------------------------------"""
@commands.command()
async def pokemon(self, ctx):
"""Random pokemon fight"""
with open('texts/pokemons.json') as js:
jk = json.load(js)
poke1 = jk[random.randint(1, 150)]
poke2 = jk[random.randint(1, 150)]
try:
if poke1['MaxHP'] > poke2['MaxHP']:
winer = poke1
else:
winer = poke2
except KeyError:
winer = poke1
await ctx.send(":flag_white: **Le combat commence !**")
await asyncio.sleep(1)
await ctx.send(":loudspeaker: Les concurants sont {} contre {} ! Bonne"
" chance à eux !".format(poke1["Name"], poke2["Name"]))
await asyncio.sleep(0.5)
await ctx.send(":boom: {} commence et utilise {}".format(
poke1["Name"], poke1["Fast Attack(s)"][0]["Name"]))
await asyncio.sleep(1)
await ctx.send(":dash: {} réplique avec {}".format(
poke2["Name"], poke2["Fast Attack(s)"][0]["Name"]))
await asyncio.sleep(1.2)
await ctx.send("_le combat continue de se dérouler..._")
await asyncio.sleep(1.5)
await ctx.send(":trophy: Le gagnant est **{}** !".format(
winer["Name"]))
"""---------------------------------------------------------------------"""
@commands.command()
async def randomcat(self, ctx):
"""Display a random cat"""
r = requests.get('http://aws.random.cat/meow')
cat = str(r.json()['file'])
embed = discord.Embed(title="Meow",
description="[Voir le chat plus grand]({})".
format(cat), colour=0x03C9A9)
embed.set_thumbnail(url=cat)
embed.set_author(name="Random.cat", url='https://random.cat/')
await ctx.send(embed=embed)
def setup(bot):
bot.add_cog(Funs(bot))

View File

@ -1,41 +1,41 @@
import asyncio
import threading
from aiohttp import web
from discord.ext import commands
from bot import TuxBot
class Monitoring(commands.Cog):
def __init__(self):
self.app = web.Application()
t = threading.Thread(
target=self.run_server,
args=(self.aiohttp_server(),)
)
t.start()
def aiohttp_server(self):
async def hi(request):
return web.Response(text="I'm alive !")
self.app.add_routes([web.get('/', hi)])
runner = web.AppRunner(self.app)
return runner
@staticmethod
def run_server(runner):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(runner.setup())
site = web.TCPSite(runner, '0.0.0.0', 3389)
loop.run_until_complete(site.start())
loop.run_forever()
def setup(bot: TuxBot):
bot.add_cog(Monitoring())
import asyncio
import threading
from aiohttp import web
from discord.ext import commands
from bot import TuxBot
class Monitoring(commands.Cog):
def __init__(self):
self.app = web.Application()
t = threading.Thread(
target=self.run_server,
args=(self.aiohttp_server(),)
)
t.start()
def aiohttp_server(self):
async def hi(request):
return web.Response(text="I'm alive !")
self.app.add_routes([web.get('/', hi)])
runner = web.AppRunner(self.app)
return runner
@staticmethod
def run_server(runner):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(runner.setup())
site = web.TCPSite(runner, '0.0.0.0', 3389)
loop.run_until_complete(site.start())
loop.run_forever()
def setup(bot: TuxBot):
bot.add_cog(Monitoring())

View File

@ -1,152 +1,152 @@
from discord.ext import commands
import discord
class Role(commands.Cog):
"""Commandes role."""
def __init__(self, bot):
self.bot = bot
self.ARCH_ROLE = 393077257826205706
self.DEBIAN_ROLE = 393077933209550859
self.RHEL_ROLE = 393078333245751296
self.ANDROID_ROLE = 393087862972612627
self.BSD_ROLE = 401791543708745738
@commands.group(name="role", no_pm=True, pass_context=True,
case_insensitive=True)
async def _role(self, ctx):
"""Affiche l'aide sur la commande role"""
if ctx.message.guild.id != 280805240977227776:
return
if ctx.invoked_subcommand is None:
text = open('texts/roles.md').read()
em = discord.Embed(title='Gestionnaires de rôles',
description=text, colour=0x89C4F9)
await ctx.send(embed=em)
"""---------------------------------------------------------------------"""
@_role.command(name="arch", aliases=["archlinux", "arch_linux"],
pass_context=True)
async def role_arch(self, ctx):
"""Ajoute/retire le role 'Arch user'"""
roles = ctx.message.author.roles
role_id = []
for role in roles:
role_id.append(role.id)
user = ctx.message.author
if self.ARCH_ROLE in role_id:
await user.remove_roles(discord.Object(id=self.ARCH_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Pourquoi tu viens "
f"de supprimer Arch Linux, c'était trop compliqué "
f"pour toi ? <:sad:343723037331292170>")
else:
await user.add_roles(discord.Object(id=self.ARCH_ROLE))
await ctx.send(f"{ctx.message.author.mention} > How un "
f"ArchLinuxien, c'est bon les ``yaourt`` ? "
f"<:hap:354275645574086656>")
"""---------------------------------------------------------------------"""
@_role.command(name="debian", pass_context=True)
async def role_debian(self, ctx):
"""Ajoute/retire le role 'debian user'"""
roles = ctx.message.author.roles
role_id = []
for role in roles:
role_id.append(role.id)
user = ctx.message.author
if self.DEBIAN_ROLE in role_id:
await user.remove_roles(discord.Object(id=self.DEBIAN_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Adieu ! Tu verras, "
f"APT te manquera ! ")
else:
await user.add_roles(discord.Object(id=self.DEBIAN_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Un utilisateur de "
f"Debian, encore et encore ! "
f"<:stuck_out_tongue:343723077412323339>")
"""---------------------------------------------------------------------"""
@_role.command(name="rhel", pass_context=True)
async def role_rhel(self, ctx):
"""Ajoute/retire le role 'rhel user'"""
roles = ctx.message.author.roles
role_id = []
for role in roles:
role_id.append(role.id)
user = ctx.message.author
if self.RHEL_ROLE in role_id:
await user.remove_roles(discord.Object(id=self.RHEL_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Pourquoi tu t'en "
f"vas, il sont déjà assez seul là-bas "
f"<:sad:343723037331292170>")
else:
await user.add_roles(discord.Object(id=self.RHEL_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Mais, voila "
f"quelqu'un qui porte des chapeaux ! "
f"<:hap:354275645574086656>")
"""---------------------------------------------------------------------"""
@_role.command(name="android", pass_context=True)
async def role_android(self, ctx):
"""Ajoute/retire le role 'android user'"""
roles = ctx.message.author.roles
role_id = []
for role in roles:
role_id.append(role.id)
user = ctx.message.author
if self.ANDROID_ROLE in role_id:
await user.remove_roles(discord.Object(id=self.ANDROID_ROLE))
await ctx.send(f"{ctx.message.author.mention} >How, me dit pas "
f"que tu as compris que les Android's allaient "
f"exterminer le monde ? "
f"<:trollface:375327667160875008>")
else:
await user.add_roles(discord.Object(id=self.ANDROID_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Hey, un utilisateur"
f" d'Android, prêt à continuer l'extermination de "
f"WP et iOS ? "
f"<:stuck_out_tongue:343723077412323339>")
"""---------------------------------------------------------------------"""
@_role.command(name="bsd", pass_context=True)
async def role_bsd(self, ctx):
"""Ajoute/retire le role 'BSD user'"""
roles = ctx.message.author.roles
role_id = []
for role in roles:
role_id.append(role.id)
user = ctx.message.author
if self.BSD_ROLE in role_id:
await user.remove_roles(discord.Object(id=self.BSD_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Ohhhh fait gaffe "
f"ou le démon va te piquer")
else:
await user.add_roles(discord.Object(id=self.BSD_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Quelqu'un sous "
f"BSD ! Au moins il a pas besoin de mettre GNU "
f"devant son OS à chaque fois :d")
"""---------------------------------------------------------------------"""
@_role.command(name="staff", pass_context=True, hidden=True)
async def role_staff(self, ctx):
"""Easter egg"""
await ctx.send(f"{ctx.message.author.mention} > Vous n'avez pas "
f"le rôle staff, tu crois quoi :joy:")
def setup(bot):
bot.add_cog(Role(bot))
from discord.ext import commands
import discord
class Role(commands.Cog):
"""Commandes role."""
def __init__(self, bot):
self.bot = bot
self.ARCH_ROLE = 393077257826205706
self.DEBIAN_ROLE = 393077933209550859
self.RHEL_ROLE = 393078333245751296
self.ANDROID_ROLE = 393087862972612627
self.BSD_ROLE = 401791543708745738
@commands.group(name="role", no_pm=True, pass_context=True,
case_insensitive=True)
async def _role(self, ctx):
"""Affiche l'aide sur la commande role"""
if ctx.message.guild.id != 280805240977227776:
return
if ctx.invoked_subcommand is None:
text = open('texts/roles.md').read()
em = discord.Embed(title='Gestionnaires de rôles',
description=text, colour=0x89C4F9)
await ctx.send(embed=em)
"""---------------------------------------------------------------------"""
@_role.command(name="arch", aliases=["archlinux", "arch_linux"],
pass_context=True)
async def role_arch(self, ctx):
"""Ajoute/retire le role 'Arch user'"""
roles = ctx.message.author.roles
role_id = []
for role in roles:
role_id.append(role.id)
user = ctx.message.author
if self.ARCH_ROLE in role_id:
await user.remove_roles(discord.Object(id=self.ARCH_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Pourquoi tu viens "
f"de supprimer Arch Linux, c'était trop compliqué "
f"pour toi ? <:sad:343723037331292170>")
else:
await user.add_roles(discord.Object(id=self.ARCH_ROLE))
await ctx.send(f"{ctx.message.author.mention} > How un "
f"ArchLinuxien, c'est bon les ``yaourt`` ? "
f"<:hap:354275645574086656>")
"""---------------------------------------------------------------------"""
@_role.command(name="debian", pass_context=True)
async def role_debian(self, ctx):
"""Ajoute/retire le role 'debian user'"""
roles = ctx.message.author.roles
role_id = []
for role in roles:
role_id.append(role.id)
user = ctx.message.author
if self.DEBIAN_ROLE in role_id:
await user.remove_roles(discord.Object(id=self.DEBIAN_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Adieu ! Tu verras, "
f"APT te manquera ! ")
else:
await user.add_roles(discord.Object(id=self.DEBIAN_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Un utilisateur de "
f"Debian, encore et encore ! "
f"<:stuck_out_tongue:343723077412323339>")
"""---------------------------------------------------------------------"""
@_role.command(name="rhel", pass_context=True)
async def role_rhel(self, ctx):
"""Ajoute/retire le role 'rhel user'"""
roles = ctx.message.author.roles
role_id = []
for role in roles:
role_id.append(role.id)
user = ctx.message.author
if self.RHEL_ROLE in role_id:
await user.remove_roles(discord.Object(id=self.RHEL_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Pourquoi tu t'en "
f"vas, il sont déjà assez seul là-bas "
f"<:sad:343723037331292170>")
else:
await user.add_roles(discord.Object(id=self.RHEL_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Mais, voila "
f"quelqu'un qui porte des chapeaux ! "
f"<:hap:354275645574086656>")
"""---------------------------------------------------------------------"""
@_role.command(name="android", pass_context=True)
async def role_android(self, ctx):
"""Ajoute/retire le role 'android user'"""
roles = ctx.message.author.roles
role_id = []
for role in roles:
role_id.append(role.id)
user = ctx.message.author
if self.ANDROID_ROLE in role_id:
await user.remove_roles(discord.Object(id=self.ANDROID_ROLE))
await ctx.send(f"{ctx.message.author.mention} >How, me dit pas "
f"que tu as compris que les Android's allaient "
f"exterminer le monde ? "
f"<:trollface:375327667160875008>")
else:
await user.add_roles(discord.Object(id=self.ANDROID_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Hey, un utilisateur"
f" d'Android, prêt à continuer l'extermination de "
f"WP et iOS ? "
f"<:stuck_out_tongue:343723077412323339>")
"""---------------------------------------------------------------------"""
@_role.command(name="bsd", pass_context=True)
async def role_bsd(self, ctx):
"""Ajoute/retire le role 'BSD user'"""
roles = ctx.message.author.roles
role_id = []
for role in roles:
role_id.append(role.id)
user = ctx.message.author
if self.BSD_ROLE in role_id:
await user.remove_roles(discord.Object(id=self.BSD_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Ohhhh fait gaffe "
f"ou le démon va te piquer")
else:
await user.add_roles(discord.Object(id=self.BSD_ROLE))
await ctx.send(f"{ctx.message.author.mention} > Quelqu'un sous "
f"BSD ! Au moins il a pas besoin de mettre GNU "
f"devant son OS à chaque fois :d")
"""---------------------------------------------------------------------"""
@_role.command(name="staff", pass_context=True, hidden=True)
async def role_staff(self, ctx):
"""Easter egg"""
await ctx.send(f"{ctx.message.author.mention} > Vous n'avez pas "
f"le rôle staff, tu crois quoi :joy:")
def setup(bot):
bot.add_cog(Role(bot))

View File

@ -1,157 +1,157 @@
from discord.ext import commands
import discord
import asyncio
import urllib.request
import wikipedia
wikipedia.set_lang("fr")
class Search(commands.Cog):
"""Commandes de WWW."""
def __init__(self, bot):
self.bot = bot
@commands.group(name="search", no_pm=True, pass_context=True)
async def _search(self, ctx):
"""Rechercher sur le world wide web"""
if ctx.invoked_subcommand is None:
text = open('texts/search.md').read()
em = discord.Embed(title='Commandes de search TuxBot',
description=text,
colour=0x89C4F9)
await ctx.send(embed=em)
@_search.command(pass_context=True, name="docubuntu")
async def search_docubuntu(self, ctx, args):
attends = await ctx.send("_Je te cherche ça {} !_".format(
ctx.message.author.mention))
html = urllib.request.urlopen("https://doc.ubuntu-fr.org/" +
args).read()
if "avez suivi un lien" in str(html):
await attends.edit(content=":sob: Nooooon ! Cette page n'existe "
"pas, mais tu peux toujours la créer : "
"https://doc.ubuntu-fr.org/" + args)
else:
await attends.delete()
embed = discord.Embed(description="Voila j'ai trouvé ! Voici la "
"page ramenant à votre recherche,"
" toujours aussi bien rédigée "
":wink: : https://doc.ubuntu-fr."
"org/" + args,
url='http://doc.ubuntu-fr.org/')
embed.set_author(name="DocUbuntu-Fr",
url='http://doc.ubuntu-fr.org/',
icon_url='https://tuxbot.outout.xyz/data/ubuntu.png')
embed.set_thumbnail(url='https://tuxbot.outout.xyz/data/ubuntu.png')
embed.set_footer(text="Merci à ceux qui ont pris le temps d'écrire "
"cette documentation")
await ctx.send(embed=embed)
@_search.command(pass_context=True, name="docarch")
async def search_docarch(self, ctx, args):
attends = await ctx.send("_Je te cherche ça {} !_".format(
ctx.message.author.mention))
html = urllib.request.urlopen("https://wiki.archlinux.org/index.php/" +
args).read()
if "There is currently no text in this page" in str(html):
await attends.edit(content=":sob: Nooooon ! Cette page n'existe "
"pas.")
else:
await attends.delete()
embed = discord.Embed(description="Voila j'ai trouvé ! Voici la "
"page ramenant à votre recherche,"
" toujours aussi bien rédigée "
":wink: : https://wiki.archlinux."
"org/index.php/" + args,
url='https://wiki.archlinux.org/index.php/')
embed.set_author(name="Doc ArchLinux",
url='https://wiki.archlinux.org/index.php/',
icon_url='https://tuxbot.outout.xyz/data/arch.png')
embed.set_thumbnail(url='https://tuxbot.outout.xyz/data/arch.png')
embed.set_footer(text="Merci à ceux qui ont pris le temps d'écrire "
"cette documentation")
await ctx.send(embed=embed)
@_search.command(pass_context=True, name="wikipedia")
async def search_wikipedia(self, ctx: commands.Context, args):
"""Fait une recherche sur wikipd"""
wait = await ctx.send("_Je cherche..._")
results = wikipedia.search(args)
nbmr = 0
mmssgg = ""
for value in results:
nbmr = nbmr + 1
mmssgg = mmssgg + "**{}**: {} \n".format(str(nbmr), value)
em = discord.Embed(title='Résultats de : ' + args,
description = mmssgg,
colour=0x4ECDC4)
em.set_thumbnail(url="https://upload.wikimedia.org/wikipedia/commons/"
"2/26/Paullusmagnus-logo_%28large%29.png")
await wait.delete()
sending = ["1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟"]
def check(reaction, user):
return user == ctx.author and reaction.emoji in sending and \
reaction.message.id == msg.id
async def waiter(future: asyncio.Future):
reaction, user = await self.bot.wait_for('reaction_add',
check=check)
future.set_result(reaction.emoji)
emoji = asyncio.Future()
self.bot.loop.create_task(waiter(emoji))
msg = await ctx.send(embed=em)
for e in sending:
await msg.add_reaction(e)
if emoji.done():
break
while not emoji.done():
await asyncio.sleep(0.1)
page = int(sending.index(emoji.result()))
args_ = results[page]
try:
await msg.delete()
await ctx.trigger_typing()
wait = await ctx.send(ctx.message.author.mention +
" ah ok sympa cette recherche, je l'effectue de suite !")
wp = wikipedia.page(args_)
wp_contenu = wp.summary[:200] + "..."
em = discord.Embed(title='Wikipedia : ' + wp.title,
description = "{} \n_Lien_ : {} ".format(
wp_contenu, wp.url),
colour=0x9B59B6)
em.set_author(name="Wikipedia",
url='http://wikipedia.org',
icon_url='https://upload.wikimedia.org/wikipedia/'
'commons/2/26/Paullusmagnus-logo_%28large'
'%29.png')
em.set_thumbnail(url = "https://upload.wikimedia.org/wikipedia/"
"commons/2/26/Paullusmagnus-logo_%28large"
"%29.png")
em.set_footer(text="Merci à eux de nous fournir une encyclopédie "
"libre !")
await wait.delete()
await ctx.send(embed=em)
except wikipedia.exceptions.PageError:
# TODO : A virer dans l'event on_error
await ctx.send(":open_mouth: Une **erreur interne** est survenue,"
" si cela ce reproduit contactez votre"
" administrateur ou faites une Issue sur"
" ``gitea`` !")
def setup(bot):
bot.add_cog(Search(bot))
from discord.ext import commands
import discord
import asyncio
import urllib.request
import wikipedia
wikipedia.set_lang("fr")
class Search(commands.Cog):
"""Commandes de WWW."""
def __init__(self, bot):
self.bot = bot
@commands.group(name="search", no_pm=True, pass_context=True)
async def _search(self, ctx):
"""Rechercher sur le world wide web"""
if ctx.invoked_subcommand is None:
text = open('texts/search.md').read()
em = discord.Embed(title='Commandes de search TuxBot',
description=text,
colour=0x89C4F9)
await ctx.send(embed=em)
@_search.command(pass_context=True, name="docubuntu")
async def search_docubuntu(self, ctx, args):
attends = await ctx.send("_Je te cherche ça {} !_".format(
ctx.message.author.mention))
html = urllib.request.urlopen("https://doc.ubuntu-fr.org/" +
args).read()
if "avez suivi un lien" in str(html):
await attends.edit(content=":sob: Nooooon ! Cette page n'existe "
"pas, mais tu peux toujours la créer : "
"https://doc.ubuntu-fr.org/" + args)
else:
await attends.delete()
embed = discord.Embed(description="Voila j'ai trouvé ! Voici la "
"page ramenant à votre recherche,"
" toujours aussi bien rédigée "
":wink: : https://doc.ubuntu-fr."
"org/" + args,
url='http://doc.ubuntu-fr.org/')
embed.set_author(name="DocUbuntu-Fr",
url='http://doc.ubuntu-fr.org/',
icon_url='https://tuxbot.outout.xyz/data/ubuntu.png')
embed.set_thumbnail(url='https://tuxbot.outout.xyz/data/ubuntu.png')
embed.set_footer(text="Merci à ceux qui ont pris le temps d'écrire "
"cette documentation")
await ctx.send(embed=embed)
@_search.command(pass_context=True, name="docarch")
async def search_docarch(self, ctx, args):
attends = await ctx.send("_Je te cherche ça {} !_".format(
ctx.message.author.mention))
html = urllib.request.urlopen("https://wiki.archlinux.org/index.php/" +
args).read()
if "There is currently no text in this page" in str(html):
await attends.edit(content=":sob: Nooooon ! Cette page n'existe "
"pas.")
else:
await attends.delete()
embed = discord.Embed(description="Voila j'ai trouvé ! Voici la "
"page ramenant à votre recherche,"
" toujours aussi bien rédigée "
":wink: : https://wiki.archlinux."
"org/index.php/" + args,
url='https://wiki.archlinux.org/index.php/')
embed.set_author(name="Doc ArchLinux",
url='https://wiki.archlinux.org/index.php/',
icon_url='https://tuxbot.outout.xyz/data/arch.png')
embed.set_thumbnail(url='https://tuxbot.outout.xyz/data/arch.png')
embed.set_footer(text="Merci à ceux qui ont pris le temps d'écrire "
"cette documentation")
await ctx.send(embed=embed)
@_search.command(pass_context=True, name="wikipedia")
async def search_wikipedia(self, ctx: commands.Context, args):
"""Fait une recherche sur wikipd"""
wait = await ctx.send("_Je cherche..._")
results = wikipedia.search(args)
nbmr = 0
mmssgg = ""
for value in results:
nbmr = nbmr + 1
mmssgg = mmssgg + "**{}**: {} \n".format(str(nbmr), value)
em = discord.Embed(title='Résultats de : ' + args,
description = mmssgg,
colour=0x4ECDC4)
em.set_thumbnail(url="https://upload.wikimedia.org/wikipedia/commons/"
"2/26/Paullusmagnus-logo_%28large%29.png")
await wait.delete()
sending = ["1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟"]
def check(reaction, user):
return user == ctx.author and reaction.emoji in sending and \
reaction.message.id == msg.id
async def waiter(future: asyncio.Future):
reaction, user = await self.bot.wait_for('reaction_add',
check=check)
future.set_result(reaction.emoji)
emoji = asyncio.Future()
self.bot.loop.create_task(waiter(emoji))
msg = await ctx.send(embed=em)
for e in sending:
await msg.add_reaction(e)
if emoji.done():
break
while not emoji.done():
await asyncio.sleep(0.1)
page = int(sending.index(emoji.result()))
args_ = results[page]
try:
await msg.delete()
await ctx.trigger_typing()
wait = await ctx.send(ctx.message.author.mention +
" ah ok sympa cette recherche, je l'effectue de suite !")
wp = wikipedia.page(args_)
wp_contenu = wp.summary[:200] + "..."
em = discord.Embed(title='Wikipedia : ' + wp.title,
description = "{} \n_Lien_ : {} ".format(
wp_contenu, wp.url),
colour=0x9B59B6)
em.set_author(name="Wikipedia",
url='http://wikipedia.org',
icon_url='https://upload.wikimedia.org/wikipedia/'
'commons/2/26/Paullusmagnus-logo_%28large'
'%29.png')
em.set_thumbnail(url = "https://upload.wikimedia.org/wikipedia/"
"commons/2/26/Paullusmagnus-logo_%28large"
"%29.png")
em.set_footer(text="Merci à eux de nous fournir une encyclopédie "
"libre !")
await wait.delete()
await ctx.send(embed=em)
except wikipedia.exceptions.PageError:
# TODO : A virer dans l'event on_error
await ctx.send(":open_mouth: Une **erreur interne** est survenue,"
" si cela ce reproduit contactez votre"
" administrateur ou faites une Issue sur"
" ``gitea`` !")
def setup(bot):
bot.add_cog(Search(bot))

View File

@ -1,105 +1,105 @@
import datetime
import socket
import discord
from discord.ext import commands
class SendLogs(commands.Cog):
"""Send logs to a specific channel"""
def __init__(self, bot):
self.bot = bot
self.log_channel = None
self.main_server_id = int(self.bot.config.main_server_id)
@commands.Cog.listener()
async def on_resumed(self):
em = discord.Embed(title="Et hop je me reconnecte à l'api 😃",
colour=0x5cb85c)
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
@commands.Cog.listener()
async def on_ready(self):
self.log_channel = await self.bot.fetch_channel(int(self.bot.config.log_channel_id))
em = discord.Embed(title="Je suis opérationnel 😃",
description=f"*Instance lancée sur "
f"{socket.gethostname()}*", colour=0x5cb85c)
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.Cog.listener()
async def on_guild_join(self, guild: discord.Guild):
em = discord.Embed(title=f"On m'a ajouté à : {str(guild.name)} 😃",
colour=0x51A351)
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
@commands.Cog.listener()
async def on_guild_remove(self, guild: discord.Guild):
em = discord.Embed(title=f"On m'a viré de : {str(guild.name)} 😦",
colour=0xBD362F)
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.Cog.listener()
async def on_member_join(self, member):
if member.guild.id == self.main_server_id:
em = discord.Embed(title=f"{str(member)} *`({str(member.id)})`* "
f"nous a rejoint 😃", colour=0x51A351)
em.set_footer(text=f"Compte crée le {member.created_at}")
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
@commands.Cog.listener()
async def on_member_remove(self, member):
if member.guild.id == self.main_server_id:
em = discord.Embed(title=f"{str(member)} *`({str(member.id)})`* "
f"nous a quitté 😦", colour=0xBD362F)
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.Cog.listener()
async def on_message_delete(self, message):
if message.guild.id == self.main_server_id and not message.author.bot:
async def is_a_command(message):
prefix_lenght = len(await self.bot.get_prefix(message))
command = (message.content.split()[0])[prefix_lenght:]
if command == '':
command = "not_a_command"
return self.bot.get_command(str(command))
if await is_a_command(message) is None:
em = discord.Embed(title=f"Message supprimé dans :"
f" {str(message.channel.name)}",
colour=0xBD362F)
em.add_field(name=f"{str(message.author)} "
f"*`({str(message.author.id)})`* "
f"a supprimé :", value=str(message.content))
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
@commands.Cog.listener()
async def on_message_edit(self, before, after):
if before.guild.id == self.main_server_id and not before.author.bot:
em = discord.Embed(title=f"Message edité dans : "
f"{before.channel.name}", colour=0x0088CC)
em.add_field(name=f"{str(before.author)} "
f"*`({str(before.author.id)})`* a"
f" edité :", value=str(before.content))
em.add_field(name="Pour remplacer par :", value=str(after.content))
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
def setup(bot):
bot.add_cog(SendLogs(bot))
import datetime
import socket
import discord
from discord.ext import commands
class SendLogs(commands.Cog):
"""Send logs to a specific channel"""
def __init__(self, bot):
self.bot = bot
self.log_channel = None
self.main_server_id = int(self.bot.config.main_server_id)
@commands.Cog.listener()
async def on_resumed(self):
em = discord.Embed(title="Et hop je me reconnecte à l'api 😃",
colour=0x5cb85c)
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
@commands.Cog.listener()
async def on_ready(self):
self.log_channel = await self.bot.fetch_channel(int(self.bot.config.log_channel_id))
em = discord.Embed(title="Je suis opérationnel 😃",
description=f"*Instance lancée sur "
f"{socket.gethostname()}*", colour=0x5cb85c)
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.Cog.listener()
async def on_guild_join(self, guild: discord.Guild):
em = discord.Embed(title=f"On m'a ajouté à : {str(guild.name)} 😃",
colour=0x51A351)
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
@commands.Cog.listener()
async def on_guild_remove(self, guild: discord.Guild):
em = discord.Embed(title=f"On m'a viré de : {str(guild.name)} 😦",
colour=0xBD362F)
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.Cog.listener()
async def on_member_join(self, member):
if member.guild.id == self.main_server_id:
em = discord.Embed(title=f"{str(member)} *`({str(member.id)})`* "
f"nous a rejoint 😃", colour=0x51A351)
em.set_footer(text=f"Compte crée le {member.created_at}")
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
@commands.Cog.listener()
async def on_member_remove(self, member):
if member.guild.id == self.main_server_id:
em = discord.Embed(title=f"{str(member)} *`({str(member.id)})`* "
f"nous a quitté 😦", colour=0xBD362F)
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.Cog.listener()
async def on_message_delete(self, message):
if message.guild.id == self.main_server_id and not message.author.bot:
async def is_a_command(message):
prefix_lenght = len(await self.bot.get_prefix(message))
command = (message.content.split()[0])[prefix_lenght:]
if command == '':
command = "not_a_command"
return self.bot.get_command(str(command))
if await is_a_command(message) is None:
em = discord.Embed(title=f"Message supprimé dans :"
f" {str(message.channel.name)}",
colour=0xBD362F)
em.add_field(name=f"{str(message.author)} "
f"*`({str(message.author.id)})`* "
f"a supprimé :", value=str(message.content))
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
@commands.Cog.listener()
async def on_message_edit(self, before, after):
if before.guild.id == self.main_server_id and not before.author.bot:
em = discord.Embed(title=f"Message edité dans : "
f"{before.channel.name}", colour=0x0088CC)
em.add_field(name=f"{str(before.author)} "
f"*`({str(before.author.id)})`* a"
f" edité :", value=str(before.content))
em.add_field(name="Pour remplacer par :", value=str(after.content))
em.timestamp = datetime.datetime.utcnow()
await self.log_channel.send(embed=em)
def setup(bot):
bot.add_cog(SendLogs(bot))

View File

@ -1,99 +1,99 @@
import asyncio
import discord
from discord.ext import commands
class Sondage(commands.Cog):
"""Commandes sondage."""
def __init__(self, bot):
self.bot = bot
@commands.command(pass_context=True)
async def sondage(self, ctx, *, msg="help"):
if msg != "help":
await ctx.message.delete()
options = msg.split(" | ")
times = [x for x in options if x.startswith("time=")]
if times:
time = int(times[0].strip("time="))
options.remove(times[0])
else:
time = 0
if len(options) <= 1:
raise commands.errors.MissingRequiredArgument
if len(options) >= 22:
return await ctx.send(f"{ctx.message.author.mention}> "
f":octagonal_sign: Vous ne pouvez pas "
f"mettre plus de 20 réponses !")
emoji = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟', '0⃣',
'🇦', '🇧', '🇨', '🇩', '🇪', '🇫', '🇬', '🇭', '🇮']
to_react = []
confirmation_msg = f"**{options[0].rstrip('?')}?**:\n\n"
for idx, option in enumerate(options[1:]):
confirmation_msg += f"{emoji[idx]} - {option}\n"
to_react.append(emoji[idx])
confirmation_msg += "*Sondage proposé par* " + \
str(ctx.message.author.mention)
if time == 0:
confirmation_msg += ""
else:
confirmation_msg += f"\n\nVous avez {time} secondes pour voter!"
poll_msg = await ctx.send(confirmation_msg)
for emote in to_react:
await poll_msg.add_reaction(emote)
if time != 0:
await asyncio.sleep(time)
async for message in ctx.message.channel.history():
if message.id == poll_msg.id:
poll_msg = message
results = {}
for reaction in poll_msg.reactions:
if reaction.emoji in to_react:
results[reaction.emoji] = reaction.count - 1
end_msg = "Le sondage est términé. Les résultats sont:\n\n"
for result in results:
end_msg += "{} {} - {} votes\n". \
format(result,
options[emoji.index(result)+1],
results[result])
top_result = max(results, key=lambda key: results[key])
if len([x for x in results
if results[x] == results[top_result]]) > 1:
top_results = []
for key, value in results.items():
if value == results[top_result]:
top_results.append(options[emoji.index(key)+1])
end_msg += "\nLes gagnants sont : {}". \
format(", ".join(top_results))
else:
top_result = options[emoji.index(top_result)+1]
end_msg += "\n\"{}\" est le gagnant!".format(top_result)
await ctx.send(end_msg)
else:
await ctx.send("please use `@tuxbot poll` (this is rewrite version in beta")
await ctx.message.delete()
text = open('texts/rpoll.md').read()
em = discord.Embed(title='Aide sur le sondage',
description=text,
colour=0xEEEEEE)
await ctx.send(embed=em)
def setup(bot):
bot.add_cog(Sondage(bot))
import asyncio
import discord
from discord.ext import commands
class Sondage(commands.Cog):
"""Commandes sondage."""
def __init__(self, bot):
self.bot = bot
@commands.command(pass_context=True)
async def sondage(self, ctx, *, msg="help"):
if msg != "help":
await ctx.message.delete()
options = msg.split(" | ")
times = [x for x in options if x.startswith("time=")]
if times:
time = int(times[0].strip("time="))
options.remove(times[0])
else:
time = 0
if len(options) <= 1:
raise commands.errors.MissingRequiredArgument
if len(options) >= 22:
return await ctx.send(f"{ctx.message.author.mention}> "
f":octagonal_sign: Vous ne pouvez pas "
f"mettre plus de 20 réponses !")
emoji = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟', '0⃣',
'🇦', '🇧', '🇨', '🇩', '🇪', '🇫', '🇬', '🇭', '🇮']
to_react = []
confirmation_msg = f"**{options[0].rstrip('?')}?**:\n\n"
for idx, option in enumerate(options[1:]):
confirmation_msg += f"{emoji[idx]} - {option}\n"
to_react.append(emoji[idx])
confirmation_msg += "*Sondage proposé par* " + \
str(ctx.message.author.mention)
if time == 0:
confirmation_msg += ""
else:
confirmation_msg += f"\n\nVous avez {time} secondes pour voter!"
poll_msg = await ctx.send(confirmation_msg)
for emote in to_react:
await poll_msg.add_reaction(emote)
if time != 0:
await asyncio.sleep(time)
async for message in ctx.message.channel.history():
if message.id == poll_msg.id:
poll_msg = message
results = {}
for reaction in poll_msg.reactions:
if reaction.emoji in to_react:
results[reaction.emoji] = reaction.count - 1
end_msg = "Le sondage est términé. Les résultats sont:\n\n"
for result in results:
end_msg += "{} {} - {} votes\n". \
format(result,
options[emoji.index(result)+1],
results[result])
top_result = max(results, key=lambda key: results[key])
if len([x for x in results
if results[x] == results[top_result]]) > 1:
top_results = []
for key, value in results.items():
if value == results[top_result]:
top_results.append(options[emoji.index(key)+1])
end_msg += "\nLes gagnants sont : {}". \
format(", ".join(top_results))
else:
top_result = options[emoji.index(top_result)+1]
end_msg += "\n\"{}\" est le gagnant!".format(top_result)
await ctx.send(end_msg)
else:
await ctx.send("please use `@tuxbot poll` (this is rewrite version in beta")
await ctx.message.delete()
text = open('texts/rpoll.md').read()
em = discord.Embed(title='Aide sur le sondage',
description=text,
colour=0xEEEEEE)
await ctx.send(embed=em)
def setup(bot):
bot.add_cog(Sondage(bot))

View File

@ -1,363 +1,452 @@
import datetime
import json
import pytz
import random
import urllib
import discord
import requests
from discord.ext import commands
import socket
class Utility(commands.Cog):
"""Commandes utilitaires."""
def __init__(self, bot):
self.bot = bot
@commands.group(name="clock", pass_context=True, case_insensitive=True)
async def clock(self, ctx):
"""Display hour in a country"""
if ctx.invoked_subcommand is None:
text = open('texts/clocks.md').read()
em = discord.Embed(title='Liste des Horloges', description=text, colour=0xEEEEEE)
await ctx.send(embed=em)
@clock.command(name="montréal", aliases=["mtl", "montreal"], pass_context=True)
async def clock_montreal(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('America/Montreal'))
site = "http://ville.montreal.qc.ca/"
img = "https://upload.wikimedia.org/wikipedia/commons/e/e0/Rentier_fws_1.jpg"
country = "au Canada, Québec"
description = "Montréal est la deuxième ville la plus peuplée du Canada. Elle se situe dans la région du Québec"
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Montréal', description=f"A [Montréal]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="vancouver", pass_context=True)
async def clock_vancouver(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('America/Vancouver'))
site = "http://vancouver.ca/"
img = "https://upload.wikimedia.org/wikipedia/commons/f/fe/Dock_Vancouver.JPG"
country = "au Canada"
description = "Vancouver, officiellement City of Vancouver, est une cité portuaire au Canada"
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Vancouver', description=f"A [Vancouver]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="new-york",aliases=["ny", "n-y", "new york"], pass_context=True)
async def clock_new_york(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('America/New_York'))
site = "http://www1.nyc.gov/"
img = "https://upload.wikimedia.org/wikipedia/commons/e/e3/NewYork_LibertyStatue.jpg"
country = "aux U.S.A."
description = "New York, est la plus grande ville des États-Unis en termes d'habitants et l'une des plus importantes du continent américain. "
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à New York', description=f"A [str(New York]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="la", aliases=["los-angeles", "losangeles", "l-a", "los angeles"], pass_context=True)
async def clock_la(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('America/Los_Angeles'))
site = "https://www.lacity.org/"
img = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/57/LA_Skyline_Mountains2.jpg/800px-LA_Skyline_Mountains2.jpg"
country = "aux U.S.A."
description = "Los Angeles est la deuxième ville la plus peuplée des États-Unis après New York. Elle est située dans le sud de l'État de Californie, sur la côte pacifique."
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Los Angeles', description=f"A [Los Angeles]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="paris", aliases=["baguette"],pass_context=True)
async def clock_paris(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('Europe/Paris'))
site = "http://www.paris.fr/"
img = "https://upload.wikimedia.org/wikipedia/commons/a/af/Tour_eiffel_at_sunrise_from_the_trocadero.jpg"
country = "en France"
description = "Paris est la capitale de la France. Elle se situe au cœur d'un vaste bassin sédimentaire aux sols fertiles et au climat tempéré, le bassin parisien."
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Paris', description=f"A [Paris]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="berlin", pass_context=True)
async def clock_berlin(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('Europe/Berlin'))
site = "http://www.berlin.de/"
img = "https://upload.wikimedia.org/wikipedia/commons/9/91/Eduard_Gaertner_Schlossfreiheit.jpg"
country = "en Allemagne"
description = "Berlin est la capitale et la plus grande ville d'Allemagne. Située dans le nord-est du pays, elle compte environ 3,5 millions d'habitants. "
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Berlin', description=f"A [Berlin]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="berne", aliases=["zurich", "bern"], pass_context=True)
async def clock_berne(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('Europe/Zurich'))
site = "http://www.berne.ch/"
img = "https://upload.wikimedia.org/wikipedia/commons/d/db/Justitia_Statue_02.jpg"
country = "en Suisse"
description = "Berne est la cinquième plus grande ville de Suisse et la capitale du canton homonyme. Depuis 1848, Berne est la « ville fédérale »."
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Berne', description=f"A [Berne]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="tokyo", pass_context=True)
async def clock_tokyo(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('Asia/Tokyo'))
site = "http://www.gotokyo.org/"
img = "https://upload.wikimedia.org/wikipedia/commons/3/37/TaroTokyo20110213-TokyoTower-01.jpg"
country = "au Japon"
description = "Tokyo, anciennement Edo, officiellement la préfecture métropolitaine de Tokyo, est la capitale du Japon."
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Tokyo', description=f"A [Tokyo]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="moscou", aliases=["moscow", "moskova"], pass_context=True)
async def clock_moscou(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('Europe/Moscow'))
site = "https://www.mos.ru/"
img = "https://upload.wikimedia.org/wikipedia/commons/f/f7/Andreyevsky_Zal.jpg"
country = "en Russie"
description = "Moscou est la capitale de la Fédération de Russie et la plus grande ville d'Europe. Moscou est situé sur la rivière Moskova. "
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Moscou', description=f"A [Moscou]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.command()
async def ytdiscover(self, ctx):
"""Random youtube channel"""
with open('texts/ytb.json') as js:
ytb = json.load(js)
clef = str(random.randint(0,12))
chaine = ytb["{}".format(clef)]
embed = discord.Embed(title=chaine['name'],
url=chaine['url'],
description=f"**{chaine['name']}**, {chaine['desc']} \n[Je veux voir ça]({chaine['url']})")
embed.set_thumbnail(url='https://outout.tech/tuxbot/yt.png')
await ctx.send(embed=embed)
"""---------------------------------------------------------------------"""
@commands.command(name='iplocalise', pass_context=True)
async def _iplocalise(self, ctx, ipaddress, iptype=""):
realipaddress = ipaddress
"""Recup headers."""
if ipaddress.startswith("http://"):
if ipaddress[-1:] == '/':
ipaddress = ipaddress[:-1]
ipaddress = ipaddress.split("http://")[1]
if ipaddress.startswith("https://"):
if ipaddress[-1:] == '/':
ipaddress = ipaddress[:-1]
ipaddress = ipaddress.split("https://")[1]
if(iptype=="ipv6" or iptype=="v6"):
try:
ipaddress = socket.getaddrinfo(ipaddress, None, socket.AF_INET6)[1][4][0]
if (ipaddress == "2602:fe53:b:1::87") and not ("gnous" in realipaddress):
await ctx.send("Erreur, cette adresse n'est pas disponible en IPv6.")
return
except Exception as e:
await ctx.send("Erreur, cette adresse n'est pas disponible en IPv6.")
print(e)
return
iploading = await ctx.send("_réfléchis..._")
ipapi = urllib.request.urlopen("http://ip-api.com/json/" + ipaddress)
ipinfo = json.loads(ipapi.read().decode())
if ipinfo["status"] != "fail":
if ipinfo['query']:
embed = discord.Embed(title=f"Informations pour ``{realipaddress}`` *`({ipinfo['query']})`*", color=0x5858d7)
if ipinfo['org']:
embed.add_field(name="Appartient à :", value=ipinfo['org'], inline = False)
if ipinfo['city']:
embed.add_field(name="Se situe à :", value=ipinfo['city'], inline = True)
if ipinfo['country']:
if ipinfo['regionName']:
regionName = ipinfo['regionName']
else:
regionName = "N/A"
embed.add_field(name="Region :", value=f"{regionName} ({ipinfo['country']})", inline = True)
embed.set_thumbnail(url=f"https://www.countryflags.io/{ipinfo['countryCode']}/flat/64.png")
await ctx.send(embed=embed)
else:
await ctx.send(content=f"Erreur, impossible d'avoir des informations sur l'adresse IP ``{ipinfo['query']}``")
await iploading.delete()
"""---------------------------------------------------------------------"""
@commands.command(name='getheaders', pass_context=True)
async def _getheaders(self, ctx, *, adresse):
"""Recuperer les HEADERS :d"""
if adresse.startswith("http://") != True and adresse.startswith("https://") != True:
adresse = "http://" + adresse
if len(adresse) > 200:
await ctx.send("{0} Essaye d'entrer une adresse de moins de 200 caractères plutôt.".format(ctx.author.mention))
elif adresse.startswith("http://") or adresse.startswith("https://") or adresse.startswith("ftp://"):
try:
get = urllib.request.urlopen(adresse, timeout = 1)
embed = discord.Embed(title="Entêtes de {0}".format(adresse), color=0xd75858)
embed.add_field(name="Code Réponse", value=get.getcode(), inline = True)
embed.set_thumbnail(url="https://http.cat/{}".format(str(get.getcode())))
if get.getheader('location'):
embed.add_field(name="Redirection vers", value=get.getheader('location'), inline=True)
if get.getheader('server'):
embed.add_field(name="Serveur", value=get.getheader('server'), inline=True)
if get.getheader('content-type'):
embed.add_field(name="Type de contenu", value = get.getheader('content-type'), inline = True)
if get.getheader('x-content-type-options'):
embed.add_field(name="x-content-type", value= get.getheader('x-content-type-options'), inline=True)
if get.getheader('x-frame-options'):
embed.add_field(name="x-frame-options", value= get.getheader('x-frame-options'), inline=True)
if get.getheader('cache-control'):
embed.add_field(name="Controle du cache", value = get.getheader('cache-control'), inline = True)
await ctx.send(embed=embed)
except urllib.error.HTTPError as e:
embed = discord.Embed(title="Entêtes de {0}".format(adresse), color=0xd75858)
embed.add_field(name="Code Réponse", value=e.getcode(), inline = True)
embed.set_thumbnail(url="https://http.cat/{}".format(str(e.getcode())))
await ctx.send(embed=embed)
print('''An error occurred: {} The response code was {}'''.format(e, e.getcode()))
except urllib.error.URLError as e:
if "No address associated" in str(e):
await ctx.send("Erreur, aucune adresse n'est associé à ce nom d'hôte.")
return
if "timed out" in str(e):
await ctx.send("Erreur, l'adresse en question dépasse le délais d'attente :(")
return
if "SSL" in str(e):
await ctx.send("Erreur avec le certificat SSL, essayez sans ``https://`` !")
return
return
if "no host":
await ctx.send("Erreur, aucun nom d'hôte n'a été donné.")
return
if "not known":
await ctx.send("Erreur, nom de l'hôte inconnu.")
return
print("ERROR @ getheaders @ urlerror : {} - adress {}".format(e, adresse))
await ctx.send('[CONTACTER ADMIN] URLError: {}'.format(e.reason))
except Exception as e:
print("ERROR @ getheaders @ Exception : {} - adress {}".format(e, adresse))
await ctx.send("{0} Impossible d'accèder à {1}, es-tu sur que l'adresse {1} est correcte et que le serveur est allumé ?".format(ctx.author.mention, adresse))
else:
await ctx.send("{0} Merci de faire commencer {1} par ``https://``, ``http://`` ou ``ftp://``.".format(ctx.author.mention, adresse))
"""---------------------------------------------------------------------"""
@commands.command(name='git', pass_context=True)
async def _git(self, ctx):
"""Pour voir mon code"""
text = "How 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"
em = discord.Embed(title='Repos TuxBot-Bot', description=text, colour=0xE9D460)
em.set_author(name='Gnous', icon_url="https://cdn.discordapp.com/"
"icons/280805240977227776/"
"9ba1f756c9d9bfcf27989d0d0abb3862"
".png")
await ctx.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.command(name='quote', pass_context=True)
async def _quote(self, ctx, quote_id):
global quoted_message
async def get_message(message_id: int):
for channel in ctx.message.guild.channels:
if isinstance(channel, discord.TextChannel):
test_chan = await self.bot.fetch_channel(channel.id)
try:
return await test_chan.fetch_message(message_id)
except (discord.NotFound, discord.Forbidden):
pass
return None
quoted_message = await get_message(int(quote_id))
if quoted_message is not None:
embed = discord.Embed(colour=quoted_message.author.colour,
description=quoted_message.clean_content,
timestamp=quoted_message.created_at)
embed.set_author(name=quoted_message.author.display_name,
icon_url=quoted_message.author.avatar_url_as(
format="jpg"))
if len(quoted_message.attachments) >= 1:
embed.set_image(url=quoted_message.attachments[0].url)
embed.add_field(name="**Original**",
value=f"[Go!]({quoted_message.jump_url})")
embed.set_footer(text="#" + quoted_message.channel.name)
await ctx.send(embed=embed)
else:
await ctx.send("Impossible de trouver le message.")
def setup(bot):
bot.add_cog(Utility(bot))
import datetime
import json
import pytz
import random
import urllib
import ipinfo as ipinfoio
from ipwhois.net import Net
from ipwhois.asn import IPASN
import discord
import requests
from discord.ext import commands
import socket
class Utility(commands.Cog):
"""Commandes utilitaires."""
def __init__(self, bot):
self.bot = bot
@commands.group(name="clock", pass_context=True, case_insensitive=True)
async def clock(self, ctx):
"""Display hour in a country"""
if ctx.invoked_subcommand is None:
text = open('texts/clocks.md').read()
em = discord.Embed(title='Liste des Horloges', description=text, colour=0xEEEEEE)
await ctx.send(embed=em)
@clock.command(name="montréal", aliases=["mtl", "montreal"], pass_context=True)
async def clock_montreal(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('America/Montreal'))
site = "http://ville.montreal.qc.ca/"
img = "https://upload.wikimedia.org/wikipedia/commons/e/e0/Rentier_fws_1.jpg"
country = "au Canada, Québec"
description = "Montréal est la deuxième ville la plus peuplée du Canada. Elle se situe dans la région du Québec"
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Montréal', description=f"A [Montréal]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="vancouver", pass_context=True)
async def clock_vancouver(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('America/Vancouver'))
site = "http://vancouver.ca/"
img = "https://upload.wikimedia.org/wikipedia/commons/f/fe/Dock_Vancouver.JPG"
country = "au Canada"
description = "Vancouver, officiellement City of Vancouver, est une cité portuaire au Canada"
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Vancouver', description=f"A [Vancouver]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="new-york",aliases=["ny", "n-y", "new york"], pass_context=True)
async def clock_new_york(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('America/New_York'))
site = "http://www1.nyc.gov/"
img = "https://upload.wikimedia.org/wikipedia/commons/e/e3/NewYork_LibertyStatue.jpg"
country = "aux U.S.A."
description = "New York, est la plus grande ville des États-Unis en termes d'habitants et l'une des plus importantes du continent américain. "
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à New York', description=f"A [str(New York]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="la", aliases=["los-angeles", "losangeles", "l-a", "los angeles"], pass_context=True)
async def clock_la(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('America/Los_Angeles'))
site = "https://www.lacity.org/"
img = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/57/LA_Skyline_Mountains2.jpg/800px-LA_Skyline_Mountains2.jpg"
country = "aux U.S.A."
description = "Los Angeles est la deuxième ville la plus peuplée des États-Unis après New York. Elle est située dans le sud de l'État de Californie, sur la côte pacifique."
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Los Angeles', description=f"A [Los Angeles]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="paris", aliases=["baguette"],pass_context=True)
async def clock_paris(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('Europe/Paris'))
site = "http://www.paris.fr/"
img = "https://upload.wikimedia.org/wikipedia/commons/a/af/Tour_eiffel_at_sunrise_from_the_trocadero.jpg"
country = "en France"
description = "Paris est la capitale de la France. Elle se situe au cœur d'un vaste bassin sédimentaire aux sols fertiles et au climat tempéré, le bassin parisien."
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Paris', description=f"A [Paris]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="berlin", pass_context=True)
async def clock_berlin(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('Europe/Berlin'))
site = "http://www.berlin.de/"
img = "https://upload.wikimedia.org/wikipedia/commons/9/91/Eduard_Gaertner_Schlossfreiheit.jpg"
country = "en Allemagne"
description = "Berlin est la capitale et la plus grande ville d'Allemagne. Située dans le nord-est du pays, elle compte environ 3,5 millions d'habitants. "
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Berlin', description=f"A [Berlin]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="berne", aliases=["zurich", "bern"], pass_context=True)
async def clock_berne(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('Europe/Zurich'))
site = "http://www.berne.ch/"
img = "https://upload.wikimedia.org/wikipedia/commons/d/db/Justitia_Statue_02.jpg"
country = "en Suisse"
description = "Berne est la cinquième plus grande ville de Suisse et la capitale du canton homonyme. Depuis 1848, Berne est la « ville fédérale »."
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Berne', description=f"A [Berne]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="tokyo", pass_context=True)
async def clock_tokyo(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('Asia/Tokyo'))
site = "http://www.gotokyo.org/"
img = "https://upload.wikimedia.org/wikipedia/commons/3/37/TaroTokyo20110213-TokyoTower-01.jpg"
country = "au Japon"
description = "Tokyo, anciennement Edo, officiellement la préfecture métropolitaine de Tokyo, est la capitale du Japon."
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Tokyo', description=f"A [Tokyo]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
@clock.command(name="moscou", aliases=["moscow", "moskova"], pass_context=True)
async def clock_moscou(self, ctx):
then = datetime.datetime.now(pytz.utc)
utc = then.astimezone(pytz.timezone('Europe/Moscow'))
site = "https://www.mos.ru/"
img = "https://upload.wikimedia.org/wikipedia/commons/f/f7/Andreyevsky_Zal.jpg"
country = "en Russie"
description = "Moscou est la capitale de la Fédération de Russie et la plus grande ville d'Europe. Moscou est situé sur la rivière Moskova. "
form = '%H heures %M'
tt = utc.strftime(form)
em = discord.Embed(title='Heure à Moscou', description=f"A [Moscou]({site}) {country}, Il est **{str(tt)}** ! \n {description} \n _source des images et du texte : [Wikimedia foundation](http://commons.wikimedia.org/)_", colour=0xEEEEEE)
em.set_thumbnail(url = img)
await ctx.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.command()
async def ytdiscover(self, ctx):
"""Random youtube channel"""
with open('texts/ytb.json') as js:
ytb = json.load(js)
clef = str(random.randint(0,12))
chaine = ytb["{}".format(clef)]
embed = discord.Embed(title=chaine['name'],
url=chaine['url'],
description=f"**{chaine['name']}**, {chaine['desc']} \n[Je veux voir ça]({chaine['url']})")
embed.set_thumbnail(url='https://outout.tech/tuxbot/yt.png')
await ctx.send(embed=embed)
"""---------------------------------------------------------------------"""
@commands.command(name='iplocalise', pass_context=True)
async def _iplocalise(self, ctx, ipaddress, iptype=""):
realipaddress = ipaddress
"""Getting headers."""
if ipaddress.startswith("http://"):
if ipaddress[-1:] == '/':
ipaddress = ipaddress[:-1]
ipaddress = ipaddress.split("http://")[1]
if ipaddress.startswith("https://"):
if ipaddress[-1:] == '/':
ipaddress = ipaddress[:-1]
ipaddress = ipaddress.split("https://")[1]
if(iptype=="ipv6" or iptype=="v6" or iptype=="-6"):
try:
ipaddress = socket.getaddrinfo(ipaddress, None, socket.AF_INET6)[1][4][0]
except Exception as e:
await ctx.send("Erreur, cette adresse n'est pas disponible en IPv6.")
return
elif(iptype=="ipv4" or iptype=="v4" or iptype=="-4"):
try:
ipaddress = socket.getaddrinfo(ipaddress, None, socket.AF_INET)[1][4][0]
except Exception as e:
await ctx.send("Erreur, cette adresse n'est pas disponible en IPv4.")
return
else:
try:
ipaddress = socket.getaddrinfo(ipaddress, None)[1][4][0]
except Exception as e:
await ctx.send("Erreur, cette adresse n'est pas disponible.")
return
iploading = await ctx.send("_Récupération des informations..._")
net = Net(ipaddress)
obj = IPASN(net)
ipinfo = obj.lookup()
try:
iphostname = socket.gethostbyaddr(ipaddress)[0]
except:
iphostname = "N/A"
# IPINFO api
api_result = True
try:
access_token = open('ipinfoio.key').read()
handler = ipinfoio.getHandler(access_token)
details = handler.getDetails(ipaddress)
except:
api_result = False
try:
embed = discord.Embed(title=f"Informations pour ``{realipaddress} ({ipaddress})``", color=0x5858d7)
if(api_result):
embed.add_field(name="Appartient à :", value=f"{details.org}")
else:
embed.add_field(name="Appartient à :", value=f"{ipinfo['asn_description']} (AS{ipinfo['asn']})", inline = False)
embed.add_field(name="RIR :", value=f"{ipinfo['asn_registry']}", inline = True)
if(api_result):
embed.add_field(name="Région :", value=f"{details.city} - {details.region} ({details.country})")
else:
embed.add_field(name="Région :", value=f"{ipinfo['asn_country_code']}")
embed.add_field(name="Nom de l'hôte :", value=f"{iphostname}")
# Adding country flag
if(api_result):
embed.set_thumbnail(url=f"https://www.countryflags.io/{details.country}/shiny/64.png")
else:
embed.set_thumbnail(url=f"https://www.countryflags.io/{ipinfo['asn_country_code']}/shiny/64.png")
await ctx.send(embed=embed)
except:
await ctx.send(content=f"Erreur, impossible d'avoir des informations sur l'adresse IP ``{realipaddress}``")
await iploading.delete()
"""---------------------------------------------------------------------"""
@commands.command(name='getheaders', pass_context=True)
async def _getheaders(self, ctx, *, adresse):
"""Recuperer les HEADERS :d"""
if adresse.startswith("http://") != True and adresse.startswith("https://") != True:
adresse = "http://" + adresse
if len(adresse) > 200:
await ctx.send("{0} Essaye d'entrer une adresse de moins de 200 caractères plutôt.".format(ctx.author.mention))
elif adresse.startswith("http://") or adresse.startswith("https://") or adresse.startswith("ftp://"):
try:
get = urllib.request.urlopen(adresse, timeout = 1)
embed = discord.Embed(title="Entêtes de {0}".format(adresse), color=0xd75858)
embed.add_field(name="Code Réponse", value=get.getcode(), inline = True)
embed.set_thumbnail(url="https://http.cat/{}".format(str(get.getcode())))
if get.getheader('location'):
embed.add_field(name="Redirection vers", value=get.getheader('location'), inline=True)
if get.getheader('server'):
embed.add_field(name="Serveur", value=get.getheader('server'), inline=True)
if get.getheader('content-type'):
embed.add_field(name="Type de contenu", value = get.getheader('content-type'), inline = True)
if get.getheader('x-content-type-options'):
embed.add_field(name="x-content-type", value= get.getheader('x-content-type-options'), inline=True)
if get.getheader('x-frame-options'):
embed.add_field(name="x-frame-options", value= get.getheader('x-frame-options'), inline=True)
if get.getheader('cache-control'):
embed.add_field(name="Controle du cache", value = get.getheader('cache-control'), inline = True)
await ctx.send(embed=embed)
except urllib.error.HTTPError as e:
embed = discord.Embed(title="Entêtes de {0}".format(adresse), color=0xd75858)
embed.add_field(name="Code Réponse", value=e.getcode(), inline = True)
embed.set_thumbnail(url="https://http.cat/{}".format(str(e.getcode())))
await ctx.send(embed=embed)
print('''An error occurred: {} The response code was {}'''.format(e, e.getcode()))
except urllib.error.URLError as e:
if "No address associated" in str(e):
await ctx.send("Erreur, aucune adresse n'est associé à ce nom d'hôte.")
return
if "timed out" in str(e):
await ctx.send("Erreur, l'adresse en question dépasse le délais d'attente :(")
return
if "SSL" in str(e):
await ctx.send("Erreur avec le certificat SSL, essayez sans ``https://`` !")
return
return
if "no host":
await ctx.send("Erreur, aucun nom d'hôte n'a été donné.")
return
if "not known":
await ctx.send("Erreur, nom de l'hôte inconnu.")
return
print("ERROR @ getheaders @ urlerror : {} - adress {}".format(e, adresse))
await ctx.send('[CONTACTER ADMIN] URLError: {}'.format(e.reason))
except Exception as e:
print("ERROR @ getheaders @ Exception : {} - adress {}".format(e, adresse))
await ctx.send("{0} Impossible d'accèder à {1}, es-tu sur que l'adresse {1} est correcte et que le serveur est allumé ?".format(ctx.author.mention, adresse))
else:
await ctx.send("{0} Merci de faire commencer {1} par ``https://``, ``http://`` ou ``ftp://``.".format(ctx.author.mention, adresse))
"""---------------------------------------------------------------------"""
@commands.command(name='peeringdb', pass_context=True)
async def _peeringdb(self, ctx, *, asn):
def notEmptyField(embed, name, value):
if(value != ""):
embed.add_field(name=name, value=value)
if asn.startswith("AS"):
asn = asn[2:]
loadingmsg = await ctx.send("_Récupération des informations..._")
"""Getting the ASN id in the peeringdb database"""
try:
asnid = urllib.request.urlopen("https://www.peeringdb.com/api/as_set/" + asn)
asnid = json.loads(asnid.read().decode())
pdbid = asnid["data"][0][asn]
asinfo = urllib.request.urlopen("https://www.peeringdb.com/api/net?irr_as_set=" + pdbid)
asinfo = json.loads(asinfo.read().decode())["data"]
for asndata in asinfo:
if(asndata['asn'] == int(asn)):
asinfo = asndata
asproto = ""
if(asinfo["info_ipv6"]):
asproto = asproto + "IPv6 "
if(asinfo["info_unicast"]):
asproto = asproto + "Unicast "
if(asinfo["info_multicast"]):
asproto = asproto + "Multicast "
if(asinfo["info_never_via_route_servers"]):
asproto = asproto + "Never via Route servers"
print(pdbid)
embed = discord.Embed(title=f"Informations pour {asinfo['name']} ``AS{asn}``", color=0x5858d7)
notEmptyField(embed, name="Nom :", value=asinfo['name'])
notEmptyField(embed, name="Aka :", value=asinfo['aka'])
notEmptyField(embed, name="Site :", value=asinfo['website'])
notEmptyField(embed, name="Looking Glass :", value=asinfo['looking_glass'])
notEmptyField(embed, name="Traffic :", value=asinfo['info_traffic'])
notEmptyField(embed, name="Ratio du traffic :", value=asinfo['info_ratio'])
notEmptyField(embed, name="Prefixes IPv4 :", value=asinfo['info_prefixes4'])
notEmptyField(embed, name="Prefixes IPv6 :", value=asinfo['info_prefixes6'])
notEmptyField(embed, name="Politique de Peering :", value=f"[{asinfo['policy_general']}]({asinfo['policy_url']})")
notEmptyField(embed, name="Protocoles supportés :", value=asproto)
embed.set_footer(text=f"https://www.peeringdb.com/")
await ctx.send(embed=embed)
await loadingmsg.delete()
except IndexError:
await ctx.send(f"Impossible d'avoir des informations sur l'AS AS{asn}")
await loadingmsg.delete()
except urllib.error.HTTPError:
await ctx.send(f"L'AS{asn} est introuvable dans la base de données de PeeringDB.")
await loadingmsg.delete()
"""---------------------------------------------------------------------"""
@commands.command(name='git', pass_context=True)
async def _git(self, ctx):
"""Pour voir mon code"""
text = "How 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"
em = discord.Embed(title='Repos TuxBot-Bot', description=text, colour=0xE9D460)
em.set_author(name='Gnous', icon_url="https://cdn.discordapp.com/"
"icons/280805240977227776/"
"9ba1f756c9d9bfcf27989d0d0abb3862"
".png")
await ctx.send(embed=em)
"""---------------------------------------------------------------------"""
@commands.command(name='quote', pass_context=True)
async def _quote(self, ctx, quote_id):
global quoted_message
async def get_message(message_id: int):
for channel in ctx.message.guild.channels:
if isinstance(channel, discord.TextChannel):
test_chan = await self.bot.fetch_channel(channel.id)
try:
return await test_chan.fetch_message(message_id)
except (discord.NotFound, discord.Forbidden):
pass
return None
quoted_message = await get_message(int(quote_id))
if quoted_message is not None:
embed = discord.Embed(colour=quoted_message.author.colour,
description=quoted_message.clean_content,
timestamp=quoted_message.created_at)
embed.set_author(name=quoted_message.author.display_name,
icon_url=quoted_message.author.avatar_url_as(
format="jpg"))
if len(quoted_message.attachments) >= 1:
embed.set_image(url=quoted_message.attachments[0].url)
embed.add_field(name="**Original**",
value=f"[Go!]({quoted_message.jump_url})")
embed.set_footer(text="#" + quoted_message.channel.name)
await ctx.send(embed=embed)
else:
await ctx.send("Impossible de trouver le message.")
def setup(bot):
bot.add_cog(Utility(bot))

View File

@ -1,124 +1,124 @@
from discord.ext import commands
def is_owner_check(message):
return str(message.author.id) in ['171685542553976832',
'269156684155453451']
def is_owner(warn=True):
def check(ctx, log):
owner = is_owner_check(ctx.message)
if not owner and log:
print(ctx.message.author.name + " à essayer d'executer " + ctx.message.content + " sur le serveur " + ctx.message.guild.name)
return owner
owner = commands.check(lambda ctx: check(ctx, warn))
return owner
"""-------------------------------------------------------------------------"""
async def check_permissions(ctx, perms, *, check=all):
is_owner = await ctx.bot.is_owner(ctx.author)
if is_owner or is_owner_check(ctx.message) is True:
return True
resolved = ctx.channel.permissions_for(ctx.author)
return check(getattr(resolved, name, None) == value for name, value in
perms.items())
def has_permissions(*, check=all, **perms):
async def pred(ctx):
return await check_permissions(ctx, perms, check=check)
return commands.check(pred)
async def check_guild_permissions(ctx, perms, *, check=all):
is_owner = await ctx.bot.is_owner(ctx.author)
if is_owner:
return True
if ctx.guild is None:
return False
resolved = ctx.author.guild_permissions
return check(getattr(resolved, name, None) == value for name, value in
perms.items())
def has_guild_permissions(*, check=all, **perms):
async def pred(ctx):
return await check_guild_permissions(ctx, perms, check=check)
return commands.check(pred)
# These do not take channel overrides into account
def is_mod():
async def pred(ctx):
return await check_guild_permissions(ctx, {'manage_guild': True})
return commands.check(pred)
def is_admin():
async def pred(ctx):
return await check_guild_permissions(ctx, {'administrator': True})
return commands.check(pred)
def mod_or_permissions(**perms):
perms['manage_guild'] = True
async def predicate(ctx):
return await check_guild_permissions(ctx, perms, check=any)
return commands.check(predicate)
def admin_or_permissions(**perms):
perms['administrator'] = True
async def predicate(ctx):
return await check_guild_permissions(ctx, perms, check=any)
return commands.check(predicate)
def is_in_guilds(*guild_ids):
def predicate(ctx):
guild = ctx.guild
if guild is None:
return False
return guild.id in guild_ids
return commands.check(predicate)
def get_user(message, user):
try:
member = message.mentions[0]
except:
member = message.guild.get_member_named(user)
if not member:
try:
member = message.guild.get_member(int(user))
except ValueError:
pass
if not member:
return None
return member
def check_date(date: str):
if len(date) == 1:
return f"0{date}"
else:
return date
from discord.ext import commands
def is_owner_check(message):
return str(message.author.id) in ['171685542553976832',
'269156684155453451']
def is_owner(warn=True):
def check(ctx, log):
owner = is_owner_check(ctx.message)
if not owner and log:
print(ctx.message.author.name + " à essayer d'executer " + ctx.message.content + " sur le serveur " + ctx.message.guild.name)
return owner
owner = commands.check(lambda ctx: check(ctx, warn))
return owner
"""-------------------------------------------------------------------------"""
async def check_permissions(ctx, perms, *, check=all):
is_owner = await ctx.bot.is_owner(ctx.author)
if is_owner or is_owner_check(ctx.message) is True:
return True
resolved = ctx.channel.permissions_for(ctx.author)
return check(getattr(resolved, name, None) == value for name, value in
perms.items())
def has_permissions(*, check=all, **perms):
async def pred(ctx):
return await check_permissions(ctx, perms, check=check)
return commands.check(pred)
async def check_guild_permissions(ctx, perms, *, check=all):
is_owner = await ctx.bot.is_owner(ctx.author)
if is_owner:
return True
if ctx.guild is None:
return False
resolved = ctx.author.guild_permissions
return check(getattr(resolved, name, None) == value for name, value in
perms.items())
def has_guild_permissions(*, check=all, **perms):
async def pred(ctx):
return await check_guild_permissions(ctx, perms, check=check)
return commands.check(pred)
# These do not take channel overrides into account
def is_mod():
async def pred(ctx):
return await check_guild_permissions(ctx, {'manage_guild': True})
return commands.check(pred)
def is_admin():
async def pred(ctx):
return await check_guild_permissions(ctx, {'administrator': True})
return commands.check(pred)
def mod_or_permissions(**perms):
perms['manage_guild'] = True
async def predicate(ctx):
return await check_guild_permissions(ctx, perms, check=any)
return commands.check(predicate)
def admin_or_permissions(**perms):
perms['administrator'] = True
async def predicate(ctx):
return await check_guild_permissions(ctx, perms, check=any)
return commands.check(predicate)
def is_in_guilds(*guild_ids):
def predicate(ctx):
guild = ctx.guild
if guild is None:
return False
return guild.id in guild_ids
return commands.check(predicate)
def get_user(message, user):
try:
member = message.mentions[0]
except:
member = message.guild.get_member_named(user)
if not member:
try:
member = message.guild.get_member(int(user))
except ValueError:
pass
if not member:
return None
return member
def check_date(date: str):
if len(date) == 1:
return f"0{date}"
else:
return date

View File

@ -1,46 +1,46 @@
class text_colors:
BLACK = '\033[30m'
RED = '\033[31m'
GREEN = '\033[32m'
YELLOW = '\033[33m'
BLUE = '\033[34m'
MAGENTA = '\033[35m'
CYAN = '\033[36m'
LIGHT_GRAY = '\033[37m'
DARK_GRAY = '\033[90m'
LIGHT_RED = '\033[91m'
LIGHT_GREEN = '\033[92m'
LIGHT_YELLOW = '\033[93m'
LIGHT_BLUE = '\033[94m'
LIGHT_MAGENTA = '\033[95m'
LIGHT_CYAN = '\033[96m'
WHITE = '\033[97m'
class bg_colors:
BLACK = '\033[40m'
RED = '\033[41m'
GREEN = '\033[42m'
YELLOW = '\033[43m'
BLUE = '\033[44m'
MAGENTA = '\033[45m'
CYAN = '\033[46m'
LIGHT_GRAY = '\033[47m'
DARK_GRAY = '\033[100m'
LIGHT_RED = '\033[101m'
LIGHT_GREEN = '\033[102m'
LIGHT_YELLOW = '\033[103m'
LIGHT_BLUE = '\033[104m'
LIGHT_MAGENTA = '\033[105m'
LIGHT_CYAN = '\033[106m'
WHITE = '\033[107m'
class text_style:
BOLD = '\033[1m'
DIM = '\033[2m'
UNDERLINE = '\033[4m'
BLINK = '\033[5m'
ENDC = '\033[0m'
class text_colors:
BLACK = '\033[30m'
RED = '\033[31m'
GREEN = '\033[32m'
YELLOW = '\033[33m'
BLUE = '\033[34m'
MAGENTA = '\033[35m'
CYAN = '\033[36m'
LIGHT_GRAY = '\033[37m'
DARK_GRAY = '\033[90m'
LIGHT_RED = '\033[91m'
LIGHT_GREEN = '\033[92m'
LIGHT_YELLOW = '\033[93m'
LIGHT_BLUE = '\033[94m'
LIGHT_MAGENTA = '\033[95m'
LIGHT_CYAN = '\033[96m'
WHITE = '\033[97m'
class bg_colors:
BLACK = '\033[40m'
RED = '\033[41m'
GREEN = '\033[42m'
YELLOW = '\033[43m'
BLUE = '\033[44m'
MAGENTA = '\033[45m'
CYAN = '\033[46m'
LIGHT_GRAY = '\033[47m'
DARK_GRAY = '\033[100m'
LIGHT_RED = '\033[101m'
LIGHT_GREEN = '\033[102m'
LIGHT_YELLOW = '\033[103m'
LIGHT_BLUE = '\033[104m'
LIGHT_MAGENTA = '\033[105m'
LIGHT_CYAN = '\033[106m'
WHITE = '\033[107m'
class text_style:
BOLD = '\033[1m'
DIM = '\033[2m'
UNDERLINE = '\033[4m'
BLINK = '\033[5m'
ENDC = '\033[0m'

View File

@ -1,29 +1,29 @@
import pymysql
def connect_to_db(self):
mysqlHost = self.bot.config.mysql["host"]
mysqlUser = self.bot.config.mysql["username"]
mysqlPass = self.bot.config.mysql["password"]
mysqlDB = self.bot.config.mysql["dbname"]
try:
return pymysql.connect(host=mysqlHost, user=mysqlUser,
passwd=mysqlPass, db=mysqlDB, charset='utf8')
except KeyError:
print(
"Rest in peperoni, Impossible de se connecter a la base de données.")
print(str(KeyError))
return
def reconnect_to_db(self):
if not self.conn:
mysqlHost = self.bot.config.mysql["host"]
mysqlUser = self.bot.config.mysql["username"]
mysqlPass = self.bot.config.mysql["password"]
mysqlDB = self.bot.config.mysql["dbname"]
return pymysql.connect(host=mysqlHost, user=mysqlUser,
passwd=mysqlPass, db=mysqlDB, charset='utf8')
return self.conn
import pymysql
def connect_to_db(self):
mysqlHost = self.bot.config.mysql["host"]
mysqlUser = self.bot.config.mysql["username"]
mysqlPass = self.bot.config.mysql["password"]
mysqlDB = self.bot.config.mysql["dbname"]
try:
return pymysql.connect(host=mysqlHost, user=mysqlUser,
passwd=mysqlPass, db=mysqlDB, charset='utf8')
except KeyError:
print(
"Rest in peperoni, Impossible de se connecter a la base de données.")
print(str(KeyError))
return
def reconnect_to_db(self):
if not self.conn:
mysqlHost = self.bot.config.mysql["host"]
mysqlUser = self.bot.config.mysql["username"]
mysqlPass = self.bot.config.mysql["password"]
mysqlDB = self.bot.config.mysql["dbname"]
return pymysql.connect(host=mysqlHost, user=mysqlUser,
passwd=mysqlPass, db=mysqlDB, charset='utf8')
return self.conn

View File

@ -1,75 +1,75 @@
async def entry_to_code(bot, entries):
width = max(map(lambda t: len(t[0]), entries))
output = ['```']
fmt = '{0:<{width}}: {1}'
for name, entry in entries:
output.append(fmt.format(name, entry, width=width))
output.append('```')
await bot.say('\n'.join(output))
import datetime
async def indented_entry_to_code(bot, entries):
width = max(map(lambda t: len(t[0]), entries))
output = ['```']
fmt = '\u200b{0:>{width}}: {1}'
for name, entry in entries:
output.append(fmt.format(name, entry, width=width))
output.append('```')
await bot.say('\n'.join(output))
async def too_many_matches(bot, msg, matches, entry):
check = lambda m: m.content.isdigit()
await bot.say('There are too many matches... Which one did you mean? **Only say the number**.')
await bot.say('\n'.join(map(entry, enumerate(matches, 1))))
# only give them 3 tries.
for i in range(3):
message = await bot.wait_for_message(author=msg.author, channel=msg.channel, check=check)
index = int(message.content)
try:
return matches[index - 1]
except:
await bot.say('Please give me a valid number. {} tries remaining...'.format(2 - i))
raise ValueError('Too many tries. Goodbye.')
class Plural:
def __init__(self, **attr):
iterator = attr.items()
self.name, self.value = next(iter(iterator))
def __str__(self):
v = self.value
if v > 1:
return '%s %ss' % (v, self.name)
return '%s %s' % (v, self.name)
def human_timedelta(dt):
now = datetime.datetime.utcnow()
delta = now - dt
hours, remainder = divmod(int(delta.total_seconds()), 3600)
minutes, seconds = divmod(remainder, 60)
days, hours = divmod(hours, 24)
years, days = divmod(days, 365)
if years:
if days:
return '%s and %s ago' % (Plural(year=years), Plural(day=days))
return '%s ago' % Plural(year=years)
if days:
if hours:
return '%s and %s ago' % (Plural(day=days), Plural(hour=hours))
return '%s ago' % Plural(day=days)
if hours:
if minutes:
return '%s and %s ago' % (Plural(hour=hours), Plural(minute=minutes))
return '%s ago' % Plural(hour=hours)
if minutes:
if seconds:
return '%s and %s ago' % (Plural(minute=minutes), Plural(second=seconds))
return '%s ago' % Plural(minute=minutes)
return '%s ago' % Plural(second=seconds)
async def entry_to_code(bot, entries):
width = max(map(lambda t: len(t[0]), entries))
output = ['```']
fmt = '{0:<{width}}: {1}'
for name, entry in entries:
output.append(fmt.format(name, entry, width=width))
output.append('```')
await bot.say('\n'.join(output))
import datetime
async def indented_entry_to_code(bot, entries):
width = max(map(lambda t: len(t[0]), entries))
output = ['```']
fmt = '\u200b{0:>{width}}: {1}'
for name, entry in entries:
output.append(fmt.format(name, entry, width=width))
output.append('```')
await bot.say('\n'.join(output))
async def too_many_matches(bot, msg, matches, entry):
check = lambda m: m.content.isdigit()
await bot.say('There are too many matches... Which one did you mean? **Only say the number**.')
await bot.say('\n'.join(map(entry, enumerate(matches, 1))))
# only give them 3 tries.
for i in range(3):
message = await bot.wait_for_message(author=msg.author, channel=msg.channel, check=check)
index = int(message.content)
try:
return matches[index - 1]
except:
await bot.say('Please give me a valid number. {} tries remaining...'.format(2 - i))
raise ValueError('Too many tries. Goodbye.')
class Plural:
def __init__(self, **attr):
iterator = attr.items()
self.name, self.value = next(iter(iterator))
def __str__(self):
v = self.value
if v > 1:
return '%s %ss' % (v, self.name)
return '%s %s' % (v, self.name)
def human_timedelta(dt):
now = datetime.datetime.utcnow()
delta = now - dt
hours, remainder = divmod(int(delta.total_seconds()), 3600)
minutes, seconds = divmod(remainder, 60)
days, hours = divmod(hours, 24)
years, days = divmod(days, 365)
if years:
if days:
return '%s and %s ago' % (Plural(year=years), Plural(day=days))
return '%s ago' % Plural(year=years)
if days:
if hours:
return '%s and %s ago' % (Plural(day=days), Plural(hour=hours))
return '%s ago' % Plural(day=days)
if hours:
if minutes:
return '%s and %s ago' % (Plural(hour=hours), Plural(minute=minutes))
return '%s ago' % Plural(hour=hours)
if minutes:
if seconds:
return '%s and %s ago' % (Plural(minute=minutes), Plural(second=seconds))
return '%s ago' % Plural(minute=minutes)
return '%s ago' % Plural(second=seconds)

View File

@ -1,147 +1,147 @@
#!/bin/env python
# With credit to DanielKO
from lxml import etree
import datetime, re
import asyncio, aiohttp
NINTENDO_LOGIN_PAGE = "https://id.nintendo.net/oauth/authorize"
SPLATNET_CALLBACK_URL = "https://splatoon.nintendo.net/users/auth/nintendo/callback"
SPLATNET_CLIENT_ID = "12af3d0a3a1f441eb900411bb50a835a"
SPLATNET_SCHEDULE_URL = "https://splatoon.nintendo.net/schedule"
class Rotation(object):
def __init__(self):
self.start = None
self.end = None
self.turf_maps = []
self.ranked_mode = None
self.ranked_maps = []
@property
def is_over(self):
return self.end < datetime.datetime.utcnow()
def __str__(self):
now = datetime.datetime.utcnow()
prefix = ''
if self.start > now:
minutes_delta = int((self.start - now) / datetime.timedelta(minutes=1))
hours = int(minutes_delta / 60)
minutes = minutes_delta % 60
prefix = '**In {0} hours and {1} minutes**:\n'.format(hours, minutes)
else:
prefix = '**Current Rotation**:\n'
fmt = 'Turf War is {0[0]} and {0[1]}\n{1} is {2[0]} and {2[1]}'
return prefix + fmt.format(self.turf_maps, self.ranked_mode, self.ranked_maps)
# based on https://github.com/Wiwiweb/SakuraiBot/blob/master/src/sakuraibot.py
async def get_new_splatnet_cookie(username, password):
parameters = {'client_id': SPLATNET_CLIENT_ID,
'response_type': 'code',
'redirect_uri': SPLATNET_CALLBACK_URL,
'username': username,
'password': password}
async with aiohttp.post(NINTENDO_LOGIN_PAGE, data=parameters) as response:
cookie = response.history[-1].cookies.get('_wag_session')
if cookie is None:
print(req)
raise Exception("Couldn't retrieve cookie")
return cookie
def parse_splatnet_time(timestr):
# time is given as "MM/DD at H:MM [p|a].m. (PDT|PST)"
# there is a case where it goes over the year, e.g. 12/31 at ... and then 1/1 at ...
# this case is kind of weird though and is currently unexpected
# it could even end up being e.g. 12/31/2015 ... and then 1/1/2016 ...
# we'll never know
regex = r'(?P<month>\d+)\/(?P<day>\d+)\s*at\s*(?P<hour>\d+)\:(?P<minutes>\d+)\s*(?P<p>a\.m\.|p\.m\.)\s*\((?P<tz>.+)\)'
m = re.match(regex, timestr.strip())
if m is None:
raise RuntimeError('Apparently the timestamp "{}" does not match the regex.'.format(timestr))
matches = m.groupdict()
tz = matches['tz'].strip().upper()
offset = None
if tz == 'PDT':
# EDT is UTC - 4, PDT is UTC - 7, so you need +7 to make it UTC
offset = +7
elif tz == 'PST':
# EST is UTC - 5, PST is UTC - 8, so you need +8 to make it UTC
offset = +8
else:
raise RuntimeError('Unknown timezone found: {}'.format(tz))
pm = matches['p'].replace('.', '') # a.m. -> am
current_time = datetime.datetime.utcnow()
# Kind of hacky.
fmt = "{2}/{0[month]}/{0[day]} {0[hour]}:{0[minutes]} {1}".format(matches, pm, current_time.year)
splatoon_time = datetime.datetime.strptime(fmt, '%Y/%m/%d %I:%M %p') + datetime.timedelta(hours=offset)
# check for new year
if current_time.month == 12 and splatoon_time.month == 1:
splatoon_time.replace(current_time.year + 1)
return splatoon_time
async def get_splatnet_schedule(splatnet_cookie):
cookies = {'_wag_session': splatnet_cookie}
"""
This is repeated 3 times:
<span class"stage-schedule"> ... </span> <--- figure out how to parse this
<div class="stage-list">
<div class="match-type">
<span class="icon-regular-match"></span> <--- turf war
</div>
... <span class="map-name"> ... </span>
... <span class="map-name"> ... </span>
</div>
<div class="stage-list">
<div class="match-type">
<span class="icon-earnest-match"></span> <--- ranked
</div>
... <span class="rule-description"> ... </span> <--- Splat Zones, Rainmaker, Tower Control
... <span class="map-name"> ... </span>
... <span class="map-name"> ... </span>
</div>
"""
schedule = []
async with aiohttp.get(SPLATNET_SCHEDULE_URL, cookies=cookies, data={'locale':"en"}) as response:
text = await response.text()
root = etree.fromstring(text, etree.HTMLParser())
stage_schedule_nodes = root.xpath("//*[@class='stage-schedule']")
stage_list_nodes = root.xpath("//*[@class='stage-list']")
if len(stage_schedule_nodes)*2 != len(stage_list_nodes):
raise RuntimeError("SplatNet changed, need to update the parsing!")
for sched_node in stage_schedule_nodes:
r = Rotation()
start_time, end_time = sched_node.text.split("~")
r.start = parse_splatnet_time(start_time)
r.end = parse_splatnet_time(end_time)
tw_list_node = stage_list_nodes.pop(0)
r.turf_maps = tw_list_node.xpath(".//*[@class='map-name']/text()")
ranked_list_node = stage_list_nodes.pop(0)
r.ranked_maps = ranked_list_node.xpath(".//*[@class='map-name']/text()")
r.ranked_mode = ranked_list_node.xpath(".//*[@class='rule-description']/text()")[0]
schedule.append(r)
return schedule
#!/bin/env python
# With credit to DanielKO
from lxml import etree
import datetime, re
import asyncio, aiohttp
NINTENDO_LOGIN_PAGE = "https://id.nintendo.net/oauth/authorize"
SPLATNET_CALLBACK_URL = "https://splatoon.nintendo.net/users/auth/nintendo/callback"
SPLATNET_CLIENT_ID = "12af3d0a3a1f441eb900411bb50a835a"
SPLATNET_SCHEDULE_URL = "https://splatoon.nintendo.net/schedule"
class Rotation(object):
def __init__(self):
self.start = None
self.end = None
self.turf_maps = []
self.ranked_mode = None
self.ranked_maps = []
@property
def is_over(self):
return self.end < datetime.datetime.utcnow()
def __str__(self):
now = datetime.datetime.utcnow()
prefix = ''
if self.start > now:
minutes_delta = int((self.start - now) / datetime.timedelta(minutes=1))
hours = int(minutes_delta / 60)
minutes = minutes_delta % 60
prefix = '**In {0} hours and {1} minutes**:\n'.format(hours, minutes)
else:
prefix = '**Current Rotation**:\n'
fmt = 'Turf War is {0[0]} and {0[1]}\n{1} is {2[0]} and {2[1]}'
return prefix + fmt.format(self.turf_maps, self.ranked_mode, self.ranked_maps)
# based on https://github.com/Wiwiweb/SakuraiBot/blob/master/src/sakuraibot.py
async def get_new_splatnet_cookie(username, password):
parameters = {'client_id': SPLATNET_CLIENT_ID,
'response_type': 'code',
'redirect_uri': SPLATNET_CALLBACK_URL,
'username': username,
'password': password}
async with aiohttp.post(NINTENDO_LOGIN_PAGE, data=parameters) as response:
cookie = response.history[-1].cookies.get('_wag_session')
if cookie is None:
print(req)
raise Exception("Couldn't retrieve cookie")
return cookie
def parse_splatnet_time(timestr):
# time is given as "MM/DD at H:MM [p|a].m. (PDT|PST)"
# there is a case where it goes over the year, e.g. 12/31 at ... and then 1/1 at ...
# this case is kind of weird though and is currently unexpected
# it could even end up being e.g. 12/31/2015 ... and then 1/1/2016 ...
# we'll never know
regex = r'(?P<month>\d+)\/(?P<day>\d+)\s*at\s*(?P<hour>\d+)\:(?P<minutes>\d+)\s*(?P<p>a\.m\.|p\.m\.)\s*\((?P<tz>.+)\)'
m = re.match(regex, timestr.strip())
if m is None:
raise RuntimeError('Apparently the timestamp "{}" does not match the regex.'.format(timestr))
matches = m.groupdict()
tz = matches['tz'].strip().upper()
offset = None
if tz == 'PDT':
# EDT is UTC - 4, PDT is UTC - 7, so you need +7 to make it UTC
offset = +7
elif tz == 'PST':
# EST is UTC - 5, PST is UTC - 8, so you need +8 to make it UTC
offset = +8
else:
raise RuntimeError('Unknown timezone found: {}'.format(tz))
pm = matches['p'].replace('.', '') # a.m. -> am
current_time = datetime.datetime.utcnow()
# Kind of hacky.
fmt = "{2}/{0[month]}/{0[day]} {0[hour]}:{0[minutes]} {1}".format(matches, pm, current_time.year)
splatoon_time = datetime.datetime.strptime(fmt, '%Y/%m/%d %I:%M %p') + datetime.timedelta(hours=offset)
# check for new year
if current_time.month == 12 and splatoon_time.month == 1:
splatoon_time.replace(current_time.year + 1)
return splatoon_time
async def get_splatnet_schedule(splatnet_cookie):
cookies = {'_wag_session': splatnet_cookie}
"""
This is repeated 3 times:
<span class"stage-schedule"> ... </span> <--- figure out how to parse this
<div class="stage-list">
<div class="match-type">
<span class="icon-regular-match"></span> <--- turf war
</div>
... <span class="map-name"> ... </span>
... <span class="map-name"> ... </span>
</div>
<div class="stage-list">
<div class="match-type">
<span class="icon-earnest-match"></span> <--- ranked
</div>
... <span class="rule-description"> ... </span> <--- Splat Zones, Rainmaker, Tower Control
... <span class="map-name"> ... </span>
... <span class="map-name"> ... </span>
</div>
"""
schedule = []
async with aiohttp.get(SPLATNET_SCHEDULE_URL, cookies=cookies, data={'locale':"en"}) as response:
text = await response.text()
root = etree.fromstring(text, etree.HTMLParser())
stage_schedule_nodes = root.xpath("//*[@class='stage-schedule']")
stage_list_nodes = root.xpath("//*[@class='stage-list']")
if len(stage_schedule_nodes)*2 != len(stage_list_nodes):
raise RuntimeError("SplatNet changed, need to update the parsing!")
for sched_node in stage_schedule_nodes:
r = Rotation()
start_time, end_time = sched_node.text.split("~")
r.start = parse_splatnet_time(start_time)
r.end = parse_splatnet_time(end_time)
tw_list_node = stage_list_nodes.pop(0)
r.turf_maps = tw_list_node.xpath(".//*[@class='map-name']/text()")
ranked_list_node = stage_list_nodes.pop(0)
r.ranked_maps = ranked_list_node.xpath(".//*[@class='map-name']/text()")
r.ranked_mode = ranked_list_node.xpath(".//*[@class='rule-description']/text()")[0]
schedule.append(r)
return schedule

View File

@ -1,140 +1,140 @@
import asyncio
class Menu:
"""An interactive menu class for Discord."""
class Submenu:
"""A metaclass of the Menu class."""
def __init__(self, name, content):
self.content = content
self.leads_to = []
self.name = name
def get_text(self):
text = ""
for idx, menu in enumerate(self.leads_to):
text += "[{}] {}\n".format(idx+1, menu.name)
return text
def get_child(self, child_idx):
try:
return self.leads_to[child_idx]
except IndexError:
raise IndexError("child index out of range")
def add_child(self, child):
self.leads_to.append(child)
class InputSubmenu:
"""A metaclass of the Menu class for submenu options that take input, instead of prompting the user to pick an option."""
def __init__(self, name, content, input_function, leads_to):
self.content = content
self.name = name
self.input_function = input_function
self.leads_to = leads_to
def next_child(self):
return self.leads_to
class ChoiceSubmenu:
"""A metaclass of the Menu class for submenu options for choosing an option from a list."""
def __init__(self, name, content, options, input_function, leads_to):
self.content = content
self.name = name
self.options = options
self.input_function = input_function
self.leads_to = leads_to
def next_child(self):
return self.leads_to
def __init__(self, main_page):
self.children = []
self.main = self.Submenu("main", main_page)
def add_child(self, child):
self.main.add_child(child)
async def start(self, ctx):
current = self.main
menu_msg = None
while True:
output = ""
if type(current) == self.Submenu:
if type(current.content) == str:
output += current.content + "\n"
elif callable(current.content):
current.content()
else:
raise TypeError("submenu body is not a str or function")
if not current.leads_to:
if not menu_msg:
menu_msg = await ctx.send("```" + output + "```")
else:
await menu_msg.edit(content="```" + output + "```")
break
output += "\n" + current.get_text() + "\n"
output += "Enter a number."
if not menu_msg:
menu_msg = await ctx.send("```" + output + "```")
else:
await menu_msg.edit(content="```" + output + "```")
reply = await ctx.bot.wait_for("message", check=lambda m: m.author == ctx.bot.user and m.content.isdigit() and m.channel == ctx.message.channel)
await reply.delete()
try:
current = current.get_child(int(reply.content) - 1)
except IndexError:
print("Invalid number.")
break
elif type(current) == self.InputSubmenu:
if type(current.content) == list:
answers = []
for question in current.content:
await menu_msg.edit(content="```" + question + "\n\nEnter a value." + "```")
reply = await ctx.bot.wait_for("message", check=lambda m: m.author == ctx.bot.user and m.channel == ctx.message.channel)
await reply.delete()
answers.append(reply)
current.input_function(*answers)
else:
await menu_msg.edit(content="```" + current.content + "\n\nEnter a value." + "```")
reply = await ctx.bot.wait_for("message", check=lambda m: m.author == ctx.bot.user and m.channel == ctx.message.channel)
await reply.delete()
current.input_function(reply)
if not current.leads_to:
break
current = current.leads_to
elif type(current) == self.ChoiceSubmenu:
result = "```" + current.content + "\n\n"
if type(current.options) == dict:
indexes = {}
for idx, option in enumerate(current.options):
result += "[{}] {}: {}\n".format(idx+1, option, current.options[option])
indexes[idx] = option
else:
for idx, option in current.options:
result += "[{}] {}\n".format(idx+1, option)
await menu_msg.edit(content=result + "\nPick an option.```")
reply = await ctx.bot.wait_for("message", check=lambda m: m.author == ctx.bot.user and m.content.isdigit() and m.channel == ctx.message.channel)
await reply.delete()
if type(current.options) == dict:
current.input_function(reply, indexes[int(reply.content)-1])
else:
current.input_function(reply, current.options[reply-1])
if not current.leads_to:
break
current = current.leads_to
import asyncio
class Menu:
"""An interactive menu class for Discord."""
class Submenu:
"""A metaclass of the Menu class."""
def __init__(self, name, content):
self.content = content
self.leads_to = []
self.name = name
def get_text(self):
text = ""
for idx, menu in enumerate(self.leads_to):
text += "[{}] {}\n".format(idx+1, menu.name)
return text
def get_child(self, child_idx):
try:
return self.leads_to[child_idx]
except IndexError:
raise IndexError("child index out of range")
def add_child(self, child):
self.leads_to.append(child)
class InputSubmenu:
"""A metaclass of the Menu class for submenu options that take input, instead of prompting the user to pick an option."""
def __init__(self, name, content, input_function, leads_to):
self.content = content
self.name = name
self.input_function = input_function
self.leads_to = leads_to
def next_child(self):
return self.leads_to
class ChoiceSubmenu:
"""A metaclass of the Menu class for submenu options for choosing an option from a list."""
def __init__(self, name, content, options, input_function, leads_to):
self.content = content
self.name = name
self.options = options
self.input_function = input_function
self.leads_to = leads_to
def next_child(self):
return self.leads_to
def __init__(self, main_page):
self.children = []
self.main = self.Submenu("main", main_page)
def add_child(self, child):
self.main.add_child(child)
async def start(self, ctx):
current = self.main
menu_msg = None
while True:
output = ""
if type(current) == self.Submenu:
if type(current.content) == str:
output += current.content + "\n"
elif callable(current.content):
current.content()
else:
raise TypeError("submenu body is not a str or function")
if not current.leads_to:
if not menu_msg:
menu_msg = await ctx.send("```" + output + "```")
else:
await menu_msg.edit(content="```" + output + "```")
break
output += "\n" + current.get_text() + "\n"
output += "Enter a number."
if not menu_msg:
menu_msg = await ctx.send("```" + output + "```")
else:
await menu_msg.edit(content="```" + output + "```")
reply = await ctx.bot.wait_for("message", check=lambda m: m.author == ctx.bot.user and m.content.isdigit() and m.channel == ctx.message.channel)
await reply.delete()
try:
current = current.get_child(int(reply.content) - 1)
except IndexError:
print("Invalid number.")
break
elif type(current) == self.InputSubmenu:
if type(current.content) == list:
answers = []
for question in current.content:
await menu_msg.edit(content="```" + question + "\n\nEnter a value." + "```")
reply = await ctx.bot.wait_for("message", check=lambda m: m.author == ctx.bot.user and m.channel == ctx.message.channel)
await reply.delete()
answers.append(reply)
current.input_function(*answers)
else:
await menu_msg.edit(content="```" + current.content + "\n\nEnter a value." + "```")
reply = await ctx.bot.wait_for("message", check=lambda m: m.author == ctx.bot.user and m.channel == ctx.message.channel)
await reply.delete()
current.input_function(reply)
if not current.leads_to:
break
current = current.leads_to
elif type(current) == self.ChoiceSubmenu:
result = "```" + current.content + "\n\n"
if type(current.options) == dict:
indexes = {}
for idx, option in enumerate(current.options):
result += "[{}] {}: {}\n".format(idx+1, option, current.options[option])
indexes[idx] = option
else:
for idx, option in current.options:
result += "[{}] {}\n".format(idx+1, option)
await menu_msg.edit(content=result + "\nPick an option.```")
reply = await ctx.bot.wait_for("message", check=lambda m: m.author == ctx.bot.user and m.content.isdigit() and m.channel == ctx.message.channel)
await reply.delete()
if type(current.options) == dict:
current.input_function(reply, indexes[int(reply.content)-1])
else:
current.input_function(reply, current.options[reply-1])
if not current.leads_to:
break
current = current.leads_to

File diff suppressed because it is too large Load Diff

View File

@ -1,113 +1,113 @@
import asyncio
import os
import re
import subprocess
import uuid
import discord
from discord.ext import commands
from gtts import gTTS
class Vocal(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.playing = False
self.author = None
self.voice = None
"""---------------------------------------------------------------------"""
@staticmethod
def get_duration(file):
popen = subprocess.Popen(("ffprobe",
"-show_entries",
"format=duration",
"-i", file),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
output, err = popen.communicate()
match = re.search(r"[-+]?\d*\.\d+|\d+", str(output))
return float(match.group())
@commands.command(name="voc", no_pm=True, pass_context=True)
async def _voc(self, ctx, *, message=""):
if message == "":
await ctx.send("Veuillez écrire un message...")
return
if message == "stop_playing" \
and (
ctx.author.id == self.author.id
or ctx.message.channel.permissions_for(
ctx.message.author
).administrator is True
) \
and self.playing is True:
await ctx.send('stop')
await self.voice.disconnect()
self.playing = False
return
if self.playing is True:
await ctx.send("Je suis déja en train de parler,"
" merci de réenvoyer ton message"
" quand j'aurais fini.")
return
user = ctx.author
self.author = user
if user.voice:
self.playing = True
filename = f"data/tmp/voc/{uuid.uuid1()}.mp3"
lang = [x for x in message.split(" ") if x.startswith("lang=")]
loading = await ctx.send("*Chargement du message en cours...*")
if lang:
choice_lang = (lang[0])[5:]
message = f"{user.display_name} à dit: {message.strip(lang[0])}" if len(ctx.author.voice.channel.members) >= 4 else message.strip(lang[0])
try:
tts = gTTS(
text=message,
lang=str(choice_lang))
except ValueError:
tts = gTTS(
text=message,
lang="fr")
await ctx.send("La langue n'est pas supportée,"
" le francais a donc été choisi")
else:
message = f"{user.display_name} à dit: {message}" if len(ctx.author.voice.channel.members) >= 4 else message
tts = gTTS(text=message,
lang="fr")
tts.save(filename)
self.voice = await user.voice.channel.connect()
self.voice.play(discord.FFmpegPCMAudio(filename))
counter = 0
duration = self.get_duration(filename)
while not counter >= duration:
if self.playing:
await loading.edit(
content=f"Lecture du message de {self.author.display_name} en cours : {counter}sec/{duration}sec")
await asyncio.sleep(1)
counter += 1
else:
break
await self.voice.disconnect()
await loading.edit(content="Lecture terminée")
self.voice = None
os.remove(filename)
self.playing = False
else:
await ctx.send('Veuillez aller dans un channel vocal.')
def setup(bot):
bot.add_cog(Vocal(bot))
import asyncio
import os
import re
import subprocess
import uuid
import discord
from discord.ext import commands
from gtts import gTTS
class Vocal(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.playing = False
self.author = None
self.voice = None
"""---------------------------------------------------------------------"""
@staticmethod
def get_duration(file):
popen = subprocess.Popen(("ffprobe",
"-show_entries",
"format=duration",
"-i", file),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
output, err = popen.communicate()
match = re.search(r"[-+]?\d*\.\d+|\d+", str(output))
return float(match.group())
@commands.command(name="voc", no_pm=True, pass_context=True)
async def _voc(self, ctx, *, message=""):
if message == "":
await ctx.send("Veuillez écrire un message...")
return
if message == "stop_playing" \
and (
ctx.author.id == self.author.id
or ctx.message.channel.permissions_for(
ctx.message.author
).administrator is True
) \
and self.playing is True:
await ctx.send('stop')
await self.voice.disconnect()
self.playing = False
return
if self.playing is True:
await ctx.send("Je suis déja en train de parler,"
" merci de réenvoyer ton message"
" quand j'aurais fini.")
return
user = ctx.author
self.author = user
if user.voice:
self.playing = True
filename = f"data/tmp/voc/{uuid.uuid1()}.mp3"
lang = [x for x in message.split(" ") if x.startswith("lang=")]
loading = await ctx.send("*Chargement du message en cours...*")
if lang:
choice_lang = (lang[0])[5:]
message = f"{user.display_name} à dit: {message.strip(lang[0])}" if len(ctx.author.voice.channel.members) >= 4 else message.strip(lang[0])
try:
tts = gTTS(
text=message,
lang=str(choice_lang))
except ValueError:
tts = gTTS(
text=message,
lang="fr")
await ctx.send("La langue n'est pas supportée,"
" le francais a donc été choisi")
else:
message = f"{user.display_name} à dit: {message}" if len(ctx.author.voice.channel.members) >= 4 else message
tts = gTTS(text=message,
lang="fr")
tts.save(filename)
self.voice = await user.voice.channel.connect()
self.voice.play(discord.FFmpegPCMAudio(filename))
counter = 0
duration = self.get_duration(filename)
while not counter >= duration:
if self.playing:
await loading.edit(
content=f"Lecture du message de {self.author.display_name} en cours : {counter}sec/{duration}sec")
await asyncio.sleep(1)
counter += 1
else:
break
await self.voice.disconnect()
await loading.edit(content="Lecture terminée")
self.voice = None
os.remove(filename)
self.playing = False
else:
await ctx.send('Veuillez aller dans un channel vocal.')
def setup(bot):
bot.add_cog(Vocal(bot))

View File

@ -1,3 +1,3 @@
#pip install -U "https://github.com/Rapptz/discord.py/archive/rewrite.zip#egg=discord.py[voice]"
python3 -m pip install -U discord.py[voice]
python3 -m pip install -r requirements.txt
#pip install -U "https://github.com/Rapptz/discord.py/archive/rewrite.zip#egg=discord.py[voice]"
python3 -m pip install -U discord.py[voice]
python3 -m pip install -r requirements.txt

View File

@ -1,9 +1,11 @@
pymysql
gtts
beautifulsoup4
lxml==4.2.4
bs4
pytz
requests
wikipedia
pillow
pymysql
gtts
beautifulsoup4
lxml==4.2.4
bs4
pytz
requests
wikipedia
pillow
ipwhois
ipinfo

View File

@ -1,11 +1,11 @@
La carte d'identité est un petit système dans tuxbot permetant de vous démarquer de vos amis en ayant la possibilité d'y renseigner plusieurs informations !
**Liste des commandes : **
-> .ci : Affiche l'aide sur les cartes d'identité
-> .ci show _pseudo_ : Affiche la carte d'identité de _pseudo_
-> .ci register : Vous enregistre dans la base de donnée des cartes d'identité
-> .ci setos _nom de l'os_ : Défini votre système d'exploitation
-> .ci setconfig _votre configuration pc_ : Défini la configuration de votre ordinateur
-> .ci setcountry : Défini votre pays
-> .ci update : Met à jour votre image si vous l'avez changé
-> .ci delete : Supprime votre carte d'identité
La carte d'identité est un petit système dans tuxbot permetant de vous démarquer de vos amis en ayant la possibilité d'y renseigner plusieurs informations !
**Liste des commandes : **
-> .ci : Affiche l'aide sur les cartes d'identité
-> .ci show _pseudo_ : Affiche la carte d'identité de _pseudo_
-> .ci register : Vous enregistre dans la base de donnée des cartes d'identité
-> .ci setos _nom de l'os_ : Défini votre système d'exploitation
-> .ci setconfig _votre configuration pc_ : Défini la configuration de votre ordinateur
-> .ci setcountry : Défini votre pays
-> .ci update : Met à jour votre image si vous l'avez changé
-> .ci delete : Supprime votre carte d'identité

View File

@ -1,11 +1,11 @@
_Pour utiliser les horloges utilisez la commande : **clock ** ville_
-> Montreal (Canada, QC)
-> Vancouver (Canada, BC)
-> New-York / N-Y (U.S.A.)
-> LosAngeles / L-A (U.S.A.)
-> Berlin (Allemagne)
-> Bern / Zurich (Suisse)
-> Paris (France)
-> Tokyo (Japon)
-> Moscou (Russie)
_Pour utiliser les horloges utilisez la commande : **clock ** ville_
-> Montreal (Canada, QC)
-> Vancouver (Canada, BC)
-> New-York / N-Y (U.S.A.)
-> LosAngeles / L-A (U.S.A.)
-> Berlin (Allemagne)
-> Bern / Zurich (Suisse)
-> Paris (France)
-> Tokyo (Japon)
-> Moscou (Russie)

View File

@ -1,39 +1,39 @@
**Commandes utilitaires**
-> .afk : Signaler son absence *(commande désactivée)*
-> .back : Signaler son retour *(commande désactivée)*
-> .clock _ville_: Affiche l'heure et quelques infos sur la ville en question
-> .ytdiscover : Découvrir des chaines youtube
-> .search _site_ _contenu_ : Fait une recherche sur un site (.search pour plus d'infos)
-> .avatar _@pseudo_ : Récupère l'avatar de _@pseudo_
-> .poke _@pseudo_ : Poke _@pseudo_
-> .sondage _question_ | _reponse_ | _reponse_ | _option_ : Créer un sondage avec des réactions
-> .sondage : Affiche l'aide pour la commande sondage
-> .role _nomdurole_ : Ajoute/Retire le rôle en question
-> .iplocalise _IP ou NDD_ : affiche la localisation et le propriétaire de l'ip (ou de l'IP lié a un nom de domaine)
-> .iplocalise _IP ou NDD_ **ipv6**: affiche la localisation et le propriétaire de l'ip en forçant sur l'IPv6 (ou de l'IP lié a un nom de domaine)
-> .getheaders _IP ou NDD_ : affiche les en-têtes (headers) d'une IP/Nom de domaine via HTTP/HTTPS/FTP
-> .btcprice : Affiche le prix du bitcoin
[split]
**Commandes Funs**
-> .joke : Affiche une blague aléatoire
-> .ethylotest : Simule un ethylotest détraqué
-> .pokemon : Lance un combat de pokémons
-> .coin : Simule un pile ou face
-> .randomcat : Affiche des image de chats :3
[split]
**Commandes Carte d'Identité**
-> .ci : Affiche l'aide sur les cartes d'identité
-> .ci show _pseudo_ : Affiche la carte d'identité de _pseudo_
-> .ci register : Vous enregistre dans la base de donnée des cartes d'identité
-> .ci setos _nom de l'os_ : Défini le système d'exploitation
-> .ci setconfig _votre configuration pc_ : Défini la configuration de votre ordinateur
-> .ci setcountry : Défini votre pays
-> .ci update : Met à jour votre image si vous l'avez changé :wink:
-> .ci delete : Supprime votre carte d'identité **a tous jamais**
[split]
**Commandes diverses** :
-> .info : Affiche des informations sur le bot
-> .help : Affiche ce message
-> .clock : Affiche la liste des horloges des villes
-> .ping : Ping le bot
-> .git : Affiche le repos Gitea du Bot :heart:
**Commandes utilitaires**
-> .afk : Signaler son absence *(commande désactivée)*
-> .back : Signaler son retour *(commande désactivée)*
-> .clock _ville_: Affiche l'heure et quelques infos sur la ville en question
-> .ytdiscover : Découvrir des chaines youtube
-> .search _site_ _contenu_ : Fait une recherche sur un site (.search pour plus d'infos)
-> .avatar _@pseudo_ : Récupère l'avatar de _@pseudo_
-> .poke _@pseudo_ : Poke _@pseudo_
-> .sondage _question_ | _reponse_ | _reponse_ | _option_ : Créer un sondage avec des réactions
-> .sondage : Affiche l'aide pour la commande sondage
-> .role _nomdurole_ : Ajoute/Retire le rôle en question
-> .iplocalise _IP ou NDD_ : affiche la localisation et le propriétaire de l'ip (ou de l'IP lié a un nom de domaine)
-> .iplocalise _IP ou NDD_ **ipv6**: affiche la localisation et le propriétaire de l'ip en forçant sur l'IPv6 (ou de l'IP lié a un nom de domaine)
-> .getheaders _IP ou NDD_ : affiche les en-têtes (headers) d'une IP/Nom de domaine via HTTP/HTTPS/FTP
-> .btcprice : Affiche le prix du bitcoin
[split]
**Commandes Funs**
-> .joke : Affiche une blague aléatoire
-> .ethylotest : Simule un ethylotest détraqué
-> .pokemon : Lance un combat de pokémons
-> .coin : Simule un pile ou face
-> .randomcat : Affiche des image de chats :3
[split]
**Commandes Carte d'Identité**
-> .ci : Affiche l'aide sur les cartes d'identité
-> .ci show _pseudo_ : Affiche la carte d'identité de _pseudo_
-> .ci register : Vous enregistre dans la base de donnée des cartes d'identité
-> .ci setos _nom de l'os_ : Défini le système d'exploitation
-> .ci setconfig _votre configuration pc_ : Défini la configuration de votre ordinateur
-> .ci setcountry : Défini votre pays
-> .ci update : Met à jour votre image si vous l'avez changé :wink:
-> .ci delete : Supprime votre carte d'identité **a tous jamais**
[split]
**Commandes diverses** :
-> .info : Affiche des informations sur le bot
-> .help : Affiche ce message
-> .clock : Affiche la liste des horloges des villes
-> .ping : Ping le bot
-> .git : Affiche le repos Gitea du Bot :heart:

View File

@ -1,24 +1,24 @@
:tools: **Développement** :
└> Outout : [outout.xyz](https://outout.xyz/)
└> Romain : [son github](http://git.gnous.eu/Romain)
└> Langage : [Python3](http://www.python.org/)
└> Api : [discord.py {3}](https://github.com/Rapptz/discord.py)
└> Discord Api : [{4}]({4})
└> En se basant sur : [RobotDanny](https://github.com/Rapptz/RoboDanny)
:desktop: **Hébergé sur "{2}"**:
└> OS : {0}
└> Version : {1}
:telephone: **Contact** :
└> Discord : Outout#8406
└> Twitter : [@outoutxyz](https://twitter.com/outouxyz)
└> Courriel : [mael@gnous.eu](mailto:mael@gnous.eu)
└> Discord : Romain#5117
:link: **Serveur** :
└> Serveur GnousEU : [rejoindre](https://discord.gg/NFW3EeS)
:tools: **Développement** :
└> Outout : [outout.xyz](https://outout.xyz/)
└> Romain : [son github](http://git.gnous.eu/Romain)
└> Langage : [Python3](http://www.python.org/)
└> Api : [discord.py {3}](https://github.com/Rapptz/discord.py)
└> Discord Api : [{4}]({4})
└> En se basant sur : [RobotDanny](https://github.com/Rapptz/RoboDanny)
:desktop: **Hébergé sur "{2}"**:
└> OS : {0}
└> Version : {1}
:telephone: **Contact** :
└> Discord : Outout#8406
└> Twitter : [@outoutxyz](https://twitter.com/outouxyz)
└> Courriel : [mael@gnous.eu](mailto:mael@gnous.eu)
└> Discord : Romain#5117
:link: **Serveur** :
└> Serveur GnousEU : [rejoindre](https://discord.gg/NFW3EeS)

View File

@ -1,15 +1,15 @@
{
"1": {"content": "Les hyperboles sa sert à manger des hyper-soupes :3 (Lawl!)", "author": "Crumble14 (bukkit.fr)"},
"2": {"content": "Le comble de Windows, cest que pour larrêter, il faut cliquer sur démarrer.", "author": "Keke142 (bukkit.fr)"},
"3": {"content": "Chrome: On est le 8 avril 2016 13h02 \n Safari: On est le 8 avril 2016 13h02 \n Internet Explorer: On est le... **[Internet Explorer a cessé de fonctionner, veuillez redémarrer votre machine]**", "author": "NyoSan"},
"4": {"content": "Il y a 10 types de personnes dans le monde, ceux qui comprennent le binaire et les autres.", "author": "Dartasen (bukkit.fr)"},
"5": {"content": "C'est une requête SQL qui rentre dans un bar et qui s'adresse à deux tables \"Puis-je vous joindre ?\".\"", "author": "Dartasen (bukkit.fr)"},
"6": {"content": "Combien de développeurs faut-il pour remplacer une ampoule grillée ? Aucun, c'est un problème Hardware.", "author": "Dartasen (bukkit.fr)"},
"7": {"content": "Tu sais que tu as affaire à un développeur quand ça ne le gêne pas d'avoir un String dans l'Array.", "author": "Dartasen (bukkit.fr)"},
"8": {"content": "Pourquoi y'a pas d'adresse windows ou linux ? Si y'a l'addresse mac !", "author": "Antho"},
"9": {"content": "Les appareils apple ont ils une adresse personnalisée ?", "author": "Outout"},
"10": {"content": "Le 1er janvier 1970 c'est le jour où il y a eu le plus de plantages. (cf : http://bit.ly/2rArLVe)", "author": "NyoSan"},
"11": {"content": "Pourquoi est-ce que les girafes aiment magasiner à bas prix? Tout est une question de cou.", "author": "Maxx_Qc (bukkit.fr)"},
"12": {"content": "``Même éteint le hackeur peut pirater l'ordi`` \"Le SuperGeek tournant sous Ubuntu (ou Windows)\"", "author": "Outout"},
"13": {"content": "Trois ingénieurs (1 chimiste, 1 électronicien, 1 Microsoft) dans un bus roulant dans un désert. \n\n Le bus « tombe en panne » sans raison apparente, et voila les 3 gars à discuter. \n Lélectronicien : je pourrais regarder les circuits et voir si quelque chose cloche. \n Le chimiste : on devrait vérifier l'essence avant. \n Lingé Microsoft : non, on remonte dans le bus, on ferme toutes les fenêtres, et logiquement ça devrait redémarrer.", "author": "Internet"}
{
"1": {"content": "Les hyperboles sa sert à manger des hyper-soupes :3 (Lawl!)", "author": "Crumble14 (bukkit.fr)"},
"2": {"content": "Le comble de Windows, cest que pour larrêter, il faut cliquer sur démarrer.", "author": "Keke142 (bukkit.fr)"},
"3": {"content": "Chrome: On est le 8 avril 2016 13h02 \n Safari: On est le 8 avril 2016 13h02 \n Internet Explorer: On est le... **[Internet Explorer a cessé de fonctionner, veuillez redémarrer votre machine]**", "author": "NyoSan"},
"4": {"content": "Il y a 10 types de personnes dans le monde, ceux qui comprennent le binaire et les autres.", "author": "Dartasen (bukkit.fr)"},
"5": {"content": "C'est une requête SQL qui rentre dans un bar et qui s'adresse à deux tables \"Puis-je vous joindre ?\".\"", "author": "Dartasen (bukkit.fr)"},
"6": {"content": "Combien de développeurs faut-il pour remplacer une ampoule grillée ? Aucun, c'est un problème Hardware.", "author": "Dartasen (bukkit.fr)"},
"7": {"content": "Tu sais que tu as affaire à un développeur quand ça ne le gêne pas d'avoir un String dans l'Array.", "author": "Dartasen (bukkit.fr)"},
"8": {"content": "Pourquoi y'a pas d'adresse windows ou linux ? Si y'a l'addresse mac !", "author": "Antho"},
"9": {"content": "Les appareils apple ont ils une adresse personnalisée ?", "author": "Outout"},
"10": {"content": "Le 1er janvier 1970 c'est le jour où il y a eu le plus de plantages. (cf : http://bit.ly/2rArLVe)", "author": "NyoSan"},
"11": {"content": "Pourquoi est-ce que les girafes aiment magasiner à bas prix? Tout est une question de cou.", "author": "Maxx_Qc (bukkit.fr)"},
"12": {"content": "``Même éteint le hackeur peut pirater l'ordi`` \"Le SuperGeek tournant sous Ubuntu (ou Windows)\"", "author": "Outout"},
"13": {"content": "Trois ingénieurs (1 chimiste, 1 électronicien, 1 Microsoft) dans un bus roulant dans un désert. \n\n Le bus « tombe en panne » sans raison apparente, et voila les 3 gars à discuter. \n Lélectronicien : je pourrais regarder les circuits et voir si quelque chose cloche. \n Le chimiste : on devrait vérifier l'essence avant. \n Lingé Microsoft : non, on remonte dans le bus, on ferme toutes les fenêtres, et logiquement ça devrait redémarrer.", "author": "Internet"}
}

View File

@ -1,9 +1,9 @@
Le passeport est un petit système dans tuxbot permetant de vous démarquer de vos amis en ayant la possibilité d'y renseigner plusieurs informations !
**Liste des commandes : **
-> .passeport : Affiche l'aide sur les cartes d'identité
-> .passeport show _pseudo_ : Affiche le passeport de _pseudo_
-> .passeport config : Vous envois un message privé afin de configurer votre passeport
-> .passeport background _url_ : Défini _url_ comme étant le fond d'écran de votre passeport
-> .passeport thème <dark/light/preview>: Definie le theme de votre passeport (`.passeport preview` envoi une image avec les 2 thèmes pour comparer)
-> .passeport delete : Supprime les informations de votre carte passeport
Le passeport est un petit système dans tuxbot permetant de vous démarquer de vos amis en ayant la possibilité d'y renseigner plusieurs informations !
**Liste des commandes : **
-> .passeport : Affiche l'aide sur les cartes d'identité
-> .passeport show _pseudo_ : Affiche le passeport de _pseudo_
-> .passeport config : Vous envois un message privé afin de configurer votre passeport
-> .passeport background _url_ : Défini _url_ comme étant le fond d'écran de votre passeport
-> .passeport thème <dark/light/preview>: Definie le theme de votre passeport (`.passeport preview` envoi une image avec les 2 thèmes pour comparer)
-> .passeport delete : Supprime les informations de votre carte passeport

File diff suppressed because one or more lines are too long

View File

@ -1,13 +1,13 @@
Pour améliorer l'expérience utilisateur de tout le monde, vous pouvez spécifier la·les distribution·s que vous utilisez via les rôles Discord.
**Liste des rôles**
└> Arch : pour les utilisateurs de Arch, Manjaro et TuxNVape
└> Debian : pour Debian et ses dérivés (Ubuntu, Kali, etc.)
└> Rhel : pour Red Hat Entreprise Linux et ses dérivés (Fedora, CentOS, etc.)
└> Android : pour Android
└> BSD : pour les systèmes basés sur BSD
**Commandes**
└> Pour ajouter un rôle : ``.role Nomdurole``
Pour améliorer l'expérience utilisateur de tout le monde, vous pouvez spécifier la·les distribution·s que vous utilisez via les rôles Discord.
**Liste des rôles**
└> Arch : pour les utilisateurs de Arch, Manjaro et TuxNVape
└> Debian : pour Debian et ses dérivés (Ubuntu, Kali, etc.)
└> Rhel : pour Red Hat Entreprise Linux et ses dérivés (Fedora, CentOS, etc.)
└> Android : pour Android
└> BSD : pour les systèmes basés sur BSD
**Commandes**
└> Pour ajouter un rôle : ``.role Nomdurole``
└> Pour retirer un rôle : ``.role Nomdurole``

View File

@ -1,14 +1,14 @@
**Créez un sondage avec les réactions !**
**Usage** :
``.sondage <question> | <reponse> | <reponse> | <reponse>`` Vous pouvez utiliser autant de réponses que vous le souhaitez, en plaçant un symbole | entre chaque choix.
**Exemple**:
``.sondage Quelle est votre couleur préférée ? | Rouge | Vert | Bleu | Autre``
**Definir un temps limite** :
Vous pouvez également utiliser l'option "time" pour définir le temps en secondes pendant lequel le sondage durera.
**Exemple**:
``.sondage Utilisez vous twitteur ? | Oui | Non | Pas souvent | time=10``.
**Créez un sondage avec les réactions !**
**Usage** :
``.sondage <question> | <reponse> | <reponse> | <reponse>`` Vous pouvez utiliser autant de réponses que vous le souhaitez, en plaçant un symbole | entre chaque choix.
**Exemple**:
``.sondage Quelle est votre couleur préférée ? | Rouge | Vert | Bleu | Autre``
**Definir un temps limite** :
Vous pouvez également utiliser l'option "time" pour définir le temps en secondes pendant lequel le sondage durera.
**Exemple**:
``.sondage Utilisez vous twitteur ? | Oui | Non | Pas souvent | time=10``.

View File

@ -1,6 +1,6 @@
_Attention ! entrez vos termes de recherche sans espaces !_
Pour effectuer une recherche utilisez la commande ``.search {site_de_recherche} {termes_recherche}``
-> [**docubuntu**](https://doc.ubuntu-fr.org) : Effectuer une recherche sur un paquet dans la Documentation du site ubuntu-fr.org.
-> [**wikipedia**](https://fr.wikipedia.org) : Effectuer une recherche sur l'encyclopédie libre Wikipedia en Français !
-> [**docaur**](https://doc.archlinux.org) : Effectuer une recherche sur la doc ArchLinux !
_Attention ! entrez vos termes de recherche sans espaces !_
Pour effectuer une recherche utilisez la commande ``.search {site_de_recherche} {termes_recherche}``
-> [**docubuntu**](https://doc.ubuntu-fr.org) : Effectuer une recherche sur un paquet dans la Documentation du site ubuntu-fr.org.
-> [**wikipedia**](https://fr.wikipedia.org) : Effectuer une recherche sur l'encyclopédie libre Wikipedia en Français !
-> [**docaur**](https://doc.archlinux.org) : Effectuer une recherche sur la doc ArchLinux !

View File

@ -1,15 +1,15 @@
{
"1": {"name": "KickSama", "desc": "Des dessins annimés sympatiques par un jeune !", "url": "https://www.youtube.com/user/TheKickGuy"},
"2": {"name": "U=RI", "desc": "Des vidéos interessantes sur l'électricité dont des tutoriels !", "url": "https://www.youtube.com/channel/UCVqx3vXNghSqUcVg2nmegYA"},
"3": {"name": "Outout", "desc": "Outout, chaine vraiment nul et peu alimenté par mon créateur...", "url": "https://www.youtube.com/channel/UC2XpYyT5X5tq9UQpXdc1JaQ"},
"4": {"name": "SuperJDay64", "desc": "Des LetsPlay sur Nintendo64 avec beaucoup de plombiers moustachus !", "url": "https://www.youtube.com/channel/UCjkQgODdmhR9I2TatJZtGSQ/"},
"5": {"name": "Monsieur Plouf", "desc": "Vidéos comiques de critiques de jeux AAA avec un décors assez spécial !", "url": "https://www.youtube.com/channel/UCrt_PUTF9LdJyuDfXweHwuQ"},
"6": {"name": "MaxEstLa", "desc": "Petite chaîne bien _sympatique_ sur la réaction de vidéos malsaine ! Très éducative x)", "url": "https://www.youtube.com/channel/UCsk9XguwTfgbenCZ4AlIcYQ"},
"7": {"name": "Met-Hardware", "desc": "Chaine youtube sur l'hardware et des let's play bien sypatique !", "url": "https://www.youtube.com/channel/UC7rse81OttysA1m1yn_f-OA"},
"8": {"name": "ElectronikHeart", "desc": "~~Test de produits de merde ~~ L'informatique sous un angle différent et agréable", "url": "https://www.youtube.com/user/ElectronikHeart"},
"9": {"name": "Caljbeut", "desc": "Cartoon Trash ! Dessins annimés par un ancien de l'armée sur la politique et d'autre sujets ! **On est pas la pour rigoler**", "url": "https://www.youtube.com/channel/UCNM-UkIP1BL5jv9ZrN5JMCA"},
"10": {"name": "Autodisciple", "desc": "Defis, Bitcoins, Geek, la vie quoi ! Sans oublier des défis de 30 Jours !", "url": "https://www.youtube.com/channel/UCDMxcev7u9Nf7KMJuyIm-BA"},
"11": {"name": "CineAstuces", "desc": "Techniques, metiers du cinema, reportages et autres en rapport avec la cinématographie !", "url": "https://www.youtube.com/channel/UC--84qgkrqqqYivuuXuQIQg"},
"12": {"name": "Epic Teaching of the History", "desc": "L'Histoire c'est hyper méga giga ultra _(j'ai pas été payé)_ drôle avec RaAak le renard ! ", "url": "https://www.youtube.com/channel/UCHwd4qMCzN4A2r6piZxTl4A"}
}
{
"1": {"name": "KickSama", "desc": "Des dessins annimés sympatiques par un jeune !", "url": "https://www.youtube.com/user/TheKickGuy"},
"2": {"name": "U=RI", "desc": "Des vidéos interessantes sur l'électricité dont des tutoriels !", "url": "https://www.youtube.com/channel/UCVqx3vXNghSqUcVg2nmegYA"},
"3": {"name": "Outout", "desc": "Outout, chaine vraiment nul et peu alimenté par mon créateur...", "url": "https://www.youtube.com/channel/UC2XpYyT5X5tq9UQpXdc1JaQ"},
"4": {"name": "SuperJDay64", "desc": "Des LetsPlay sur Nintendo64 avec beaucoup de plombiers moustachus !", "url": "https://www.youtube.com/channel/UCjkQgODdmhR9I2TatJZtGSQ/"},
"5": {"name": "Monsieur Plouf", "desc": "Vidéos comiques de critiques de jeux AAA avec un décors assez spécial !", "url": "https://www.youtube.com/channel/UCrt_PUTF9LdJyuDfXweHwuQ"},
"6": {"name": "MaxEstLa", "desc": "Petite chaîne bien _sympatique_ sur la réaction de vidéos malsaine ! Très éducative x)", "url": "https://www.youtube.com/channel/UCsk9XguwTfgbenCZ4AlIcYQ"},
"7": {"name": "Met-Hardware", "desc": "Chaine youtube sur l'hardware et des let's play bien sypatique !", "url": "https://www.youtube.com/channel/UC7rse81OttysA1m1yn_f-OA"},
"8": {"name": "ElectronikHeart", "desc": "~~Test de produits de merde ~~ L'informatique sous un angle différent et agréable", "url": "https://www.youtube.com/user/ElectronikHeart"},
"9": {"name": "Caljbeut", "desc": "Cartoon Trash ! Dessins annimés par un ancien de l'armée sur la politique et d'autre sujets ! **On est pas la pour rigoler**", "url": "https://www.youtube.com/channel/UCNM-UkIP1BL5jv9ZrN5JMCA"},
"10": {"name": "Autodisciple", "desc": "Defis, Bitcoins, Geek, la vie quoi ! Sans oublier des défis de 30 Jours !", "url": "https://www.youtube.com/channel/UCDMxcev7u9Nf7KMJuyIm-BA"},
"11": {"name": "CineAstuces", "desc": "Techniques, metiers du cinema, reportages et autres en rapport avec la cinématographie !", "url": "https://www.youtube.com/channel/UC--84qgkrqqqYivuuXuQIQg"},
"12": {"name": "Epic Teaching of the History", "desc": "L'Histoire c'est hyper méga giga ultra _(j'ai pas été payé)_ drôle avec RaAak le renard ! ", "url": "https://www.youtube.com/channel/UCHwd4qMCzN4A2r6piZxTl4A"}
}